diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-06-03 10:34:19 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-06-03 13:29:57 -0400 |
commit | 47eff8b9e35dec9305d22484c17dd6c0649a876a (patch) | |
tree | 83e806aab8759069b3d6847e063cb310f5bda74d /lib/sqlalchemy/orm/util.py | |
parent | ad86d32f7fbd1c6deda8ff3bebe0595c0f2986cc (diff) | |
download | sqlalchemy-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.py | 36 |
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 |