From: Mike Bayer Date: Sat, 15 Jun 2019 02:44:59 +0000 (-0400) Subject: Consult is_attrbute flag to determine descriptor; enable for assoc proxy X-Git-Tag: rel_1_3_5~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b927836a33ea0dcfd8290e09e1ec5d3833e36df9;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Consult is_attrbute flag to determine descriptor; enable for assoc proxy Fixed bug where the :attr:`.Mapper.all_orm_descriptors` accessor would return an entry for the :class:`.Mapper` itself under the declarative ``__mapper___`` key, when this is not a descriptor. The ``.is_attribute`` flag that's present on all :class:`.InspectionAttr` objects is now consulted, which has also been modified to be ``True`` for an association proxy, as it was erroneously set to False for this object. Fixes: #4729 Change-Id: Ia02388cc25d004e32d337140b62a587f3e5a0b7b (cherry picked from commit 79d07c9abc7d4d3abb6bf2ca5ca66e87d3a11f08) --- diff --git a/doc/build/changelog/unreleased_13/4729.rst b/doc/build/changelog/unreleased_13/4729.rst new file mode 100644 index 0000000000..3fa55b6674 --- /dev/null +++ b/doc/build/changelog/unreleased_13/4729.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, orm + :tickets: 4729 + + Fixed bug where the :attr:`.Mapper.all_orm_descriptors` accessor would + return an entry for the :class:`.Mapper` itself under the declarative + ``__mapper___`` key, when this is not a descriptor. The ``.is_attribute`` + flag that's present on all :class:`.InspectionAttr` objects is now + consulted, which has also been modified to be ``True`` for an association + proxy, as it was erroneously set to False for this object. diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py index 9a8294f3c7..32dee79c69 100644 --- a/lib/sqlalchemy/ext/associationproxy.py +++ b/lib/sqlalchemy/ext/associationproxy.py @@ -93,7 +93,7 @@ ASSOCIATION_PROXY = util.symbol("ASSOCIATION_PROXY") class AssociationProxy(interfaces.InspectionAttrInfo): """A descriptor that presents a read/write view of an object attribute.""" - is_attribute = False + is_attribute = True extension_type = ASSOCIATION_PROXY def __init__( diff --git a/lib/sqlalchemy/orm/instrumentation.py b/lib/sqlalchemy/orm/instrumentation.py index 7cf46227f8..ee0cc06006 100644 --- a/lib/sqlalchemy/orm/instrumentation.py +++ b/lib/sqlalchemy/orm/instrumentation.py @@ -143,7 +143,10 @@ class ClassManager(dict): for key in set(supercls.__dict__).difference(exclude): exclude.add(key) val = supercls.__dict__[key] - if isinstance(val, interfaces.InspectionAttr): + if ( + isinstance(val, interfaces.InspectionAttr) + and val.is_attribute + ): yield key, val def _get_class_attr_mro(self, key, default=None): diff --git a/test/ext/declarative/test_basic.py b/test/ext/declarative/test_basic.py index 3fe2f1bfe9..264c4839c9 100644 --- a/test/ext/declarative/test_basic.py +++ b/test/ext/declarative/test_basic.py @@ -1337,6 +1337,15 @@ class DeclarativeTest(DeclarativeTestBase): eq_(Foo.__mapper__.CHECK, True) + def test_no_change_to_all_descriptors(self): + base = decl.declarative_base() + + class Foo(base): + __tablename__ = "foo" + id = Column(Integer, primary_key=True) + + eq_(Foo.__mapper__.all_orm_descriptors.keys(), ["id"]) + def test_oops(self): with testing.expect_warnings( diff --git a/test/orm/test_inspect.py b/test/orm/test_inspect.py index 368199b96f..4a39fc87d0 100644 --- a/test/orm/test_inspect.py +++ b/test/orm/test_inspect.py @@ -11,6 +11,7 @@ from sqlalchemy.orm import Session from sqlalchemy.orm import synonym from sqlalchemy.orm.attributes import instance_state from sqlalchemy.orm.attributes import NO_VALUE +from sqlalchemy.orm.base import InspectionAttr from sqlalchemy.orm.util import identity_key from sqlalchemy.testing import assert_raises_message from sqlalchemy.testing import eq_ @@ -400,9 +401,13 @@ class TestORMInspection(_fixtures.FixtureTest): assert "name" in u1.__dict__ def test_attrs_props_prop_added_after_configure(self): - class AnonClass(object): + class Thing(InspectionAttr): pass + class AnonClass(object): + __foo__ = "bar" + __bat__ = Thing() + from sqlalchemy.orm import mapper, column_property from sqlalchemy.ext.hybrid import hybrid_property