diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-03-08 17:14:41 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-03-13 15:29:20 -0400 |
commit | 769fa67d842035dd852ab8b6a26ea3f110a51131 (patch) | |
tree | 5c121caca336071091c6f5ea4c54743c92d6458a /lib/sqlalchemy/sql/coercions.py | |
parent | 77fc8216a74e6b2d0efc6591c6c735687bd10002 (diff) | |
download | sqlalchemy-769fa67d842035dd852ab8b6a26ea3f110a51131.tar.gz |
pep-484: sqlalchemy.sql pass one
sqlalchemy.sql will require many passes to get all
modules even gradually typed. Will have to pick and
choose what modules can be strictly typed vs. which
can be gradual.
in this patch, emphasis is on visitors.py, cache_key.py,
annotations.py for strict typing, compiler.py is on gradual
typing but has much more structure, in particular where it
connects with the outside world.
The work within compiler.py also reached back out to
engine/cursor.py , default.py quite a bit.
References: #6810
Change-Id: I6e8a29f6013fd216e43d45091bc193f8be0368fd
Diffstat (limited to 'lib/sqlalchemy/sql/coercions.py')
-rw-r--r-- | lib/sqlalchemy/sql/coercions.py | 235 |
1 files changed, 154 insertions, 81 deletions
diff --git a/lib/sqlalchemy/sql/coercions.py b/lib/sqlalchemy/sql/coercions.py index d616417ab..834bfb75d 100644 --- a/lib/sqlalchemy/sql/coercions.py +++ b/lib/sqlalchemy/sql/coercions.py @@ -13,6 +13,9 @@ import re import typing from typing import Any from typing import Any as TODO_Any +from typing import Dict +from typing import List +from typing import NoReturn from typing import Optional from typing import Type from typing import TypeVar @@ -42,6 +45,7 @@ if typing.TYPE_CHECKING: from . import selectable from . import traversals from .elements import ClauseElement + from .elements import ColumnClause _SR = TypeVar("_SR", bound=roles.SQLRole) _StringOnlyR = TypeVar("_StringOnlyR", bound=roles.StringRole) @@ -252,7 +256,7 @@ def expect_col_expression_collection(role, expressions): if isinstance(resolved, str): strname = resolved = expr else: - cols = [] + cols: List[ColumnClause[Any]] = [] visitors.traverse(resolved, {}, {"column": cols.append}) if cols: column = cols[0] @@ -266,7 +270,7 @@ class RoleImpl: def _literal_coercion(self, element, **kw): raise NotImplementedError() - _post_coercion = None + _post_coercion: Any = None _resolve_literal_only = False _skip_clauseelement_for_target_match = False @@ -276,19 +280,24 @@ class RoleImpl: self._use_inspection = issubclass(role_class, roles.UsesInspection) def _implicit_coercions( - self, element, resolved, argname=None, **kw + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, ) -> Any: self._raise_for_expected(element, argname, resolved) def _raise_for_expected( self, - element, - argname=None, - resolved=None, - advice=None, - code=None, - err=None, - ): + element: Any, + argname: Optional[str] = None, + resolved: Optional[Any] = None, + advice: Optional[str] = None, + code: Optional[str] = None, + err: Optional[Exception] = None, + **kw: Any, + ) -> NoReturn: if resolved is not None and resolved is not element: got = "%r object resolved from %r object" % (resolved, element) else: @@ -324,22 +333,20 @@ class _StringOnly: _resolve_literal_only = True -class _ReturnsStringKey: +class _ReturnsStringKey(RoleImpl): __slots__ = () - def _implicit_coercions( - self, original_element, resolved, argname=None, **kw - ): - if isinstance(original_element, str): - return original_element + def _implicit_coercions(self, element, resolved, argname=None, **kw): + if isinstance(element, str): + return element else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) def _literal_coercion(self, element, **kw): return element -class _ColumnCoercions: +class _ColumnCoercions(RoleImpl): __slots__ = () def _warn_for_scalar_subquery_coercion(self): @@ -368,8 +375,12 @@ class _ColumnCoercions: def _no_text_coercion( - element, argname=None, exc_cls=exc.ArgumentError, extra=None, err=None -): + element: Any, + argname: Optional[str] = None, + exc_cls: Type[exc.SQLAlchemyError] = exc.ArgumentError, + extra: Optional[str] = None, + err: Optional[Exception] = None, +) -> NoReturn: raise exc_cls( "%(extra)sTextual SQL expression %(expr)r %(argname)sshould be " "explicitly declared as text(%(expr)r)" @@ -381,7 +392,7 @@ def _no_text_coercion( ) from err -class _NoTextCoercion: +class _NoTextCoercion(RoleImpl): __slots__ = () def _literal_coercion(self, element, argname=None, **kw): @@ -393,7 +404,7 @@ class _NoTextCoercion: self._raise_for_expected(element, argname) -class _CoerceLiterals: +class _CoerceLiterals(RoleImpl): __slots__ = () _coerce_consts = False _coerce_star = False @@ -440,12 +451,19 @@ class LiteralValueImpl(RoleImpl): return element -class _SelectIsNotFrom: +class _SelectIsNotFrom(RoleImpl): __slots__ = () def _raise_for_expected( - self, element, argname=None, resolved=None, advice=None, **kw - ): + self, + element: Any, + argname: Optional[str] = None, + resolved: Optional[Any] = None, + advice: Optional[str] = None, + code: Optional[str] = None, + err: Optional[Exception] = None, + **kw: Any, + ) -> NoReturn: if ( not advice and isinstance(element, roles.SelectStatementRole) @@ -460,26 +478,33 @@ class _SelectIsNotFrom: else: code = None - return super(_SelectIsNotFrom, self)._raise_for_expected( + super()._raise_for_expected( element, argname=argname, resolved=resolved, advice=advice, code=code, + err=err, **kw, ) + # never reached + assert False class HasCacheKeyImpl(RoleImpl): __slots__ = () def _implicit_coercions( - self, original_element, resolved, argname=None, **kw - ): - if isinstance(original_element, traversals.HasCacheKey): - return original_element + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, + ) -> Any: + if isinstance(element, HasCacheKey): + return element else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) def _literal_coercion(self, element, **kw): return element @@ -489,12 +514,16 @@ class ExecutableOptionImpl(RoleImpl): __slots__ = () def _implicit_coercions( - self, original_element, resolved, argname=None, **kw - ): - if isinstance(original_element, ExecutableOption): - return original_element + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, + ) -> Any: + if isinstance(element, ExecutableOption): + return element else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) def _literal_coercion(self, element, **kw): return element @@ -560,8 +589,12 @@ class InElementImpl(RoleImpl): __slots__ = () def _implicit_coercions( - self, original_element, resolved, argname=None, **kw - ): + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, + ) -> Any: if resolved._is_from_clause: if ( isinstance(resolved, selectable.Alias) @@ -573,7 +606,7 @@ class InElementImpl(RoleImpl): self._warn_for_implicit_coercion(resolved) return self._post_coercion(resolved.select(), **kw) else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) def _warn_for_implicit_coercion(self, elem): util.warn( @@ -586,12 +619,16 @@ class InElementImpl(RoleImpl): if isinstance(element, collections_abc.Iterable) and not isinstance( element, str ): - non_literal_expressions = {} + non_literal_expressions: Dict[ + Optional[operators.ColumnOperators[Any]], + operators.ColumnOperators[Any], + ] = {} element = list(element) for o in element: if not _is_literal(o): if not isinstance(o, operators.ColumnOperators): self._raise_for_expected(element, **kw) + else: non_literal_expressions[o] = o elif o is None: @@ -712,8 +749,12 @@ class GroupByImpl(ByOfImpl, RoleImpl): __slots__ = () def _implicit_coercions( - self, original_element, resolved, argname=None, **kw - ): + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, + ) -> Any: if isinstance(resolved, roles.StrictFromClauseRole): return elements.ClauseList(*resolved.c) else: @@ -748,12 +789,16 @@ class TruncatedLabelImpl(_StringOnly, RoleImpl): __slots__ = () def _implicit_coercions( - self, original_element, resolved, argname=None, **kw - ): - if isinstance(original_element, str): + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, + ) -> Any: + if isinstance(element, str): return resolved else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) def _literal_coercion(self, element, argname=None, **kw): """coerce the given value to :class:`._truncated_label`. @@ -794,7 +839,13 @@ class DDLReferredColumnImpl(DDLConstraintColumnImpl): class LimitOffsetImpl(RoleImpl): __slots__ = () - def _implicit_coercions(self, element, resolved, argname=None, **kw): + def _implicit_coercions( + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, + ) -> Any: if resolved is None: return None else: @@ -814,18 +865,22 @@ class LabeledColumnExprImpl(ExpressionElementImpl): __slots__ = () def _implicit_coercions( - self, original_element, resolved, argname=None, **kw - ): + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, + ) -> Any: if isinstance(resolved, roles.ExpressionElementRole): return resolved.label(None) else: new = super(LabeledColumnExprImpl, self)._implicit_coercions( - original_element, resolved, argname=argname, **kw + element, resolved, argname=argname, **kw ) if isinstance(new, roles.ExpressionElementRole): return new.label(None) else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) class ColumnsClauseImpl(_SelectIsNotFrom, _CoerceLiterals, RoleImpl): @@ -899,13 +954,17 @@ class StatementImpl(_CoerceLiterals, RoleImpl): return resolved def _implicit_coercions( - self, original_element, resolved, argname=None, **kw - ): + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, + ) -> Any: if resolved._is_lambda_element: return resolved else: - return super(StatementImpl, self)._implicit_coercions( - original_element, resolved, argname=argname, **kw + return super()._implicit_coercions( + element, resolved, argname=argname, **kw ) @@ -913,12 +972,16 @@ class SelectStatementImpl(_NoTextCoercion, RoleImpl): __slots__ = () def _implicit_coercions( - self, original_element, resolved, argname=None, **kw - ): + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, + ) -> Any: if resolved._is_text_clause: return resolved.columns() else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) class HasCTEImpl(ReturnsRowsImpl): @@ -938,13 +1001,18 @@ class JoinTargetImpl(RoleImpl): self._raise_for_expected(element, argname) def _implicit_coercions( - self, original_element, resolved, argname=None, legacy=False, **kw - ): - if isinstance(original_element, roles.JoinTargetRole): + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + legacy: bool = False, + **kw: Any, + ) -> Any: + if isinstance(element, roles.JoinTargetRole): # note that this codepath no longer occurs as of # #6550, unless JoinTargetImpl._skip_clauseelement_for_target_match # were set to False. - return original_element + return element elif legacy and resolved._is_select_statement: util.warn_deprecated( "Implicit coercion of SELECT and textual SELECT " @@ -959,7 +1027,7 @@ class JoinTargetImpl(RoleImpl): # in _ORMJoin->Join return resolved else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) class FromClauseImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl): @@ -967,13 +1035,13 @@ class FromClauseImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl): def _implicit_coercions( self, - original_element, - resolved, - argname=None, - explicit_subquery=False, - allow_select=True, - **kw, - ): + element: Any, + resolved: Any, + argname: Optional[str] = None, + explicit_subquery: bool = False, + allow_select: bool = True, + **kw: Any, + ) -> Any: if resolved._is_select_statement: if explicit_subquery: return resolved.subquery() @@ -989,7 +1057,7 @@ class FromClauseImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl): elif resolved._is_text_clause: return resolved else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) def _post_coercion(self, element, deannotate=False, **kw): if deannotate: @@ -1003,12 +1071,13 @@ class StrictFromClauseImpl(FromClauseImpl): def _implicit_coercions( self, - original_element, - resolved, - argname=None, - allow_select=False, - **kw, - ): + element: Any, + resolved: Any, + argname: Optional[str] = None, + explicit_subquery: bool = False, + allow_select: bool = False, + **kw: Any, + ) -> Any: if resolved._is_select_statement and allow_select: util.warn_deprecated( "Implicit coercion of SELECT and textual SELECT constructs " @@ -1019,7 +1088,7 @@ class StrictFromClauseImpl(FromClauseImpl): ) return resolved._implicit_subquery else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) class AnonymizedFromClauseImpl(StrictFromClauseImpl): @@ -1045,8 +1114,12 @@ class DMLSelectImpl(_NoTextCoercion, RoleImpl): __slots__ = () def _implicit_coercions( - self, original_element, resolved, argname=None, **kw - ): + self, + element: Any, + resolved: Any, + argname: Optional[str] = None, + **kw: Any, + ) -> Any: if resolved._is_from_clause: if ( isinstance(resolved, selectable.Alias) @@ -1056,7 +1129,7 @@ class DMLSelectImpl(_NoTextCoercion, RoleImpl): else: return resolved.select() else: - self._raise_for_expected(original_element, argname, resolved) + self._raise_for_expected(element, argname, resolved) class CompoundElementImpl(_NoTextCoercion, RoleImpl): |