--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 11305
+
+ Fixed issue in ORM Annotated Declarative where typing issue where literals
+ defined using :pep:`695` type aliases would not work with inference of
+ :class:`.Enum` datatypes. Pull request courtesy of Alc-Alc.
def _resolve_type(
self, python_type: _MatchedOnType
) -> Optional[sqltypes.TypeEngine[Any]]:
- search: Iterable[Tuple[_MatchedOnType, Type[Any]]]
+
+ python_type_to_check = python_type
+ while is_pep695(python_type_to_check):
+ python_type_to_check = python_type_to_check.__value__
+
+ check_is_pt = python_type is python_type_to_check
+
python_type_type: Type[Any]
+ search: Iterable[Tuple[_MatchedOnType, Type[Any]]]
- if is_generic(python_type):
- if is_literal(python_type):
- python_type_type = cast("Type[Any]", python_type)
+ if is_generic(python_type_to_check):
+ if is_literal(python_type_to_check):
+ python_type_type = cast("Type[Any]", python_type_to_check)
search = ( # type: ignore[assignment]
(python_type, python_type_type),
(Literal, python_type_type),
)
else:
- python_type_type = python_type.__origin__
+ python_type_type = python_type_to_check.__origin__
search = ((python_type, python_type_type),)
- elif is_newtype(python_type):
- python_type_type = flatten_newtype(python_type)
- search = ((python_type, python_type_type),)
- elif is_pep695(python_type):
- python_type_type = python_type.__value__
- flattened = None
+ elif is_newtype(python_type_to_check):
+ python_type_type = flatten_newtype(python_type_to_check)
search = ((python_type, python_type_type),)
+ elif isinstance(python_type_to_check, type):
+ python_type_type = python_type_to_check
+ search = (
+ (pt if check_is_pt else python_type, pt)
+ for pt in python_type_type.__mro__
+ )
else:
- python_type_type = cast("Type[Any]", python_type)
- flattened = None
- search = ((pt, pt) for pt in python_type_type.__mro__)
+ python_type_type = python_type_to_check # type: ignore[assignment]
+ search = ((python_type, python_type_type),)
for pt, flattened in search:
# we search through full __mro__ for types. however...
_StrTypeAlias: TypeAlias = str
-_StrPep695: TypeAlias = Union[_SomeDict1, _SomeDict2]
-_UnionPep695: TypeAlias = str
+_StrPep695: TypeAlias = str
+_UnionPep695: TypeAlias = Union[_SomeDict1, _SomeDict2]
+
+_Literal695: TypeAlias = Literal["to-do", "in-progress", "done"]
+_Recursive695_0: TypeAlias = _Literal695
+_Recursive695_1: TypeAlias = _Recursive695_0
+_Recursive695_2: TypeAlias = _Recursive695_1
if compat.py312:
exec(
str, mapped_column(info={"hi": "there"})]
strtypalias_plain = Annotated[str, mapped_column(info={"hi": "there"})]
+
+type _Literal695 = Literal["to-do", "in-progress", "done"]
+type _Recursive695_0 = _Literal695
+type _Recursive695_1 = _Recursive695_0
+type _Recursive695_2 = _Recursive695_1
""",
globals(),
)
class Test(decl_base):
__tablename__ = "test"
id: Mapped[int] = mapped_column(primary_key=True)
- data: Mapped[_StrPep695] # type: ignore
- structure: Mapped[_UnionPep695] # type: ignore
+ data: Mapped[_StrPep695]
+ structure: Mapped[_UnionPep695]
+ eq_(Test.__table__.c.data.type._type_affinity, String)
eq_(Test.__table__.c.data.type.length, 30)
is_(Test.__table__.c.structure.type._type_affinity, JSON)
eq_(MyClass.data_one.expression.info, {"hi": "there"})
+ @testing.requires.python312
+ def test_pep695_literal_defaults_to_enum(self, decl_base):
+ """test #11305."""
+
+ class Foo(decl_base):
+ __tablename__ = "footable"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+ status: Mapped[_Literal695]
+ r2: Mapped[_Recursive695_2]
+
+ for col in (Foo.__table__.c.status, Foo.__table__.c.r2):
+ is_true(isinstance(col.type, Enum))
+ eq_(col.type.enums, ["to-do", "in-progress", "done"])
+ is_(col.type.native_enum, False)
+
@testing.requires.python310
def test_we_got_all_attrs_test_annotated(self):
argnames = _py_inspect.getfullargspec(mapped_column)
_StrTypeAlias: TypeAlias = str
-_StrPep695: TypeAlias = Union[_SomeDict1, _SomeDict2]
-_UnionPep695: TypeAlias = str
+_StrPep695: TypeAlias = str
+_UnionPep695: TypeAlias = Union[_SomeDict1, _SomeDict2]
+
+_Literal695: TypeAlias = Literal["to-do", "in-progress", "done"]
+_Recursive695_0: TypeAlias = _Literal695
+_Recursive695_1: TypeAlias = _Recursive695_0
+_Recursive695_2: TypeAlias = _Recursive695_1
if compat.py312:
exec(
str, mapped_column(info={"hi": "there"})]
strtypalias_plain = Annotated[str, mapped_column(info={"hi": "there"})]
+
+type _Literal695 = Literal["to-do", "in-progress", "done"]
+type _Recursive695_0 = _Literal695
+type _Recursive695_1 = _Recursive695_0
+type _Recursive695_2 = _Recursive695_1
""",
globals(),
)
class Test(decl_base):
__tablename__ = "test"
id: Mapped[int] = mapped_column(primary_key=True)
- data: Mapped[_StrPep695] # type: ignore
- structure: Mapped[_UnionPep695] # type: ignore
+ data: Mapped[_StrPep695]
+ structure: Mapped[_UnionPep695]
+ eq_(Test.__table__.c.data.type._type_affinity, String)
eq_(Test.__table__.c.data.type.length, 30)
is_(Test.__table__.c.structure.type._type_affinity, JSON)
eq_(MyClass.data_one.expression.info, {"hi": "there"})
+ @testing.requires.python312
+ def test_pep695_literal_defaults_to_enum(self, decl_base):
+ """test #11305."""
+
+ class Foo(decl_base):
+ __tablename__ = "footable"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+ status: Mapped[_Literal695]
+ r2: Mapped[_Recursive695_2]
+
+ for col in (Foo.__table__.c.status, Foo.__table__.c.r2):
+ is_true(isinstance(col.type, Enum))
+ eq_(col.type.enums, ["to-do", "in-progress", "done"])
+ is_(col.type.native_enum, False)
+
@testing.requires.python310
def test_we_got_all_attrs_test_annotated(self):
argnames = _py_inspect.getfullargspec(mapped_column)