]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Removed the deprecated mypy plugin.
authorFederico Caselli <cfederico87@gmail.com>
Thu, 30 Jan 2025 21:05:59 +0000 (22:05 +0100)
committerFederico Caselli <cfederico87@gmail.com>
Thu, 30 Jan 2025 21:17:12 +0000 (22:17 +0100)
The plugin was non-functional with newer version of mypy and it's no
longer needed with modern SQLAlchemy declarative style.

Fixes: #12293
Change-Id: If4581ab58623f0a2992f4e33a6dcdae002c68dad

83 files changed:
doc/build/changelog/unreleased_21/12293.rst [new file with mode: 0644]
doc/build/orm/declarative_mixins.rst
doc/build/orm/extensions/index.rst
doc/build/orm/extensions/mypy.rst [deleted file]
lib/sqlalchemy/ext/mypy/__init__.py [deleted file]
lib/sqlalchemy/ext/mypy/apply.py [deleted file]
lib/sqlalchemy/ext/mypy/decl_class.py [deleted file]
lib/sqlalchemy/ext/mypy/infer.py [deleted file]
lib/sqlalchemy/ext/mypy/names.py [deleted file]
lib/sqlalchemy/ext/mypy/plugin.py [deleted file]
lib/sqlalchemy/ext/mypy/util.py [deleted file]
lib/sqlalchemy/testing/fixtures/mypy.py
pyproject.toml
setup.cfg
test/ext/mypy/incremental/stubs_14/__init__.py [deleted file]
test/ext/mypy/incremental/stubs_14/address.py [deleted file]
test/ext/mypy/incremental/stubs_14/patch1.testpatch [deleted file]
test/ext/mypy/incremental/stubs_14/user.py [deleted file]
test/ext/mypy/incremental/ticket_6147/__init__.py [deleted file]
test/ext/mypy/incremental/ticket_6147/base.py [deleted file]
test/ext/mypy/incremental/ticket_6147/one.py [deleted file]
test/ext/mypy/incremental/ticket_6147/patch1.testpatch [deleted file]
test/ext/mypy/incremental/ticket_6147/patch2.testpatch [deleted file]
test/ext/mypy/incremental/ticket_6435/__init__.py [deleted file]
test/ext/mypy/incremental/ticket_6435/enum_col_import1.py [deleted file]
test/ext/mypy/incremental/ticket_6435/enum_col_import2.py [deleted file]
test/ext/mypy/incremental/ticket_6476/__init__.py [deleted file]
test/ext/mypy/incremental/ticket_6476/base.py [deleted file]
test/ext/mypy/incremental/ticket_6476/patch1.testpatch [deleted file]
test/ext/mypy/incremental/ticket_6476/table.py [deleted file]
test/ext/mypy/plugin_files/abstract_one.py [deleted file]
test/ext/mypy/plugin_files/as_declarative.py [deleted file]
test/ext/mypy/plugin_files/as_declarative_base.py [deleted file]
test/ext/mypy/plugin_files/boolean_col.py [deleted file]
test/ext/mypy/plugin_files/cols_noninferred_plain_nonopt.py [deleted file]
test/ext/mypy/plugin_files/cols_notype_on_fk_col.py [deleted file]
test/ext/mypy/plugin_files/composite_props.py [deleted file]
test/ext/mypy/plugin_files/constr_cols_only.py [deleted file]
test/ext/mypy/plugin_files/dataclasses_workaround.py [deleted file]
test/ext/mypy/plugin_files/decl_attrs_one.py [deleted file]
test/ext/mypy/plugin_files/decl_attrs_two.py [deleted file]
test/ext/mypy/plugin_files/decl_base_subclass_one.py [deleted file]
test/ext/mypy/plugin_files/decl_base_subclass_two.py [deleted file]
test/ext/mypy/plugin_files/declarative_base_dynamic.py [deleted file]
test/ext/mypy/plugin_files/declarative_base_explicit.py [deleted file]
test/ext/mypy/plugin_files/ensure_descriptor_type_fully_inferred.py [deleted file]
test/ext/mypy/plugin_files/ensure_descriptor_type_noninferred.py [deleted file]
test/ext/mypy/plugin_files/ensure_descriptor_type_semiinferred.py [deleted file]
test/ext/mypy/plugin_files/enum_col.py [deleted file]
test/ext/mypy/plugin_files/imperative_table.py [deleted file]
test/ext/mypy/plugin_files/invalid_noninferred_lh_type.py [deleted file]
test/ext/mypy/plugin_files/issue_7321.py [deleted file]
test/ext/mypy/plugin_files/issue_7321_part2.py [deleted file]
test/ext/mypy/plugin_files/issue_9102.py [deleted file]
test/ext/mypy/plugin_files/issue_9102_workaround.py [deleted file]
test/ext/mypy/plugin_files/issue_9156.py [deleted file]
test/ext/mypy/plugin_files/lambda_default.py [deleted file]
test/ext/mypy/plugin_files/mapped_attr_assign.py [deleted file]
test/ext/mypy/plugin_files/mixin_not_mapped.py [deleted file]
test/ext/mypy/plugin_files/mixin_one.py [deleted file]
test/ext/mypy/plugin_files/mixin_three.py [deleted file]
test/ext/mypy/plugin_files/mixin_two.py [deleted file]
test/ext/mypy/plugin_files/mixin_w_tablename.py [deleted file]
test/ext/mypy/plugin_files/orderinglist1.py [deleted file]
test/ext/mypy/plugin_files/orderinglist2.py [deleted file]
test/ext/mypy/plugin_files/other_mapper_props.py [deleted file]
test/ext/mypy/plugin_files/plugin_doesnt_break_one.py [deleted file]
test/ext/mypy/plugin_files/relationship_6255_one.py [deleted file]
test/ext/mypy/plugin_files/relationship_6255_three.py [deleted file]
test/ext/mypy/plugin_files/relationship_6255_two.py [deleted file]
test/ext/mypy/plugin_files/relationship_direct_cls.py [deleted file]
test/ext/mypy/plugin_files/relationship_err1.py [deleted file]
test/ext/mypy/plugin_files/relationship_err2.py [deleted file]
test/ext/mypy/plugin_files/relationship_err3.py [deleted file]
test/ext/mypy/plugin_files/sa_module_prefix.py [deleted file]
test/ext/mypy/plugin_files/t_6950.py [deleted file]
test/ext/mypy/plugin_files/type_decorator.py [deleted file]
test/ext/mypy/plugin_files/typeless_fk_col_cant_infer.py [deleted file]
test/ext/mypy/plugin_files/typing_err1.py [deleted file]
test/ext/mypy/plugin_files/typing_err2.py [deleted file]
test/ext/mypy/plugin_files/typing_err3.py [deleted file]
test/ext/mypy/test_mypy_plugin_py3k.py [deleted file]
tox.ini

diff --git a/doc/build/changelog/unreleased_21/12293.rst b/doc/build/changelog/unreleased_21/12293.rst
new file mode 100644 (file)
index 0000000..c8782bb
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: typing
+    :tickets: 12293
+
+    Removed the deprecated mypy plugin.
+    The plugin was non-functional with newer version of mypy and it's no
+    longer needed with modern SQLAlchemy declarative style.
index 9f26207c07aaa1d5efcdd8ae4f1ffa24cd26cb75..1c6179809a2040e4401245755af5a7feb73e0ca7 100644 (file)
@@ -141,7 +141,7 @@ attribute is used on the newly defined class.
    :func:`_orm.mapped_column`.
 
 .. versionchanged:: 2.0 For users coming from the 1.4 series of SQLAlchemy
-   who may have been using the :ref:`mypy plugin <mypy_toplevel>`, the
+   who may have been using the ``mypy plugin``, the
    :func:`_orm.declarative_mixin` class decorator is no longer needed
    to mark declarative mixins, assuming the mypy plugin is no longer in use.
 
index 0dda58affa6ad7452e69536d957e56c605b07e3e..ba040b9f65f84d608b03080ff9e7379477a83814 100644 (file)
@@ -20,7 +20,6 @@ behavior.   In particular the "Horizontal Sharding", "Hybrid Attributes", and
     automap
     baked
     declarative/index
-    mypy
     mutable
     orderinglist
     horizontal_shard
