From: Mike Bayer Date: Fri, 23 Oct 2009 19:46:58 +0000 (+0000) Subject: - Mutable collection passed to the "extension" attribute X-Git-Tag: rel_0_6beta1~231 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=52fab3ed3448205dbbc6ed8e3be0113ee78e1e0a;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 baa34e7896..a5b759163e 100644 --- a/CHANGES +++ b/CHANGES @@ -591,6 +591,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 9f9bbd62a7..0ad0ef5a03 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -1044,7 +1044,7 @@ class RelationProperty(StrategizedProperty): 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 5ee99ac136..08c60a1d23 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.insert(0, _SingleParentValidator(prop)) diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py index 1fe0eead37..62bf636b89 100644 --- a/test/orm/test_mapper.py +++ b/test/orm/test_mapper.py @@ -7,7 +7,7 @@ from sqlalchemy import MetaData, Integer, String, ForeignKey, func, util from sqlalchemy.test.schema import Table, 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 @@ -255,6 +255,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)