]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
update typing for mypy 1.11; pin plugin to <1.11
authorFederico Caselli <cfederico87@gmail.com>
Mon, 22 Jul 2024 21:17:45 +0000 (23:17 +0200)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 27 Jul 2024 16:20:32 +0000 (12:20 -0400)
Fixed internal typing issues to establish compatibility with mypy 1.11.0.
Note that this does not include issues which have arisen with the
deprecated mypy plugin used by SQLAlchemy 1.4-style code; see the addiional
change note for this plugin indicating revised compatibility.

The legacy mypy plugin is no longer fully functional with the latest series
of mypy 1.11.0, as changes in the mypy interpreter are no longer compatible
with the approach used by the plugin.  If code is dependent on the legacy
mypy plugin with sqlalchemy2-stubs, it's recommended to pin mypy to be
below the 1.11.0 series.    Seek upgrading to the 2.0 series of SQLAlchemy
and migrating to the modern type annotations.

Change-Id: Ib8fef93ede588430dc0f7ed44ef887649a415821

19 files changed:
doc/build/changelog/unreleased_14/mypy1110.rst [new file with mode: 0644]
doc/build/changelog/unreleased_20/mypy1110.rst [new file with mode: 0644]
doc/build/orm/extensions/mypy.rst
lib/sqlalchemy/engine/interfaces.py
lib/sqlalchemy/ext/mypy/util.py
lib/sqlalchemy/orm/descriptor_props.py
lib/sqlalchemy/orm/mapped_collection.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/util.py
lib/sqlalchemy/sql/base.py
lib/sqlalchemy/sql/coercions.py
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/crud.py
lib/sqlalchemy/sql/elements.py
lib/sqlalchemy/sql/sqltypes.py
lib/sqlalchemy/util/compat.py
lib/sqlalchemy/util/langhelpers.py
test/ext/mypy/test_mypy_plugin_py3k.py
tox.ini

diff --git a/doc/build/changelog/unreleased_14/mypy1110.rst b/doc/build/changelog/unreleased_14/mypy1110.rst
new file mode 100644 (file)
index 0000000..1dc5e0d
--- /dev/null
@@ -0,0 +1,14 @@
+.. change::
+    :tags: bug, mypy
+    :versions: 2.0
+
+    The deprecated mypy plugin is no longer fully functional with the latest
+    series of mypy 1.11.0, as changes in the mypy interpreter are no longer
+    compatible with the approach used by the plugin.  If code is dependent on
+    the mypy plugin with sqlalchemy2-stubs, it's recommended to pin mypy to be
+    below the 1.11.0 series.    Seek upgrading to the 2.0 series of SQLAlchemy
+    and migrating to the modern type annotations.
+
+    .. seealso::
+
+        :ref:`mypy_toplevel`
diff --git a/doc/build/changelog/unreleased_20/mypy1110.rst b/doc/build/changelog/unreleased_20/mypy1110.rst
new file mode 100644 (file)
index 0000000..f722c40
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: bug, mypy
+
+    Fixed internal typing issues to establish compatibility with mypy 1.11.0.
+    Note that this does not include issues which have arisen with the
+    deprecated mypy plugin used by SQLAlchemy 1.4-style code; see the addiional
+    change note for this plugin indicating revised compatibility.
index afd34929af628803972fba21aa8b83c362f323b7..dbca3f35f91bf9ec8dba7c6c19ff8ba1c596bd13 100644 (file)
@@ -13,7 +13,8 @@ the :func:`_orm.mapped_column` construct introduced in SQLAlchemy 2.0.
 
     **The SQLAlchemy Mypy Plugin is DEPRECATED, and will be removed possibly
     as early as the SQLAlchemy 2.1 release.  We would urge users to please
-    migrate away from it ASAP.**
+    migrate away from it ASAP.   The mypy plugin also works only up until
+    mypy version 1.10.1.    version 1.11.0 and greater may not work properly.**
 
     This plugin cannot be maintained across constantly changing releases
     of mypy and its stability going forward CANNOT be guaranteed.
