summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/coercions.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2022-03-08 17:14:41 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2022-03-13 15:29:20 -0400
commit769fa67d842035dd852ab8b6a26ea3f110a51131 (patch)
tree5c121caca336071091c6f5ea4c54743c92d6458a /lib/sqlalchemy/sql/coercions.py
parent77fc8216a74e6b2d0efc6591c6c735687bd10002 (diff)
downloadsqlalchemy-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.py235
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):