diff --git a/doc/build/orm/extensions/mypy.rst b/doc/build/orm/extensions/mypy.rst
deleted file mode 100644 (file)
index dbca3f3..0000000
+++ /dev/null
@@ -1,606 +0,0 @@
-.. _mypy_toplevel:
-
-Mypy  / Pep-484 Support for ORM Mappings
-========================================
-
-Support for :pep:`484` typing annotations as well as the
-MyPy_ type checking tool when using SQLAlchemy
-:ref:`declarative <orm_declarative_mapper_config_toplevel>` mappings
-that refer to the :class:`_schema.Column` object directly, rather than
-the :func:`_orm.mapped_column` construct introduced in SQLAlchemy 2.0.
-
-.. deprecated:: 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.   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.
-
-    Modern SQLAlchemy now offers
-    :ref:`fully pep-484 compliant mapping syntaxes <whatsnew_20_orm_declarative_typing>`;
-    see the linked section for migration details.
-
-.. topic:: SQLAlchemy Mypy Plugin Status Update
-
-   **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
-   an
-   :ref:`all new typing system <whatsnew_20_orm_declarative_typing>`
-   for ORM Declarative models that removes the need for the Mypy plugin and
-   delivers much more consistent behavior with generally superior capabilities.
-   Note that this new capability is **not
-   part of SQLAlchemy 1.4, it is only in SQLAlchemy 2.0**.
-
-   The SQLAlchemy Mypy plugin, while it has technically never left the "alpha"
-   stage, should **now be considered as deprecated in SQLAlchemy 2.0, even
-   though it is still necessary for full Mypy support when using
-   SQLAlchemy 1.4**.
-
-   The Mypy plugin itself does not solve the issue of supplying correct typing
-   with other typing tools such as Pylance/Pyright, Pytype, Pycharm, etc, which
-   cannot make use of Mypy plugins. Additionally, Mypy plugins are extremely
-   difficult to develop, maintain and test, as a Mypy plugin must be deeply
-   integrated with Mypy's internal datastructures and processes, which itself
-   are not stable within the Mypy project itself. The SQLAlchemy Mypy plugin
-   has lots of limitations when used with code that deviates from very basic
-   patterns which are reported regularly.
-
-   For these reasons, new non-regression issues reported against the Mypy
-   plugin are unlikely to be fixed.  **Existing code that passes Mypy checks
-   using the plugin with SQLAlchemy 1.4 installed will continue to pass all
-   checks in SQLAlchemy 2.0 without any changes required, provided the plugin
-   is still used. SQLAlchemy 2.0's API is fully
-   backwards compatible with the SQLAlchemy 1.4 API and Mypy plugin behavior.**
-
-   End-user code that passes all checks under SQLAlchemy 1.4 with the Mypy
-   plugin may incrementally migrate to the new structures, once
-   that code is running exclusively on SQLAlchemy 2.0.  See the section
-   :ref:`whatsnew_20_orm_declarative_typing` for background on how this
-   migration may proceed.
-
-   Code that is running exclusively on SQLAlchemy version
-   2.0 and has fully migrated to the new declarative constructs will enjoy full
-   compliance with pep-484 as well as working correctly within IDEs and other
-   typing tools, without the need for plugins.
-
-
-Installation
-------------
-
-For **SQLAlchemy 2.0 only**: No stubs should be installed and packages
-like sqlalchemy-stubs_ and sqlalchemy2-stubs_ should be fully uninstalled.
-
-The Mypy_ package itself is a dependency.
-
-Mypy may be installed using the "mypy" extras hook using pip:
-
-.. sourcecode:: text
-
-    pip install sqlalchemy[mypy]
-
-The plugin itself is configured as described in
-`Configuring mypy to use Plugins <https://mypy.readthedocs.io/en/latest/extending_mypy.html#configuring-mypy-to-use-plugins>`_,
-using the ``sqlalchemy.ext.mypy.plugin`` module name, such as within
-``setup.cfg``::
-
-    [mypy]
-    plugins = sqlalchemy.ext.mypy.plugin
-
-.. _sqlalchemy-stubs: https://github.com/dropbox/sqlalchemy-stubs
-
-.. _sqlalchemy2-stubs: https://github.com/sqlalchemy/sqlalchemy2-stubs
-
-What the Plugin Does
---------------------
-
-The primary purpose of the Mypy plugin is to intercept and alter the static
-definition of SQLAlchemy
-:ref:`declarative mappings <orm_declarative_mapper_config_toplevel>` so that
-they match up to how they are structured after they have been
-:term:`instrumented` by their :class:`_orm.Mapper` objects. This allows both
-the class structure itself as well as code that uses the class to make sense to
-the Mypy tool, which otherwise would not be the case based on how declarative
-mappings currently function.    The plugin is not unlike similar plugins
-that are required for libraries like
-`dataclasses <https://docs.python.org/3/library/dataclasses.html>`_ which
-alter classes dynamically at runtime.
-
-To cover the major areas where this occurs, consider the following ORM
-mapping, using the typical example of the ``User`` class::
-
-    from sqlalchemy import Column, Integer, String, select
-    from sqlalchemy.orm import declarative_base
-
-    # "Base" is a class that is created dynamically from the
-    # declarative_base() function
-    Base = declarative_base()
-
-
-    class User(Base):
-        __tablename__ = "user"
-
-        id = Column(Integer, primary_key=True)
-        name = Column(String)
-
-
-    # "some_user" is an instance of the User class, which
-    # accepts "id" and "name" kwargs based on the mapping
-    some_user = User(id=5, name="user")
-
-    # it has an attribute called .name that's a string
-    print(f"Username: {some_user.name}")
-
-    # a select() construct makes use of SQL expressions derived from the
-    # User class itself
-    select_stmt = select(User).where(User.id.in_([3, 4, 5])).where(User.name.contains("s"))
-
-Above, the steps that the Mypy extension can take include:
-
-* Interpretation of the ``Base`` dynamic class generated by
-  :func:`_orm.declarative_base`, so that classes which inherit from it
-  are known to be mapped.  It also can accommodate the class decorator
-  approach described at :ref:`orm_declarative_decorator`.
-
-* Type inference for ORM mapped attributes that are defined in declarative
-  "inline" style, in the above example the ``id`` and ``name`` attributes of
-  the ``User`` class. This includes that an instance of ``User`` will use
-  ``int`` for ``id`` and ``str`` for ``name``. It also includes that when the
-  ``User.id`` and ``User.name`` class-level attributes are accessed, as they
-  are above in the ``select()`` statement, they are compatible with SQL
-  expression behavior, which is derived from the
-  :class:`_orm.InstrumentedAttribute` attribute descriptor class.
-
-* Application of an ``__init__()`` method to mapped classes that do not
-  already include an explicit constructor, which accepts keyword arguments
-  of specific types for all mapped attributes detected.
-
-When the Mypy plugin processes the above file, the resulting static class
-definition and Python code passed to the Mypy tool is equivalent to the
-following::
-
-    from sqlalchemy import Column, Integer, String, select
-    from sqlalchemy.orm import Mapped
-    from sqlalchemy.orm.decl_api import DeclarativeMeta
-
-
-    class Base(metaclass=DeclarativeMeta):
-        __abstract__ = True
-
-
-    class User(Base):
-        __tablename__ = "user"
-
-        id: Mapped[Optional[int]] = Mapped._special_method(
-            Column(Integer, primary_key=True)
-        )
-        name: Mapped[Optional[str]] = Mapped._special_method(Column(String))
-
-        def __init__(self, id: Optional[int] = ..., name: Optional[str] = ...) -> None: ...
-
-
-    some_user = User(id=5, name="user")
-
-    print(f"Username: {some_user.name}")
-
-    select_stmt = select(User).where(User.id.in_([3, 4, 5])).where(User.name.contains("s"))
-
-The key steps which have been taken above include:
-
-* The ``Base`` class is now defined in terms of the :class:`_orm.DeclarativeMeta`
-  class explicitly, rather than being a dynamic class.
-
-* The ``id`` and ``name`` attributes are defined in terms of the
-  :class:`_orm.Mapped` class, which represents a Python descriptor that
-  exhibits different behaviors at the class vs. instance levels.  The
-  :class:`_orm.Mapped` class is now the base class for the :class:`_orm.InstrumentedAttribute`
-  class that is used for all ORM mapped attributes.
-
-  :class:`_orm.Mapped` is defined as a generic class against arbitrary Python
-  types, meaning specific occurrences of :class:`_orm.Mapped` are associated
-  with a specific Python type, such as ``Mapped[Optional[int]]`` and
-  ``Mapped[Optional[str]]`` above.
-
-* The right-hand side of the declarative mapped attribute assignments are
-  **removed**, as this resembles the operation that the :class:`_orm.Mapper`
-  class would normally be doing, which is that it would be replacing these
-  attributes with specific instances of :class:`_orm.InstrumentedAttribute`.
-  The original expression is moved into a function call that will allow it to
-  still be type-checked without conflicting with the left-hand side of the
-  expression. For Mypy purposes, the left-hand typing annotation is sufficient
-  for the attribute's behavior to be understood.
-
-* A type stub for the ``User.__init__()`` method is added which includes the
-  correct keywords and datatypes.
-
-Usage
-------
-
-The following subsections will address individual uses cases that have
-so far been considered for pep-484 compliance.
-
-
-Introspection of Columns based on TypeEngine
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-For mapped columns that include an explicit datatype, when they are mapped
-as inline attributes, the mapped type will be introspected automatically::
-
-    class MyClass(Base):
-        # ...
-
-        id = Column(Integer, primary_key=True)
-        name = Column("employee_name", String(50), nullable=False)
-        other_name = Column(String(50))
-
-Above, the ultimate class-level datatypes of ``id``, ``name`` and
-``other_name`` will be introspected as ``Mapped[Optional[int]]``,
-``Mapped[Optional[str]]`` and ``Mapped[Optional[str]]``. The types are by
-default **always** considered to be ``Optional``, even for the primary key and
-non-nullable column. The reason is because while the database columns "id" and
-"name" can't be NULL, the Python attributes ``id`` and ``name`` most certainly
-can be ``None`` without an explicit constructor::
-
-    >>> m1 = MyClass()
-    >>> m1.id
-    None
-
-The types of the above columns can be stated **explicitly**, providing the
-two advantages of clearer self-documentation as well as being able to
-control which types are optional::
-
-    class MyClass(Base):
-        # ...
-
-        id: int = Column(Integer, primary_key=True)
-        name: str = Column("employee_name", String(50), nullable=False)
-        other_name: Optional[str] = Column(String(50))
-
-The Mypy plugin will accept the above ``int``, ``str`` and ``Optional[str]``
-and convert them to include the ``Mapped[]`` type surrounding them.  The
-``Mapped[]`` construct may also be used explicitly::
-
-    from sqlalchemy.orm import Mapped
-
-
-    class MyClass(Base):
-        # ...
-
-        id: Mapped[int] = Column(Integer, primary_key=True)
-        name: Mapped[str] = Column("employee_name", String(50), nullable=False)
-        other_name: Mapped[Optional[str]] = Column(String(50))
-
-When the type is non-optional, it simply means that the attribute as accessed
-from an instance of ``MyClass`` will be considered to be non-None::
-
-    mc = MyClass(...)
-
-    # will pass mypy --strict
-    name: str = mc.name
-
-For optional attributes, Mypy considers that the type must include None
-or otherwise be ``Optional``::
-
-    mc = MyClass(...)
-
-    # will pass mypy --strict
-    other_name: Optional[str] = mc.name
-
-Whether or not the mapped attribute is typed as ``Optional``, the
-generation of the ``__init__()`` method will **still consider all keywords
-to be optional**.  This is again matching what the SQLAlchemy ORM actually
-does when it creates the constructor, and should not be confused with the
-behavior of a validating system such as Python ``dataclasses`` which will
-generate a constructor that matches the annotations in terms of optional
-vs. required attributes.
-
-
-Columns that Don't have an Explicit Type
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Columns that include a :class:`_schema.ForeignKey` modifier do not need
-to specify a datatype in a SQLAlchemy declarative mapping.  For
-this type of attribute, the Mypy plugin will inform the user that it
-needs an explicit type to be sent::
-
-    # .. other imports
-    from sqlalchemy.sql.schema import ForeignKey
-
-    Base = declarative_base()
-
-
-    class User(Base):
-        __tablename__ = "user"
-
-        id = Column(Integer, primary_key=True)
-        name = Column(String)
-
-
-    class Address(Base):
-        __tablename__ = "address"
-
-        id = Column(Integer, primary_key=True)
-        user_id = Column(ForeignKey("user.id"))
-
-The plugin will deliver the message as follows:
-
-.. sourcecode:: text
-
-    $ mypy test3.py --strict
-    test3.py:20: error: [SQLAlchemy Mypy plugin] Can't infer type from
-    ORM mapped expression assigned to attribute 'user_id'; please specify a
-    Python type or Mapped[<python type>] on the left hand side.
-    Found 1 error in 1 file (checked 1 source file)
-
-To resolve, apply an explicit type annotation to the ``Address.user_id``
-column::
-
-    class Address(Base):
-        __tablename__ = "address"
-
-        id = Column(Integer, primary_key=True)
-        user_id: int = Column(ForeignKey("user.id"))
-
-Mapping Columns with Imperative Table
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-In :ref:`imperative table style <orm_imperative_table_configuration>`, the
-:class:`_schema.Column` definitions are given inside of a :class:`_schema.Table`
-construct which is separate from the mapped attributes themselves.  The Mypy
-plugin does not consider this :class:`_schema.Table`, but instead supports that
-the attributes can be explicitly stated with a complete annotation that
-**must** use the :class:`_orm.Mapped` class to identify them as mapped attributes::
-
-    class MyClass(Base):
-        __table__ = Table(
-            "mytable",
-            Base.metadata,
-            Column(Integer, primary_key=True),
-            Column("employee_name", String(50), nullable=False),
-            Column(String(50)),
-        )
-
-        id: Mapped[int]
-        name: Mapped[str]
-        other_name: Mapped[Optional[str]]
-
-The above :class:`_orm.Mapped` annotations are considered as mapped columns and
-will be included in the default constructor, as well as provide the correct
-typing profile for ``MyClass`` both at the class level and the instance level.
-
-Mapping Relationships
-^^^^^^^^^^^^^^^^^^^^^^
-
-The plugin has limited support for using type inference to detect the types
-for relationships.    For all those cases where it can't detect the type,
-it will emit an informative error message, and in all cases the appropriate
-type may be provided explicitly, either with the :class:`_orm.Mapped`
-class or optionally omitting it for an inline declaration.     The plugin
-also needs to determine whether or not the relationship refers to a collection
-or a scalar, and for that it relies upon the explicit value of
-the :paramref:`_orm.relationship.uselist` and/or :paramref:`_orm.relationship.collection_class`
-parameters.  An explicit type is needed if neither of these parameters are
-present, as well as if the target type of the :func:`_orm.relationship`
-is a string or callable, and not a class::
-
-    class User(Base):
-        __tablename__ = "user"
-
-        id = Column(Integer, primary_key=True)
-        name = Column(String)
-
-
-    class Address(Base):
-        __tablename__ = "address"
-
-        id = Column(Integer, primary_key=True)
-        user_id: int = Column(ForeignKey("user.id"))
-
-        user = relationship(User)
-
-The above mapping will produce the following error:
-
-.. sourcecode:: text
-
-    test3.py:22: error: [SQLAlchemy Mypy plugin] Can't infer scalar or
-    collection for ORM mapped expression assigned to attribute 'user'
-    if both 'uselist' and 'collection_class' arguments are absent from the
-    relationship(); please specify a type annotation on the left hand side.
-    Found 1 error in 1 file (checked 1 source file)
-
-The error can be resolved either by using ``relationship(User, uselist=False)``
-or by providing the type, in this case the scalar ``User`` object::
-
-    class Address(Base):
-        __tablename__ = "address"
-
-        id = Column(Integer, primary_key=True)
-        user_id: int = Column(ForeignKey("user.id"))
-
-        user: User = relationship(User)
-
-For collections, a similar pattern applies, where in the absence of
-``uselist=True`` or a :paramref:`_orm.relationship.collection_class`,
-a collection annotation such as ``List`` may be used.   It is also fully
-appropriate to use the string name of the class in the annotation as supported
-by pep-484, ensuring the class is imported with in
-the `TYPE_CHECKING block <https://www.python.org/dev/peps/pep-0484/#runtime-or-type-checking>`_
-as appropriate::
-
-    from typing import TYPE_CHECKING, List
-
-    from .mymodel import Base
-
-    if TYPE_CHECKING:
-        # if the target of the relationship is in another module
-        # that cannot normally be imported at runtime
-        from .myaddressmodel import Address
-
-
-    class User(Base):
-        __tablename__ = "user"
-
-        id = Column(Integer, primary_key=True)
-        name = Column(String)
-        addresses: List["Address"] = relationship("Address")
-
-As is the case with columns, the :class:`_orm.Mapped` class may also be
-applied explicitly::
-
-    class User(Base):
-        __tablename__ = "user"
-
-        id = Column(Integer, primary_key=True)
-        name = Column(String)
-
-        addresses: Mapped[List["Address"]] = relationship("Address", back_populates="user")
-
-
-    class Address(Base):
-        __tablename__ = "address"
-
-        id = Column(Integer, primary_key=True)
-        user_id: int = Column(ForeignKey("user.id"))
-
-        user: Mapped[User] = relationship(User, back_populates="addresses")
-
-.. _mypy_declarative_mixins:
-
-Using @declared_attr and Declarative Mixins
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The :class:`_orm.declared_attr` class allows Declarative mapped attributes to
-be declared in class level functions, and is particularly useful when using
-:ref:`declarative mixins <orm_mixins_toplevel>`. For these functions, the return
-type of the function should be annotated using either the ``Mapped[]``
-construct or by indicating the exact kind of object returned by the function.
-Additionally, "mixin" classes that are not otherwise mapped (i.e. don't extend
-from a :func:`_orm.declarative_base` class nor are they mapped with a method
-such as :meth:`_orm.registry.mapped`) should be decorated with the
-:func:`_orm.declarative_mixin` decorator, which provides a hint to the Mypy
-plugin that a particular class intends to serve as a declarative mixin::
-
-    from sqlalchemy.orm import declarative_mixin, declared_attr
-
-
-    @declarative_mixin
-    class HasUpdatedAt:
-        @declared_attr
-        def updated_at(cls) -> Column[DateTime]:  # uses Column
-            return Column(DateTime)
-
-
-    @declarative_mixin
-    class HasCompany:
-        @declared_attr
-        def company_id(cls) -> Mapped[int]:  # uses Mapped
-            return mapped_column(ForeignKey("company.id"))
-
-        @declared_attr
-        def company(cls) -> Mapped["Company"]:
-            return relationship("Company")
-
-
-    class Employee(HasUpdatedAt, HasCompany, Base):
-        __tablename__ = "employee"
-
-        id = Column(Integer, primary_key=True)
-        name = Column(String)
-
-Note the mismatch between the actual return type of a method like
-``HasCompany.company`` vs. what is annotated.  The Mypy plugin converts
-all ``@declared_attr`` functions into simple annotated attributes to avoid
-this complexity::
-
-    # what Mypy sees
-    class HasCompany:
-        company_id: Mapped[int]
-        company: Mapped["Company"]
-
-Combining with Dataclasses or Other Type-Sensitive Attribute Systems
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The examples of Python dataclasses integration at :ref:`orm_declarative_dataclasses`
-presents a problem; Python dataclasses expect an explicit type that it will
-use to build the class, and the value given in each assignment statement
-is significant.    That is, a class as follows has to be stated exactly
-as it is in order to be accepted by dataclasses::
-
-    mapper_registry: registry = registry()
-
-
-    @mapper_registry.mapped
-    @dataclass
-    class User:
-        __table__ = Table(
-            "user",
-            mapper_registry.metadata,
-            Column("id", Integer, primary_key=True),
-            Column("name", String(50)),
-            Column("fullname", String(50)),
-            Column("nickname", String(12)),
-        )
-        id: int = field(init=False)
-        name: Optional[str] = None
-        fullname: Optional[str] = None
-        nickname: Optional[str] = None
-        addresses: List[Address] = field(default_factory=list)
-
-        __mapper_args__ = {  # type: ignore
-            "properties": {"addresses": relationship("Address")}
-        }
-
-We can't apply our ``Mapped[]`` types to the attributes ``id``, ``name``,
-etc. because they will be rejected by the ``@dataclass`` decorator.   Additionally,
-Mypy has another plugin for dataclasses explicitly which can also get in the
-way of what we're doing.
-
-The above class will actually pass Mypy's type checking without issue; the
-only thing we are missing is the ability for attributes on ``User`` to be
-used in SQL expressions, such as::
-
-    stmt = select(User.name).where(User.id.in_([1, 2, 3]))
-
-To provide a workaround for this, the Mypy plugin has an additional feature
-whereby we can specify an extra attribute ``_mypy_mapped_attrs``, that is
-a list that encloses the class-level objects or their string names.
-This attribute can be conditional within the ``TYPE_CHECKING`` variable::
-
-    @mapper_registry.mapped
-    @dataclass
-    class User:
-        __table__ = Table(
-            "user",
-            mapper_registry.metadata,
-            Column("id", Integer, primary_key=True),
-            Column("name", String(50)),
-            Column("fullname", String(50)),
-            Column("nickname", String(12)),
-        )
-        id: int = field(init=False)
-        name: Optional[str] = None
-        fullname: Optional[str]
-        nickname: Optional[str]
-        addresses: List[Address] = field(default_factory=list)
-
-        if TYPE_CHECKING:
-            _mypy_mapped_attrs = [id, name, "fullname", "nickname", addresses]
-
-        __mapper_args__ = {  # type: ignore
-            "properties": {"addresses": relationship("Address")}
-        }
-
-With the above recipe, the attributes listed in ``_mypy_mapped_attrs``
-will be applied with the :class:`_orm.Mapped` typing information so that the
-``User`` class will behave as a SQLAlchemy mapped class when used in a
-class-bound context.
-
-.. _Mypy: https://mypy.readthedocs.io/
diff --git a/lib/sqlalchemy/ext/mypy/__init__.py b/lib/sqlalchemy/ext/mypy/__init__.py
deleted file mode 100644 (file)
index b5827cb..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# ext/mypy/__init__.py
-# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: https://www.opensource.org/licenses/mit-license.php
diff --git a/lib/sqlalchemy/ext/mypy/apply.py b/lib/sqlalchemy/ext/mypy/apply.py
deleted file mode 100644 (file)
index 02908cc..0000000
+++ /dev/null
@@ -1,324 +0,0 @@
-# ext/mypy/apply.py
-# Copyright (C) 2021-2025 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: https://www.opensource.org/licenses/mit-license.php
-
-from __future__ import annotations
-
-from typing import List
-from typing import Optional
-from typing import Union
-
-from mypy.nodes import ARG_NAMED_OPT
-from mypy.nodes import Argument
-from mypy.nodes import AssignmentStmt
-from mypy.nodes import CallExpr
-from mypy.nodes import ClassDef
-from mypy.nodes import MDEF
-from mypy.nodes import MemberExpr
-from mypy.nodes import NameExpr
-from mypy.nodes import RefExpr
-from mypy.nodes import StrExpr
-from mypy.nodes import SymbolTableNode
-from mypy.nodes import TempNode
-from mypy.nodes import TypeInfo
-from mypy.nodes import Var
-from mypy.plugin import SemanticAnalyzerPluginInterface
-from mypy.plugins.common import add_method_to_class
-from mypy.types import AnyType
-from mypy.types import get_proper_type
-from mypy.types import Instance
-from mypy.types import NoneTyp
-from mypy.types import ProperType
-from mypy.types import TypeOfAny
-from mypy.types import UnboundType
-from mypy.types import UnionType
-
-from . import infer
-from . import util
-from .names import expr_to_mapped_constructor
-from .names import NAMED_TYPE_SQLA_MAPPED
-
-
-def apply_mypy_mapped_attr(
-    cls: ClassDef,
-    api: SemanticAnalyzerPluginInterface,
-    item: Union[NameExpr, StrExpr],
-    attributes: List[util.SQLAlchemyAttribute],
-) -> None:
-    if isinstance(item, NameExpr):
-        name = item.name
-    elif isinstance(item, StrExpr):
-        name = item.value
-    else:
-        return None
-
-    for stmt in cls.defs.body:
-        if (
-            isinstance(stmt, AssignmentStmt)
-            and isinstance(stmt.lvalues[0], NameExpr)
-            and stmt.lvalues[0].name == name
-        ):
-            break
-    else:
-        util.fail(api, f"Can't find mapped attribute {name}", cls)
-        return None
-
-    if stmt.type is None:
-        util.fail(
-            api,
-            "Statement linked from _mypy_mapped_attrs has no "
-            "typing information",
-            stmt,
-        )
-        return None
-
-    left_hand_explicit_type = get_proper_type(stmt.type)
-    assert isinstance(
-        left_hand_explicit_type, (Instance, UnionType, UnboundType)
-    )
-
-    attributes.append(
-        util.SQLAlchemyAttribute(
-            name=name,
-            line=item.line,
-            column=item.column,
-            typ=left_hand_explicit_type,
-            info=cls.info,
-        )
-    )
-
-    apply_type_to_mapped_statement(
-        api, stmt, stmt.lvalues[0], left_hand_explicit_type, None
-    )
-
-
-def re_apply_declarative_assignments(
-    cls: ClassDef,
-    api: SemanticAnalyzerPluginInterface,
-    attributes: List[util.SQLAlchemyAttribute],
-) -> None:
-    """For multiple class passes, re-apply our left-hand side types as mypy
-    seems to reset them in place.
-
-    """
-    mapped_attr_lookup = {attr.name: attr for attr in attributes}
-    update_cls_metadata = False
-
-    for stmt in cls.defs.body:
-        # for a re-apply, all of our statements are AssignmentStmt;
-        # @declared_attr calls will have been converted and this
-        # currently seems to be preserved by mypy (but who knows if this
-        # will change).
-        if (
-            isinstance(stmt, AssignmentStmt)
-            and isinstance(stmt.lvalues[0], NameExpr)
-            and stmt.lvalues[0].name in mapped_attr_lookup
-            and isinstance(stmt.lvalues[0].node, Var)
-        ):
-            left_node = stmt.lvalues[0].node
-
-            python_type_for_type = mapped_attr_lookup[
-                stmt.lvalues[0].name
-            ].type
-
-            left_node_proper_type = get_proper_type(left_node.type)
-
-            # if we have scanned an UnboundType and now there's a more
-            # specific type than UnboundType, call the re-scan so we
-            # can get that set up correctly
-            if (
-                isinstance(python_type_for_type, UnboundType)
-                and not isinstance(left_node_proper_type, UnboundType)
-                and (
-                    isinstance(stmt.rvalue, CallExpr)
-                    and isinstance(stmt.rvalue.callee, MemberExpr)
-                    and isinstance(stmt.rvalue.callee.expr, NameExpr)
-                    and stmt.rvalue.callee.expr.node is not None
-                    and stmt.rvalue.callee.expr.node.fullname
-                    == 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)
-                )
-            ):
-                new_python_type_for_type = (
-                    infer.infer_type_from_right_hand_nameexpr(
-                        api,
-                        stmt,
-                        left_node,
-                        left_node_proper_type,
-                        stmt.rvalue.args[0].callee,
-                    )
-                )
-
-                if new_python_type_for_type is not None and not isinstance(
-                    new_python_type_for_type, UnboundType
-                ):
-                    python_type_for_type = new_python_type_for_type
-
-                    # update the SQLAlchemyAttribute with the better
-                    # information
-                    mapped_attr_lookup[stmt.lvalues[0].name].type = (
-                        python_type_for_type
-                    )
-
-                    update_cls_metadata = True
-
-            if (
-                not isinstance(left_node.type, Instance)
-                or left_node.type.type.fullname != NAMED_TYPE_SQLA_MAPPED
-            ):
-                assert python_type_for_type is not None
-                left_node.type = api.named_type(
-                    NAMED_TYPE_SQLA_MAPPED, [python_type_for_type]
-                )
-
-    if update_cls_metadata:
-        util.set_mapped_attributes(cls.info, attributes)
-
-
-def apply_type_to_mapped_statement(
-    api: SemanticAnalyzerPluginInterface,
-    stmt: AssignmentStmt,
-    lvalue: NameExpr,
-    left_hand_explicit_type: Optional[ProperType],
-    python_type_for_type: Optional[ProperType],
-) -> None:
-    """Apply the Mapped[<type>] annotation and right hand object to a
-    declarative assignment statement.
-
-    This converts a Python declarative class statement such as::
-
-        class User(Base):
-            # ...
-
-            attrname = Column(Integer)
-
-    To one that describes the final Python behavior to Mypy::
-
-    ... format: off
-
-        class User(Base):
-            # ...
-
-            attrname : Mapped[Optional[int]] = <meaningless temp node>
-
-    ... format: on
-
-    """
-    left_node = lvalue.node
-    assert isinstance(left_node, Var)
-
-    # to be completely honest I have no idea what the difference between
-    # left_node.type and stmt.type is, what it means if these are different
-    # vs. the same, why in order to get tests to pass I have to assign
-    # to stmt.type for the second case and not the first.  this is complete
-    # trying every combination until it works stuff.
-
-    if left_hand_explicit_type is not None:
-        lvalue.is_inferred_def = False
-        left_node.type = api.named_type(
-            NAMED_TYPE_SQLA_MAPPED, [left_hand_explicit_type]
-        )
-    else:
-        lvalue.is_inferred_def = False
-        left_node.type = api.named_type(
-            NAMED_TYPE_SQLA_MAPPED,
-            (
-                [AnyType(TypeOfAny.special_form)]
-                if python_type_for_type is None
-                else [python_type_for_type]
-            ),
-        )
-
-    # so to have it skip the right side totally, we can do this:
-    # stmt.rvalue = TempNode(AnyType(TypeOfAny.special_form))
-
-    # however, if we instead manufacture a new node that uses the old
-    # one, then we can still get type checking for the call itself,
-    # e.g. the Column, relationship() call, etc.
-
-    # rewrite the node as:
-    # <attr> : Mapped[<typ>] =
-    # _sa_Mapped._empty_constructor(<original CallExpr from rvalue>)
-    # the original right-hand side is maintained so it gets type checked
-    # internally
-    stmt.rvalue = expr_to_mapped_constructor(stmt.rvalue)
-
-    if stmt.type is not None and python_type_for_type is not None:
-        stmt.type = python_type_for_type
-
-
-def add_additional_orm_attributes(
-    cls: ClassDef,
-    api: SemanticAnalyzerPluginInterface,
-    attributes: List[util.SQLAlchemyAttribute],
-) -> None:
-    """Apply __init__, __table__ and other attributes to the mapped class."""
-
-    info = util.info_for_cls(cls, api)
-
-    if info is None:
-        return
-
-    is_base = util.get_is_base(info)
-
-    if "__init__" not in info.names and not is_base:
-        mapped_attr_names = {attr.name: attr.type for attr in attributes}
-
-        for base in info.mro[1:-1]:
-            if "sqlalchemy" not in info.metadata:
-                continue
-
-            base_cls_attributes = util.get_mapped_attributes(base, api)
-            if base_cls_attributes is None:
-                continue
-
-            for attr in base_cls_attributes:
-                mapped_attr_names.setdefault(attr.name, attr.type)
-
-        arguments = []
-        for name, typ in mapped_attr_names.items():
-            if typ is None:
-                typ = AnyType(TypeOfAny.special_form)
-            arguments.append(
-                Argument(
-                    variable=Var(name, typ),
-                    type_annotation=typ,
-                    initializer=TempNode(typ),
-                    kind=ARG_NAMED_OPT,
-                )
-            )
-
-        add_method_to_class(api, cls, "__init__", arguments, NoneTyp())
-
-    if "__table__" not in info.names and util.get_has_table(info):
-        _apply_placeholder_attr_to_class(
-            api, cls, "sqlalchemy.sql.schema.Table", "__table__"
-        )
-    if not is_base:
-        _apply_placeholder_attr_to_class(
-            api, cls, "sqlalchemy.orm.mapper.Mapper", "__mapper__"
-        )
-
-
-def _apply_placeholder_attr_to_class(
-    api: SemanticAnalyzerPluginInterface,
-    cls: ClassDef,
-    qualified_name: str,
-    attrname: str,
-) -> None:
-    sym = api.lookup_fully_qualified_or_none(qualified_name)
-    if sym:
-        assert isinstance(sym.node, TypeInfo)
-        type_: ProperType = Instance(sym.node, [])
-    else:
-        type_ = AnyType(TypeOfAny.special_form)
-    var = Var(attrname)
-    var._fullname = cls.fullname + "." + attrname
-    var.info = cls.info
-    var.type = type_
-    cls.info.names[attrname] = SymbolTableNode(MDEF, var)
diff --git a/lib/sqlalchemy/ext/mypy/decl_class.py b/lib/sqlalchemy/ext/mypy/decl_class.py
deleted file mode 100644 (file)
index 2ce7ad5..0000000
+++ /dev/null
@@ -1,515 +0,0 @@
-# ext/mypy/decl_class.py
-# Copyright (C) 2021-2025 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: https://www.opensource.org/licenses/mit-license.php
-
-from __future__ import annotations
-
-from typing import List
-from typing import Optional
-from typing import Union
-
-from mypy.nodes import AssignmentStmt
-from mypy.nodes import CallExpr
-from mypy.nodes import ClassDef
-from mypy.nodes import Decorator
-from mypy.nodes import LambdaExpr
-from mypy.nodes import ListExpr
-from mypy.nodes import MemberExpr
-from mypy.nodes import NameExpr
-from mypy.nodes import PlaceholderNode
-from mypy.nodes import RefExpr
-from mypy.nodes import StrExpr
-from mypy.nodes import SymbolNode
-from mypy.nodes import SymbolTableNode
-from mypy.nodes import TempNode
-from mypy.nodes import TypeInfo
-from mypy.nodes import Var
-from mypy.plugin import SemanticAnalyzerPluginInterface
-from mypy.types import AnyType
-from mypy.types import CallableType
-from mypy.types import get_proper_type
-from mypy.types import Instance
-from mypy.types import NoneType
-from mypy.types import ProperType
-from mypy.types import Type
-from mypy.types import TypeOfAny
-from mypy.types import UnboundType
-from mypy.types import UnionType
-
-from . import apply
-from . import infer
-from . import names
-from . import util
-
-
-def scan_declarative_assignments_and_apply_types(
-    cls: ClassDef,
-    api: SemanticAnalyzerPluginInterface,
-    is_mixin_scan: bool = False,
-) -> Optional[List[util.SQLAlchemyAttribute]]:
-    info = util.info_for_cls(cls, api)
-
-    if info is None:
-        # this can occur during cached passes
-        return None
-    elif cls.fullname.startswith("builtins"):
-        return None
-
-    mapped_attributes: Optional[List[util.SQLAlchemyAttribute]] = (
-        util.get_mapped_attributes(info, api)
-    )
-
-    # used by assign.add_additional_orm_attributes among others
-    util.establish_as_sqlalchemy(info)
-
-    if mapped_attributes is not None:
-        # ensure that a class that's mapped is always picked up by
-        # its mapped() decorator or declarative metaclass before
-        # it would be detected as an unmapped mixin class
-
-        if not is_mixin_scan:
-            # mypy can call us more than once.  it then *may* have reset the
-            # left hand side of everything, but not the right that we removed,
-            # removing our ability to re-scan.   but we have the types
-            # here, so lets re-apply them, or if we have an UnboundType,
-            # we can re-scan
-
-            apply.re_apply_declarative_assignments(cls, api, mapped_attributes)
-
-        return mapped_attributes
-
-    mapped_attributes = []
-
-    if not cls.defs.body:
-        # when we get a mixin class from another file, the body is
-        # empty (!) but the names are in the symbol table.  so use that.
-
-        for sym_name, sym in info.names.items():
-            _scan_symbol_table_entry(
-                cls, api, sym_name, sym, mapped_attributes
-            )
-    else:
-        for stmt in util.flatten_typechecking(cls.defs.body):
-            if isinstance(stmt, AssignmentStmt):
-                _scan_declarative_assignment_stmt(
-                    cls, api, stmt, mapped_attributes
-                )
-            elif isinstance(stmt, Decorator):
-                _scan_declarative_decorator_stmt(
-                    cls, api, stmt, mapped_attributes
-                )
-    _scan_for_mapped_bases(cls, api)
-
-    if not is_mixin_scan:
-        apply.add_additional_orm_attributes(cls, api, mapped_attributes)
-
-    util.set_mapped_attributes(info, mapped_attributes)
-
-    return mapped_attributes
-
-
-def _scan_symbol_table_entry(
-    cls: ClassDef,
-    api: SemanticAnalyzerPluginInterface,
-    name: str,
-    value: SymbolTableNode,
-    attributes: List[util.SQLAlchemyAttribute],
-) -> None:
-    """Extract mapping information from a SymbolTableNode that's in the
-    type.names dictionary.
-
-    """
-    value_type = get_proper_type(value.type)
-    if not isinstance(value_type, Instance):
-        return
-
-    left_hand_explicit_type = None
-    type_id = names.type_id_for_named_node(value_type.type)
-    # type_id = names._type_id_for_unbound_type(value.type.type, cls, api)
-
-    err = False
-
-    # TODO: this is nearly the same logic as that of
-    # _scan_declarative_decorator_stmt, likely can be merged
-    if type_id in {
-        names.MAPPED,
-        names.RELATIONSHIP,
-        names.COMPOSITE_PROPERTY,
-        names.MAPPER_PROPERTY,
-        names.SYNONYM_PROPERTY,
-        names.COLUMN_PROPERTY,
-    }:
-        if value_type.args:
-            left_hand_explicit_type = get_proper_type(value_type.args[0])
-        else:
-            err = True
-    elif type_id is names.COLUMN:
-        if not value_type.args:
-            err = True
-        else:
-            typeengine_arg: Union[ProperType, TypeInfo] = get_proper_type(
-                value_type.args[0]
-            )
-            if isinstance(typeengine_arg, Instance):
-                typeengine_arg = typeengine_arg.type
-
-            if isinstance(typeengine_arg, (UnboundType, TypeInfo)):
-                sym = api.lookup_qualified(typeengine_arg.name, typeengine_arg)
-                if sym is not None and isinstance(sym.node, TypeInfo):
-                    if names.has_base_type_id(sym.node, names.TYPEENGINE):
-                        left_hand_explicit_type = UnionType(
-                            [
-                                infer.extract_python_type_from_typeengine(
-                                    api, sym.node, []
-                                ),
-                                NoneType(),
-                            ]
-                        )
-                    else:
-                        util.fail(
-                            api,
-                            "Column type should be a TypeEngine "
-                            "subclass not '{}'".format(sym.node.fullname),
-                            value_type,
-                        )
-
-    if err:
-        msg = (
-            "Can't infer type from attribute {} on class {}. "
-            "please specify a return type from this function that is "
-            "one of: Mapped[<python type>], relationship[<target class>], "
-            "Column[<TypeEngine>], MapperProperty[<python type>]"
-        )
-        util.fail(api, msg.format(name, cls.name), cls)
-
-        left_hand_explicit_type = AnyType(TypeOfAny.special_form)
-
-    if left_hand_explicit_type is not None:
-        assert value.node is not None
-        attributes.append(
-            util.SQLAlchemyAttribute(
-                name=name,
-                line=value.node.line,
-                column=value.node.column,
-                typ=left_hand_explicit_type,
-                info=cls.info,
-            )
-        )
-
-
-def _scan_declarative_decorator_stmt(
-    cls: ClassDef,
-    api: SemanticAnalyzerPluginInterface,
-    stmt: Decorator,
-    attributes: List[util.SQLAlchemyAttribute],
-) -> None:
-    """Extract mapping information from a @declared_attr in a declarative
-    class.
-
-    E.g.::
-
-        @reg.mapped
-        class MyClass:
-            # ...
-
-            @declared_attr
-            def updated_at(cls) -> Column[DateTime]:
-                return Column(DateTime)
-
-    Will resolve in mypy as::
-
-        @reg.mapped
-        class MyClass:
-            # ...
-
-            updated_at: Mapped[Optional[datetime.datetime]]
-
-    """
-    for dec in stmt.decorators:
-        if (
-            isinstance(dec, (NameExpr, MemberExpr, SymbolNode))
-            and names.type_id_for_named_node(dec) is names.DECLARED_ATTR
-        ):
-            break
-    else:
-        return
-
-    dec_index = cls.defs.body.index(stmt)
-
-    left_hand_explicit_type: Optional[ProperType] = None
-
-    if util.name_is_dunder(stmt.name):
-        # for dunder names like __table_args__, __tablename__,
-        # __mapper_args__ etc., rewrite these as simple assignment
-        # statements; otherwise mypy doesn't like if the decorated
-        # function has an annotation like ``cls: Type[Foo]`` because
-        # it isn't @classmethod
-        any_ = AnyType(TypeOfAny.special_form)
-        left_node = NameExpr(stmt.var.name)
-        left_node.node = stmt.var
-        new_stmt = AssignmentStmt([left_node], TempNode(any_))
-        new_stmt.type = left_node.node.type
-        cls.defs.body[dec_index] = new_stmt
-        return
-    elif isinstance(stmt.func.type, CallableType):
-        func_type = stmt.func.type.ret_type
-        if isinstance(func_type, UnboundType):
-            type_id = names.type_id_for_unbound_type(func_type, cls, api)
-        else:
-            # this does not seem to occur unless the type argument is
-            # incorrect
-            return
-
-        if (
-            type_id
-            in {
-                names.MAPPED,
-                names.RELATIONSHIP,
-                names.COMPOSITE_PROPERTY,
-                names.MAPPER_PROPERTY,
-                names.SYNONYM_PROPERTY,
-                names.COLUMN_PROPERTY,
-            }
-            and func_type.args
-        ):
-            left_hand_explicit_type = get_proper_type(func_type.args[0])
-        elif type_id is names.COLUMN and func_type.args:
-            typeengine_arg = func_type.args[0]
-            if isinstance(typeengine_arg, UnboundType):
-                sym = api.lookup_qualified(typeengine_arg.name, typeengine_arg)
-                if sym is not None and isinstance(sym.node, TypeInfo):
-                    if names.has_base_type_id(sym.node, names.TYPEENGINE):
-                        left_hand_explicit_type = UnionType(
-                            [
-                                infer.extract_python_type_from_typeengine(
-                                    api, sym.node, []
-                                ),
-                                NoneType(),
-                            ]
-                        )
-                    else:
-                        util.fail(
-                            api,
-                            "Column type should be a TypeEngine "
-                            "subclass not '{}'".format(sym.node.fullname),
-                            func_type,
-                        )
-
-    if left_hand_explicit_type is None:
-        # no type on the decorated function.  our option here is to
-        # dig into the function body and get the return type, but they
-        # should just have an annotation.
-        msg = (
-            "Can't infer type from @declared_attr on function '{}';  "
-            "please specify a return type from this function that is "
-            "one of: Mapped[<python type>], relationship[<target class>], "
-            "Column[<TypeEngine>], MapperProperty[<python type>]"
-        )
-        util.fail(api, msg.format(stmt.var.name), stmt)
-
-        left_hand_explicit_type = AnyType(TypeOfAny.special_form)
-
-    left_node = NameExpr(stmt.var.name)
-    left_node.node = stmt.var
-
-    # totally feeling around in the dark here as I don't totally understand
-    # the significance of UnboundType.  It seems to be something that is
-    # not going to do what's expected when it is applied as the type of
-    # an AssignmentStatement.  So do a feeling-around-in-the-dark version
-    # of converting it to the regular Instance/TypeInfo/UnionType structures
-    # we see everywhere else.
-    if isinstance(left_hand_explicit_type, UnboundType):
-        left_hand_explicit_type = get_proper_type(
-            util.unbound_to_instance(api, left_hand_explicit_type)
-        )
-
-    left_node.node.type = api.named_type(
-        names.NAMED_TYPE_SQLA_MAPPED, [left_hand_explicit_type]
-    )
-
-    # this will ignore the rvalue entirely
-    # rvalue = TempNode(AnyType(TypeOfAny.special_form))
-
-    # rewrite the node as:
-    # <attr> : Mapped[<typ>] =
-    # _sa_Mapped._empty_constructor(lambda: <function body>)
-    # the function body is maintained so it gets type checked internally
-    rvalue = names.expr_to_mapped_constructor(
-        LambdaExpr(stmt.func.arguments, stmt.func.body)
-    )
-
-    new_stmt = AssignmentStmt([left_node], rvalue)
-    new_stmt.type = left_node.node.type
-
-    attributes.append(
-        util.SQLAlchemyAttribute(
-            name=left_node.name,
-            line=stmt.line,
-            column=stmt.column,
-            typ=left_hand_explicit_type,
-            info=cls.info,
-        )
-    )
-    cls.defs.body[dec_index] = new_stmt
-
-
-def _scan_declarative_assignment_stmt(
-    cls: ClassDef,
-    api: SemanticAnalyzerPluginInterface,
-    stmt: AssignmentStmt,
-    attributes: List[util.SQLAlchemyAttribute],
-) -> None:
-    """Extract mapping information from an assignment statement in a
-    declarative class.
-
-    """
-    lvalue = stmt.lvalues[0]
-    if not isinstance(lvalue, NameExpr):
-        return
-
-    sym = cls.info.names.get(lvalue.name)
-
-    # this establishes that semantic analysis has taken place, which
-    # means the nodes are populated and we are called from an appropriate
-    # hook.
-    assert sym is not None
-    node = sym.node
-
-    if isinstance(node, PlaceholderNode):
-        return
-
-    assert node is lvalue.node
-    assert isinstance(node, Var)
-
-    if node.name == "__abstract__":
-        if api.parse_bool(stmt.rvalue) is True:
-            util.set_is_base(cls.info)
-        return
-    elif node.name == "__tablename__":
-        util.set_has_table(cls.info)
-    elif node.name.startswith("__"):
-        return
-    elif node.name == "_mypy_mapped_attrs":
-        if not isinstance(stmt.rvalue, ListExpr):
-            util.fail(api, "_mypy_mapped_attrs is expected to be a list", stmt)
-        else:
-            for item in stmt.rvalue.items:
-                if isinstance(item, (NameExpr, StrExpr)):
-                    apply.apply_mypy_mapped_attr(cls, api, item, attributes)
-
-    left_hand_mapped_type: Optional[Type] = None
-    left_hand_explicit_type: Optional[ProperType] = None
-
-    if node.is_inferred or node.type is None:
-        if isinstance(stmt.type, UnboundType):
-            # look for an explicit Mapped[] type annotation on the left
-            # side with nothing on the right
-
-            # print(stmt.type)
-            # Mapped?[Optional?[A?]]
-
-            left_hand_explicit_type = stmt.type
-
-            if stmt.type.name == "Mapped":
-                mapped_sym = api.lookup_qualified("Mapped", cls)
-                if (
-                    mapped_sym is not None
-                    and mapped_sym.node is not None
-                    and names.type_id_for_named_node(mapped_sym.node)
-                    is names.MAPPED
-                ):
-                    left_hand_explicit_type = get_proper_type(
-                        stmt.type.args[0]
-                    )
-                    left_hand_mapped_type = stmt.type
-
-            # TODO: do we need to convert from unbound for this case?
-            # left_hand_explicit_type = util._unbound_to_instance(
-            #     api, left_hand_explicit_type
-            # )
-    else:
-        node_type = get_proper_type(node.type)
-        if (
-            isinstance(node_type, Instance)
-            and names.type_id_for_named_node(node_type.type) is names.MAPPED
-        ):
-            # print(node.type)
-            # sqlalchemy.orm.attributes.Mapped[<python type>]
-            left_hand_explicit_type = get_proper_type(node_type.args[0])
-            left_hand_mapped_type = node_type
-        else:
-            # print(node.type)
-            # <python type>
-            left_hand_explicit_type = node_type
-            left_hand_mapped_type = None
-
-    if isinstance(stmt.rvalue, TempNode) and left_hand_mapped_type is not None:
-        # annotation without assignment and Mapped is present
-        # as type annotation
-        # equivalent to using _infer_type_from_left_hand_type_only.
-
-        python_type_for_type = left_hand_explicit_type
-    elif isinstance(stmt.rvalue, CallExpr) and isinstance(
-        stmt.rvalue.callee, RefExpr
-    ):
-        python_type_for_type = infer.infer_type_from_right_hand_nameexpr(
-            api, stmt, node, left_hand_explicit_type, stmt.rvalue.callee
-        )
-
-        if python_type_for_type is None:
-            return
-
-    else:
-        return
-
-    assert python_type_for_type is not None
-
-    attributes.append(
-        util.SQLAlchemyAttribute(
-            name=node.name,
-            line=stmt.line,
-            column=stmt.column,
-            typ=python_type_for_type,
-            info=cls.info,
-        )
-    )
-
-    apply.apply_type_to_mapped_statement(
-        api,
-        stmt,
-        lvalue,
-        left_hand_explicit_type,
-        python_type_for_type,
-    )
-
-
-def _scan_for_mapped_bases(
-    cls: ClassDef,
-    api: SemanticAnalyzerPluginInterface,
-) -> None:
-    """Given a class, iterate through its superclass hierarchy to find
-    all other classes that are considered as ORM-significant.
-
-    Locates non-mapped mixins and scans them for mapped attributes to be
-    applied to subclasses.
-
-    """
-
-    info = util.info_for_cls(cls, api)
-
-    if info is None:
-        return
-
-    for base_info in info.mro[1:-1]:
-        if base_info.fullname.startswith("builtins"):
-            continue
-
-        # scan each base for mapped attributes.  if they are not already
-        # scanned (but have all their type info), that means they are unmapped
-        # mixins
-        scan_declarative_assignments_and_apply_types(
-            base_info.defn, api, is_mixin_scan=True
-        )
diff --git a/lib/sqlalchemy/ext/mypy/infer.py b/lib/sqlalchemy/ext/mypy/infer.py
deleted file mode 100644 (file)
index 26a83cc..0000000
+++ /dev/null
@@ -1,590 +0,0 @@
-# ext/mypy/infer.py
-# Copyright (C) 2021-2025 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: https://www.opensource.org/licenses/mit-license.php
-
-from __future__ import annotations
-
-from typing import Optional
-from typing import Sequence
-
-from mypy.maptype import map_instance_to_supertype
-from mypy.nodes import AssignmentStmt
-from mypy.nodes import CallExpr
-from mypy.nodes import Expression
-from mypy.nodes import FuncDef
-from mypy.nodes import LambdaExpr
-from mypy.nodes import MemberExpr
-from mypy.nodes import NameExpr
-from mypy.nodes import RefExpr
-from mypy.nodes import StrExpr
-from mypy.nodes import TypeInfo
-from mypy.nodes import Var
-from mypy.plugin import SemanticAnalyzerPluginInterface
-from mypy.subtypes import is_subtype
-from mypy.types import AnyType
-from mypy.types import CallableType
-from mypy.types import get_proper_type
-from mypy.types import Instance
-from mypy.types import NoneType
-from mypy.types import ProperType
-from mypy.types import TypeOfAny
-from mypy.types import UnionType
-
-from . import names
-from . import util
-
-
-def infer_type_from_right_hand_nameexpr(
-    api: SemanticAnalyzerPluginInterface,
-    stmt: AssignmentStmt,
-    node: Var,
-    left_hand_explicit_type: Optional[ProperType],
-    infer_from_right_side: RefExpr,
-) -> Optional[ProperType]:
-    type_id = names.type_id_for_callee(infer_from_right_side)
-    if type_id is None:
-        return None
-    elif type_id is names.MAPPED:
-        python_type_for_type = _infer_type_from_mapped(
-            api, stmt, node, left_hand_explicit_type, infer_from_right_side
-        )
-    elif type_id is names.COLUMN:
-        python_type_for_type = _infer_type_from_decl_column(
-            api, stmt, node, left_hand_explicit_type
-        )
-    elif type_id is names.RELATIONSHIP:
-        python_type_for_type = _infer_type_from_relationship(
-            api, stmt, node, left_hand_explicit_type
-        )
-    elif type_id is names.COLUMN_PROPERTY:
-        python_type_for_type = _infer_type_from_decl_column_property(
-            api, stmt, node, left_hand_explicit_type
-        )
-    elif type_id is names.SYNONYM_PROPERTY:
-        python_type_for_type = infer_type_from_left_hand_type_only(
-            api, node, left_hand_explicit_type
-        )
-    elif type_id is names.COMPOSITE_PROPERTY:
-        python_type_for_type = _infer_type_from_decl_composite_property(
-            api, stmt, node, left_hand_explicit_type
-        )
-    else:
-        return None
-
-    return python_type_for_type
-
-
-def _infer_type_from_relationship(
-    api: SemanticAnalyzerPluginInterface,
-    stmt: AssignmentStmt,
-    node: Var,
-    left_hand_explicit_type: Optional[ProperType],
-) -> Optional[ProperType]:
-    """Infer the type of mapping from a relationship.
-
-    E.g.::
-
-        @reg.mapped
-        class MyClass:
-            # ...
-
-            addresses = relationship(Address, uselist=True)
-
-            order: Mapped["Order"] = relationship("Order")
-
-    Will resolve in mypy as::
-
-        @reg.mapped
-        class MyClass:
-            # ...
-
-            addresses: Mapped[List[Address]]
-
-            order: Mapped["Order"]
-
-    """
-
-    assert isinstance(stmt.rvalue, CallExpr)
-    target_cls_arg = stmt.rvalue.args[0]
-    python_type_for_type: Optional[ProperType] = None
-
-    if isinstance(target_cls_arg, NameExpr) and isinstance(
-        target_cls_arg.node, TypeInfo
-    ):
-        # type
-        related_object_type = target_cls_arg.node
-        python_type_for_type = Instance(related_object_type, [])
-
-    # other cases not covered - an error message directs the user
-    # to set an explicit type annotation
-    #
-    # node.type == str, it's a string
-    # if isinstance(target_cls_arg, NameExpr) and isinstance(
-    #     target_cls_arg.node, Var
-    # )
-    # points to a type
-    # isinstance(target_cls_arg, NameExpr) and isinstance(
-    #     target_cls_arg.node, TypeAlias
-    # )
-    # string expression
-    # isinstance(target_cls_arg, StrExpr)
-
-    uselist_arg = util.get_callexpr_kwarg(stmt.rvalue, "uselist")
-    collection_cls_arg: Optional[Expression] = util.get_callexpr_kwarg(
-        stmt.rvalue, "collection_class"
-    )
-    type_is_a_collection = False
-
-    # this can be used to determine Optional for a many-to-one
-    # in the same way nullable=False could be used, if we start supporting
-    # that.
-    # innerjoin_arg = util.get_callexpr_kwarg(stmt.rvalue, "innerjoin")
-
-    if (
-        uselist_arg is not None
-        and api.parse_bool(uselist_arg) is True
-        and collection_cls_arg is None
-    ):
-        type_is_a_collection = True
-        if python_type_for_type is not None:
-            python_type_for_type = api.named_type(
-                names.NAMED_TYPE_BUILTINS_LIST, [python_type_for_type]
-            )
-    elif (
-        uselist_arg is None or api.parse_bool(uselist_arg) is True
-    ) and collection_cls_arg is not None:
-        type_is_a_collection = True
-        if isinstance(collection_cls_arg, CallExpr):
-            collection_cls_arg = collection_cls_arg.callee
-
-        if isinstance(collection_cls_arg, NameExpr) and isinstance(
-            collection_cls_arg.node, TypeInfo
-        ):
-            if python_type_for_type is not None:
-                # this can still be overridden by the left hand side
-                # within _infer_Type_from_left_and_inferred_right
-                python_type_for_type = Instance(
-                    collection_cls_arg.node, [python_type_for_type]
-                )
-        elif (
-            isinstance(collection_cls_arg, NameExpr)
-            and isinstance(collection_cls_arg.node, FuncDef)
-            and collection_cls_arg.node.type is not None
-        ):
-            if python_type_for_type is not None:
-                # this can still be overridden by the left hand side
-                # within _infer_Type_from_left_and_inferred_right
-
-                # TODO: handle mypy.types.Overloaded
-                if isinstance(collection_cls_arg.node.type, CallableType):
-                    rt = get_proper_type(collection_cls_arg.node.type.ret_type)
-
-                    if isinstance(rt, CallableType):
-                        callable_ret_type = get_proper_type(rt.ret_type)
-                        if isinstance(callable_ret_type, Instance):
-                            python_type_for_type = Instance(
-                                callable_ret_type.type,
-                                [python_type_for_type],
-                            )
-        else:
-            util.fail(
-                api,
-                "Expected Python collection type for "
-                "collection_class parameter",
-                stmt.rvalue,
-            )
-            python_type_for_type = None
-    elif uselist_arg is not None and api.parse_bool(uselist_arg) is False:
-        if collection_cls_arg is not None:
-            util.fail(
-                api,
-                "Sending uselist=False and collection_class at the same time "
-                "does not make sense",
-                stmt.rvalue,
-            )
-        if python_type_for_type is not None:
-            python_type_for_type = UnionType(
-                [python_type_for_type, NoneType()]
-            )
-
-    else:
-        if left_hand_explicit_type is None:
-            msg = (
-                "Can't infer scalar or collection for ORM mapped expression "
-                "assigned to attribute '{}' if both 'uselist' and "
-                "'collection_class' arguments are absent from the "
-                "relationship(); please specify a "
-                "type annotation on the left hand side."
-            )
-            util.fail(api, msg.format(node.name), node)
-
-    if python_type_for_type is None:
-        return infer_type_from_left_hand_type_only(
-            api, node, left_hand_explicit_type
-        )
-    elif left_hand_explicit_type is not None:
-        if type_is_a_collection:
-            assert isinstance(left_hand_explicit_type, Instance)
-            assert isinstance(python_type_for_type, Instance)
-            return _infer_collection_type_from_left_and_inferred_right(
-                api, node, left_hand_explicit_type, python_type_for_type
-            )
-        else:
-            return _infer_type_from_left_and_inferred_right(
-                api,
-                node,
-                left_hand_explicit_type,
-                python_type_for_type,
-            )
-    else:
-        return python_type_for_type
-
-
-def _infer_type_from_decl_composite_property(
-    api: SemanticAnalyzerPluginInterface,
-    stmt: AssignmentStmt,
-    node: Var,
-    left_hand_explicit_type: Optional[ProperType],
-) -> Optional[ProperType]:
-    """Infer the type of mapping from a Composite."""
-
-    assert isinstance(stmt.rvalue, CallExpr)
-    target_cls_arg = stmt.rvalue.args[0]
-    python_type_for_type = None
-
-    if isinstance(target_cls_arg, NameExpr) and isinstance(
-        target_cls_arg.node, TypeInfo
-    ):
-        related_object_type = target_cls_arg.node
-        python_type_for_type = Instance(related_object_type, [])
-    else:
-        python_type_for_type = None
-
-    if python_type_for_type is None:
-        return infer_type_from_left_hand_type_only(
-            api, node, left_hand_explicit_type
-        )
-    elif left_hand_explicit_type is not None:
-        return _infer_type_from_left_and_inferred_right(
-            api, node, left_hand_explicit_type, python_type_for_type
-        )
-    else:
-        return python_type_for_type
-
-
-def _infer_type_from_mapped(
-    api: SemanticAnalyzerPluginInterface,
-    stmt: AssignmentStmt,
-    node: Var,
-    left_hand_explicit_type: Optional[ProperType],
-    infer_from_right_side: RefExpr,
-) -> Optional[ProperType]:
-    """Infer the type of mapping from a right side expression
-    that returns Mapped.
-
-
-    """
-    assert isinstance(stmt.rvalue, CallExpr)
-
-    # (Pdb) print(stmt.rvalue.callee)
-    # NameExpr(query_expression [sqlalchemy.orm._orm_constructors.query_expression])  # noqa: E501
-    # (Pdb) stmt.rvalue.callee.node
-    # <mypy.nodes.FuncDef object at 0x7f8d92fb5940>
-    # (Pdb) stmt.rvalue.callee.node.type
-    # def [_T] (default_expr: sqlalchemy.sql.elements.ColumnElement[_T`-1] =) -> sqlalchemy.orm.base.Mapped[_T`-1]  # noqa: E501
-    # sqlalchemy.orm.base.Mapped[_T`-1]
-    # the_mapped_type = stmt.rvalue.callee.node.type.ret_type
-
-    # TODO: look at generic ref and either use that,
-    # or reconcile w/ what's present, etc.
-    the_mapped_type = util.type_for_callee(infer_from_right_side)  # noqa
-
-    return infer_type_from_left_hand_type_only(
-        api, node, left_hand_explicit_type
-    )
-
-
-def _infer_type_from_decl_column_property(
-    api: SemanticAnalyzerPluginInterface,
-    stmt: AssignmentStmt,
-    node: Var,
-    left_hand_explicit_type: Optional[ProperType],
-) -> Optional[ProperType]:
-    """Infer the type of mapping from a ColumnProperty.
-
-    This includes mappings against ``column_property()`` as well as the
-    ``deferred()`` function.
-
-    """
-    assert isinstance(stmt.rvalue, CallExpr)
-
-    if stmt.rvalue.args:
-        first_prop_arg = stmt.rvalue.args[0]
-
-        if isinstance(first_prop_arg, CallExpr):
-            type_id = names.type_id_for_callee(first_prop_arg.callee)
-
-            # look for column_property() / deferred() etc with Column as first
-            # argument
-            if type_id is names.COLUMN:
-                return _infer_type_from_decl_column(
-                    api,
-                    stmt,
-                    node,
-                    left_hand_explicit_type,
-                    right_hand_expression=first_prop_arg,
-                )
-
-    if isinstance(stmt.rvalue, CallExpr):
-        type_id = names.type_id_for_callee(stmt.rvalue.callee)
-        # this is probably not strictly necessary as we have to use the left
-        # hand type for query expression in any case.  any other no-arg
-        # column prop objects would go here also
-        if type_id is names.QUERY_EXPRESSION:
-            return _infer_type_from_decl_column(
-                api,
-                stmt,
-                node,
-                left_hand_explicit_type,
-            )
-
-    return infer_type_from_left_hand_type_only(
-        api, node, left_hand_explicit_type
-    )
-
-
-def _infer_type_from_decl_column(
-    api: SemanticAnalyzerPluginInterface,
-    stmt: AssignmentStmt,
-    node: Var,
-    left_hand_explicit_type: Optional[ProperType],
-    right_hand_expression: Optional[CallExpr] = None,
-) -> Optional[ProperType]:
-    """Infer the type of mapping from a Column.
-
-    E.g.::
-
-        @reg.mapped
-        class MyClass:
-            # ...
-
-            a = Column(Integer)
-
-            b = Column("b", String)
-
-            c: Mapped[int] = Column(Integer)
-
-            d: bool = Column(Boolean)
-
-    Will resolve in MyPy as::
-
-        @reg.mapped
-        class MyClass:
-            # ...
-
-            a: Mapped[int]
-
-            b: Mapped[str]
-
-            c: Mapped[int]
-
-            d: Mapped[bool]
-
-    """
-    assert isinstance(node, Var)
-
-    callee = None
-
-    if right_hand_expression is None:
-        if not isinstance(stmt.rvalue, CallExpr):
-            return None
-
-        right_hand_expression = stmt.rvalue
-
-    for column_arg in right_hand_expression.args[0:2]:
-        if isinstance(column_arg, CallExpr):
-            if isinstance(column_arg.callee, RefExpr):
-                # x = Column(String(50))
-                callee = column_arg.callee
-                type_args: Sequence[Expression] = column_arg.args
-                break
-        elif isinstance(column_arg, (NameExpr, MemberExpr)):
-            if isinstance(column_arg.node, TypeInfo):
-                # x = Column(String)
-                callee = column_arg
-                type_args = ()
-                break
-            else:
-                # x = Column(some_name, String), go to next argument
-                continue
-        elif isinstance(column_arg, (StrExpr,)):
-            # x = Column("name", String), go to next argument
-            continue
-        elif isinstance(column_arg, (LambdaExpr,)):
-            # x = Column("name", String, default=lambda: uuid.uuid4())
-            # go to next argument
-            continue
-        else:
-            assert False
-
-    if callee is None:
-        return None
-
-    if isinstance(callee.node, TypeInfo) and names.mro_has_id(
-        callee.node.mro, names.TYPEENGINE
-    ):
-        python_type_for_type = extract_python_type_from_typeengine(
-            api, callee.node, type_args
-        )
-
-        if left_hand_explicit_type is not None:
-            return _infer_type_from_left_and_inferred_right(
-                api, node, left_hand_explicit_type, python_type_for_type
-            )
-
-        else:
-            return UnionType([python_type_for_type, NoneType()])
-    else:
-        # it's not TypeEngine, it's typically implicitly typed
-        # like ForeignKey.  we can't infer from the right side.
-        return infer_type_from_left_hand_type_only(
-            api, node, left_hand_explicit_type
-        )
-
-
-def _infer_type_from_left_and_inferred_right(
-    api: SemanticAnalyzerPluginInterface,
-    node: Var,
-    left_hand_explicit_type: ProperType,
-    python_type_for_type: ProperType,
-    orig_left_hand_type: Optional[ProperType] = None,
-    orig_python_type_for_type: Optional[ProperType] = None,
-) -> Optional[ProperType]:
-    """Validate type when a left hand annotation is present and we also
-    could infer the right hand side::
-
-        attrname: SomeType = Column(SomeDBType)
-
-    """
-
-    if orig_left_hand_type is None:
-        orig_left_hand_type = left_hand_explicit_type
-    if orig_python_type_for_type is None:
-        orig_python_type_for_type = python_type_for_type
-
-    if not is_subtype(left_hand_explicit_type, python_type_for_type):
-        effective_type = api.named_type(
-            names.NAMED_TYPE_SQLA_MAPPED, [orig_python_type_for_type]
-        )
-
-        msg = (
-            "Left hand assignment '{}: {}' not compatible "
-            "with ORM mapped expression of type {}"
-        )
-        util.fail(
-            api,
-            msg.format(
-                node.name,
-                util.format_type(orig_left_hand_type, api.options),
-                util.format_type(effective_type, api.options),
-            ),
-            node,
-        )
-
-    return orig_left_hand_type
-
-
-def _infer_collection_type_from_left_and_inferred_right(
-    api: SemanticAnalyzerPluginInterface,
-    node: Var,
-    left_hand_explicit_type: Instance,
-    python_type_for_type: Instance,
-) -> Optional[ProperType]:
-    orig_left_hand_type = left_hand_explicit_type
-    orig_python_type_for_type = python_type_for_type
-
-    if left_hand_explicit_type.args:
-        left_hand_arg = get_proper_type(left_hand_explicit_type.args[0])
-        python_type_arg = get_proper_type(python_type_for_type.args[0])
-    else:
-        left_hand_arg = left_hand_explicit_type
-        python_type_arg = python_type_for_type
-
-    assert isinstance(left_hand_arg, (Instance, UnionType))
-    assert isinstance(python_type_arg, (Instance, UnionType))
-
-    return _infer_type_from_left_and_inferred_right(
-        api,
-        node,
-        left_hand_arg,
-        python_type_arg,
-        orig_left_hand_type=orig_left_hand_type,
-        orig_python_type_for_type=orig_python_type_for_type,
-    )
-
-
-def infer_type_from_left_hand_type_only(
-    api: SemanticAnalyzerPluginInterface,
-    node: Var,
-    left_hand_explicit_type: Optional[ProperType],
-) -> Optional[ProperType]:
-    """Determine the type based on explicit annotation only.
-
-    if no annotation were present, note that we need one there to know
-    the type.
-
-    """
-    if left_hand_explicit_type is None:
-        msg = (
-            "Can't infer type from ORM mapped expression "
-            "assigned to attribute '{}'; please specify a "
-            "Python type or "
-            "Mapped[<python type>] on the left hand side."
-        )
-        util.fail(api, msg.format(node.name), node)
-
-        return api.named_type(
-            names.NAMED_TYPE_SQLA_MAPPED, [AnyType(TypeOfAny.special_form)]
-        )
-
-    else:
-        # use type from the left hand side
-        return left_hand_explicit_type
-
-
-def extract_python_type_from_typeengine(
-    api: SemanticAnalyzerPluginInterface,
-    node: TypeInfo,
-    type_args: Sequence[Expression],
-) -> ProperType:
-    if node.fullname == "sqlalchemy.sql.sqltypes.Enum" and type_args:
-        first_arg = type_args[0]
-        if isinstance(first_arg, RefExpr) and isinstance(
-            first_arg.node, TypeInfo
-        ):
-            for base_ in first_arg.node.mro:
-                if base_.fullname == "enum.Enum":
-                    return Instance(first_arg.node, [])
-            # TODO: support other pep-435 types here
-        else:
-            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
-    )
-
-    type_engine_sym = api.lookup_fully_qualified_or_none(
-        "sqlalchemy.sql.type_api.TypeEngine"
-    )
-
-    assert type_engine_sym is not None and isinstance(
-        type_engine_sym.node, TypeInfo
-    )
-    type_engine = map_instance_to_supertype(
-        Instance(node, []),
-        type_engine_sym.node,
-    )
-    return get_proper_type(type_engine.args[-1])
diff --git a/lib/sqlalchemy/ext/mypy/names.py b/lib/sqlalchemy/ext/mypy/names.py
deleted file mode 100644 (file)
index 3197862..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-# ext/mypy/names.py
-# Copyright (C) 2021-2025 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: https://www.opensource.org/licenses/mit-license.php
-
-from __future__ import annotations
-
-from typing import Dict
-from typing import List
-from typing import Optional
-from typing import Set
-from typing import Tuple
-from typing import Union
-
-from mypy.nodes import ARG_POS
-from mypy.nodes import CallExpr
-from mypy.nodes import ClassDef
-from mypy.nodes import Decorator
-from mypy.nodes import Expression
-from mypy.nodes import FuncDef
-from mypy.nodes import MemberExpr
-from mypy.nodes import NameExpr
-from mypy.nodes import OverloadedFuncDef
-from mypy.nodes import SymbolNode
-from mypy.nodes import TypeAlias
-from mypy.nodes import TypeInfo
-from mypy.plugin import SemanticAnalyzerPluginInterface
-from mypy.types import CallableType
-from mypy.types import get_proper_type
-from mypy.types import Instance
-from mypy.types import UnboundType
-
-from ... import util
-
-COLUMN: int = util.symbol("COLUMN")
-RELATIONSHIP: int = util.symbol("RELATIONSHIP")
-REGISTRY: int = util.symbol("REGISTRY")
-COLUMN_PROPERTY: int = util.symbol("COLUMN_PROPERTY")
-TYPEENGINE: int = util.symbol("TYPEENGNE")
-MAPPED: int = util.symbol("MAPPED")
-DECLARATIVE_BASE: int = util.symbol("DECLARATIVE_BASE")
-DECLARATIVE_META: int = util.symbol("DECLARATIVE_META")
-MAPPED_DECORATOR: int = util.symbol("MAPPED_DECORATOR")
-SYNONYM_PROPERTY: int = util.symbol("SYNONYM_PROPERTY")
-COMPOSITE_PROPERTY: int = util.symbol("COMPOSITE_PROPERTY")
-DECLARED_ATTR: int = util.symbol("DECLARED_ATTR")
-MAPPER_PROPERTY: int = util.symbol("MAPPER_PROPERTY")
-AS_DECLARATIVE: int = util.symbol("AS_DECLARATIVE")
-AS_DECLARATIVE_BASE: int = util.symbol("AS_DECLARATIVE_BASE")
-DECLARATIVE_MIXIN: int = util.symbol("DECLARATIVE_MIXIN")
-QUERY_EXPRESSION: int = util.symbol("QUERY_EXPRESSION")
-
-# 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.base.Mapped"
-
-_RelFullNames = {
-    "sqlalchemy.orm.relationships.Relationship",
-    "sqlalchemy.orm.relationships.RelationshipProperty",
-    "sqlalchemy.orm.relationships._RelationshipDeclared",
-    "sqlalchemy.orm.Relationship",
-    "sqlalchemy.orm.RelationshipProperty",
-}
-
-_lookup: Dict[str, Tuple[int, Set[str]]] = {
-    "Column": (
-        COLUMN,
-        {
-            "sqlalchemy.sql.schema.Column",
-            "sqlalchemy.sql.Column",
-        },
-    ),
-    "Relationship": (RELATIONSHIP, _RelFullNames),
-    "RelationshipProperty": (RELATIONSHIP, _RelFullNames),
-    "_RelationshipDeclared": (RELATIONSHIP, _RelFullNames),
-    "registry": (
-        REGISTRY,
-        {
-            "sqlalchemy.orm.decl_api.registry",
-            "sqlalchemy.orm.registry",
-        },
-    ),
-    "ColumnProperty": (
-        COLUMN_PROPERTY,
-        {
-            "sqlalchemy.orm.properties.MappedSQLExpression",
-            "sqlalchemy.orm.MappedSQLExpression",
-            "sqlalchemy.orm.properties.ColumnProperty",
-            "sqlalchemy.orm.ColumnProperty",
-        },
-    ),
-    "MappedSQLExpression": (
-        COLUMN_PROPERTY,
-        {
-            "sqlalchemy.orm.properties.MappedSQLExpression",
-            "sqlalchemy.orm.MappedSQLExpression",
-            "sqlalchemy.orm.properties.ColumnProperty",
-            "sqlalchemy.orm.ColumnProperty",
-        },
-    ),
-    "Synonym": (
-        SYNONYM_PROPERTY,
-        {
-            "sqlalchemy.orm.descriptor_props.Synonym",
-            "sqlalchemy.orm.Synonym",
-            "sqlalchemy.orm.descriptor_props.SynonymProperty",
-            "sqlalchemy.orm.SynonymProperty",
-        },
-    ),
-    "SynonymProperty": (
-        SYNONYM_PROPERTY,
-        {
-            "sqlalchemy.orm.descriptor_props.Synonym",
-            "sqlalchemy.orm.Synonym",
-            "sqlalchemy.orm.descriptor_props.SynonymProperty",
-            "sqlalchemy.orm.SynonymProperty",
-        },
-    ),
-    "Composite": (
-        COMPOSITE_PROPERTY,
-        {
-            "sqlalchemy.orm.descriptor_props.Composite",
-            "sqlalchemy.orm.Composite",
-            "sqlalchemy.orm.descriptor_props.CompositeProperty",
-            "sqlalchemy.orm.CompositeProperty",
-        },
-    ),
-    "CompositeProperty": (
-        COMPOSITE_PROPERTY,
-        {
-            "sqlalchemy.orm.descriptor_props.Composite",
-            "sqlalchemy.orm.Composite",
-            "sqlalchemy.orm.descriptor_props.CompositeProperty",
-            "sqlalchemy.orm.CompositeProperty",
-        },
-    ),
-    "MapperProperty": (
-        MAPPER_PROPERTY,
-        {
-            "sqlalchemy.orm.interfaces.MapperProperty",
-            "sqlalchemy.orm.MapperProperty",
-        },
-    ),
-    "TypeEngine": (TYPEENGINE, {"sqlalchemy.sql.type_api.TypeEngine"}),
-    "Mapped": (MAPPED, {NAMED_TYPE_SQLA_MAPPED}),
-    "declarative_base": (
-        DECLARATIVE_BASE,
-        {
-            "sqlalchemy.ext.declarative.declarative_base",
-            "sqlalchemy.orm.declarative_base",
-            "sqlalchemy.orm.decl_api.declarative_base",
-        },
-    ),
-    "DeclarativeMeta": (
-        DECLARATIVE_META,
-        {
-            "sqlalchemy.ext.declarative.DeclarativeMeta",
-            "sqlalchemy.orm.DeclarativeMeta",
-            "sqlalchemy.orm.decl_api.DeclarativeMeta",
-        },
-    ),
-    "mapped": (
-        MAPPED_DECORATOR,
-        {
-            "sqlalchemy.orm.decl_api.registry.mapped",
-            "sqlalchemy.orm.registry.mapped",
-        },
-    ),
-    "as_declarative": (
-        AS_DECLARATIVE,
-        {
-            "sqlalchemy.ext.declarative.as_declarative",
-            "sqlalchemy.orm.decl_api.as_declarative",
-            "sqlalchemy.orm.as_declarative",
-        },
-    ),
-    "as_declarative_base": (
-        AS_DECLARATIVE_BASE,
-        {
-            "sqlalchemy.orm.decl_api.registry.as_declarative_base",
-            "sqlalchemy.orm.registry.as_declarative_base",
-        },
-    ),
-    "declared_attr": (
-        DECLARED_ATTR,
-        {
-            "sqlalchemy.orm.decl_api.declared_attr",
-            "sqlalchemy.orm.declared_attr",
-        },
-    ),
-    "declarative_mixin": (
-        DECLARATIVE_MIXIN,
-        {
-            "sqlalchemy.orm.decl_api.declarative_mixin",
-            "sqlalchemy.orm.declarative_mixin",
-        },
-    ),
-    "query_expression": (
-        QUERY_EXPRESSION,
-        {
-            "sqlalchemy.orm.query_expression",
-            "sqlalchemy.orm._orm_constructors.query_expression",
-        },
-    ),
-}
-
-
-def has_base_type_id(info: TypeInfo, type_id: int) -> bool:
-    for mr in info.mro:
-        check_type_id, fullnames = _lookup.get(mr.name, (None, None))
-        if check_type_id == type_id:
-            break
-    else:
-        return False
-
-    if fullnames is None:
-        return False
-
-    return mr.fullname in fullnames
-
-
-def mro_has_id(mro: List[TypeInfo], type_id: int) -> bool:
-    for mr in mro:
-        check_type_id, fullnames = _lookup.get(mr.name, (None, None))
-        if check_type_id == type_id:
-            break
-    else:
-        return False
-
-    if fullnames is None:
-        return False
-
-    return mr.fullname in fullnames
-
-
-def type_id_for_unbound_type(
-    type_: UnboundType, cls: ClassDef, api: SemanticAnalyzerPluginInterface
-) -> Optional[int]:
-    sym = api.lookup_qualified(type_.name, type_)
-    if sym is not None:
-        if isinstance(sym.node, TypeAlias):
-            target_type = get_proper_type(sym.node.target)
-            if isinstance(target_type, Instance):
-                return type_id_for_named_node(target_type.type)
-        elif isinstance(sym.node, TypeInfo):
-            return type_id_for_named_node(sym.node)
-
-    return None
-
-
-def type_id_for_callee(callee: Expression) -> Optional[int]:
-    if isinstance(callee, (MemberExpr, NameExpr)):
-        if isinstance(callee.node, Decorator) and isinstance(
-            callee.node.func, FuncDef
-        ):
-            if callee.node.func.type and isinstance(
-                callee.node.func.type, CallableType
-            ):
-                ret_type = get_proper_type(callee.node.func.type.ret_type)
-
-                if isinstance(ret_type, Instance):
-                    return type_id_for_fullname(ret_type.type.fullname)
-
-            return None
-
-        elif isinstance(callee.node, OverloadedFuncDef):
-            if (
-                callee.node.impl
-                and callee.node.impl.type
-                and isinstance(callee.node.impl.type, CallableType)
-            ):
-                ret_type = get_proper_type(callee.node.impl.type.ret_type)
-
-                if isinstance(ret_type, Instance):
-                    return type_id_for_fullname(ret_type.type.fullname)
-
-            return None
-        elif isinstance(callee.node, FuncDef):
-            if callee.node.type and isinstance(callee.node.type, CallableType):
-                ret_type = get_proper_type(callee.node.type.ret_type)
-
-                if isinstance(ret_type, Instance):
-                    return type_id_for_fullname(ret_type.type.fullname)
-
-            return None
-        elif isinstance(callee.node, TypeAlias):
-            target_type = get_proper_type(callee.node.target)
-            if isinstance(target_type, Instance):
-                return type_id_for_fullname(target_type.type.fullname)
-        elif isinstance(callee.node, TypeInfo):
-            return type_id_for_named_node(callee)
-    return None
-
-
-def type_id_for_named_node(
-    node: Union[NameExpr, MemberExpr, SymbolNode]
-) -> Optional[int]:
-    type_id, fullnames = _lookup.get(node.name, (None, None))
-
-    if type_id is None or fullnames is None:
-        return None
-    elif node.fullname in fullnames:
-        return type_id
-    else:
-        return None
-
-
-def type_id_for_fullname(fullname: str) -> Optional[int]:
-    tokens = fullname.split(".")
-    immediate = tokens[-1]
-
-    type_id, fullnames = _lookup.get(immediate, (None, None))
-
-    if type_id is None or fullnames is None:
-        return None
-    elif fullname in fullnames:
-        return type_id
-    else:
-        return None
-
-
-def expr_to_mapped_constructor(expr: Expression) -> CallExpr:
-    column_descriptor = NameExpr("__sa_Mapped")
-    column_descriptor.fullname = NAMED_TYPE_SQLA_MAPPED
-    member_expr = MemberExpr(column_descriptor, "_empty_constructor")
-    return CallExpr(
-        member_expr,
-        [expr],
-        [ARG_POS],
-        ["arg1"],
-    )
diff --git a/lib/sqlalchemy/ext/mypy/plugin.py b/lib/sqlalchemy/ext/mypy/plugin.py
deleted file mode 100644 (file)
index 1ec2c02..0000000
+++ /dev/null
@@ -1,303 +0,0 @@
-# ext/mypy/plugin.py
-# Copyright (C) 2021-2025 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: https://www.opensource.org/licenses/mit-license.php
-
-"""
-Mypy plugin for SQLAlchemy ORM.
-
-"""
-from __future__ import annotations
-
-from typing import Callable
-from typing import List
-from typing import Optional
-from typing import Tuple
-from typing import Type as TypingType
-from typing import Union
-
-from mypy import nodes
-from mypy.mro import calculate_mro
-from mypy.mro import MroError
-from mypy.nodes import Block
-from mypy.nodes import ClassDef
-from mypy.nodes import GDEF
-from mypy.nodes import MypyFile
-from mypy.nodes import NameExpr
-from mypy.nodes import SymbolTable
-from mypy.nodes import SymbolTableNode
-from mypy.nodes import TypeInfo
-from mypy.plugin import AttributeContext
-from mypy.plugin import ClassDefContext
-from mypy.plugin import DynamicClassDefContext
-from mypy.plugin import Plugin
-from mypy.plugin import SemanticAnalyzerPluginInterface
-from mypy.types import get_proper_type
-from mypy.types import Instance
-from mypy.types import Type
-
-from . import decl_class
-from . import names
-from . import util
-
-try:
-    __import__("sqlalchemy-stubs")
-except ImportError:
-    pass
-else:
-    raise ImportError(
-        "The SQLAlchemy mypy plugin in SQLAlchemy "
-        "2.0 does not work with sqlalchemy-stubs or "
-        "sqlalchemy2-stubs installed, as well as with any other third party "
-        "SQLAlchemy stubs.  Please uninstall all SQLAlchemy stubs "
-        "packages."
-    )
-
-
-class SQLAlchemyPlugin(Plugin):
-    def get_dynamic_class_hook(
-        self, fullname: str
-    ) -> Optional[Callable[[DynamicClassDefContext], None]]:
-        if names.type_id_for_fullname(fullname) is names.DECLARATIVE_BASE:
-            return _dynamic_class_hook
-        return None
-
-    def get_customize_class_mro_hook(
-        self, fullname: str
-    ) -> Optional[Callable[[ClassDefContext], None]]:
-        return _fill_in_decorators
-
-    def get_class_decorator_hook(
-        self, fullname: str
-    ) -> Optional[Callable[[ClassDefContext], None]]:
-        sym = self.lookup_fully_qualified(fullname)
-
-        if sym is not None and sym.node is not None:
-            type_id = names.type_id_for_named_node(sym.node)
-            if type_id is names.MAPPED_DECORATOR:
-                return _cls_decorator_hook
-            elif type_id in (
-                names.AS_DECLARATIVE,
-                names.AS_DECLARATIVE_BASE,
-            ):
-                return _base_cls_decorator_hook
-            elif type_id is names.DECLARATIVE_MIXIN:
-                return _declarative_mixin_hook
-
-        return None
-
-    def get_metaclass_hook(
-        self, fullname: str
-    ) -> Optional[Callable[[ClassDefContext], None]]:
-        if names.type_id_for_fullname(fullname) is names.DECLARATIVE_META:
-            # Set any classes that explicitly have metaclass=DeclarativeMeta
-            # as declarative so the check in `get_base_class_hook()` works
-            return _metaclass_cls_hook
-
-        return None
-
-    def get_base_class_hook(
-        self, fullname: str
-    ) -> Optional[Callable[[ClassDefContext], None]]:
-        sym = self.lookup_fully_qualified(fullname)
-
-        if (
-            sym
-            and isinstance(sym.node, TypeInfo)
-            and util.has_declarative_base(sym.node)
-        ):
-            return _base_cls_hook
-
-        return None
-
-    def get_attribute_hook(
-        self, fullname: str
-    ) -> Optional[Callable[[AttributeContext], Type]]:
-        if fullname.startswith(
-            "sqlalchemy.orm.attributes.QueryableAttribute."
-        ):
-            return _queryable_getattr_hook
-
-        return None
-
-    def get_additional_deps(
-        self, file: MypyFile
-    ) -> List[Tuple[int, str, int]]:
-        return [
-            #
-            (10, "sqlalchemy.orm", -1),
-            (10, "sqlalchemy.orm.attributes", -1),
-            (10, "sqlalchemy.orm.decl_api", -1),
-        ]
-
-
-def plugin(version: str) -> TypingType[SQLAlchemyPlugin]:
-    return SQLAlchemyPlugin
-
-
-def _dynamic_class_hook(ctx: DynamicClassDefContext) -> None:
-    """Generate a declarative Base class when the declarative_base() function
-    is encountered."""
-
-    _add_globals(ctx)
-
-    cls = ClassDef(ctx.name, Block([]))
-    cls.fullname = ctx.api.qualified_name(ctx.name)
-
-    info = TypeInfo(SymbolTable(), cls, ctx.api.cur_mod_id)
-    cls.info = info
-    _set_declarative_metaclass(ctx.api, cls)
-
-    cls_arg = util.get_callexpr_kwarg(ctx.call, "cls", expr_types=(NameExpr,))
-    if cls_arg is not None and isinstance(cls_arg.node, TypeInfo):
-        util.set_is_base(cls_arg.node)
-        decl_class.scan_declarative_assignments_and_apply_types(
-            cls_arg.node.defn, ctx.api, is_mixin_scan=True
-        )
-        info.bases = [Instance(cls_arg.node, [])]
-    else:
-        obj = ctx.api.named_type(names.NAMED_TYPE_BUILTINS_OBJECT)
-
-        info.bases = [obj]
-
-    try:
-        calculate_mro(info)
-    except MroError:
-        util.fail(
-            ctx.api, "Not able to calculate MRO for declarative base", ctx.call
-        )
-        obj = ctx.api.named_type(names.NAMED_TYPE_BUILTINS_OBJECT)
-        info.bases = [obj]
-        info.fallback_to_any = True
-
-    ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info))
-    util.set_is_base(info)
-
-
-def _fill_in_decorators(ctx: ClassDefContext) -> None:
-    for decorator in ctx.cls.decorators:
-        # set the ".fullname" attribute of a class decorator
-        # that is a MemberExpr.   This causes the logic in
-        # semanal.py->apply_class_plugin_hooks to invoke the
-        # get_class_decorator_hook for our "registry.map_class()"
-        # and "registry.as_declarative_base()" methods.
-        # this seems like a bug in mypy that these decorators are otherwise
-        # skipped.
-
-        if (
-            isinstance(decorator, nodes.CallExpr)
-            and isinstance(decorator.callee, nodes.MemberExpr)
-            and decorator.callee.name == "as_declarative_base"
-        ):
-            target = decorator.callee
-        elif (
-            isinstance(decorator, nodes.MemberExpr)
-            and decorator.name == "mapped"
-        ):
-            target = decorator
-        else:
-            continue
-
-        if isinstance(target.expr, NameExpr):
-            sym = ctx.api.lookup_qualified(
-                target.expr.name, target, suppress_errors=True
-            )
-        else:
-            continue
-
-        if sym and sym.node:
-            sym_type = get_proper_type(sym.type)
-            if isinstance(sym_type, Instance):
-                target.fullname = f"{sym_type.type.fullname}.{target.name}"
-            else:
-                # if the registry is in the same file as where the
-                # decorator is used, it might not have semantic
-                # symbols applied and we can't get a fully qualified
-                # name or an inferred type, so we are actually going to
-                # flag an error in this case that they need to annotate
-                # it.  The "registry" is declared just
-                # once (or few times), so they have to just not use
-                # type inference for its assignment in this one case.
-                util.fail(
-                    ctx.api,
-                    "Class decorator called %s(), but we can't "
-                    "tell if it's from an ORM registry.  Please "
-                    "annotate the registry assignment, e.g. "
-                    "my_registry: registry = registry()" % target.name,
-                    sym.node,
-                )
-
-
-def _cls_decorator_hook(ctx: ClassDefContext) -> None:
-    _add_globals(ctx)
-    assert isinstance(ctx.reason, nodes.MemberExpr)
-    expr = ctx.reason.expr
-
-    assert isinstance(expr, nodes.RefExpr) and isinstance(expr.node, nodes.Var)
-
-    node_type = get_proper_type(expr.node.type)
-
-    assert (
-        isinstance(node_type, Instance)
-        and names.type_id_for_named_node(node_type.type) is names.REGISTRY
-    )
-
-    decl_class.scan_declarative_assignments_and_apply_types(ctx.cls, ctx.api)
-
-
-def _base_cls_decorator_hook(ctx: ClassDefContext) -> None:
-    _add_globals(ctx)
-
-    cls = ctx.cls
-
-    _set_declarative_metaclass(ctx.api, cls)
-
-    util.set_is_base(ctx.cls.info)
-    decl_class.scan_declarative_assignments_and_apply_types(
-        cls, ctx.api, is_mixin_scan=True
-    )
-
-
-def _declarative_mixin_hook(ctx: ClassDefContext) -> None:
-    _add_globals(ctx)
-    util.set_is_base(ctx.cls.info)
-    decl_class.scan_declarative_assignments_and_apply_types(
-        ctx.cls, ctx.api, is_mixin_scan=True
-    )
-
-
-def _metaclass_cls_hook(ctx: ClassDefContext) -> None:
-    util.set_is_base(ctx.cls.info)
-
-
-def _base_cls_hook(ctx: ClassDefContext) -> None:
-    _add_globals(ctx)
-    decl_class.scan_declarative_assignments_and_apply_types(ctx.cls, ctx.api)
-
-
-def _queryable_getattr_hook(ctx: AttributeContext) -> Type:
-    # how do I....tell it it has no attribute of a certain name?
-    # can't find any Type that seems to match that
-    return ctx.default_attr_type
-
-
-def _add_globals(ctx: Union[ClassDefContext, DynamicClassDefContext]) -> None:
-    """Add __sa_DeclarativeMeta and __sa_Mapped symbol to the global space
-    for all class defs
-
-    """
-
-    util.add_global(ctx, "sqlalchemy.orm", "Mapped", "__sa_Mapped")
-
-
-def _set_declarative_metaclass(
-    api: SemanticAnalyzerPluginInterface, target_cls: ClassDef
-) -> None:
-    info = target_cls.info
-    sym = api.lookup_fully_qualified_or_none(
-        "sqlalchemy.orm.decl_api.DeclarativeMeta"
-    )
-    assert sym is not None and isinstance(sym.node, TypeInfo)
-    info.declared_metaclass = info.metaclass_type = Instance(sym.node, [])
diff --git a/lib/sqlalchemy/ext/mypy/util.py b/lib/sqlalchemy/ext/mypy/util.py
deleted file mode 100644 (file)
index 16761b9..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-# ext/mypy/util.py
-# Copyright (C) 2021-2025 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: https://www.opensource.org/licenses/mit-license.php
-
-from __future__ import annotations
-
-import re
-from typing import Any
-from typing import Iterable
-from typing import Iterator
-from typing import List
-from typing import Optional
-from typing import overload
-from typing import Tuple
-from typing import Type as TypingType
-from typing import TypeVar
-from typing import Union
-
-from mypy import version
-from mypy.messages import format_type as _mypy_format_type
-from mypy.nodes import CallExpr
-from mypy.nodes import ClassDef
-from mypy.nodes import CLASSDEF_NO_INFO
-from mypy.nodes import Context
-from mypy.nodes import Expression
-from mypy.nodes import FuncDef
-from mypy.nodes import IfStmt
-from mypy.nodes import JsonDict
-from mypy.nodes import MemberExpr
-from mypy.nodes import NameExpr
-from mypy.nodes import Statement
-from mypy.nodes import SymbolTableNode
-from mypy.nodes import TypeAlias
-from mypy.nodes import TypeInfo
-from mypy.options import Options
-from mypy.plugin import ClassDefContext
-from mypy.plugin import DynamicClassDefContext
-from mypy.plugin import SemanticAnalyzerPluginInterface
-from mypy.plugins.common import deserialize_and_fixup_type
-from mypy.typeops import map_type_from_supertype
-from mypy.types import CallableType
-from mypy.types import get_proper_type
-from mypy.types import Instance
-from mypy.types import NoneType
-from mypy.types import Type
-from mypy.types import TypeVarType
-from mypy.types import UnboundType
-from mypy.types import UnionType
-
-_vers = tuple(
-    [int(x) for x in version.__version__.split(".") if re.match(r"^\d+$", x)]
-)
-mypy_14 = _vers >= (1, 4)
-
-
-_TArgType = TypeVar("_TArgType", bound=Union[CallExpr, NameExpr])
-
-
-class SQLAlchemyAttribute:
-    def __init__(
-        self,
-        name: str,
-        line: int,
-        column: int,
-        typ: Optional[Type],
-        info: TypeInfo,
-    ) -> None:
-        self.name = name
-        self.line = line
-        self.column = column
-        self.type = typ
-        self.info = info
-
-    def serialize(self) -> JsonDict:
-        assert self.type
-        return {
-            "name": self.name,
-            "line": self.line,
-            "column": self.column,
-            "type": serialize_type(self.type),
-        }
-
-    def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None:
-        """Expands type vars in the context of a subtype when an attribute is
-        inherited from a generic super type.
-        """
-        if not isinstance(self.type, TypeVarType):
-            return
-
-        self.type = map_type_from_supertype(self.type, sub_type, self.info)
-
-    @classmethod
-    def deserialize(
-        cls,
-        info: TypeInfo,
-        data: JsonDict,
-        api: SemanticAnalyzerPluginInterface,
-    ) -> SQLAlchemyAttribute:
-        data = data.copy()
-        typ = deserialize_and_fixup_type(data.pop("type"), api)
-        return cls(typ=typ, info=info, **data)
-
-
-def name_is_dunder(name: str) -> bool:
-    return bool(re.match(r"^__.+?__$", name))
-
-
-def _set_info_metadata(info: TypeInfo, key: str, data: Any) -> None:
-    info.metadata.setdefault("sqlalchemy", {})[key] = data
-
-
-def _get_info_metadata(info: TypeInfo, key: str) -> Optional[Any]:
-    return info.metadata.get("sqlalchemy", {}).get(key, None)
-
-
-def _get_info_mro_metadata(info: TypeInfo, key: str) -> Optional[Any]:
-    if info.mro:
-        for base in info.mro:
-            metadata = _get_info_metadata(base, key)
-            if metadata is not None:
-                return metadata
-    return None
-
-
-def establish_as_sqlalchemy(info: TypeInfo) -> None:
-    info.metadata.setdefault("sqlalchemy", {})
-
-
-def set_is_base(info: TypeInfo) -> None:
-    _set_info_metadata(info, "is_base", True)
-
-
-def get_is_base(info: TypeInfo) -> bool:
-    is_base = _get_info_metadata(info, "is_base")
-    return is_base is True
-
-
-def has_declarative_base(info: TypeInfo) -> bool:
-    is_base = _get_info_mro_metadata(info, "is_base")
-    return is_base is True
-
-
-def set_has_table(info: TypeInfo) -> None:
-    _set_info_metadata(info, "has_table", True)
-
-
-def get_has_table(info: TypeInfo) -> bool:
-    is_base = _get_info_metadata(info, "has_table")
-    return is_base is True
-
-
-def get_mapped_attributes(
-    info: TypeInfo, api: SemanticAnalyzerPluginInterface
-) -> Optional[List[SQLAlchemyAttribute]]:
-    mapped_attributes: Optional[List[JsonDict]] = _get_info_metadata(
-        info, "mapped_attributes"
-    )
-    if mapped_attributes is None:
-        return None
-
-    attributes: List[SQLAlchemyAttribute] = []
-
-    for data in mapped_attributes:
-        attr = SQLAlchemyAttribute.deserialize(info, data, api)
-        attr.expand_typevar_from_subtype(info)
-        attributes.append(attr)
-
-    return attributes
-
-
-def format_type(typ_: Type, options: Options) -> str:
-    if mypy_14:
-        return _mypy_format_type(typ_, options)
-    else:
-        return _mypy_format_type(typ_)  # type: ignore
-
-
-def set_mapped_attributes(
-    info: TypeInfo, attributes: List[SQLAlchemyAttribute]
-) -> None:
-    _set_info_metadata(
-        info,
-        "mapped_attributes",
-        [attribute.serialize() for attribute in attributes],
-    )
-
-
-def fail(api: SemanticAnalyzerPluginInterface, msg: str, ctx: Context) -> None:
-    msg = "[SQLAlchemy Mypy plugin] %s" % msg
-    return api.fail(msg, ctx)
-
-
-def add_global(
-    ctx: Union[ClassDefContext, DynamicClassDefContext],
-    module: str,
-    symbol_name: str,
-    asname: str,
-) -> None:
-    module_globals = ctx.api.modules[ctx.api.cur_mod_id].names
-
-    if asname not in module_globals:
-        lookup_sym: SymbolTableNode = ctx.api.modules[module].names[
-            symbol_name
-        ]
-
-        module_globals[asname] = lookup_sym
-
-
-@overload
-def get_callexpr_kwarg(
-    callexpr: CallExpr, name: str, *, expr_types: None = ...
-) -> Optional[Union[CallExpr, NameExpr]]: ...
-
-
-@overload
-def get_callexpr_kwarg(
-    callexpr: CallExpr,
-    name: str,
-    *,
-    expr_types: Tuple[TypingType[_TArgType], ...],
-) -> Optional[_TArgType]: ...
-
-
-def get_callexpr_kwarg(
-    callexpr: CallExpr,
-    name: str,
-    *,
-    expr_types: Optional[Tuple[TypingType[Any], ...]] = None,
-) -> Optional[Any]:
-    try:
-        arg_idx = callexpr.arg_names.index(name)
-    except ValueError:
-        return None
-
-    kwarg = callexpr.args[arg_idx]
-    if isinstance(
-        kwarg, expr_types if expr_types is not None else (NameExpr, CallExpr)
-    ):
-        return kwarg
-
-    return None
-
-
-def flatten_typechecking(stmts: Iterable[Statement]) -> Iterator[Statement]:
-    for stmt in stmts:
-        if (
-            isinstance(stmt, IfStmt)
-            and isinstance(stmt.expr[0], NameExpr)
-            and stmt.expr[0].fullname == "typing.TYPE_CHECKING"
-        ):
-            yield from stmt.body[0].body
-        else:
-            yield stmt
-
-
-def type_for_callee(callee: Expression) -> Optional[Union[Instance, TypeInfo]]:
-    if isinstance(callee, (MemberExpr, NameExpr)):
-        if isinstance(callee.node, FuncDef):
-            if callee.node.type and isinstance(callee.node.type, CallableType):
-                ret_type = get_proper_type(callee.node.type.ret_type)
-
-                if isinstance(ret_type, Instance):
-                    return ret_type
-
-            return None
-        elif isinstance(callee.node, TypeAlias):
-            target_type = get_proper_type(callee.node.target)
-            if isinstance(target_type, Instance):
-                return target_type
-        elif isinstance(callee.node, TypeInfo):
-            return callee.node
-    return None
-
-
-def unbound_to_instance(
-    api: SemanticAnalyzerPluginInterface, typ: Type
-) -> Type:
-    """Take the UnboundType that we seem to get as the ret_type from a FuncDef
-    and convert it into an Instance/TypeInfo kind of structure that seems
-    to work as the left-hand type of an AssignmentStatement.
-
-    """
-
-    if not isinstance(typ, UnboundType):
-        return typ
-
-    # TODO: figure out a more robust way to check this.  The node is some
-    # kind of _SpecialForm, there's a typing.Optional that's _SpecialForm,
-    # but I can't figure out how to get them to match up
-    if typ.name == "Optional":
-        # convert from "Optional?" to the more familiar
-        # UnionType[..., NoneType()]
-        return unbound_to_instance(
-            api,
-            UnionType(
-                [unbound_to_instance(api, typ_arg) for typ_arg in typ.args]
-                + [NoneType()]
-            ),
-        )
-
-    node = api.lookup_qualified(typ.name, typ)
-
-    if (
-        node is not None
-        and isinstance(node, SymbolTableNode)
-        and isinstance(node.node, TypeInfo)
-    ):
-        bound_type = node.node
-
-        return Instance(
-            bound_type,
-            [
-                (
-                    unbound_to_instance(api, arg)
-                    if isinstance(arg, UnboundType)
-                    else arg
-                )
-                for arg in typ.args
-            ],
-        )
-    else:
-        return typ
-
-
-def info_for_cls(
-    cls: ClassDef, api: SemanticAnalyzerPluginInterface
-) -> Optional[TypeInfo]:
-    if cls.info is CLASSDEF_NO_INFO:
-        sym = api.lookup_qualified(cls.name, cls)
-        if sym is None:
-            return None
-        assert sym and isinstance(sym.node, TypeInfo)
-        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 7718b2bd8f51dc9ee83881295679828e240a82aa..3a1ae2e9bda492cf05eaa2c7a951bd1c3e16ec18 100644 (file)
@@ -21,6 +21,17 @@ from .. import config
 from ..assertions import eq_
 from ... import util
 
