summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2023-01-17 01:18:47 +0000
committerGerrit Code Review <gerrit@ci3.zzzcomputing.com>2023-01-17 01:18:47 +0000
commit030b757d1e1efdc56c42e40ce07ee34a3d00f73a (patch)
tree97f07287ef9a21571341d2bb62c7bdf94f26bcc0 /lib/sqlalchemy
parent3bd40c1b9a265c3bed52bfe3394f21d75bf52025 (diff)
parent046272e06aa3284a87e0dd1f90d2242fb434de10 (diff)
downloadsqlalchemy-030b757d1e1efdc56c42e40ce07ee34a3d00f73a.tar.gz
Merge "dont assume copy_with() on builtins list, dict, etc; improve error msg." into main
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/relationships.py12
-rw-r--r--lib/sqlalchemy/util/typing.py21
2 files changed, 30 insertions, 3 deletions
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py
index bfd39c369..66d3a6035 100644
--- a/lib/sqlalchemy/orm/relationships.py
+++ b/lib/sqlalchemy/orm/relationships.py
@@ -18,6 +18,7 @@ from __future__ import annotations
import collections
from collections import abc
import dataclasses
+import inspect as _py_inspect
import re
import typing
from typing import Any
@@ -1768,7 +1769,18 @@ class RelationshipProperty(
arg_origin, abc.Collection
):
if self.collection_class is None:
+ if _py_inspect.isabstract(arg_origin):
+ raise sa_exc.ArgumentError(
+ f"Collection annotation type {arg_origin} cannot "
+ "be instantiated; please provide an explicit "
+ "'collection_class' parameter "
+ "(e.g. list, set, etc.) to the "
+ "relationship() function to accompany this "
+ "annotation"
+ )
+
self.collection_class = arg_origin
+
elif not is_write_only and not is_dynamic:
self.uselist = False
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__