]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Raise and re-catch NameError from _ModNS
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 12 Feb 2022 15:50:45 +0000 (10:50 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 12 Feb 2022 18:58:59 +0000 (13:58 -0500)
Fixed issue where using a fully qualified path for the classname in
:func:`_orm.relationship` that nonetheless contained an incorrect name for
path tokens that were not the first token, would fail to raise an
informative error and would instead fail randomly at a later step.

Fixes: #7697
Change-Id: I5e1a3aa4c2a6ea5b123be14666f589aec43f4b60

doc/build/changelog/unreleased_14/7697.rst [new file with mode: 0644]
lib/sqlalchemy/orm/clsregistry.py
test/orm/declarative/test_clsregistry.py

diff --git a/doc/build/changelog/unreleased_14/7697.rst b/doc/build/changelog/unreleased_14/7697.rst
new file mode 100644 (file)
index 0000000..03b318c
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 7697
+
+    Fixed issue where using a fully qualified path for the classname in
+    :func:`_orm.relationship` that nonetheless contained an incorrect name for
+    path tokens that were not the first token, would fail to raise an
+    informative error and would instead fail randomly at a later step.
index 3bf7ddde8f0a5e838e4f065fbbb7110cb9aa7450..ac6b0fd4c129bcdf0e54241b3b3176ee5f0c5109 100644 (file)
@@ -257,7 +257,7 @@ class _ModNS:
                 else:
                     assert isinstance(value, _MultipleClassMarker)
                     return value.attempt_get(self.__parent.path, key)
-        raise AttributeError(
+        raise NameError(
             "Module %r has no mapped classes "
             "registered under the name %r" % (self.__parent.name, key)
         )
index f19276126268d4d800b98ba5fd7c2ea298d49e62..0a9ea11ada06a0c8b6c3e0a09d90fbef1c9ffd4b 100644 (file)
@@ -1,9 +1,14 @@
+from sqlalchemy import Column
 from sqlalchemy import exc
+from sqlalchemy import Integer
 from sqlalchemy import MetaData
+from sqlalchemy import testing
 from sqlalchemy.orm import clsregistry
 from sqlalchemy.orm import registry
+from sqlalchemy.orm import relationship
 from sqlalchemy.testing import assert_raises_message
 from sqlalchemy.testing import eq_
+from sqlalchemy.testing import expect_raises_message
 from sqlalchemy.testing import fixtures
 from sqlalchemy.testing import is_
 from sqlalchemy.testing import mock
@@ -108,6 +113,36 @@ class ClsRegistryTest(fixtures.TestBase):
             name_resolver("alt.Foo"),
         )
 
+    @testing.combinations(
+        ("NonExistentFoo",),
+        ("nonexistent.Foo",),
+        ("existent.nonexistent.Foo",),
+        ("existent.NonExistentFoo",),
+        ("nonexistent.NonExistentFoo",),
+        ("existent.existent.NonExistentFoo",),
+        argnames="name",
+    )
+    def test_name_resolution_failures(self, name, registry):
+
+        Base = registry.generate_base()
+
+        f1 = MockClass(registry, "existent.Foo")
+        f2 = MockClass(registry, "existent.existent.Foo")
+        clsregistry.add_class("Foo", f1, registry._class_registry)
+        clsregistry.add_class("Foo", f2, registry._class_registry)
+
+        class MyClass(Base):
+            __tablename__ = "my_table"
+            id = Column(Integer, primary_key=True)
+            foo = relationship(name)
+
+        with expect_raises_message(
+            exc.InvalidRequestError,
+            r"When initializing mapper .*MyClass.*, expression '%s' "
+            r"failed to locate a name" % (name,),
+        ):
+            registry.configure()
+
     def test_no_fns_in_name_resolve(self):
         base = registry()
         f1 = MockClass(base, "foo.bar.Foo")
@@ -241,7 +276,7 @@ class ClsRegistryTest(fixtures.TestBase):
         f_resolver = resolver("foo")
         del mod_entry.contents["Foo"]
         assert_raises_message(
-            AttributeError,
+            NameError,
             "Module 'bar' has no mapped classes registered "
             "under the name 'Foo'",
             lambda: f_resolver().bar.Foo,
@@ -249,7 +284,7 @@ class ClsRegistryTest(fixtures.TestBase):
 
         f_resolver = name_resolver("foo")
         assert_raises_message(
-            AttributeError,
+            NameError,
             "Module 'bar' has no mapped classes registered "
             "under the name 'Foo'",
             lambda: f_resolver().bar.Foo,
@@ -264,7 +299,7 @@ class ClsRegistryTest(fixtures.TestBase):
         name_resolver, resolver = clsregistry._resolver(f1, MockProp())
         f_resolver = resolver("foo")
         assert_raises_message(
-            AttributeError,
+            NameError,
             "Module 'bar' has no mapped classes registered "
             "under the name 'Bat'",
             lambda: f_resolver().bar.Bat,
@@ -272,7 +307,7 @@ class ClsRegistryTest(fixtures.TestBase):
 
         f_resolver = name_resolver("foo")
         assert_raises_message(
-            AttributeError,
+            NameError,
             "Module 'bar' has no mapped classes registered "
             "under the name 'Bat'",
             lambda: f_resolver().bar.Bat,