+try:
+    from mypy import version
+
+    _mypy_vers_tuple = tuple(
+        int(x) for x in version.__version__.split(".") if x.isdecimal()
+    )
+except ImportError:
+    _mypy_vers_tuple = (0, 0, 0)
+
+mypy_14 = _mypy_vers_tuple >= (1, 4)
+
 
 @config.add_to_marker.mypy
 class MypyTest(TestBase):
@@ -39,22 +50,6 @@ class MypyTest(TestBase):
         mypy_path = ""
 
         with tempfile.TemporaryDirectory() as cachedir:
-            with open(
-                Path(cachedir) / "sqla_mypy_config.cfg", "w"
-            ) as config_file:
-                config_file.write(
-                    f"""
-                    [mypy]\n
-                    plugins = sqlalchemy.ext.mypy.plugin\n
-                    show_error_codes = True\n
-                    {mypy_path}
-                    disable_error_code = no-untyped-call
-
-                    [mypy-sqlalchemy.*]
-                    ignore_errors = True
-
-                    """
-                )
             with open(
                 Path(cachedir) / "plain_mypy_config.cfg", "w"
             ) as config_file:
@@ -75,7 +70,7 @@ class MypyTest(TestBase):
     def mypy_runner(self, cachedir):
         from mypy import api
 
