summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/util.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2022-06-03 10:34:19 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2022-06-03 13:29:57 -0400
commit47eff8b9e35dec9305d22484c17dd6c0649a876a (patch)
tree83e806aab8759069b3d6847e063cb310f5bda74d /lib/sqlalchemy/orm/util.py
parentad86d32f7fbd1c6deda8ff3bebe0595c0f2986cc (diff)
downloadsqlalchemy-47eff8b9e35dec9305d22484c17dd6c0649a876a.tar.gz
some typing fixes
* ClassVar for decl fields, add __tablename__ * dataclasses require annotations for all fields. For us, if no annotation, then skip that field as part of what is considered to be a "dataclass", as this matches the behavior of pyright right now. We could alternatively raise on this use, which is what dataclasses does. we should ask the pep people * plain field that's just "str", "int", etc., with no value. Disallow it unless __allow_unmapped__ is set. If field has dataclasses.field, Column, None, a value etc, it goes through, and when using dataclasses mixin all such fields are considered for the dataclass setup just like a dataclass. Hopefully this does not have major backwards compat issues. __allow_unmapped__ can be set on the base class, mixins, etc., it's liberal for now in case people have this problem. * accommodate for ClassVar, these are not considered at all for mapping. Change-Id: Id743aa0456bade9a5d5832796caeecc3dc4accb7
Diffstat (limited to 'lib/sqlalchemy/orm/util.py')
-rw-r--r--lib/sqlalchemy/orm/util.py36
1 files changed, 30 insertions, 6 deletions
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index c8c802d9f..e1e78c99d 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -1970,6 +1970,7 @@ def _extract_mapped_subtype(
required: bool,
is_dataclass_field: bool,
expect_mapped: bool = True,
+ raiseerr: bool = True,
) -> Optional[Union[type, str]]:
"""given an annotation, figure out if it's ``Mapped[something]`` and if
so, return the ``something`` part.
@@ -2008,12 +2009,35 @@ def _extract_mapped_subtype(
our_annotated_str = anno_name
if expect_mapped:
- raise sa_exc.ArgumentError(
- f'Type annotation for "{cls.__name__}.{key}" '
- "should use the "
- f'syntax "Mapped[{our_annotated_str}]" or '
- f'"{attr_cls.__name__}[{our_annotated_str}]".'
- )
+ if getattr(annotated, "__origin__", None) is typing.ClassVar:
+ return None
+
+ if not raiseerr:
+ return None
+
+ if attr_cls.__name__ == our_annotated_str or attr_cls is type(
+ None
+ ):
+ raise sa_exc.ArgumentError(
+ f'Type annotation for "{cls.__name__}.{key}" '
+ "should use the "
+ f'syntax "Mapped[{our_annotated_str}]". To leave '
+ f"the attribute unmapped, use "
+ f"ClassVar[{our_annotated_str}], assign a value to "
+ f"the attribute, or "
+ f"set __allow_unmapped__ = True on the class."
+ )
+ else:
+ raise sa_exc.ArgumentError(
+ f'Type annotation for "{cls.__name__}.{key}" '
+ "should use the "
+ f'syntax "Mapped[{our_annotated_str}]" or '
+ f'"{attr_cls.__name__}[{our_annotated_str}]". To '
+ f"leave the attribute unmapped, use "
+ f"ClassVar[{our_annotated_str}], assign a value to "
+ f"the attribute, or "
+ f"set __allow_unmapped__ = True on the class."
+ )
else:
return annotated