@@ -24,7 +25,11 @@ the :func:`_orm.mapped_column` construct introduced in SQLAlchemy 2.0.
 
 .. topic:: SQLAlchemy Mypy Plugin Status Update
 
-   **Updated July 2023**
+   **Updated July 2024**
+
+   The mypy plugin is supported **only up until mypy 1.10.1, and it will have
+   issues running with 1.11.0 or greater**.   Use with mypy 1.11.0 or greater
+   may have error conditions which currently cannot be resolved.
 
    For SQLAlchemy 2.0, the Mypy plugin continues to work at the level at which
    it reached in the SQLAlchemy 1.4 release.  SQLAlchemy 2.0 however features
index dc793d0ec8bdde3172a9805e9bda7c591e9321ca..58d79cdd94f009419ba62e676b385ca9a4c2af80 100644 (file)
@@ -1254,8 +1254,7 @@ class Dialect(EventTarget):
         """
         raise NotImplementedError()
 
-    @classmethod
-    def type_descriptor(cls, typeobj: TypeEngine[_T]) -> TypeEngine[_T]:
+    def type_descriptor(self, typeobj: TypeEngine[_T]) -> TypeEngine[_T]:
         """Transform a generic type to a dialect-specific type.
 
         Dialect classes will usually use the
@@ -1317,6 +1316,7 @@ class Dialect(EventTarget):
     def get_multi_columns(
         self,
         connection: Connection,
+        *,
         schema: Optional[str] = None,
         filter_names: Optional[Collection[str]] = None,
         **kw: Any,
@@ -1365,6 +1365,7 @@ class Dialect(EventTarget):
     def get_multi_pk_constraint(
         self,
         connection: Connection,
+        *,
         schema: Optional[str] = None,
         filter_names: Optional[Collection[str]] = None,
         **kw: Any,
@@ -1411,6 +1412,7 @@ class Dialect(EventTarget):
     def get_multi_foreign_keys(
         self,
         connection: Connection,
+        *,
         schema: Optional[str] = None,
         filter_names: Optional[Collection[str]] = None,
         **kw: Any,
@@ -1570,6 +1572,7 @@ class Dialect(EventTarget):
     def get_multi_indexes(
         self,
         connection: Connection,
+        *,
         schema: Optional[str] = None,
         filter_names: Optional[Collection[str]] = None,
         **kw: Any,
@@ -1616,6 +1619,7 @@ class Dialect(EventTarget):
     def get_multi_unique_constraints(
         self,
         connection: Connection,
+        *,
         schema: Optional[str] = None,
         filter_names: Optional[Collection[str]] = None,
         **kw: Any,
@@ -1663,6 +1667,7 @@ class Dialect(EventTarget):
     def get_multi_check_constraints(
         self,
         connection: Connection,
+        *,
         schema: Optional[str] = None,
         filter_names: Optional[Collection[str]] = None,
         **kw: Any,
@@ -1705,6 +1710,7 @@ class Dialect(EventTarget):
     def get_multi_table_options(
         self,
         connection: Connection,
+        *,
         schema: Optional[str] = None,
         filter_names: Optional[Collection[str]] = None,
         **kw: Any,
@@ -1756,6 +1762,7 @@ class Dialect(EventTarget):
     def get_multi_table_comment(
         self,
         connection: Connection,
+        *,
         schema: Optional[str] = None,
         filter_names: Optional[Collection[str]] = None,
         **kw: Any,
index 7f04c481d3474f271e86363e47fb6711a22e6e62..af0882bc3075006f1f9fdf9964a938b3ec4fde8e 100644 (file)
@@ -80,7 +80,7 @@ class SQLAlchemyAttribute:
             "name": self.name,
             "line": self.line,
             "column": self.column,
-            "type": self.type.serialize(),
+            "type": serialize_type(self.type),
         }
 
     def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None:
@@ -336,3 +336,22 @@ def info_for_cls(
         return sym.node
 
     return cls.info
+
+
+def serialize_type(typ: Type) -> Union[str, JsonDict]:
+    try:
+        return typ.serialize()
+    except Exception:
+        pass
+    if hasattr(typ, "args"):
+        typ.args = tuple(
+            (
+                a.resolve_string_annotation()
+                if hasattr(a, "resolve_string_annotation")
+                else a
+            )
+            for a in typ.args
+        )
+    elif hasattr(typ, "resolve_string_annotation"):
+        typ = typ.resolve_string_annotation()
+    return typ.serialize()
index d82a33d0a3c7fb78b1a9da052dabe8325a78f766..b43824e2ef0001dc0e6feab7b2d13839bf7328bc 100644 (file)
@@ -784,7 +784,9 @@ class CompositeProperty(
             elif isinstance(self.prop.composite_class, type) and isinstance(
                 value, self.prop.composite_class
             ):
-                values = self.prop._composite_values_from_instance(value)
+                values = self.prop._composite_values_from_instance(
+                    value  # type: ignore[arg-type]
+                )
             else:
                 raise sa_exc.ArgumentError(
                     "Can't UPDATE composite attribute %s to %r"
index 13c6b689e1d2e6026af9d1540d1fccb9ae537deb..0d3079fb5ab9d0b8f25ee41b75e475a9b1e451c1 100644 (file)
@@ -29,6 +29,8 @@ from .. import util
 from ..sql import coercions
 from ..sql import expression
 from ..sql import roles
+from ..util.langhelpers import Missing
+from ..util.langhelpers import MissingOr
 from ..util.typing import Literal
 
 if TYPE_CHECKING:
@@ -40,8 +42,6 @@ if TYPE_CHECKING:
 _KT = TypeVar("_KT", bound=Any)
 _VT = TypeVar("_VT", bound=Any)
 
-_F = TypeVar("_F", bound=Callable[[Any], Any])
-
 
 class _PlainColumnGetter(Generic[_KT]):
     """Plain column getter, stores collection of Column objects
@@ -70,7 +70,7 @@ class _PlainColumnGetter(Generic[_KT]):
     def _cols(self, mapper: Mapper[_KT]) -> Sequence[ColumnElement[_KT]]:
         return self.cols
 
-    def __call__(self, value: _KT) -> Union[_KT, Tuple[_KT, ...]]:
+    def __call__(self, value: _KT) -> MissingOr[Union[_KT, Tuple[_KT, ...]]]:
         state = base.instance_state(value)
         m = base._state_mapper(state)
 
@@ -83,7 +83,7 @@ class _PlainColumnGetter(Generic[_KT]):
         else:
             obj = key[0]
             if obj is None:
-                return _UNMAPPED_AMBIGUOUS_NONE
+                return Missing
             else:
                 return obj
 
@@ -198,9 +198,6 @@ def column_keyed_dict(
     )
 
 
-_UNMAPPED_AMBIGUOUS_NONE = object()
-
-
 class _AttrGetter:
     __slots__ = ("attr_name", "getter")
 
@@ -217,9 +214,9 @@ class _AttrGetter:
                 dict_ = state.dict
                 obj = dict_.get(self.attr_name, base.NO_VALUE)
                 if obj is None:
-                    return _UNMAPPED_AMBIGUOUS_NONE
+                    return Missing
             else:
-                return _UNMAPPED_AMBIGUOUS_NONE
+                return Missing
 
         return obj
 
@@ -277,7 +274,7 @@ def attribute_keyed_dict(
 
 
 def keyfunc_mapping(
-    keyfunc: _F,
+    keyfunc: Callable[[Any], Any],
     *,
     ignore_unpopulated_attribute: bool = False,
 ) -> Type[KeyFuncDict[_KT, Any]]:
@@ -353,7 +350,7 @@ class KeyFuncDict(Dict[_KT, _VT]):
 
     def __init__(
         self,
-        keyfunc: _F,
+        keyfunc: Callable[[Any], Any],
         *dict_args: Any,
         ignore_unpopulated_attribute: bool = False,
     ) -> None:
@@ -377,7 +374,7 @@ class KeyFuncDict(Dict[_KT, _VT]):
     @classmethod
     def _unreduce(
         cls,
-        keyfunc: _F,
+        keyfunc: Callable[[Any], Any],
         values: Dict[_KT, _KT],
         adapter: Optional[CollectionAdapter] = None,
     ) -> "KeyFuncDict[_KT, _KT]":
@@ -464,7 +461,7 @@ class KeyFuncDict(Dict[_KT, _VT]):
                 )
             else:
                 return
-        elif key is _UNMAPPED_AMBIGUOUS_NONE:
+        elif key is Missing:
             if not self.ignore_unpopulated_attribute:
                 self._raise_for_unpopulated(
                     value, _sa_initiator, warn_only=True
@@ -492,7 +489,7 @@ class KeyFuncDict(Dict[_KT, _VT]):
                     value, _sa_initiator, warn_only=False
                 )
             return
-        elif key is _UNMAPPED_AMBIGUOUS_NONE:
+        elif key is Missing:
             if not self.ignore_unpopulated_attribute:
                 self._raise_for_unpopulated(
                     value, _sa_initiator, warn_only=True
@@ -514,7 +511,7 @@ class KeyFuncDict(Dict[_KT, _VT]):
 
 
 def _mapped_collection_cls(
-    keyfunc: _F, ignore_unpopulated_attribute: bool
+    keyfunc: Callable[[Any], Any], ignore_unpopulated_attribute: bool
 ) -> Type[KeyFuncDict[_KT, _KT]]:
     class _MKeyfuncMapped(KeyFuncDict[_KT, _KT]):
         def __init__(self, *dict_args: Any) -> None:
index b535b9db2d2f611c45f6b7e56d3aaab0c892adf4..88b4862e47b26244a828c48a4a6ab51429c088cf 100644 (file)
@@ -745,7 +745,7 @@ class Query(
         )
 
     @overload
-    def as_scalar(
+    def as_scalar(  # type: ignore[overload-overlap]
         self: Query[Tuple[_MAYBE_ENTITY]],
     ) -> ScalarSelect[_MAYBE_ENTITY]: ...
 
index 957b934dda698aa958f96a40efa79df19f88c531..d4dff11e454ebcbfa4d3c52c56a1479d4d5368fb 100644 (file)
@@ -1683,7 +1683,7 @@ class Bundle(
     c: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]
     """An alias for :attr:`.Bundle.columns`."""
 
-    def _clone(self):
+    def _clone(self, **kw):
         cloned = self.__class__.__new__(self.__class__)
         cloned.__dict__.update(self.__dict__)
         return cloned
index dcb00e16a52b63e3c555627fd202b896cbce6e8e..970d0dd754f5d477e7944767fe1b4a7a17fc35d4 100644 (file)
@@ -2137,7 +2137,7 @@ class ColumnSet(util.OrderedSet["ColumnClause[Any]"]):
                     l.append(c == local)
         return elements.and_(*l)
 
-    def __hash__(self):
+    def __hash__(self):  # type: ignore[override]
         return hash(tuple(x for x in self))
 
 
index 22d6091552233bf6e2a077f8783f676997008e2b..0c998c667f22483b35d4187a920b50f035ecb7bc 100644 (file)
@@ -493,6 +493,7 @@ class RoleImpl:
         element: Any,
         argname: Optional[str] = None,
         resolved: Optional[Any] = None,
+        *,
         advice: Optional[str] = None,
         code: Optional[str] = None,
         err: Optional[Exception] = None,
@@ -595,7 +596,7 @@ def _no_text_coercion(
 class _NoTextCoercion(RoleImpl):
     __slots__ = ()
 
-    def _literal_coercion(self, element, argname=None, **kw):
+    def _literal_coercion(self, element, *, argname=None, **kw):
         if isinstance(element, str) and issubclass(
             elements.TextClause, self._role_class
         ):
@@ -613,7 +614,7 @@ class _CoerceLiterals(RoleImpl):
     def _text_coercion(self, element, argname=None):
         return _no_text_coercion(element, argname)
 
-    def _literal_coercion(self, element, argname=None, **kw):
+    def _literal_coercion(self, element, *, argname=None, **kw):
         if isinstance(element, str):
             if self._coerce_star and element == "*":
                 return elements.ColumnClause("*", is_literal=True)
@@ -641,7 +642,8 @@ class LiteralValueImpl(RoleImpl):
         self,
         element,
         resolved,
-        argname,
+        argname=None,
+        *,
         type_=None,
         literal_execute=False,
         **kw,
@@ -659,7 +661,7 @@ class LiteralValueImpl(RoleImpl):
             literal_execute=literal_execute,
         )
 
-    def _literal_coercion(self, element, argname=None, type_=None, **kw):
+    def _literal_coercion(self, element, **kw):
         return element
 
 
@@ -671,6 +673,7 @@ class _SelectIsNotFrom(RoleImpl):
         element: Any,
         argname: Optional[str] = None,
         resolved: Optional[Any] = None,
+        *,
         advice: Optional[str] = None,
         code: Optional[str] = None,
         err: Optional[Exception] = None,
@@ -745,7 +748,7 @@ class ExpressionElementImpl(_ColumnCoercions, RoleImpl):
     __slots__ = ()
 
     def _literal_coercion(
-        self, element, name=None, type_=None, argname=None, is_crud=False, **kw
+        self, element, *, name=None, type_=None, is_crud=False, **kw
     ):
         if (
             element is None
@@ -787,15 +790,22 @@ class ExpressionElementImpl(_ColumnCoercions, RoleImpl):
 class BinaryElementImpl(ExpressionElementImpl, RoleImpl):
     __slots__ = ()
 
-    def _literal_coercion(
-        self, element, expr, operator, bindparam_type=None, argname=None, **kw
+    def _literal_coercion(  # type: ignore[override]
+        self,
+        element,
+        *,
+        expr,
+        operator,
+        bindparam_type=None,
+        argname=None,
+        **kw,
     ):
         try:
             return expr._bind_param(operator, element, type_=bindparam_type)
         except exc.ArgumentError as err:
             self._raise_for_expected(element, err=err)
 
-    def _post_coercion(self, resolved, expr, bindparam_type=None, **kw):
+    def _post_coercion(self, resolved, *, expr, bindparam_type=None, **kw):
         if resolved.type._isnull and not expr.type._isnull:
             resolved = resolved._with_binary_element_type(
                 bindparam_type if bindparam_type is not None else expr.type
@@ -833,7 +843,9 @@ class InElementImpl(RoleImpl):
             % (elem.__class__.__name__)
         )
 
-    def _literal_coercion(self, element, expr, operator, **kw):
+    def _literal_coercion(  # type: ignore[override]
+        self, element, *, expr, operator, **kw
+    ):
         if util.is_non_string_iterable(element):
             non_literal_expressions: Dict[
                 Optional[operators.ColumnOperators],
@@ -867,7 +879,7 @@ class InElementImpl(RoleImpl):
         else:
             self._raise_for_expected(element, **kw)
 
-    def _post_coercion(self, element, expr, operator, **kw):
+    def _post_coercion(self, element, *, expr, operator, **kw):
         if element._is_select_base:
             # for IN, we are doing scalar_subquery() coercion without
             # a warning
@@ -893,12 +905,10 @@ class OnClauseImpl(_ColumnCoercions, RoleImpl):
 
     _coerce_consts = True
 
-    def _literal_coercion(
-        self, element, name=None, type_=None, argname=None, is_crud=False, **kw
-    ):
+    def _literal_coercion(self, element, **kw):
         self._raise_for_expected(element)
 
-    def _post_coercion(self, resolved, original_element=None, **kw):
+    def _post_coercion(self, resolved, *, original_element=None, **kw):
         # this is a hack right now as we want to use coercion on an
         # ORM InstrumentedAttribute, but we want to return the object
         # itself if it is one, not its clause element.
@@ -983,7 +993,7 @@ class GroupByImpl(ByOfImpl, RoleImpl):
 class DMLColumnImpl(_ReturnsStringKey, RoleImpl):
     __slots__ = ()
 
-    def _post_coercion(self, element, as_key=False, **kw):
+    def _post_coercion(self, element, *, as_key=False, **kw):
         if as_key:
             return element.key
         else:
@@ -993,7 +1003,7 @@ class DMLColumnImpl(_ReturnsStringKey, RoleImpl):
 class ConstExprImpl(RoleImpl):
     __slots__ = ()
 
-    def _literal_coercion(self, element, argname=None, **kw):
+    def _literal_coercion(self, element, *, argname=None, **kw):
         if element is None:
             return elements.Null()
         elif element is False:
@@ -1019,7 +1029,7 @@ class TruncatedLabelImpl(_StringOnly, RoleImpl):
         else:
             self._raise_for_expected(element, argname, resolved)
 
-    def _literal_coercion(self, element, argname=None, **kw):
+    def _literal_coercion(self, element, **kw):
         """coerce the given value to :class:`._truncated_label`.
 
         Existing :class:`._truncated_label` and
@@ -1069,7 +1079,9 @@ class LimitOffsetImpl(RoleImpl):
         else:
             self._raise_for_expected(element, argname, resolved)
 
-    def _literal_coercion(self, element, name, type_, **kw):
+    def _literal_coercion(  # type: ignore[override]
+        self, element, *, name, type_, **kw
+    ):
         if element is None:
             return None
         else:
@@ -1111,7 +1123,7 @@ class ColumnsClauseImpl(_SelectIsNotFrom, _CoerceLiterals, RoleImpl):
     _guess_straight_column = re.compile(r"^\w\S*$", re.I)
 
     def _raise_for_expected(
-        self, element, argname=None, resolved=None, advice=None, **kw
+        self, element, argname=None, resolved=None, *, advice=None, **kw
     ):
         if not advice and isinstance(element, list):
             advice = (
@@ -1149,7 +1161,9 @@ class ReturnsRowsImpl(RoleImpl):
 class StatementImpl(_CoerceLiterals, RoleImpl):
     __slots__ = ()
 
-    def _post_coercion(self, resolved, original_element, argname=None, **kw):
+    def _post_coercion(
+        self, resolved, *, original_element, argname=None, **kw
+    ):
         if resolved is not original_element and not isinstance(
             original_element, str
         ):
@@ -1215,7 +1229,7 @@ class JoinTargetImpl(RoleImpl):
 
     _skip_clauseelement_for_target_match = True
 
-    def _literal_coercion(self, element, argname=None, **kw):
+    def _literal_coercion(self, element, *, argname=None, **kw):
         self._raise_for_expected(element, argname)
 
     def _implicit_coercions(
@@ -1223,6 +1237,7 @@ class JoinTargetImpl(RoleImpl):
         element: Any,
         resolved: Any,
         argname: Optional[str] = None,
+        *,
         legacy: bool = False,
         **kw: Any,
     ) -> Any:
@@ -1256,6 +1271,7 @@ class FromClauseImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl):
         element: Any,
         resolved: Any,
         argname: Optional[str] = None,
+        *,
         explicit_subquery: bool = False,
         allow_select: bool = True,
         **kw: Any,
@@ -1277,7 +1293,7 @@ class FromClauseImpl(_SelectIsNotFrom, _NoTextCoercion, RoleImpl):
         else:
             self._raise_for_expected(element, argname, resolved)
 
-    def _post_coercion(self, element, deannotate=False, **kw):
+    def _post_coercion(self, element, *, deannotate=False, **kw):
         if deannotate:
             return element._deannotate()
         else:
@@ -1292,7 +1308,7 @@ class StrictFromClauseImpl(FromClauseImpl):
         element: Any,
         resolved: Any,
         argname: Optional[str] = None,
-        explicit_subquery: bool = False,
+        *,
         allow_select: bool = False,
         **kw: Any,
     ) -> Any:
@@ -1312,7 +1328,7 @@ class StrictFromClauseImpl(FromClauseImpl):
 class AnonymizedFromClauseImpl(StrictFromClauseImpl):
     __slots__ = ()
 
-    def _post_coercion(self, element, flat=False, name=None, **kw):
+    def _post_coercion(self, element, *, flat=False, name=None, **kw):
         assert name is None
 
         return element._anonymous_fromclause(flat=flat)
index 18baf0f8e7fa551aa5a85fa016828babac184ec3..3eb412e6d7259e00f50247504576234c2be86a97 100644 (file)
@@ -6477,8 +6477,10 @@ class StrSQLCompiler(SQLCompiler):
     def visit_json_path_getitem_op_binary(self, binary, operator, **kw):
         return self.visit_getitem_binary(binary, operator, **kw)
 
-    def visit_sequence(self, seq, **kw):
-        return "<next sequence value: %s>" % self.preparer.format_sequence(seq)
+    def visit_sequence(self, sequence, **kw):
+        return (
+            f"<next sequence value: {self.preparer.format_sequence(sequence)}>"
+        )
 
     def returning_clause(
         self,
@@ -6512,7 +6514,7 @@ class StrSQLCompiler(SQLCompiler):
             for t in extra_froms
         )
 
-    def visit_empty_set_expr(self, type_, **kw):
+    def visit_empty_set_expr(self, element_types, **kw):
         return "SELECT 1 WHERE 1!=1"
 
     def get_from_hint_text(self, table, text):
index 499a19d97cc2f81b5196d0bd7a2482b54ec35f1d..d142665823928ede47e113e862e050f3bdb657e9 100644 (file)
@@ -1286,7 +1286,7 @@ class _multiparam_column(elements.ColumnElement[Any]):
     def compare(self, other, **kw):
         raise NotImplementedError()
 
-    def _copy_internals(self, other, **kw):
+    def _copy_internals(self, **kw):
         raise NotImplementedError()
 
     def __eq__(self, other):
index 3271acd60d9aad1656b27db72f7c3883a70ff353..64b686fc0375a5d6bad1ad20c21a0de3ab67116a 100644 (file)
@@ -4009,7 +4009,7 @@ class Slice(ColumnElement[Any]):
         self.type = type_api.NULLTYPE
 
     def self_group(self, against: Optional[OperatorType] = None) -> Self:
-        assert against is operator.getitem  # type: ignore[comparison-overlap]
+        assert against is operator.getitem
         return self
 
 
index 8bd036551cfae3c3eca679cef45b8d106e554ecc..0a411ce349d7c5068e094d6c1cba7336847d733c 100644 (file)
@@ -1012,7 +1012,7 @@ class SchemaType(SchemaEventTarget, TypeEngineMixin):
         if _adapted_from:
             self.dispatch = self.dispatch._join(_adapted_from.dispatch)
 
-    def _set_parent(self, column, **kw):
+    def _set_parent(self, parent, **kw):
         # set parent hook is when this type is associated with a column.
         # Column calls it for all SchemaEventTarget instances, either the
         # base type and/or variants in _variant_mapping.
@@ -1026,7 +1026,7 @@ class SchemaType(SchemaEventTarget, TypeEngineMixin):
         # on_table/metadata_create/drop in this method, which is used by
         # "native" types with a separate CREATE/DROP e.g. Postgresql.ENUM
 
-        column._on_table_attach(util.portable_instancemethod(self._set_table))
+        parent._on_table_attach(util.portable_instancemethod(self._set_table))
 
     def _variant_mapping_for_set_table(self, column):
         if column.type._variant_mapping:
@@ -1670,10 +1670,10 @@ class Enum(String, SchemaType, Emulated, TypeEngine[Union[str, enum.Enum]]):
         assert "_enums" in kw
         return impltype(**kw)
 
-    def adapt(self, impltype, **kw):
+    def adapt(self, cls, **kw):
         kw["_enums"] = self._enums_argument
         kw["_disable_warnings"] = True
-        return super().adapt(impltype, **kw)
+        return super().adapt(cls, **kw)
 
     def _should_create_constraint(self, compiler, **kw):
         if not self._is_impl_for_variant(compiler.dialect, kw):
@@ -3066,13 +3066,13 @@ class ARRAY(
     def compare_values(self, x, y):
         return x == y
 
-    def _set_parent(self, column, outer=False, **kw):
+    def _set_parent(self, parent, outer=False, **kw):
         """Support SchemaEventTarget"""
 
         if not outer and isinstance(self.item_type, SchemaEventTarget):
-            self.item_type._set_parent(column, **kw)
+            self.item_type._set_parent(parent, **kw)
 
-    def _set_parent_with_dispatch(self, parent):
+    def _set_parent_with_dispatch(self, parent, **kw):
         """Support SchemaEventTarget"""
 
         super()._set_parent_with_dispatch(parent, outer=True)
index c843024579dc5b44314c46243ef22d20db487c64..c637e19cd16e29c1459a92ad3f4db3fdb310bc06 100644 (file)
@@ -54,7 +54,7 @@ class FullArgSpec(typing.NamedTuple):
     varkw: Optional[str]
     defaults: Optional[Tuple[Any, ...]]
     kwonlyargs: List[str]
-    kwonlydefaults: Dict[str, Any]
+    kwonlydefaults: Optional[Dict[str, Any]]
     annotations: Dict[str, Any]
 
 
index 9a02e7d71a86f6fd6687ac4ee3ebd6f2d8465f4f..632e6a0a5678067a5ee1054ef89cdd35c844bb15 100644 (file)
@@ -2232,3 +2232,11 @@ def load_uncompiled_module(module: _M) -> _M:
     assert py_spec.loader
     py_spec.loader.exec_module(py_module)
     return cast(_M, py_module)
+
+
+class _Missing(enum.Enum):
+    Missing = enum.auto()
+
+
+Missing = _Missing.Missing
+MissingOr = Union[_T, Literal[_Missing.Missing]]
index f1b36ac52bb836393ec0d78d800651a63970bfb9..e1aa1f9655120706e8d5b4871d2fb530dca7b68f 100644 (file)
@@ -1,4 +1,5 @@
 import os
+import pathlib
 import shutil
 
 from sqlalchemy import testing
@@ -25,8 +26,12 @@ def _incremental_dirs():
 
 class MypyPluginTest(fixtures.MypyTest):
     @testing.combinations(
-        *[(pathname) for pathname in _incremental_dirs()],
+        *[
+            (pathlib.Path(pathname).name, pathname)
+            for pathname in _incremental_dirs()
+        ],
         argnames="pathname",
+        id_="ia",
     )
     @testing.requires.patch_library
     def test_incremental(self, mypy_runner, per_func_cachedir, pathname):
diff --git a/tox.ini b/tox.ini
index c13ee761dc9bfbea087393263e01c99cb992761b..f1146007dd1e6299ec19b53787e15eed91645934 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -192,7 +192,7 @@ commands=
 [testenv:pep484]
 deps=
      greenlet != 0.4.17
-     mypy >= 1.7.0,<1.11.0   # temporary, REMOVE upper bound
+     mypy >= 1.7.0
      types-greenlet
 commands =
     mypy  {env:MYPY_COLOR} ./lib/sqlalchemy