From f8796965aefc1852d4b1137d04bf811072a1a0e9 Mon Sep 17 00:00:00 2001 From: Pascal Corpet Date: Thu, 28 Sep 2023 00:34:15 +0200 Subject: [PATCH] handle builtins types in annotations with __allow_unmapped__ Fixes #10385 --- lib/sqlalchemy/util/typing.py | 7 ++++ .../test_tm_future_annotations_sync.py | 35 +++++++++++++++++-- test/orm/declarative/test_typed_mapping.py | 35 +++++++++++++++++-- 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/lib/sqlalchemy/util/typing.py b/lib/sqlalchemy/util/typing.py index 597549ce77..4f051a1e3c 100644 --- a/lib/sqlalchemy/util/typing.py +++ b/lib/sqlalchemy/util/typing.py @@ -8,6 +8,7 @@ from __future__ import annotations +import builtins import re import sys import typing @@ -235,6 +236,12 @@ def eval_name_only( if "." in name: return eval_expression(name, module_name, locals_=locals_) + # Try builtins first, to handle `list`, `set` or `dict`, etc. + try: + return builtins.__dict__[name] + except KeyError: + pass + try: base_globals: Dict[str, Any] = sys.modules[module_name].__dict__ except KeyError as ke: diff --git a/test/orm/declarative/test_tm_future_annotations_sync.py b/test/orm/declarative/test_tm_future_annotations_sync.py index bb392ba314..57ae5a2064 100644 --- a/test/orm/declarative/test_tm_future_annotations_sync.py +++ b/test/orm/declarative/test_tm_future_annotations_sync.py @@ -2167,8 +2167,15 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL): decl_base.registry.configure() - def test_14_style_anno_accepted_w_allow_unmapped(self): - """test for #8692""" + @testing.combinations( + ("list", testing.requires.python310), + ("List",), + ("set", testing.requires.python310), + ("Set",), + argnames="collection_type", + ) + def test_14_style_anno_accepted_w_allow_unmapped(self, collection_type): + """test for #8692 and #10385""" class Base(DeclarativeBase): __allow_unmapped__ = True @@ -2178,7 +2185,29 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL): id: Mapped[int] = mapped_column(primary_key=True) data: str = Column(String) - bs: List["B"] = relationship("B", back_populates="a") # noqa: F821 + + if collection_type == "list": + bs: list["B"] = relationship( # noqa: F821 + "B", + back_populates="a", + ) + elif collection_type == "List": + bs: List["B"] = relationship( # noqa: F821 + "B", + back_populates="a", + ) + elif collection_type == "set": + bs: set["B"] = relationship( # noqa: F821 + "B", + back_populates="a", + ) + elif collection_type == "Set": + bs: Set["B"] = relationship( # noqa: F821 + "B", + back_populates="a", + ) + else: + collection_type.fail() class B(Base): __tablename__ = "b" diff --git a/test/orm/declarative/test_typed_mapping.py b/test/orm/declarative/test_typed_mapping.py index de2f6f94a7..2a4b378c51 100644 --- a/test/orm/declarative/test_typed_mapping.py +++ b/test/orm/declarative/test_typed_mapping.py @@ -2158,8 +2158,15 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL): decl_base.registry.configure() - def test_14_style_anno_accepted_w_allow_unmapped(self): - """test for #8692""" + @testing.combinations( + ("list", testing.requires.python310), + ("List",), + ("set", testing.requires.python310), + ("Set",), + argnames="collection_type", + ) + def test_14_style_anno_accepted_w_allow_unmapped(self, collection_type): + """test for #8692 and #10385""" class Base(DeclarativeBase): __allow_unmapped__ = True @@ -2169,7 +2176,29 @@ class RelationshipLHSTest(fixtures.TestBase, testing.AssertsCompiledSQL): id: Mapped[int] = mapped_column(primary_key=True) data: str = Column(String) - bs: List["B"] = relationship("B", back_populates="a") # noqa: F821 + + if collection_type == "list": + bs: list["B"] = relationship( # noqa: F821 + "B", + back_populates="a", + ) + elif collection_type == "List": + bs: List["B"] = relationship( # noqa: F821 + "B", + back_populates="a", + ) + elif collection_type == "set": + bs: set["B"] = relationship( # noqa: F821 + "B", + back_populates="a", + ) + elif collection_type == "Set": + bs: Set["B"] = relationship( # noqa: F821 + "B", + back_populates="a", + ) + else: + collection_type.fail() class B(Base): __tablename__ = "b" -- 2.47.3