]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
use fully qualified, locatable names for all use of api.named_type()
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 22 Dec 2021 21:21:33 +0000 (16:21 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 22 Dec 2021 21:22:49 +0000 (16:22 -0500)
Fixed mypy regression where the release of mypy 0.930 added additional
internal checks to the format of "named types", requiring that they be
fully qualified and locatable. This broke the mypy plugin for SQLAlchemy,
raising an assertion error, as there was use of symbols such as
``__builtins__`` and other un-locatable or unqualified names that
previously had not raised any assertions.

Fixes: #7496
Change-Id: I037680606a1d51158ef6503508ec76c5d5adc946

doc/build/changelog/unreleased_14/7496.rst [new file with mode: 0644]
lib/sqlalchemy/ext/mypy/apply.py
lib/sqlalchemy/ext/mypy/decl_class.py
lib/sqlalchemy/ext/mypy/infer.py
lib/sqlalchemy/ext/mypy/names.py
lib/sqlalchemy/ext/mypy/plugin.py

diff --git a/doc/build/changelog/unreleased_14/7496.rst b/doc/build/changelog/unreleased_14/7496.rst
new file mode 100644 (file)
index 0000000..cc5875f
--- /dev/null
@@ -0,0 +1,11 @@
+.. change::
+    :tags: bug, mypy
+    :tickets: 7496
+
+    Fixed mypy regression where the release of mypy 0.930 added additional
+    internal checks to the format of "named types", requiring that they be
+    fully qualified and locatable. This broke the mypy plugin for SQLAlchemy,
+    raising an assertion error, as there was use of symbols such as
+    ``__builtins__`` and other un-locatable or unqualified names that
+    previously had not raised any assertions.
+
index cf5b4fda257ec42eb9fb643f6e2f12f97bbec17b..b3af0560c60ac658460a67e2a52e65563a44ba4a 100644 (file)
@@ -36,6 +36,7 @@ from mypy.types import UnionType
 
 from . import infer
 from . import util
+from .names import NAMED_TYPE_SQLA_MAPPED
 
 
 def apply_mypy_mapped_attr(
@@ -134,7 +135,7 @@ def re_apply_declarative_assignments(
                     and isinstance(stmt.rvalue.callee.expr, NameExpr)
                     and stmt.rvalue.callee.expr.node is not None
                     and stmt.rvalue.callee.expr.node.fullname
-                    == "sqlalchemy.orm.attributes.Mapped"
+                    == NAMED_TYPE_SQLA_MAPPED
                     and stmt.rvalue.callee.name == "_empty_constructor"
                     and isinstance(stmt.rvalue.args[0], CallExpr)
                     and isinstance(stmt.rvalue.args[0].callee, RefExpr)
@@ -165,7 +166,7 @@ def re_apply_declarative_assignments(
 
             if python_type_for_type is not None:
                 left_node.type = api.named_type(
-                    "__sa_Mapped", [python_type_for_type]
+                    NAMED_TYPE_SQLA_MAPPED, [python_type_for_type]
                 )
 
     if update_cls_metadata:
@@ -202,12 +203,12 @@ def apply_type_to_mapped_statement(
 
     if left_hand_explicit_type is not None:
         left_node.type = api.named_type(
-            "__sa_Mapped", [left_hand_explicit_type]
+            NAMED_TYPE_SQLA_MAPPED, [left_hand_explicit_type]
         )
     else:
         lvalue.is_inferred_def = False
         left_node.type = api.named_type(
-            "__sa_Mapped",
+            NAMED_TYPE_SQLA_MAPPED,
             [] if python_type_for_type is None else [python_type_for_type],
         )
 
index 0d7462d5bde0dd0b17ca8b93ffee9abda9b4a8ed..c33c30e2574410a639ee49bbfbbe512c51f9c05e 100644 (file)
@@ -327,7 +327,7 @@ def _scan_declarative_decorator_stmt(
         )
 
     left_node.node.type = api.named_type(
-        "__sa_Mapped", [left_hand_explicit_type]
+        names.NAMED_TYPE_SQLA_MAPPED, [left_hand_explicit_type]
     )
 
     # this will ignore the rvalue entirely
index 6d243b6ec1d9445df335915610aa880cfea825b6..3cd946e04d03747718d352e141e91f02967a9237 100644 (file)
@@ -147,7 +147,7 @@ def _infer_type_from_relationship(
         type_is_a_collection = True
         if python_type_for_type is not None:
             python_type_for_type = api.named_type(
-                "__builtins__.list", [python_type_for_type]
+                names.NAMED_TYPE_BUILTINS_LIST, [python_type_for_type]
             )
     elif (
         uselist_arg is None or api.parse_bool(uselist_arg) is True
@@ -438,7 +438,7 @@ def _infer_type_from_left_and_inferred_right(
 
     if not is_subtype(left_hand_explicit_type, python_type_for_type):
         effective_type = api.named_type(
-            "__sa_Mapped", [orig_python_type_for_type]
+            names.NAMED_TYPE_SQLA_MAPPED, [orig_python_type_for_type]
         )
 
         msg = (
@@ -507,7 +507,9 @@ def infer_type_from_left_hand_type_only(
         )
         util.fail(api, msg.format(node.name), node)
 
-        return api.named_type("__sa_Mapped", [AnyType(TypeOfAny.special_form)])
+        return api.named_type(
+            names.NAMED_TYPE_SQLA_MAPPED, [AnyType(TypeOfAny.special_form)]
+        )
 
     else:
         # use type from the left hand side
@@ -529,7 +531,7 @@ def extract_python_type_from_typeengine(
                     return Instance(first_arg.node, [])
             # TODO: support other pep-435 types here
         else:
-            return api.named_type("__builtins__.str", [])
+            return api.named_type(names.NAMED_TYPE_BUILTINS_STR, [])
 
     assert node.has_base("sqlalchemy.sql.type_api.TypeEngine"), (
         "could not extract Python type from node: %s" % node
index 3dbfcc770328268a4c493a5e662e3c15138e0279..8ec15a6d43aa526b0170ac71972c404461022145 100644 (file)
@@ -47,6 +47,12 @@ AS_DECLARATIVE_BASE: int = util.symbol("AS_DECLARATIVE_BASE")  # type: ignore
 DECLARATIVE_MIXIN: int = util.symbol("DECLARATIVE_MIXIN")  # type: ignore
 QUERY_EXPRESSION: int = util.symbol("QUERY_EXPRESSION")  # type: ignore
 
+# names that must succeed with mypy.api.named_type
+NAMED_TYPE_BUILTINS_OBJECT = "builtins.object"
+NAMED_TYPE_BUILTINS_STR = "builtins.str"
+NAMED_TYPE_BUILTINS_LIST = "builtins.list"
+NAMED_TYPE_SQLA_MAPPED = "sqlalchemy.orm.attributes.Mapped"
+
 _lookup: Dict[str, Tuple[int, Set[str]]] = {
     "Column": (
         COLUMN,
index 356b0d9489eb2066e17b98988378389925903702..8687012a1e45e874a3ac0c02236b14799c44b071 100644 (file)
@@ -142,7 +142,7 @@ def _dynamic_class_hook(ctx: DynamicClassDefContext) -> None:
         )
         info.bases = [Instance(cls_arg.node, [])]
     else:
-        obj = ctx.api.named_type("__builtins__.object")
+        obj = ctx.api.named_type(names.NAMED_TYPE_BUILTINS_OBJECT)
 
         info.bases = [obj]
 
@@ -152,7 +152,7 @@ def _dynamic_class_hook(ctx: DynamicClassDefContext) -> None:
         util.fail(
             ctx.api, "Not able to calculate MRO for declarative base", ctx.call
         )
-        obj = ctx.api.named_type("__builtins__.object")
+        obj = ctx.api.named_type(names.NAMED_TYPE_BUILTINS_OBJECT)
         info.bases = [obj]
         info.fallback_to_any = True