]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Fix missing class attributes when using __class_getitem__
authorKai Mueller <15907922+kasium@users.noreply.github.com>
Tue, 21 Dec 2021 20:00:30 +0000 (15:00 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 22 Dec 2021 23:24:15 +0000 (18:24 -0500)
Fixed issue where the ``__class_getitem__()`` method of the generated
declarative base class by :func:`_orm.as_declarative` would lead to
inaccessible class attributes such as ``__table__``, for cases where a
``Generic[T]`` style typing declaration were used in the class hierarchy.
This is in continuation from the basic addition of ``__class_getitem__()``
in :ticket:`7368`. Pull request courtesy Kai Mueller.

Fixes: #7462
Closes: #7470
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/7470
Pull-request-sha: d5e5765e0e5542149f116ed9ccff1b3e2e32dee5

Change-Id: I6418af6d34532ff181343884bd419d9c2684e617

doc/build/changelog/unreleased_14/7462.rst [new file with mode: 0644]
lib/sqlalchemy/orm/decl_api.py
test/orm/declarative/test_typing_py3k.py

diff --git a/doc/build/changelog/unreleased_14/7462.rst b/doc/build/changelog/unreleased_14/7462.rst
new file mode 100644 (file)
index 0000000..fa71e14
--- /dev/null
@@ -0,0 +1,10 @@
+.. change::
+    :tags: bug, orm, mypy
+    :tickets: 7462, 7368
+
+    Fixed issue where the ``__class_getitem__()`` method of the generated
+    declarative base class by :func:`_orm.as_declarative` would lead to
+    inaccessible class attributes such as ``__table__``, for cases where a
+    ``Generic[T]`` style typing declaration were used in the class hierarchy.
+    This is in continuation from the basic addition of ``__class_getitem__()``
+    in :ticket:`7368`. Pull request courtesy Kai Mueller.
index ed8617d52fee2716501cb8adf441866c6e5ae8ce..7b6814863a3e07bac9d7e160c226e51fec49fdaa 100644 (file)
@@ -787,8 +787,14 @@ class registry:
         class_dict["__abstract__"] = True
         if mapper:
             class_dict["__mapper_cls__"] = mapper
+
         if hasattr(cls, "__class_getitem__"):
-            class_dict["__class_getitem__"] = cls.__class_getitem__
+
+            def __class_getitem__(cls, key):
+                # allow generic classes in py3.9+
+                return cls
+
+            class_dict["__class_getitem__"] = __class_getitem__
 
         return metaclass(name, bases, class_dict)
 
index 823fe54f106bdaa94cd261bd75688267f78abbc8..0be91a509f76f0056acf289f4e182a51c001ca8c 100644 (file)
@@ -3,9 +3,13 @@ from typing import Type
 from typing import TypeVar
 
 from sqlalchemy import Column
+from sqlalchemy import inspect
 from sqlalchemy import Integer
 from sqlalchemy.orm import as_declarative
+from sqlalchemy.testing import eq_
 from sqlalchemy.testing import fixtures
+from sqlalchemy.testing import is_
+from sqlalchemy.testing.assertions import expect_raises
 
 
 class DeclarativeBaseTest(fixtures.TestBase):
@@ -17,9 +21,22 @@ class DeclarativeBaseTest(fixtures.TestBase):
             def boring(cls: Type[T]) -> Type[T]:
                 return cls
 
+            @classmethod
+            def more_boring(cls: Type[T]) -> int:
+                return 27
+
         @as_declarative()
         class Base(CommonBase[T]):
-            pass
+            foo = 1
 
         class Tab(Base["Tab"]):
+            __tablename__ = "foo"
             a = Column(Integer, primary_key=True)
+
+        eq_(Tab.foo, 1)
+        is_(Tab.__table__, inspect(Tab).local_table)
+        eq_(Tab.boring(), Tab)
+        eq_(Tab.more_boring(), 27)
+
+        with expect_raises(AttributeError):
+            Tab.non_existent