summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/changelog/unreleased_14/7697.rst8
-rw-r--r--lib/sqlalchemy/orm/clsregistry.py2
-rw-r--r--test/orm/declarative/test_clsregistry.py43
3 files changed, 48 insertions, 5 deletions
diff --git a/doc/build/changelog/unreleased_14/7697.rst b/doc/build/changelog/unreleased_14/7697.rst
new file mode 100644
index 000000000..03b318cce
--- /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 3bf7ddde8..ac6b0fd4c 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 f19276126..0a9ea11ad 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,