]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
catch unexpected errors when accessing clslevel attribute
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 18 Mar 2022 14:33:40 +0000 (10:33 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 18 Mar 2022 14:38:36 +0000 (10:38 -0400)
Improved the error message that's raised for the case where the
:func:`.association_proxy` construct attempts to access a target attribute
at the class level, and this access fails. The particular use case here is
when proxying to a hybrid attribute that does not include a working
class-level implementation.

Fixes: #7827
Change-Id: Ic6ff9df010f49253e664a1e7c7e16d8546006965
(cherry picked from commit 764e36e5e7b7faf1a97b4b06be1ca307ac4fce46)

doc/build/changelog/unreleased_14/7827.rst [new file with mode: 0644]
lib/sqlalchemy/ext/associationproxy.py
test/ext/test_associationproxy.py

diff --git a/doc/build/changelog/unreleased_14/7827.rst b/doc/build/changelog/unreleased_14/7827.rst
new file mode 100644 (file)
index 0000000..aedf258
--- /dev/null
@@ -0,0 +1,10 @@
+.. change::
+    :tags: bug, ext
+    :tickets: 7827
+
+    Improved the error message that's raised for the case where the
+    :func:`.association_proxy` construct attempts to access a target attribute
+    at the class level, and this access fails. The particular use case here is
+    when proxying to a hybrid attribute that does not include a working
+    class-level implementation.
+
index 9a73bb5c2cd70b52ff88bae1eb71e9dd4643438d..fbf377afd44d397e60d8d5de253921ae31cf5089 100644 (file)
@@ -383,6 +383,22 @@ class AssociationProxyInstance(object):
             return AmbiguousAssociationProxyInstance(
                 parent, owning_class, target_class, value_attr
             )
+        except Exception as err:
+            util.raise_(
+                exc.InvalidRequestError(
+                    "Association proxy received an unexpected error when "
+                    "trying to retreive attribute "
+                    '"%s.%s" from '
+                    'class "%s": %s'
+                    % (
+                        target_class.__name__,
+                        parent.value_attr,
+                        target_class.__name__,
+                        err,
+                    )
+                ),
+                from_=err,
+            )
         else:
             return cls._construct_for_assoc(
                 target_assoc, parent, owning_class, target_class, value_attr
index 0b05fe0387e16de3dfc985b2cb7878710ffadb1a..44f3890de88c56871cd7f90d3b52ec5a780e363a 100644 (file)
@@ -34,6 +34,7 @@ from sqlalchemy.testing import expect_warnings
 from sqlalchemy.testing import fixtures
 from sqlalchemy.testing import is_
 from sqlalchemy.testing import is_false
+from sqlalchemy.testing.assertions import expect_raises_message
 from sqlalchemy.testing.fixtures import fixture_session
 from sqlalchemy.testing.mock import call
 from sqlalchemy.testing.mock import Mock
@@ -3343,6 +3344,10 @@ class ProxyHybridTest(fixtures.DeclarativeMappedTest, AssertsCompiledSQL):
             b_data = association_proxy("bs", "value")
             well_behaved_b_data = association_proxy("bs", "well_behaved_value")
 
+            fails_on_class_access = association_proxy(
+                "bs", "fails_on_class_access"
+            )
+
         class B(Base):
             __tablename__ = "b"
 
@@ -3386,6 +3391,10 @@ class ProxyHybridTest(fixtures.DeclarativeMappedTest, AssertsCompiledSQL):
             def well_behaved_w_expr(cls):
                 return cast(cls.data, Integer)
 
+            @hybrid_property
+            def fails_on_class_access(self):
+                return len(self.data)
+
         class C(Base):
             __tablename__ = "c"
 
@@ -3394,6 +3403,19 @@ class ProxyHybridTest(fixtures.DeclarativeMappedTest, AssertsCompiledSQL):
             _b = relationship("B")
             attr = association_proxy("_b", "well_behaved_w_expr")
 
+    def test_msg_fails_on_cls_access(self):
+        A, B = self.classes("A", "B")
+
+        a1 = A(bs=[B(data="b1")])
+
+        with expect_raises_message(
+            exc.InvalidRequestError,
+            "Association proxy received an unexpected error when trying to "
+            'retreive attribute "B.fails_on_class_access" from '
+            r'class "B": .* no len\(\)',
+        ):
+            a1.fails_on_class_access
+
     def test_get_ambiguous(self):
         A, B = self.classes("A", "B")