diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2023-01-16 10:31:39 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2023-01-16 14:23:49 -0500 |
commit | 046272e06aa3284a87e0dd1f90d2242fb434de10 (patch) | |
tree | 8a6ea002b34241c4b24355107361aef8590472bc /lib/sqlalchemy/util/typing.py | |
parent | 6da2f72b1144738ddae23e7fd882a8cd79ea1c60 (diff) | |
download | sqlalchemy-046272e06aa3284a87e0dd1f90d2242fb434de10.tar.gz |
dont assume copy_with() on builtins list, dict, etc; improve error msg.
Fixed issue where using an ``Annotated`` type in the
``type_annotation_map`` which itself contained a plain container type (e.g.
``list``, ``dict``) generic type as the target type would produce an
internal error where the ORM were trying to interpret the ``Annotated``
instance.
Added an error message when a :func:`_orm.relationship` is mapped against
an abstract container type, such as ``Mapped[Sequence[B]]``, without
providing the :paramref:`_orm.relationship.container_class` parameter which
is necessary when the type is abstract. Previously the the abstract
container would attempt to be instantiated and fail.
Fixes: #9099
Fixes: #9100
Change-Id: I18aa6abd5451c5ac75a9caed8441ff0cd8f44589
Diffstat (limited to 'lib/sqlalchemy/util/typing.py')
-rw-r--r-- | lib/sqlalchemy/util/typing.py | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/lib/sqlalchemy/util/typing.py b/lib/sqlalchemy/util/typing.py index b1ef87db1..e1670ed21 100644 --- a/lib/sqlalchemy/util/typing.py +++ b/lib/sqlalchemy/util/typing.py @@ -97,8 +97,12 @@ class GenericProtocol(Protocol[_T]): __args__: Tuple[_AnnotationScanType, ...] __origin__: Type[_T] - def copy_with(self, params: Tuple[_AnnotationScanType, ...]) -> Type[_T]: - ... + # Python's builtin _GenericAlias has this method, however builtins like + # list, dict, etc. do not, even though they have ``__origin__`` and + # ``__args__`` + # + # def copy_with(self, params: Tuple[_AnnotationScanType, ...]) -> Type[_T]: + # ... class SupportsKeysAndGetItem(Protocol[_KT, _VT_co]): @@ -158,10 +162,21 @@ def de_stringify_annotation( for elem in annotation.__args__ ) - return annotation.copy_with(elements) + return _copy_generic_annotation_with(annotation, elements) return annotation # type: ignore +def _copy_generic_annotation_with( + annotation: GenericProtocol[_T], elements: Tuple[_AnnotationScanType, ...] +) -> Type[_T]: + if hasattr(annotation, "copy_with"): + # List, Dict, etc. real generics + return annotation.copy_with(elements) # type: ignore + else: + # Python builtins list, dict, etc. + return annotation.__origin__[elements] # type: ignore + + def eval_expression(expression: str, module_name: str) -> Any: try: base_globals: Dict[str, Any] = sys.modules[module_name].__dict__ |