From: Mike Bayer Date: Sat, 12 Feb 2022 15:50:45 +0000 (-0500) Subject: Raise and re-catch NameError from _ModNS X-Git-Tag: rel_2_0_0b1~488 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=260ade78a70d51378de9e7b9456bfe6218859b6c;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Raise and re-catch NameError from _ModNS 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 --- diff --git a/doc/build/changelog/unreleased_14/7697.rst b/doc/build/changelog/unreleased_14/7697.rst new file mode 100644 index 0000000000..03b318cce2 --- /dev/null +++ b/doc/build/changelog/unreleased_14/7697.rst @@ -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. diff --git a/lib/sqlalchemy/orm/clsregistry.py b/lib/sqlalchemy/orm/clsregistry.py index 3bf7ddde8f..ac6b0fd4c1 100644 --- a/lib/sqlalchemy/orm/clsregistry.py +++ b/lib/sqlalchemy/orm/clsregistry.py @@ -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) ) diff --git a/test/orm/declarative/test_clsregistry.py b/test/orm/declarative/test_clsregistry.py index f192761262..0a9ea11ada 100644 --- a/test/orm/declarative/test_clsregistry.py +++ b/test/orm/declarative/test_clsregistry.py @@ -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,