--- /dev/null
+.. change::
+ :tags: bug, orm declarative
+ :tickets: 13386
+
+ Fixed issue where using :pep:`593` ``Annotated`` wrapping a :pep:`695`
+ ``type`` alias, such as ``Annotated[SomeTypeAlias, mapped_column()]``,
+ would crash with ``AttributeError: __value__``. The internal
+ ``is_pep695()`` check incorrectly identified the ``Annotated`` type as a
+ PEP 695 type alias due to a quirk in ``Annotated.__origin__`` returning
+ the first type argument rather than ``Annotated`` itself.
# though.
# NOTE: things seems to work also without this additional check
if is_generic(type_):
+ if is_pep593(type_):
+ return False
return is_pep695(type_.__origin__)
return isinstance(type_, _type_instances.TypeAliasType)
A_null_union = typing.Annotated[
typing.Union[str, int, None], "other_meta", "null"
]
+A_pep695 = typing.Annotated[TA_int, "meta"]
+A_pep695_ext = typing.Annotated[TAext_int, "meta"]
def compare_type_by_string(a, b):
for t in type_aliases():
eq_(sa_typing.is_pep695(t), True)
+ def test_is_pep695_annotated_pep695(self):
+ """test #13386"""
+ for t in (A_pep695, A_pep695_ext):
+ eq_(sa_typing.is_pep695(t), False)
+ eq_(sa_typing.is_pep593(t), True)
+
def test_pep695_value(self):
eq_(sa_typing.pep695_values(int), {int})
eq_(
def pep_593_types(pep_695_types):
global _GenericPep593TypeAlias, _GenericPep593Pep695
global _RecursivePep695Pep593
+ global _AnnotatedPep695
_GenericPep593TypeAlias = Annotated[
TV, mapped_column(info={"hi": "there"}) # type: ignore
],
)
+ _AnnotatedPep695 = Annotated[
+ _TypingStrPep695, # type: ignore
+ mapped_column(JSON),
+ ]
+
def expect_annotation_syntax_error(name):
return expect_raises_message(
):
declare()
+ @testing.requires.python312
+ def test_pep593_wrapping_pep695(
+ self, decl_base: Type[DeclarativeBase], pep_593_types
+ ):
+ """test #13386"""
+
+ class MyClass(decl_base):
+ __tablename__ = "my_table"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+
+ data_one: Mapped[_AnnotatedPep695] # noqa: F821
+
+ table = MyClass.__table__
+ assert table is not None
+ is_(MyClass.data_one.expression.type.__class__, JSON)
+
def test_extract_base_type_from_pep593(
self, decl_base: Type[DeclarativeBase]
):
def pep_593_types(pep_695_types):
global _GenericPep593TypeAlias, _GenericPep593Pep695
global _RecursivePep695Pep593
+ global _AnnotatedPep695
_GenericPep593TypeAlias = Annotated[
TV, mapped_column(info={"hi": "there"}) # type: ignore
],
)
+ _AnnotatedPep695 = Annotated[
+ _TypingStrPep695, # type: ignore
+ mapped_column(JSON),
+ ]
+
def expect_annotation_syntax_error(name):
return expect_raises_message(
):
declare()
+ @testing.requires.python312
+ def test_pep593_wrapping_pep695(
+ self, decl_base: Type[DeclarativeBase], pep_593_types
+ ):
+ """test #13386"""
+
+ class MyClass(decl_base):
+ __tablename__ = "my_table"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+
+ data_one: Mapped[_AnnotatedPep695] # noqa: F821
+
+ table = MyClass.__table__
+ assert table is not None
+ is_(MyClass.data_one.expression.type.__class__, JSON)
+
def test_extract_base_type_from_pep593(
self, decl_base: Type[DeclarativeBase]
):