diff options
author | Jason Kirtland <jek@discorporate.us> | 2008-09-27 18:11:40 +0000 |
---|---|---|
committer | Jason Kirtland <jek@discorporate.us> | 2008-09-27 18:11:40 +0000 |
commit | 8e5312975b9f05ef1b196880d116fb10b916e87e (patch) | |
tree | 68fd1ea2134ca80aae9a0410fa07d1ae02a6ef8e | |
parent | 0d9fc31fa1a82f6707a11e4d854cc5fe60a6dd07 (diff) | |
download | sqlalchemy-8e5312975b9f05ef1b196880d116fb10b916e87e.tar.gz |
- Fixed shared state bug interfering with ScopedSession.mapper's
ability to apply default __init__ implementations on object
subclasses.
-rw-r--r-- | CHANGES | 46 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/scoping.py | 27 | ||||
-rw-r--r-- | test/orm/scoping.py | 30 |
3 files changed, 66 insertions, 37 deletions
@@ -7,36 +7,38 @@ CHANGES 0.5.0rc2 ======== - orm - - Fixed bug involving read/write relation()s that - contain literal or other non-column expressions - within their primaryjoin condition equated - to a foreign key column. - - - "non-batch" mode in mapper(), a feature which allows - mapper extension methods to be called as each instance - is updated/inserted, now honors the insert order - of the objects given. - - - fixed RLock-related bug in mapper which could deadlock - upon reentrant mapper compile() calls, something that - occurs when using declarative constructs inside of - ForeignKey objects. + - Fixed bug involving read/write relation()s that contain + literal or other non-column expressions within their + primaryjoin condition equated to a foreign key column. + + - "non-batch" mode in mapper(), a feature which allows mapper + extension methods to be called as each instance is + updated/inserted, now honors the insert order of the objects + given. + + - Fixed RLock-related bug in mapper which could deadlock upon + reentrant mapper compile() calls, something that occurs when + using declarative constructs inside of ForeignKey objects. - ScopedSession.query_property now accepts a query_cls factory, overriding the session's configured query_cls. + - Fixed shared state bug interfering with ScopedSession.mapper's + ability to apply default __init__ implementations on object + subclasses. + - sql - - column.in_(someselect) can now be used as - a columns-clause expression without the subquery - bleeding into the FROM clause [ticket:1074] + - column.in_(someselect) can now be used as a columns-clause + expression without the subquery bleeding into the FROM clause + [ticket:1074] - sqlite - - Overhauled SQLite date/time bind/result processing - to use regular expressions and format strings, rather - than strptime/strftime, to generically support - pre-1900 dates, dates with microseconds. [ticket:968] + - Overhauled SQLite date/time bind/result processing to use + regular expressions and format strings, rather than + strptime/strftime, to generically support pre-1900 dates, + dates with microseconds. [ticket:968] + - 0.5.0rc1 ======== diff --git a/lib/sqlalchemy/orm/scoping.py b/lib/sqlalchemy/orm/scoping.py index 5dd17a289..b557e0232 100644 --- a/lib/sqlalchemy/orm/scoping.py +++ b/lib/sqlalchemy/orm/scoping.py @@ -142,7 +142,7 @@ class _ScopedExt(MapperExtension): self.context = context self.validate = validate self.save_on_init = save_on_init - self.set_kwargs_on_init = None + self.set_kwargs_on_init = True def validating(self): return _ScopedExt(self.context, validate=True) @@ -162,26 +162,23 @@ class _ScopedExt(MapperExtension): if not 'query' in class_.__dict__: class_.query = query() - if self.set_kwargs_on_init is None: - self.set_kwargs_on_init = class_.__init__ is object.__init__ - if self.set_kwargs_on_init: - def __init__(self, **kwargs): - pass - class_.__init__ = __init__ + if self.set_kwargs_on_init and class_.__init__ is object.__init__: + class_.__init__ = self._default__init__(mapper) - def init_instance(self, mapper, class_, oldinit, instance, args, kwargs): - if self.save_on_init: - session = kwargs.pop('_sa_session', None) - - if self.set_kwargs_on_init: + def _default__init__(ext, mapper): + def __init__(self, **kwargs): for key, value in kwargs.items(): - if self.validate: + if ext.validate: if not mapper.get_property(key, resolve_synonyms=False, raiseerr=False): raise sa_exc.ArgumentError( "Invalid __init__ argument: '%s'" % key) - setattr(instance, key, value) - kwargs.clear() + setattr(self, key, value) + return __init__ + + def init_instance(self, mapper, class_, oldinit, instance, args, kwargs): + if self.save_on_init: + session = kwargs.pop('_sa_session', None) if self.save_on_init: session = session or self.context.registry() diff --git a/test/orm/scoping.py b/test/orm/scoping.py index 32e0dedb0..eea855490 100644 --- a/test/orm/scoping.py +++ b/test/orm/scoping.py @@ -132,6 +132,36 @@ class ScopedMapperTest(_ScopedTest): assert hasattr(Baz, 'query') @testing.resolve_artifact_names + def test_default_constructor_state_not_shared(self): + scope = scoped_session(sa.orm.sessionmaker()) + + class A(object): + pass + class B(object): + def __init__(self): + pass + + scope.mapper(A, table1) + scope.mapper(B, table2) + + A(foo='bar') + self.assertRaises(TypeError, B, foo='bar') + + scope = scoped_session(sa.orm.sessionmaker()) + + class C(object): + def __init__(self): + pass + class D(object): + pass + + scope.mapper(C, table1) + scope.mapper(D, table2) + + self.assertRaises(TypeError, C, foo='bar') + D(foo='bar') + + @testing.resolve_artifact_names def test_validating_constructor(self): s2 = SomeObject(someid=12) s3 = SomeOtherObject(someid=123, bogus=345) |