-        def run(path, use_plugin=False, use_cachedir=None):
+        def run(path, use_cachedir=None):
             if use_cachedir is None:
                 use_cachedir = cachedir
             args = [
@@ -84,14 +79,7 @@ class MypyTest(TestBase):
                 "--cache-dir",
                 use_cachedir,
                 "--config-file",
-                os.path.join(
-                    use_cachedir,
-                    (
-                        "sqla_mypy_config.cfg"
-                        if use_plugin
-                        else "plain_mypy_config.cfg"
-                    ),
-                ),
+                os.path.join(use_cachedir, "plain_mypy_config.cfg"),
             ]
 
             # mypy as of 0.990 is more aggressively blocking messaging
@@ -116,9 +104,9 @@ class MypyTest(TestBase):
 
     @config.fixture
     def mypy_typecheck_file(self, mypy_runner):
-        def run(path, use_plugin=False):
+        def run(path):
             expected_messages = self._collect_messages(path)
-            stdout, stderr, exitcode = mypy_runner(path, use_plugin=use_plugin)
+            stdout, stderr, exitcode = mypy_runner(path)
             self._check_output(
                 path, expected_messages, stdout, stderr, exitcode
             )
@@ -140,8 +128,6 @@ class MypyTest(TestBase):
         return files
 
     def _collect_messages(self, path):
