From: Mike Bayer Date: Fri, 23 Oct 2009 19:46:36 +0000 (+0000) Subject: - Mutable collection passed to the "extension" attribute X-Git-Tag: rel_0_5_7~28 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c676898f67d74a99f5ada5857d390a8be937c450;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Mutable collection passed to the "extension" attribute of relation(), column_property() etc. will not be mutated or shared among multiple instrumentation calls, preventing duplicate extensions, such as backref populators, from being inserted into the list. [ticket:1585] --- diff --git a/CHANGES b/CHANGES index fb16947b28..09214ce19c 100644 --- a/CHANGES +++ b/CHANGES @@ -23,6 +23,13 @@ CHANGES by contains_eager() out of individual instance states. [ticket:1553] + - Mutable collection passed to the "extension" attribute + of relation(), column_property() etc. will not be mutated + or shared among multiple instrumentation calls, preventing + duplicate extensions, such as backref populators, + from being inserted into the list. + [ticket:1585] + - sql - Fixed the "numeric" paramstyle, which apparently is the default paramstyle used by Informixdb. diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 0fa32f73f6..3453342a87 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -1019,7 +1019,7 @@ class RelationProperty(StrategizedProperty): # primary property handler, set up class attributes if self.is_primary(): if self.back_populates: - self.extension = util.to_list(self.extension) or [] + self.extension = list(util.to_list(self.extension, default=[])) self.extension.append(attributes.GenericBackrefExtension(self.back_populates)) self._add_reverse_property(self.back_populates) diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 38073811b3..68669a1502 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -32,7 +32,7 @@ def _register_attribute(strategy, mapper, useobject, ): prop = strategy.parent_property - attribute_ext = util.to_list(prop.extension) or [] + attribute_ext = list(util.to_list(prop.extension, default=[])) if useobject and prop.single_parent: attribute_ext.append(_SingleParentValidator(prop)) diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py index 5cf20beb96..4ba05daed2 100644 --- a/test/orm/test_mapper.py +++ b/test/orm/test_mapper.py @@ -8,7 +8,7 @@ from sqlalchemy.test.schema import Table from sqlalchemy.test.schema import Column from sqlalchemy.engine import default from sqlalchemy.orm import mapper, relation, backref, create_session, class_mapper, compile_mappers, reconstructor, validates, aliased -from sqlalchemy.orm import defer, deferred, synonym, attributes, column_property, composite, relation, dynamic_loader, comparable_property +from sqlalchemy.orm import defer, deferred, synonym, attributes, column_property, composite, relation, dynamic_loader, comparable_property,AttributeExtension from sqlalchemy.test.testing import eq_, AssertsCompiledSQL from test.orm import _base, _fixtures from sqlalchemy.test.assertsql import AllOf, CompiledSQL @@ -259,6 +259,28 @@ class MapperTest(_fixtures.FixtureTest): mapper(Foo, addresses, inherits=User) assert getattr(Foo().__class__, 'name').impl is not None + @testing.resolve_artifact_names + def test_extension_collection_frozen(self): + class Foo(User):pass + m = mapper(User, users) + mapper(Order, orders) + compile_mappers() + mapper(Foo, addresses, inherits=User) + ext_list = [AttributeExtension()] + m.add_property('somename', column_property(users.c.name, extension=ext_list)) + m.add_property('orders', relation(Order, extension=ext_list, backref='user')) + assert len(ext_list) == 1 + + assert Foo.orders.impl.extensions is User.orders.impl.extensions + assert Foo.orders.impl.extensions is not ext_list + + compile_mappers() + assert len(User.somename.impl.extensions) == 1 + assert len(Foo.somename.impl.extensions) == 1 + assert len(Foo.orders.impl.extensions) == 3 + assert len(User.orders.impl.extensions) == 3 + + @testing.resolve_artifact_names def test_compile_on_get_props_1(self): m =mapper(User, users)