--- /dev/null
+.. 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.
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)
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):
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