-        from sqlalchemy.ext.mypy.util import mypy_14
-
         expected_messages = []
         expected_re = re.compile(r"\s*# EXPECTED(_MYPY)?(_RE)?(_TYPE)?: (.+)")
         py_ver_re = re.compile(r"^#\s*PYTHON_VERSION\s?>=\s?(\d+\.\d+)")
index 7e6b12b37aaefae3639310df6deda1b7adb69bef..ade402dd6bec69892d06c42caa956fc8336627af 100644 (file)
@@ -126,10 +126,7 @@ black-line-length = 79
 
 [tool.slotscheck]
 exclude-modules = '''
-^sqlalchemy\.(
-  testing
-  |ext\.mypy  # see slotscheck/issues/178
-)
+^sqlalchemy\.testing
 '''
 
 
index bbb765c0aa99be00767c945821baf91f10c1ee9b..76e1f1825b066e0a19257dcf5e23192935998f58 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -20,7 +20,6 @@ per-file-ignores =
     **/__init__.py:F401
     test/*:FA100
     test/typing/plain_files/*:F821,E501,FA100
-    test/ext/mypy/plugin_files/*:F821,E501,FA100
     lib/sqlalchemy/events.py:F401
     lib/sqlalchemy/schema.py:F401
     lib/sqlalchemy/types.py:F401
diff --git a/test/ext/mypy/incremental/stubs_14/__init__.py b/test/ext/mypy/incremental/stubs_14/__init__.py
deleted file mode 100644 (file)
index 3169645..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import TYPE_CHECKING
-
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy.orm import as_declarative
-from sqlalchemy.orm import declared_attr
-from sqlalchemy.orm import Mapped
-from .address import Address
-from .user import User
-
-if TYPE_CHECKING:
-    from sqlalchemy.orm.decl_api import DeclarativeMeta
-
-
-@as_declarative()
-class Base:
-    @declared_attr
-    def __tablename__(self) -> Mapped[str]:
-        return self.__name__.lower()
-
-    id = Column(Integer, primary_key=True)
-
-
-__all__ = ["User", "Address"]
diff --git a/test/ext/mypy/incremental/stubs_14/address.py b/test/ext/mypy/incremental/stubs_14/address.py
deleted file mode 100644 (file)
index 061dbc6..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-from typing import TYPE_CHECKING
-
-from . import Base
-from .user import HasUser
-
-if TYPE_CHECKING:
-    from sqlalchemy import Column  # noqa
-    from sqlalchemy import Integer  # noqa
-    from sqlalchemy.orm import RelationshipProperty  # noqa
-    from .user import User  # noqa
-
-
-class Address(Base, HasUser):
-    pass
diff --git a/test/ext/mypy/incremental/stubs_14/patch1.testpatch b/test/ext/mypy/incremental/stubs_14/patch1.testpatch
deleted file mode 100644 (file)
index 528236a..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/test/ext/mypy/incremental/stubs_14/user.py b/test/ext/mypy/incremental/stubs_14/user.py
-index 2c60403e4..c7e8f8874 100644
---- a/user.py
-+++ b/user.py
-@@ -18,6 +18,8 @@ if TYPE_CHECKING:
- class User(Base):
-     name = Column(String)
-
-+    othername = Column(String)
-+
-     addresses: Mapped[List["Address"]] = relationship(
-         "Address", back_populates="user"
-     )
diff --git a/test/ext/mypy/incremental/stubs_14/user.py b/test/ext/mypy/incremental/stubs_14/user.py
deleted file mode 100644 (file)
index c7e8f88..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-from typing import List
-from typing import TYPE_CHECKING
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import relationship
-from sqlalchemy.orm.decl_api import declared_attr
-from sqlalchemy.orm.relationships import RelationshipProperty
-from . import Base
-
-if TYPE_CHECKING:
-    from .address import Address
-
-
-class User(Base):
-    name = Column(String)
-
-    othername = Column(String)
-
-    addresses: Mapped[List["Address"]] = relationship(
-        "Address", back_populates="user"
-    )
-
-
-class HasUser:
-    @declared_attr
-    def user_id(self) -> "Column[Integer]":
-        return Column(
-            Integer,
-            ForeignKey(User.id, ondelete="CASCADE", onupdate="CASCADE"),
-            nullable=False,
-        )
-
-    @declared_attr
-    def user(self) -> RelationshipProperty[User]:
-        return relationship(User)
diff --git a/test/ext/mypy/incremental/ticket_6147/__init__.py b/test/ext/mypy/incremental/ticket_6147/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/test/ext/mypy/incremental/ticket_6147/base.py b/test/ext/mypy/incremental/ticket_6147/base.py
deleted file mode 100644 (file)
index 59be703..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-from sqlalchemy.orm import declarative_base
-
-Base = declarative_base()
diff --git a/test/ext/mypy/incremental/ticket_6147/one.py b/test/ext/mypy/incremental/ticket_6147/one.py
deleted file mode 100644 (file)
index 17fb075..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from .base import Base
-
-
-class One(Base):
-    __tablename__ = "one"
-    id = Column(Integer, primary_key=True)
-
-
-o1 = One(id=5)
-
-One.id.in_([1, 2])
diff --git a/test/ext/mypy/incremental/ticket_6147/patch1.testpatch b/test/ext/mypy/incremental/ticket_6147/patch1.testpatch
deleted file mode 100644 (file)
index b1d9bde..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
---- a/one.py   2021-04-03 15:32:22.214287290 -0400
-+++ b/one.py   2021-04-03 15:34:56.397398510 -0400
-@@ -1,15 +1,13 @@
- from sqlalchemy import Column
- from sqlalchemy import Integer
--from sqlalchemy import String
- from .base import Base
-
-
- class One(Base):
-     __tablename__ = "one"
-     id = Column(Integer, primary_key=True)
--    name = Column(String(50))
-
-
--o1 = One(id=5, name="name")
-+o1 = One(id=5)
-
- One.id.in_([1, 2])
diff --git a/test/ext/mypy/incremental/ticket_6147/patch2.testpatch b/test/ext/mypy/incremental/ticket_6147/patch2.testpatch
deleted file mode 100644 (file)
index 7551659..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
---- a/base.py  2021-04-03 16:36:30.201594994 -0400
-+++ b/base.py  2021-04-03 16:38:26.404475025 -0400
-@@ -1,3 +1,15 @@
-+from sqlalchemy import Column
-+from sqlalchemy import Integer
-+from sqlalchemy import String
- from sqlalchemy.orm import declarative_base
-+from sqlalchemy.orm import declarative_mixin
-+from sqlalchemy.orm import Mapped
-
- Base = declarative_base()
-+
-+
-+@declarative_mixin
-+class Mixin:
-+    mixed = Column(String)
-+
-+    b_int: Mapped[int] = Column(Integer)
---- a/one.py   2021-04-03 16:37:17.906956282 -0400
-+++ b/one.py   2021-04-03 16:38:33.469528528 -0400
-@@ -1,13 +1,15 @@
- from sqlalchemy import Column
- from sqlalchemy import Integer
-+
- from .base import Base
-+from .base import Mixin
-
-
--class One(Base):
-+class One(Mixin, Base):
-     __tablename__ = "one"
-     id = Column(Integer, primary_key=True)
-
-
--o1 = One(id=5)
-+o1 = One(id=5, mixed="mixed", b_int=5)
-
- One.id.in_([1, 2])
diff --git a/test/ext/mypy/incremental/ticket_6435/__init__.py b/test/ext/mypy/incremental/ticket_6435/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/test/ext/mypy/incremental/ticket_6435/enum_col_import1.py b/test/ext/mypy/incremental/ticket_6435/enum_col_import1.py
deleted file mode 100644 (file)
index fbdbb4f..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-import enum
-
-
-class StrEnum(enum.Enum):
-    one = "one"
-    two = "two"
-
-
-class IntEnum(enum.Enum):
-    one = 1
-    two = 2
diff --git a/test/ext/mypy/incremental/ticket_6435/enum_col_import2.py b/test/ext/mypy/incremental/ticket_6435/enum_col_import2.py
deleted file mode 100644 (file)
index 161dce0..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Enum
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import Mapped
-from . import enum_col_import1
-from .enum_col_import1 import IntEnum
-from .enum_col_import1 import StrEnum
-
-Base = declarative_base()
-
-
-class TestEnum(Base):
-    __tablename__ = "test_enum"
-
-    e1: Mapped[StrEnum] = Column(Enum(StrEnum))
-    e2: StrEnum = Column(Enum(StrEnum))
-
-    e3: Mapped[IntEnum] = Column(Enum(IntEnum))
-    e4: IntEnum = Column(Enum(IntEnum))
-
-    e5: Mapped[enum_col_import1.StrEnum] = Column(
-        Enum(enum_col_import1.StrEnum)
-    )
-    e6: enum_col_import1.StrEnum = Column(Enum(enum_col_import1.StrEnum))
-
-    e7: Mapped[enum_col_import1.IntEnum] = Column(
-        Enum(enum_col_import1.IntEnum)
-    )
-    e8: enum_col_import1.IntEnum = Column(Enum(enum_col_import1.IntEnum))
diff --git a/test/ext/mypy/incremental/ticket_6476/__init__.py b/test/ext/mypy/incremental/ticket_6476/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/test/ext/mypy/incremental/ticket_6476/base.py b/test/ext/mypy/incremental/ticket_6476/base.py
deleted file mode 100644 (file)
index fc14e47..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-from sqlalchemy.ext.declarative import declarative_base
-
-
-class CustomBase:
-    x = 5
-
-
-sql_base = declarative_base(cls=CustomBase)
diff --git a/test/ext/mypy/incremental/ticket_6476/patch1.testpatch b/test/ext/mypy/incremental/ticket_6476/patch1.testpatch
deleted file mode 100644 (file)
index ee4ec68..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
---- a/table.py 2021-06-21 15:52:47.733131711 -0400
-+++ b/table.py 2021-06-21 16:32:25.437594701 -0400
-@@ -3,3 +3,5 @@
-
- class Table(sql_base):
-     pass
-+
-+x = Table.x
diff --git a/test/ext/mypy/incremental/ticket_6476/table.py b/test/ext/mypy/incremental/ticket_6476/table.py
deleted file mode 100644 (file)
index bf971db..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-from .base import sql_base
-
-
-class Table(sql_base):
-    pass
diff --git a/test/ext/mypy/plugin_files/abstract_one.py b/test/ext/mypy/plugin_files/abstract_one.py
deleted file mode 100644 (file)
index d11631d..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import declarative_base
-
-
-Base = declarative_base()
-
-
-class FooBase(Base):
-    __abstract__ = True
-
-    updated_at = Column(Integer)
-
-
-class Foo(FooBase):
-    __tablename__ = "foo"
-    id: int = Column(Integer(), primary_key=True)
-    name: str = Column(String)
-
-
-Foo.updated_at.in_([1, 2, 3])
-
-f1 = Foo(name="name", updated_at=5)
-
-# test that we read the __abstract__ flag and don't apply a constructor
-# EXPECTED_MYPY: Unexpected keyword argument "updated_at" for "FooBase"
-FooBase(updated_at=5)
diff --git a/test/ext/mypy/plugin_files/as_declarative.py b/test/ext/mypy/plugin_files/as_declarative.py
deleted file mode 100644 (file)
index 08f08f9..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-from typing import List
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.ext.declarative import as_declarative
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import relationship
-from sqlalchemy.sql.schema import ForeignKey
-
-
-@as_declarative()
-class Base:
-    updated_at = Column(Integer)
-
-
-class Foo(Base):
-    __tablename__ = "foo"
-    id: int = Column(Integer(), primary_key=True)
-    name: Mapped[str] = Column(String)
-
-    bar: List["Bar"] = relationship("Bar")
-
-
-class Bar(Base):
-    __tablename__ = "bar"
-    id: int = Column(Integer(), primary_key=True)
-    foo_id: int = Column(ForeignKey("foo.id"))
-
-    foo: Optional[Foo] = relationship(Foo)
-
-
-f1 = Foo()
-
-val: int = f1.id
-
-p: str = f1.name
-
-Foo.id.property
-
-f2 = Foo(name="some name", updated_at=5)
diff --git a/test/ext/mypy/plugin_files/as_declarative_base.py b/test/ext/mypy/plugin_files/as_declarative_base.py
deleted file mode 100644 (file)
index ba62e72..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import registry
-
-reg: registry = registry()
-
-
-@reg.as_declarative_base()
-class Base:
-    updated_at = Column(Integer)
-
-
-class Foo(Base):
-    __tablename__ = "foo"
-    id: int = Column(Integer(), primary_key=True)
-    name: str = Column(String)
-
-
-f1 = Foo()
-
-val: int = f1.id
-
-p: str = f1.name
-
-Foo.id.property
-
-f2 = Foo(name="some name", updated_at=5)
diff --git a/test/ext/mypy/plugin_files/boolean_col.py b/test/ext/mypy/plugin_files/boolean_col.py
deleted file mode 100644 (file)
index 3e361ad..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import Optional
-
-from sqlalchemy import Boolean
-from sqlalchemy import Column
-from sqlalchemy.orm import declarative_base
-
-Base = declarative_base()
-
-
-class TestBoolean(Base):
-    __tablename__ = "test_boolean"
-
-    flag = Column(Boolean)
-
-    bflag: bool = Column(Boolean(create_constraint=True))
-
-
-expr = TestBoolean.flag.is_(True)
-
-t1 = TestBoolean(flag=True)
-
-x: Optional[bool] = t1.flag
-
-y: bool = t1.bflag
diff --git a/test/ext/mypy/plugin_files/cols_noninferred_plain_nonopt.py b/test/ext/mypy/plugin_files/cols_noninferred_plain_nonopt.py
deleted file mode 100644 (file)
index a2825e0..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import registry
-
-
-reg: registry = registry()
-
-
-@reg.mapped
-class Foo:
-    id: int = Column(Integer())
-    name: str = Column(String)
-    other_name: str = Column(String(50))
-
-    # has a string key in it
-    third_name = Column("foo", String(50))
-
-    some_name = "fourth_name"
-
-    fourth_name = Column(some_name, String(50))
-
-
-f1 = Foo()
-
-# This needs to work, e.g., value is "int" at the instance level
-val: int = f1.id  # noqa
-
-# also, the type are not optional, since we used an explicit
-# type without Optional
-p: str = f1.name
-
-Foo.id.property
-
-
-Foo(name="n", other_name="on", third_name="tn", fourth_name="fn")
diff --git a/test/ext/mypy/plugin_files/cols_notype_on_fk_col.py b/test/ext/mypy/plugin_files/cols_notype_on_fk_col.py
deleted file mode 100644 (file)
index 3195714..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import registry
-
-reg: registry = registry()
-
-
-@reg.mapped
-class User:
-    __tablename__ = "user"
-
-    id = Column(Integer(), primary_key=True)
-    name = Column(String)
-
-
-@reg.mapped
-class Address:
-    __tablename__ = "address"
-
-    id = Column(Integer, primary_key=True)
-    user_id: Mapped[int] = Column(ForeignKey("user.id"))
-    email_address = Column(String)
-
-
-ad1 = Address()
-
-p: Optional[int] = ad1.user_id
-
-# it's not optional because we called it Mapped[int]
-# and not Mapped[Optional[int]]
-p2: int = ad1.user_id
-
-
-# class-level descriptor access
-User.name.in_(["x", "y"])
-
-
-# class-level descriptor access
-Address.user_id.in_([1, 2])
diff --git a/test/ext/mypy/plugin_files/composite_props.py b/test/ext/mypy/plugin_files/composite_props.py
deleted file mode 100644 (file)
index d717ca0..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-from typing import Any
-from typing import Tuple
-
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import select
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import composite
-
-Base = declarative_base()
-
-
-class Point:
-    def __init__(self, x: int, y: int):
-        self.x = x
-        self.y = y
-
-    def __composite_values__(self) -> Tuple[int, int]:
-        return self.x, self.y
-
-    def __repr__(self) -> str:
-        return "Point(x=%r, y=%r)" % (self.x, self.y)
-
-    def __eq__(self, other: Any) -> bool:
-        return (
-            isinstance(other, Point)
-            and other.x == self.x
-            and other.y == self.y
-        )
-
-    def __ne__(self, other: Any) -> bool:
-        return not self.__eq__(other)
-
-
-class Vertex(Base):
-    __tablename__ = "vertices"
-
-    id = Column(Integer, primary_key=True)
-    x1 = Column(Integer)
-    y1 = Column(Integer)
-    x2 = Column(Integer)
-    y2 = Column(Integer)
-
-    # inferred from right hand side
-    start = composite(Point, x1, y1)
-
-    # taken from left hand side
-    end: Point = composite(Point, x2, y2)
-
-
-v1 = Vertex(start=Point(3, 4), end=Point(5, 6))
-
-# I'm not even sure composites support this but it should work from a
-# typing perspective
-stmt = select(Vertex).where(Vertex.start.in_([Point(3, 4)]))
-
-p1: Point = v1.start
-p2: Point = v1.end
-
-y3: int = v1.end.y
diff --git a/test/ext/mypy/plugin_files/constr_cols_only.py b/test/ext/mypy/plugin_files/constr_cols_only.py
deleted file mode 100644 (file)
index cd4da55..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.ext.declarative import declarative_base
-
-Base = declarative_base()
-
-
-class A(Base):
-    __tablename__ = "a"
-
-    id = Column(Integer, primary_key=True)
-    data = Column(String)
-    x = Column(Integer)
-    y = Column(Integer)
-
-
-a1 = A(data="d", x=5, y=4)
-
-
-# EXPECTED_MYPY: Argument "data" to "A" has incompatible type "int"; expected "Optional[str]" # noqa
-a2 = A(data=5)
-
-# EXPECTED_MYPY: Unexpected keyword argument "nonexistent" for "A"
-a3 = A(nonexistent="hi")
-
-print(a1)
-print(a2)
-print(a3)
diff --git a/test/ext/mypy/plugin_files/dataclasses_workaround.py b/test/ext/mypy/plugin_files/dataclasses_workaround.py
deleted file mode 100644 (file)
index a4d1389..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-from __future__ import annotations
-
-from dataclasses import dataclass
-from dataclasses import field
-from typing import Any
-from typing import Dict
-from typing import List
-from typing import Optional
-from typing import TYPE_CHECKING
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import select
-from sqlalchemy import String
-from sqlalchemy import Table
-from sqlalchemy.orm import registry
-from sqlalchemy.orm import relationship
-
-mapper_registry: registry = registry()
-
-
-@mapper_registry.mapped
-@dataclass
-class User:
-    __table__ = Table(
-        "user",
-        mapper_registry.metadata,
-        Column("id", Integer, primary_key=True),
-        Column("name", String(50)),
-        Column("fullname", String(50)),
-        Column("nickname", String(12)),
-    )
-    id: int = field(init=False)
-    name: Optional[str] = None
-    fullname: Optional[str] = None
-    nickname: Optional[str] = None
-    addresses: List[Address] = field(default_factory=list)
-
-    if TYPE_CHECKING:
-        _mypy_mapped_attrs = [id, name, fullname, nickname, addresses]
-
-    __mapper_args__: Dict[str, Any] = {
-        "properties": {"addresses": relationship("Address")}
-    }
-
-
-@mapper_registry.mapped
-@dataclass
-class Address:
-    __table__ = Table(
-        "address",
-        mapper_registry.metadata,
-        Column("id", Integer, primary_key=True),
-        Column("user_id", Integer, ForeignKey("user.id")),
-        Column("email_address", String(50)),
-    )
-
-    id: int = field(init=False)
-    user_id: int = field(init=False)
-    email_address: Optional[str] = None
-
-    if TYPE_CHECKING:
-        _mypy_mapped_attrs = [id, user_id, email_address]
-
-
-stmt1 = select(User.name).where(User.id.in_([1, 2, 3]))
-stmt2 = select(Address).where(Address.email_address.contains(["foo"]))
diff --git a/test/ext/mypy/plugin_files/decl_attrs_one.py b/test/ext/mypy/plugin_files/decl_attrs_one.py
deleted file mode 100644 (file)
index 1f2261c..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import registry
-from sqlalchemy.sql.schema import ForeignKey
-from sqlalchemy.sql.schema import MetaData
-from sqlalchemy.sql.schema import Table
-
-
-reg: registry = registry()
-
-
-@reg.mapped
-class Foo:
-    __tablename__ = "foo"
-    id: int = Column(Integer(), primary_key=True)
-    name: str = Column(String)
-
-
-@reg.mapped
-class Bar(Foo):
-    __tablename__ = "bar"
-    id: int = Column(ForeignKey("foo.id"), primary_key=True)
-
-
-@reg.mapped
-class Bat(Foo):
-    pass
-
-
-m1: MetaData = reg.metadata
-
-t1: Table = Foo.__table__
-
-t2: Table = Bar.__table__
-
-t3: Table = Bat.__table__
diff --git a/test/ext/mypy/plugin_files/decl_attrs_two.py b/test/ext/mypy/plugin_files/decl_attrs_two.py
deleted file mode 100644 (file)
index a20af49..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import registry
-from sqlalchemy.sql.schema import ForeignKey
-from sqlalchemy.sql.schema import MetaData
-from sqlalchemy.sql.schema import Table
-
-
-Base = declarative_base()
-
-
-class Foo(Base):
-    __tablename__ = "foo"
-    id: int = Column(Integer(), primary_key=True)
-    name: str = Column(String)
-
-
-class Bar(Foo):
-    __tablename__ = "bar"
-    id: int = Column(ForeignKey("foo.id"), primary_key=True)
-
-
-class Bat(Foo):
-    pass
-
-
-m0: MetaData = Base.metadata
-r0: registry = Base.registry
-
-t1: Table = Foo.__table__
-m1: MetaData = Foo.metadata
-
-t2: Table = Bar.__table__
-m2: MetaData = Bar.metadata
-
-t3: Table = Bat.__table__
-m3: MetaData = Bat.metadata
diff --git a/test/ext/mypy/plugin_files/decl_base_subclass_one.py b/test/ext/mypy/plugin_files/decl_base_subclass_one.py
deleted file mode 100644 (file)
index abe28a4..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import declarative_base
-
-
-class _Base:
-    updated_at = Column(Integer)
-
-
-Base = declarative_base(cls=_Base)
-
-
-class Foo(Base):
-    __tablename__ = "foo"
-    id: int = Column(Integer(), primary_key=True)
-    name: str = Column(String)
-
-
-class Bar(Base):
-    __tablename__ = "bar"
-    id = Column(Integer(), primary_key=True)
-    num = Column(Integer)
-
-
-Foo.updated_at.in_([1, 2, 3])
-
-f1 = Foo(name="name", updated_at=5)
-
-b1 = Bar(num=5, updated_at=6)
diff --git a/test/ext/mypy/plugin_files/decl_base_subclass_two.py b/test/ext/mypy/plugin_files/decl_base_subclass_two.py
deleted file mode 100644 (file)
index 78b7a9b..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-from typing import List
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import registry
-from sqlalchemy.orm import relationship
-from sqlalchemy.orm.decl_api import declared_attr
-from sqlalchemy.sql.schema import ForeignKey
-from sqlalchemy.sql.sqltypes import Integer
-from sqlalchemy.sql.sqltypes import String
-
-reg: registry = registry()
-
-
-@reg.mapped
-class User:
-    __tablename__ = "user"
-
-    id = Column(Integer, primary_key=True)
-    name = Column(String(50))
-
-    name3 = Column(String(50))
-
-    addresses: List["Address"] = relationship("Address")
-
-
-@reg.mapped
-class SubUser(User):
-    __tablename__ = "subuser"
-
-    id: int = Column(ForeignKey("user.id"), primary_key=True)
-
-    @declared_attr
-    def name(cls) -> Column[String]:
-        return Column(String(50))
-
-    @declared_attr
-    def name2(cls) -> Mapped[Optional[str]]:
-        return Column(String(50))
-
-    @declared_attr
-    def name3(cls) -> Mapped[str]:
-        return Column(String(50))
-
-    subname = Column(String)
-
-
-@reg.mapped
-class Address:
-    __tablename__ = "address"
-
-    id = Column(Integer, primary_key=True)
-    user_id: int = Column(ForeignKey("user.id"))
-    email = Column(String(50))
-
-    user = relationship(User, uselist=False)
-
-
-s1 = SubUser()
-
-# EXPECTED_MYPY: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str" # noqa
-x1: str = s1.name
-
-# EXPECTED_MYPY: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str") # noqa
-x2: str = s1.name2
-
-x3: str = s1.name3
-
-u1 = User()
-
-# EXPECTED_MYPY: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str") # noqa
-x4: str = u1.name3
diff --git a/test/ext/mypy/plugin_files/declarative_base_dynamic.py b/test/ext/mypy/plugin_files/declarative_base_dynamic.py
deleted file mode 100644 (file)
index eee9b31..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.ext.declarative import declarative_base
-
-# this is actually in orm now
-
-
-Base = declarative_base()
-
-
-class Foo(Base):
-    __tablename__ = "foo"
-    id: int = Column(Integer(), primary_key=True)
-    name: str = Column(String)
-    other_name: str = Column(String(50))
-
-
-f1 = Foo()
-
-val: int = f1.id
-
-p: str = f1.name
-
-Foo.id.property
-
-# TODO: getitem checker?  this should raise
-Foo.id.property_nonexistent
-
-
-f2 = Foo(name="some name", other_name="some other name")
diff --git a/test/ext/mypy/plugin_files/declarative_base_explicit.py b/test/ext/mypy/plugin_files/declarative_base_explicit.py
deleted file mode 100644 (file)
index b1b02bf..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import registry
-from sqlalchemy.orm.decl_api import DeclarativeMeta
-
-
-class Base(metaclass=DeclarativeMeta):
-    __abstract__ = True
-    registry = registry()
-    metadata = registry.metadata
-
-
-class Foo(Base):
-    __tablename__ = "foo"
-    id: int = Column(Integer(), primary_key=True)
-    name: str = Column(String)
-    other_name: str = Column(String(50))
-
-
-f1 = Foo()
-
-val: int = f1.id
-
-p: str = f1.name
-
-Foo.id.property
-
-# TODO: getitem checker?  this should raise
-Foo.id.property_nonexistent
diff --git a/test/ext/mypy/plugin_files/ensure_descriptor_type_fully_inferred.py b/test/ext/mypy/plugin_files/ensure_descriptor_type_fully_inferred.py
deleted file mode 100644 (file)
index 9ee9c76..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import registry
-
-reg: registry = registry()
-
-
-@reg.mapped
-class User:
-    __tablename__ = "user"
-
-    id = Column(Integer(), primary_key=True)
-    name = Column(String, nullable=False)
-
-
-u1 = User()
-
-# EXPECTED_MYPY: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str")  # noqa: E501
-p: str = u1.name
diff --git a/test/ext/mypy/plugin_files/ensure_descriptor_type_noninferred.py b/test/ext/mypy/plugin_files/ensure_descriptor_type_noninferred.py
deleted file mode 100644 (file)
index e8ce351..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import registry
-
-reg: registry = registry()
-
-
-@reg.mapped
-class User:
-    __tablename__ = "user"
-
-    id = Column(Integer(), primary_key=True)
-    name: Mapped[Optional[str]] = Column(String)
-
-
-u1 = User()
-
-# EXPECTED_MYPY: Incompatible types in assignment (expression has type "Optional[str]", variable has type "Optional[int]") # noqa: E501
-p: Optional[int] = u1.name
diff --git a/test/ext/mypy/plugin_files/ensure_descriptor_type_semiinferred.py b/test/ext/mypy/plugin_files/ensure_descriptor_type_semiinferred.py
deleted file mode 100644 (file)
index d72649b..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import registry
-
-reg: registry = registry()
-
-
-@reg.mapped
-class User:
-    __tablename__ = "user"
-
-    id = Column(Integer(), primary_key=True)
-
-    # we will call this "semi-inferred", since the real
-    # type will be Mapped[Optional[str]], but the Optional[str]
-    # which is not inferred, we use that to create it
-    name: Optional[str] = Column(String)
-
-
-u1 = User()
-
-# EXPECTED_MYPY: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str")  # noqa: E501
-p: str = u1.name
diff --git a/test/ext/mypy/plugin_files/enum_col.py b/test/ext/mypy/plugin_files/enum_col.py
deleted file mode 100644 (file)
index cfea388..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-import enum
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import Enum
-from sqlalchemy.orm import declarative_base
-
-
-class MyEnum(enum.Enum):
-    one = 1
-    two = 2
-    three = 3
-
-
-Base = declarative_base()
-
-one, two, three = "one", "two", "three"
-
-
-class TestEnum(Base):
-    __tablename__ = "test_enum"
-
-    e1: str = Column(Enum("one", "two", "three"))
-
-    e2: MyEnum = Column(Enum(MyEnum))
-
-    e3 = Column(Enum(one, two, three))
-
-    e4 = Column(Enum(MyEnum))
-
-
-t1 = TestEnum(e1="two", e2=MyEnum.three, e3="one", e4=MyEnum.one)
-
-x: str = t1.e1
-
-y: MyEnum = t1.e2
-
-z: Optional[str] = t1.e3
-
-z2: Optional[MyEnum] = t1.e4
diff --git a/test/ext/mypy/plugin_files/imperative_table.py b/test/ext/mypy/plugin_files/imperative_table.py
deleted file mode 100644 (file)
index 0548a79..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-import datetime
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import DateTime
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy import Table
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import Mapped
-
-
-Base = declarative_base()
-
-
-class MyMappedClass(Base):
-    __table_ = Table(
-        "some_table",
-        Base.metadata,
-        Column("id", Integer, primary_key=True),
-        Column("data", String(50)),
-        Column("created_at", DateTime),
-    )
-
-    id: Mapped[int]
-    data: Mapped[Optional[str]]
-    created_at: Mapped[datetime.datetime]
-
-
-m1 = MyMappedClass(id=5, data="string", created_at=datetime.datetime.now())
-
-# EXPECTED_MYPY: Argument "created_at" to "MyMappedClass" has incompatible type "int"; expected "datetime" # noqa
-m2 = MyMappedClass(id=5, data="string", created_at=12)
-
-
-# EXPECTED_MYPY: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str") # noqa
-x: str = MyMappedClass().data
diff --git a/test/ext/mypy/plugin_files/invalid_noninferred_lh_type.py b/test/ext/mypy/plugin_files/invalid_noninferred_lh_type.py
deleted file mode 100644 (file)
index e9ff303..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import registry
-
-reg: registry = registry()
-
-
-@reg.mapped
-class User:
-    __tablename__ = "user"
-
-    id = Column(Integer(), primary_key=True)
-    # EXPECTED: Left hand assignment 'name: "int"' not compatible with ORM mapped expression # noqa: E501
-    name: int = Column(String())
diff --git a/test/ext/mypy/plugin_files/issue_7321.py b/test/ext/mypy/plugin_files/issue_7321.py
deleted file mode 100644 (file)
index d4cd7f2..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-from typing import Any
-from typing import Dict
-
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import declared_attr
-
-
-Base = declarative_base()
-
-
-class Foo(Base):
-    @declared_attr
-    def __tablename__(cls) -> str:
-        return "name"
-
-    @declared_attr
-    def __mapper_args__(cls) -> Dict[Any, Any]:
-        return {}
-
-    @declared_attr
-    def __table_args__(cls) -> Dict[Any, Any]:
-        return {}
diff --git a/test/ext/mypy/plugin_files/issue_7321_part2.py b/test/ext/mypy/plugin_files/issue_7321_part2.py
deleted file mode 100644 (file)
index 4227f27..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-from typing import Any
-from typing import Dict
-from typing import Type
-
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import declared_attr
-
-
-Base = declarative_base()
-
-
-class Foo(Base):
-    # no mypy error emitted regarding the
-    # Type[Foo] part
-    @declared_attr
-    def __tablename__(cls: Type["Foo"]) -> str:
-        return "name"
-
-    @declared_attr
-    def __mapper_args__(cls: Type["Foo"]) -> Dict[Any, Any]:
-        return {}
-
-    # this was a workaround that works if there's no plugin present, make
-    # sure that doesn't crash anything
-    @classmethod
-    @declared_attr
-    def __table_args__(cls: Type["Foo"]) -> Dict[Any, Any]:
-        return {}
diff --git a/test/ext/mypy/plugin_files/issue_9102.py b/test/ext/mypy/plugin_files/issue_9102.py
deleted file mode 100644 (file)
index aec8401..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy.orm import registry
-
-
-class BackendMeta:
-    __abstract__ = True
-    mapped_registry: registry = registry()
-    metadata = mapped_registry.metadata
-
-
-# this decorator is not picked up now, but at least it doesn't crash
-@BackendMeta.mapped_registry.mapped
-class User:
-    __tablename__ = "user"
-
-    # EXPECTED_MYPY: Incompatible types in assignment (expression has type "Column[int]", variable has type "int")
-    id: int = Column(Integer(), primary_key=True)
diff --git a/test/ext/mypy/plugin_files/issue_9102_workaround.py b/test/ext/mypy/plugin_files/issue_9102_workaround.py
deleted file mode 100644 (file)
index 3682d29..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy.orm import registry
-
-
-class BackendMeta:
-    __abstract__ = True
-    mapped_registry: registry = registry()
-    metadata = mapped_registry.metadata
-
-
-reg: registry = BackendMeta.mapped_registry
-
-
-@reg.mapped
-class User:
-    __tablename__ = "user"
-
-    id: int = Column(Integer(), primary_key=True)
diff --git a/test/ext/mypy/plugin_files/issue_9156.py b/test/ext/mypy/plugin_files/issue_9156.py
deleted file mode 100644 (file)
index e67f644..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import Any
-from typing import Type
-
-from sqlalchemy.sql.elements import ColumnElement
-from sqlalchemy.sql.type_api import TypeEngine
-
-col: ColumnElement[Any]
-type_: Type[TypeEngine[Any]]
-obj: TypeEngine[Any]
-
-col.cast(type_)
-col.cast(obj)
diff --git a/test/ext/mypy/plugin_files/lambda_default.py b/test/ext/mypy/plugin_files/lambda_default.py
deleted file mode 100644 (file)
index a1019f0..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-import uuid
-
-from sqlalchemy import Column
-from sqlalchemy import String
-from sqlalchemy.orm import declarative_base
-
-Base = declarative_base()
-
-
-class MyClass(Base):
-    id = Column(String, default=lambda: uuid.uuid4(), primary_key=True)
diff --git a/test/ext/mypy/plugin_files/mapped_attr_assign.py b/test/ext/mypy/plugin_files/mapped_attr_assign.py
deleted file mode 100644 (file)
index c7244c2..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-"""Test patterns that can be used for assignment of mapped attributes
-after the mapping is complete
-
-
-"""
-
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import inspect
-from sqlalchemy import Integer
-from sqlalchemy import select
-from sqlalchemy import String
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import column_property
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import relationship
-
-Base = declarative_base()
-
-
-class B(Base):
-    __tablename__ = "b"
-    id = Column(Integer, primary_key=True)
-    a_id: int = Column(ForeignKey("a.id"))
-
-    # to attach attrs after the fact, declare them with Mapped
-    # on the class...
-    data: Mapped[str]
-
-    a: Mapped[Optional["A"]]
-
-
-class A(Base):
-    __tablename__ = "a"
-
-    id = Column(Integer, primary_key=True)
-    data = Column(String)
-    bs = relationship(B, uselist=True, back_populates="a")
-
-
-# There's no way to intercept the __setattr__() from the metaclass
-# here, and also when @reg.mapped() is used there is no metaclass.
-# so have them do it the old way
-inspect(B).add_property(
-    "data",
-    column_property(select(A.data).where(A.id == B.a_id).scalar_subquery()),
-)
-inspect(B).add_property("a", relationship(A))
-
-
-# the constructor will pick them up
-a1 = A()
-b1 = B(data="b", a=a1)
-
-# and it's mapped
-B.data.in_(["x", "y"])
-B.a.any()
diff --git a/test/ext/mypy/plugin_files/mixin_not_mapped.py b/test/ext/mypy/plugin_files/mixin_not_mapped.py
deleted file mode 100644 (file)
index e9aa336..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import registry
-
-
-reg: registry = registry()
-
-Base = declarative_base()
-
-
-class SomeAbstract(Base):
-    __abstract__ = True
-
-
-class HasUpdatedAt:
-    updated_at = Column(Integer)
-
-
-@reg.mapped
-class Foo(SomeAbstract):
-    __tablename__ = "foo"
-    id: int = Column(Integer(), primary_key=True)
-    name: str = Column(String)
-
-
-class Bar(HasUpdatedAt, Base):
-    __tablename__ = "bar"
-    id = Column(Integer(), primary_key=True)
-    num = Column(Integer)
-
-
-Bar.__mapper__
-
-# EXPECTED_MYPY: "type[HasUpdatedAt]" has no attribute "__mapper__"
-HasUpdatedAt.__mapper__
-
-
-# EXPECTED_MYPY: "type[SomeAbstract]" has no attribute "__mapper__"
-SomeAbstract.__mapper__
diff --git a/test/ext/mypy/plugin_files/mixin_one.py b/test/ext/mypy/plugin_files/mixin_one.py
deleted file mode 100644 (file)
index a471edf..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import registry
-
-
-reg: registry = registry()
-
-# TODO: also reg.as_declarative_base()
-Base = declarative_base()
-
-
-class HasUpdatedAt:
-    updated_at = Column(Integer)
-
-
-@reg.mapped
-class Foo(HasUpdatedAt):
-    __tablename__ = "foo"
-    id: int = Column(Integer(), primary_key=True)
-    name: str = Column(String)
-
-
-class Bar(HasUpdatedAt, Base):
-    __tablename__ = "bar"
-    id = Column(Integer(), primary_key=True)
-    num = Column(Integer)
-
-
-Foo.updated_at.in_([1, 2, 3])
-Bar.updated_at.in_([1, 2, 3])
-
-f1 = Foo(name="name", updated_at=5)
-
-b1 = Bar(num=5, updated_at=6)
-
-
-# test that we detected this as an unmapped mixin
-# EXPECTED_MYPY: Unexpected keyword argument "updated_at" for "HasUpdatedAt"
-HasUpdatedAt(updated_at=5)
diff --git a/test/ext/mypy/plugin_files/mixin_three.py b/test/ext/mypy/plugin_files/mixin_three.py
deleted file mode 100644 (file)
index cb8e30d..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-from typing import Callable
-
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import deferred
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm.decl_api import declarative_mixin
-from sqlalchemy.orm.decl_api import declared_attr
-from sqlalchemy.orm.interfaces import MapperProperty
-
-
-def some_other_decorator(fn: Callable[..., None]) -> Callable[..., None]:
-    return fn
-
-
-@declarative_mixin
-class HasAMixin:
-    x: Mapped[int] = Column(Integer)
-
-    y = Column(String)
-
-    @declared_attr
-    def data(cls) -> Column[String]:
-        return Column(String)
-
-    @declared_attr
-    def data2(cls) -> MapperProperty[str]:
-        return deferred(Column(String))
-
-    @some_other_decorator
-    def q(cls) -> None:
-        return None
diff --git a/test/ext/mypy/plugin_files/mixin_two.py b/test/ext/mypy/plugin_files/mixin_two.py
deleted file mode 100644 (file)
index 900b28f..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-from typing import Callable
-
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import deferred
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import registry
-from sqlalchemy.orm import relationship
-from sqlalchemy.orm import RelationshipProperty
-from sqlalchemy.orm.decl_api import declared_attr
-from sqlalchemy.orm.interfaces import MapperProperty
-from sqlalchemy.sql.schema import ForeignKey
-
-
-reg: registry = registry()
-
-
-@reg.mapped
-class C:
-    __tablename__ = "c"
-    id = Column(Integer, primary_key=True)
-
-
-def some_other_decorator(fn: Callable[..., None]) -> Callable[..., None]:
-    return fn
-
-
-class HasAMixin:
-    @declared_attr
-    def a(cls) -> Mapped["A"]:
-        return relationship("A", back_populates="bs")
-
-    # EXPECTED: Can't infer type from @declared_attr on function 'a2';
-    @declared_attr
-    def a2(cls):
-        return relationship("A", back_populates="bs")
-
-    @declared_attr
-    def a3(cls) -> RelationshipProperty["A"]:
-        return relationship("A", back_populates="bs")
-
-    @declared_attr
-    def c1(cls) -> RelationshipProperty[C]:
-        return relationship(C, back_populates="bs")
-
-    @declared_attr
-    def c2(cls) -> Mapped[C]:
-        return relationship(C, back_populates="bs")
-
-    @declared_attr
-    def data(cls) -> Column[String]:
-        return Column(String)
-
-    @declared_attr
-    def data2(cls) -> MapperProperty[str]:
-        return deferred(Column(String))
-
-    @some_other_decorator
-    def q(cls) -> None:
-        return None
-
-
-@reg.mapped
-class B(HasAMixin):
-    __tablename__ = "b"
-    id = Column(Integer, primary_key=True)
-    a_id: int = Column(ForeignKey("a.id"))
-    c_id: int = Column(ForeignKey("c.id"))
-
-
-@reg.mapped
-class A:
-    __tablename__ = "a"
-
-    id = Column(Integer, primary_key=True)
-
-    @declared_attr
-    def data(cls) -> Column[String]:
-        return Column(String)
-
-    # EXPECTED: Can't infer type from @declared_attr on function 'data2';
-    @declared_attr
-    def data2(cls):
-        return Column(String)
-
-    bs = relationship(B, uselist=True, back_populates="a")
-
-
-a1 = A(id=1, data="d1", data2="d2")
-
-
-b1 = B(a=A(), a2=A(), c1=C(), c2=C(), data="d1", data2="d2")
-
-# descriptor access as Mapped[<type>]
-B.a.any()
-B.a2.any()
-B.c1.any()
-B.c2.any()
-
-# sanity check against another fn that isn't mapped
-# EXPECTED_MYPY: "Callable[..., None]" has no attribute "any"
-B.q.any()
-
-B.data.in_(["a", "b"])
-B.data2.in_(["a", "b"])
diff --git a/test/ext/mypy/plugin_files/mixin_w_tablename.py b/test/ext/mypy/plugin_files/mixin_w_tablename.py
deleted file mode 100644 (file)
index cfbe83d..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-# test #6937
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import declared_attr
-from sqlalchemy.orm import Mapped
-
-
-Base = declarative_base()
-
-
-class UpdatedCls:
-    @declared_attr
-    def __tablename__(cls) -> Mapped[str]:
-        return cls.__name__.lower()
-
-    updated_at = Column(Integer)
-
-
-class Bar(UpdatedCls, Base):
-    id = Column(Integer(), primary_key=True)
-    num = Column(Integer)
-
-
-Bar.updated_at.in_([1, 2, 3])
-
-b1 = Bar(num=5, updated_at=6)
diff --git a/test/ext/mypy/plugin_files/orderinglist1.py b/test/ext/mypy/plugin_files/orderinglist1.py
deleted file mode 100644 (file)
index fb05b76..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy.ext.orderinglist import ordering_list
-from sqlalchemy.orm import registry
-from sqlalchemy.orm import relationship
-
-mapper_registry: registry = registry()
-
-
-@mapper_registry.mapped
-class A:
-    __tablename__ = "a"
-    id = Column(Integer, primary_key=True)
-
-    # EXPECTED: Can't infer type from ORM mapped expression assigned to attribute 'parents'; please specify a Python type or Mapped[<python type>] on the left hand side.  # noqa
-    parents = relationship("A", collection_class=ordering_list("ordering"))
-    parent_id = Column(Integer, ForeignKey("a.id"))
-    ordering = Column(Integer)
-
-
-a1 = A(id=5, ordering=10)
-
-# EXPECTED_MYPY: Argument "parents" to "A" has incompatible type "list[A]"; expected "Mapped[Any]"  # noqa
-a2 = A(parents=[a1])
diff --git a/test/ext/mypy/plugin_files/orderinglist2.py b/test/ext/mypy/plugin_files/orderinglist2.py
deleted file mode 100644 (file)
index d8b179e..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-from typing import List
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy.ext.orderinglist import ordering_list
-from sqlalchemy.orm import registry
-from sqlalchemy.orm import relationship
-
-mapper_registry: registry = registry()
-
-
-@mapper_registry.mapped
-class B:
-    __tablename__ = "b"
-    id = Column(Integer, primary_key=True)
-    parent_id = Column(Integer, ForeignKey("a.id"))
-    ordering = Column(Integer)
-
-
-@mapper_registry.mapped
-class C:
-    __tablename__ = "c"
-    id = Column(Integer, primary_key=True)
-    parent_id = Column(Integer, ForeignKey("a.id"))
-    ordering = Column(Integer)
-
-
-@mapper_registry.mapped
-class A:
-    __tablename__ = "a"
-    id = Column(Integer, primary_key=True)
-
-    bs = relationship(B, collection_class=ordering_list("ordering"))
-
-    bs_w_list: List[B] = relationship(
-        B, collection_class=ordering_list("ordering")
-    )
-
-    # EXPECTED: Left hand assignment 'cs: "list[B]"' not compatible with ORM mapped expression of type "Mapped[list[C]]"  # noqa
-    cs: List[B] = relationship(C, uselist=True)
-
-    # EXPECTED: Left hand assignment 'cs_2: "B"' not compatible with ORM mapped expression of type "Mapped[list[C]]"  # noqa
-    cs_2: B = relationship(C, uselist=True)
-
-
-b1 = B(ordering=10)
-
-# in this case, the plugin infers OrderingList as the type.  not great
-a1 = A()
-a1.bs.append(b1)
-
-# so we want to support being able to override it at least
-a2 = A(bs_w_list=[b1])
diff --git a/test/ext/mypy/plugin_files/other_mapper_props.py b/test/ext/mypy/plugin_files/other_mapper_props.py
deleted file mode 100644 (file)
index d87165f..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import column_property
-from sqlalchemy.orm import deferred
-from sqlalchemy.orm import registry
-from sqlalchemy.orm import Session
-from sqlalchemy.orm import synonym
-from sqlalchemy.sql.functions import func
-from sqlalchemy.sql.sqltypes import Text
-
-reg: registry = registry()
-
-
-@reg.mapped
-class User:
-    __tablename__ = "user"
-
-    id = Column(Integer(), primary_key=True)
-    name = Column(String)
-
-    # this gets inferred
-    big_col = deferred(Column(Text))
-
-    # this gets inferred
-    explicit_col = column_property(Column(Integer))
-
-    # EXPECTED: Can't infer type from ORM mapped expression assigned to attribute 'lower_name'; # noqa
-    lower_name = column_property(func.lower(name))
-
-    # EXPECTED: Can't infer type from ORM mapped expression assigned to attribute 'syn_name'; # noqa
-    syn_name = synonym("name")
-
-    # this uses our type
-    lower_name_exp: str = column_property(func.lower(name))
-
-    # this uses our type
-    syn_name_exp: Optional[str] = synonym("name")
-
-
-s = Session()
-
-u1: Optional[User] = s.get(User, 5)
-assert u1
-
-q1: Optional[str] = u1.big_col
-
-q2: Optional[int] = u1.explicit_col
-
-
-# EXPECTED_MYPY: Incompatible types in assignment (expression has type "str", variable has type "int") # noqa
-x: int = u1.lower_name_exp
-
-# EXPECTED_MYPY: Incompatible types in assignment (expression has type "Optional[str]", variable has type "int") # noqa
-y: int = u1.syn_name_exp
diff --git a/test/ext/mypy/plugin_files/plugin_doesnt_break_one.py b/test/ext/mypy/plugin_files/plugin_doesnt_break_one.py
deleted file mode 100644 (file)
index 19cb2bf..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import registry
-
-reg: registry = registry()
-
-
-@reg.mapped
-class Foo:
-    pass
-    id: int = Column(Integer())
-    name: str = Column(String)
-
-
-f1 = Foo()
-
-
-# EXPECTED_MYPY: Name 'u1' is not defined
-p: str = u1.name  # noqa
diff --git a/test/ext/mypy/plugin_files/relationship_6255_one.py b/test/ext/mypy/plugin_files/relationship_6255_one.py
deleted file mode 100644 (file)
index 15961c7..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-from typing import List
-from typing import Optional
-
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import select
-from sqlalchemy import String
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import mapped_column
-from sqlalchemy.orm import relationship
-
-Base = declarative_base()
-
-
-class User(Base):
-    __tablename__ = "user"
-
-    id = mapped_column(Integer, primary_key=True)
-    name: Mapped[Optional[str]] = mapped_column(String, nullable=True)
-
-    addresses: Mapped[List["Address"]] = relationship(
-        "Address", back_populates="user"
-    )
-
-    @property
-    def some_property(self) -> List[Optional[int]]:
-        return [i.id for i in self.addresses]
-
-
-class Address(Base):
-    __tablename__ = "address"
-
-    id = mapped_column(Integer, primary_key=True)
-    user_id: Mapped[int] = mapped_column(ForeignKey("user.id"))
-
-    user: Mapped["User"] = relationship("User", back_populates="addresses")
-
-    @property
-    def some_other_property(self) -> Optional[str]:
-        return self.user.name
-
-
-# it's in the constructor, correct type
-u1 = User(addresses=[Address()])
-
-# knows it's an iterable
-[x for x in u1.addresses]
-
-# knows it's Mapped
-stmt = select(User).where(User.addresses.any(id=5))
diff --git a/test/ext/mypy/plugin_files/relationship_6255_three.py b/test/ext/mypy/plugin_files/relationship_6255_three.py
deleted file mode 100644 (file)
index 121d8de..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-from typing import List
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import select
-from sqlalchemy import String
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import relationship
-
-Base = declarative_base()
-
-
-class User(Base):
-    __tablename__ = "user"
-
-    id = Column(Integer, primary_key=True)
-    name = Column(String)
-
-    addresses: List["Address"] = relationship("Address", back_populates="user")
-
-    @property
-    def some_property(self) -> List[Optional[int]]:
-        return [i.id for i in self.addresses]
-
-
-class Address(Base):
-    __tablename__ = "address"
-
-    id = Column(Integer, primary_key=True)
-    user_id: int = Column(ForeignKey("user.id"))
-
-    user: "User" = relationship("User", back_populates="addresses")
-
-    @property
-    def some_other_property(self) -> Optional[str]:
-        return self.user.name
-
-
-# it's in the constructor, correct type
-u1 = User(addresses=[Address()])
-
-# knows it's an iterable
-[x for x in u1.addresses]
-
-# knows it's Mapped
-stmt = select(User).where(User.addresses.any(id=5))
diff --git a/test/ext/mypy/plugin_files/relationship_6255_two.py b/test/ext/mypy/plugin_files/relationship_6255_two.py
deleted file mode 100644 (file)
index 121d8de..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-from typing import List
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import select
-from sqlalchemy import String
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import relationship
-
-Base = declarative_base()
-
-
-class User(Base):
-    __tablename__ = "user"
-
-    id = Column(Integer, primary_key=True)
-    name = Column(String)
-
-    addresses: List["Address"] = relationship("Address", back_populates="user")
-
-    @property
-    def some_property(self) -> List[Optional[int]]:
-        return [i.id for i in self.addresses]
-
-
-class Address(Base):
-    __tablename__ = "address"
-
-    id = Column(Integer, primary_key=True)
-    user_id: int = Column(ForeignKey("user.id"))
-
-    user: "User" = relationship("User", back_populates="addresses")
-
-    @property
-    def some_other_property(self) -> Optional[str]:
-        return self.user.name
-
-
-# it's in the constructor, correct type
-u1 = User(addresses=[Address()])
-
-# knows it's an iterable
-[x for x in u1.addresses]
-
-# knows it's Mapped
-stmt = select(User).where(User.addresses.any(id=5))
diff --git a/test/ext/mypy/plugin_files/relationship_direct_cls.py b/test/ext/mypy/plugin_files/relationship_direct_cls.py
deleted file mode 100644 (file)
index 1c4efde..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-from typing import List
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import relationship
-
-Base = declarative_base()
-
-
-class B(Base):
-    __tablename__ = "b"
-    id = Column(Integer, primary_key=True)
-    a_id: int = Column(ForeignKey("a.id"))
-    data = Column(String)
-
-    a: Optional["A"] = relationship("A", back_populates="bs")
-
-
-class A(Base):
-    __tablename__ = "a"
-
-    id = Column(Integer, primary_key=True)
-    data = Column(String)
-    bs = relationship(B, uselist=True, back_populates="a")
-
-
-a1 = A(bs=[B(data="b"), B(data="b")])
-
-x: List[B] = a1.bs
-
-
-b1 = B(a=A())
diff --git a/test/ext/mypy/plugin_files/relationship_err1.py b/test/ext/mypy/plugin_files/relationship_err1.py
deleted file mode 100644 (file)
index 46e7067..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-from typing import List
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import relationship
-
-Base = declarative_base()
-
-
-class B(Base):
-    __tablename__ = "b"
-    id = Column(Integer, primary_key=True)
-
-    # EXPECTED: Expected Python collection type for collection_class parameter # noqa
-    as_: List["A"] = relationship("A", collection_class=None)
-
-    # EXPECTED: Can't infer type from ORM mapped expression assigned to attribute 'another_as_'; # noqa
-    another_as_ = relationship("A", uselist=True)
-
-
-class A(Base):
-    __tablename__ = "a"
-
-    id = Column(Integer, primary_key=True)
-    b_id: int = Column(ForeignKey("b.id"))
-
-    # EXPECTED: Sending uselist=False and collection_class at the same time does not make sense # noqa
-    b: B = relationship(B, uselist=False, collection_class=set)
diff --git a/test/ext/mypy/plugin_files/relationship_err2.py b/test/ext/mypy/plugin_files/relationship_err2.py
deleted file mode 100644 (file)
index 04db946..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-from typing import Set
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import relationship
-
-Base = declarative_base()
-
-
-class B(Base):
-    __tablename__ = "b"
-    id = Column(Integer, primary_key=True)
-    a_id: int = Column(ForeignKey("a.id"))
-    data = Column(String)
-
-
-class A(Base):
-    __tablename__ = "a"
-
-    id = Column(Integer, primary_key=True)
-    data = Column(String)
-    bs = relationship(B, uselist=True)
-
-
-# EXPECTED_MYPY: List item 1 has incompatible type "A"; expected "B"
-a1 = A(bs=[B(data="b"), A()])
-
-# EXPECTED_MYPY: Incompatible types in assignment (expression has type "list[B]", variable has type "set[B]") # noqa
-x: Set[B] = a1.bs
diff --git a/test/ext/mypy/plugin_files/relationship_err3.py b/test/ext/mypy/plugin_files/relationship_err3.py
deleted file mode 100644 (file)
index 95d77fd..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-from typing import Optional
-from typing import Set
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import relationship
-
-Base = declarative_base()
-
-
-class B(Base):
-    __tablename__ = "b"
-    id = Column(Integer, primary_key=True)
-    a_id: int = Column(ForeignKey("a.id"))
-    data = Column(String)
-    a: Optional["A"] = relationship("A", back_populates="bs")
-
-
-class A(Base):
-    __tablename__ = "a"
-
-    id = Column(Integer, primary_key=True)
-    data = Column(String)
-
-    bs: Set[B] = relationship(B, uselist=True, back_populates="a")
-
-    # EXPECTED: Left hand assignment 'another_bs: "set[B]"' not compatible with ORM mapped expression of type "Mapped[B]" # noqa
-    another_bs: Set[B] = relationship(B, viewonly=True)
-
-
-# EXPECTED_MYPY: Argument "a" to "B" has incompatible type "str"; expected "Optional[A]" # noqa
-b1 = B(a="not an a")
diff --git a/test/ext/mypy/plugin_files/sa_module_prefix.py b/test/ext/mypy/plugin_files/sa_module_prefix.py
deleted file mode 100644 (file)
index a37ae6b..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-from typing import List
-from typing import Optional
-
-import sqlalchemy as sa
-from sqlalchemy import orm as saorm
-
-
-Base = saorm.declarative_base()
-
-
-class B(Base):
-    __tablename__ = "b"
-    id = sa.Column(sa.Integer, primary_key=True)
-    a_id: int = sa.Column(sa.ForeignKey("a.id"))
-    data = sa.Column(sa.String)
-
-    a: Optional["A"] = saorm.relationship("A", back_populates="bs")
-
-
-class A(Base):
-    __tablename__ = "a"
-
-    id = sa.Column(sa.Integer, primary_key=True)
-    data = sa.Column(sa.String)
-    bs = saorm.relationship(B, uselist=True, back_populates="a")
-
-
-a1 = A(bs=[B(data="b"), B(data="b")])
-
-x: List[B] = a1.bs
-
-
-b1 = B(a=A())
diff --git a/test/ext/mypy/plugin_files/t_6950.py b/test/ext/mypy/plugin_files/t_6950.py
deleted file mode 100644 (file)
index 3ebbf66..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-from typing import cast
-
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import query_expression
-from sqlalchemy.orm import Session
-from sqlalchemy.orm import with_expression
-
-Base = declarative_base()
-
-
-class User(Base):
-    __tablename__ = "users"
-
-    id = Column(Integer, primary_key=True)
-
-    foo = Column(Integer)
-
-    question_count: Mapped[int] = query_expression()
-    answer_count: int = query_expression()
-
-
-s = Session()
-
-q = s.query(User).options(with_expression(User.question_count, User.foo + 5))
-
-u1: User = cast(User, q.first())
-
-qc: int = u1.question_count
-print(qc)
diff --git a/test/ext/mypy/plugin_files/type_decorator.py b/test/ext/mypy/plugin_files/type_decorator.py
deleted file mode 100644 (file)
index 07a13ca..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-from typing import Any
-from typing import Optional
-
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy import TypeDecorator
-from sqlalchemy.ext.declarative import declarative_base
-
-Base = declarative_base()
-
-
-class IntToStr(TypeDecorator[int]):
-    impl = String
-    cache_ok = True
-
-    def process_bind_param(
-        self,
-        value: Any,
-        dialect: Any,
-    ) -> Optional[str]:
-        return str(value) if value is not None else value
-
-    def process_result_value(
-        self,
-        value: Any,
-        dialect: Any,
-    ) -> Optional[int]:
-        return int(value) if value is not None else value
-
-    def copy(self, **kwargs: Any) -> "IntToStr":
-        return IntToStr(self.impl.length)
-
-
-class Thing(Base):
-    __tablename__ = "things"
-
-    id: int = Column(Integer, primary_key=True)
-    intToStr: int = Column(IntToStr)
-
-
-t1 = Thing(intToStr=5)
-
-i5: int = t1.intToStr
-
-t1.intToStr = 8
diff --git a/test/ext/mypy/plugin_files/typeless_fk_col_cant_infer.py b/test/ext/mypy/plugin_files/typeless_fk_col_cant_infer.py
deleted file mode 100644 (file)
index 0b933db..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import registry
-
-reg: registry = registry()
-
-
-@reg.mapped
-class User:
-    __tablename__ = "user"
-
-    id = Column(Integer(), primary_key=True)
-    name = Column(String)
-
-
-@reg.mapped
-class Address:
-    __tablename__ = "address"
-
-    id = Column(Integer, primary_key=True)
-    # EXPECTED: Can't infer type from ORM mapped expression assigned to attribute 'user_id';  # noqa: E501
-    user_id = Column(ForeignKey("user.id"))
-    email_address = Column(String)
diff --git a/test/ext/mypy/plugin_files/typing_err1.py b/test/ext/mypy/plugin_files/typing_err1.py
deleted file mode 100644 (file)
index f262cd5..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy.orm import registry
-from sqlalchemy.types import TypeEngine
-
-
-# EXPECTED_MYPY: Missing type parameters for generic type "TypeEngine"
-class MyCustomType(TypeEngine):
-    pass
-
-
-# correct way
-class MyOtherCustomType(TypeEngine[str]):
-    pass
-
-
-reg: registry = registry()
-
-
-@reg.mapped
-class Foo:
-    id: int = Column(Integer())
-
-    name = Column(MyCustomType())
-    other_name: str = Column(MyCustomType())
-
-    name2 = Column(MyOtherCustomType())
-    other_name2: str = Column(MyOtherCustomType())
-
-
-Foo(name="x", other_name="x", name2="x", other_name2="x")
diff --git a/test/ext/mypy/plugin_files/typing_err2.py b/test/ext/mypy/plugin_files/typing_err2.py
deleted file mode 100644 (file)
index 5b8dfe4..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import declared_attr
-from sqlalchemy.orm import registry
-from sqlalchemy.orm import relationship
-from sqlalchemy.orm import RelationshipProperty
-
-reg: registry = registry()
-
-
-@reg.mapped
-class Foo:
-    id: int = Column(Integer())
-
-    # EXPECTED: Can't infer type from @declared_attr on function 'name'; # noqa
-    @declared_attr
-    # EXPECTED: Column type should be a TypeEngine subclass not 'builtins.str'
-    def name(cls) -> Column[str]:
-        return Column(String)
-
-    # EXPECTED: Left hand assignment 'other_name: "Column[String]"' not compatible with ORM mapped expression of type "Mapped[str]" # noqa
-    other_name: Column[String] = Column(String)
-
-    # EXPECTED: Can't infer type from @declared_attr on function 'third_name';
-    @declared_attr
-    # EXPECTED_MYPY: Missing type parameters for generic type "Column"
-    def third_name(cls) -> Column:
-        return Column(String)
-
-    # EXPECTED: Can't infer type from @declared_attr on function 'some_relationship' # noqa
-    @declared_attr
-    # EXPECTED_MYPY: Missing type parameters for generic type "RelationshipProperty"
-    def some_relationship(cls) -> RelationshipProperty:
-        return relationship("Bar")
-
-
-Foo(name="x")
diff --git a/test/ext/mypy/plugin_files/typing_err3.py b/test/ext/mypy/plugin_files/typing_err3.py
deleted file mode 100644 (file)
index 146b96b..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-"""Test that the right-hand expressions we normally "replace" are actually
-type checked.
-
-"""
-
-from typing import List
-
-from sqlalchemy import Column
-from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.orm import declarative_base
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import relationship
-from sqlalchemy.orm.decl_api import declared_attr
-
-
-Base = declarative_base()
-
-
-class User(Base):
-    __tablename__ = "user"
-
-    id = Column(Integer, primary_key=True)
-
-    addresses: Mapped[List["Address"]] = relationship(
-        "Address", wrong_arg="imwrong"
-    )
-
-
-class SubUser(User):
-    __tablename__ = "subuser"
-
-    id: int = Column(Integer, ForeignKey("user.id"), primary_key=True)
-
-
-class Address(Base):
-    __tablename__ = "address"
-
-    id: int = Column(Integer, primary_key=True)
-
-    user_id: int = Column(ForeignKey("user.id"))
-
-    @declared_attr
-    def email_address(cls) -> Column[String]:
-        # EXPECTED_MYPY: Argument 1 to "Column" has incompatible type "bool";
-        return Column(True)
-
-    @declared_attr
-    # EXPECTED_MYPY: Invalid type comment or annotation
-    def thisisweird(cls) -> Column(String):
-        # EXPECTED_MYPY: Argument 1 to "Column" has incompatible type "bool";
-        return Column(False)
diff --git a/test/ext/mypy/test_mypy_plugin_py3k.py b/test/ext/mypy/test_mypy_plugin_py3k.py
deleted file mode 100644 (file)
index 1d75137..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-import os
-import pathlib
-import shutil
-
-try:
-    from mypy.version import __version__ as _mypy_version_str
-except ImportError:
-    _mypy_version = None
-else:
-    _mypy_version = tuple(int(x) for x in _mypy_version_str.split("."))
-
-from sqlalchemy import testing
-from sqlalchemy.testing import eq_
-from sqlalchemy.testing import fixtures
-
-
-def _incremental_dirs():
-    path = os.path.join(os.path.dirname(__file__), "incremental")
-    files = []
-    for d in os.listdir(path):
-        if os.path.isdir(os.path.join(path, d)):
-            files.append(
-                os.path.join(os.path.dirname(__file__), "incremental", d)
-            )
-
-    for extra_dir in testing.config.options.mypy_extra_test_paths:
-        if extra_dir and os.path.isdir(extra_dir):
-            for d in os.listdir(os.path.join(extra_dir, "incremental")):
-                if os.path.isdir(os.path.join(path, d)):
-                    files.append(os.path.join(extra_dir, "incremental", d))
-    return files
-
-
-def _mypy_missing_or_incompatible():
-    return not _mypy_version or _mypy_version > (1, 10, 1)
-
-
-class MypyPluginTest(fixtures.MypyTest):
-    @testing.skip_if(
-        _mypy_missing_or_incompatible,
-        "Mypy must be present and compatible (<= 1.10.1)",
-    )
-    @testing.combinations(
-        *[
-            (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):
-        import patch
-
-        cachedir = per_func_cachedir
-
-        dest = os.path.join(cachedir, "mymodel")
-        os.mkdir(dest)
-
-        patches = set()
-
-        print("incremental test: %s" % pathname)
-
-        for fname in os.listdir(pathname):
-            if fname.endswith(".py"):
-                shutil.copy(
-                    os.path.join(pathname, fname), os.path.join(dest, fname)
-                )
-                print("copying to: %s" % os.path.join(dest, fname))
-            elif fname.endswith(".testpatch"):
-                patches.add(fname)
-
-        for patchfile in [None] + sorted(patches):
-            if patchfile is not None:
-                print("Applying patchfile %s" % patchfile)
-                patch_obj = patch.fromfile(os.path.join(pathname, patchfile))
-                assert patch_obj.apply(1, dest), (
-                    "pathfile %s failed" % patchfile
-                )
-            print("running mypy against %s" % dest)
-            result = mypy_runner(
-                dest,
-                use_plugin=True,
-                use_cachedir=cachedir,
-            )
-            eq_(
-                result[2],
-                0,
-                msg="Failure after applying patch %s: %s"
-                % (patchfile, result[0]),
-            )
-
-    @testing.skip_if(
-        _mypy_missing_or_incompatible,
-        "Mypy must be present and compatible (<= 1.10.1)",
-    )
-    @testing.combinations(
-        *(
-            (os.path.basename(path), path, True)
-            for path in fixtures.MypyTest.file_combinations("plugin_files")
-        ),
-        argnames="path",
-        id_="ia",
-    )
-    def test_plugin_files(self, mypy_typecheck_file, path):
-        mypy_typecheck_file(path, use_plugin=True)
diff --git a/tox.ini b/tox.ini
index 79d872b58da474249c888c305d3c9aa86015eff5..789bef0e2bfad3b804dacf84675e1299a6fc0768 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -204,8 +204,7 @@ deps=
      pytest>=7.0.0rc1,<8
      pytest-xdist
      greenlet != 0.4.17
-     mypy >= 1.7.0,<1.11.0
-     patch==1.*
+     mypy >= 1.14
      types-greenlet
 extras=
      {[greenletextras]extras}