From: Mike Bayer Date: Fri, 10 Jul 2009 20:01:56 +0000 (+0000) Subject: - Fixed potential memory leak whereby previously pickled objects X-Git-Tag: rel_0_5_5~6 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=910961fccdf9fe6933c56c595c1126df132a31ff;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed potential memory leak whereby previously pickled objects placed back in a session would not be fully garbage collected unless the Session were explicitly closed out. --- diff --git a/CHANGES b/CHANGES index 16b21f634a..0d16bcf848 100644 --- a/CHANGES +++ b/CHANGES @@ -33,6 +33,10 @@ CHANGES - Fixed bug whereby session.is_modified() would raise an exception if any synonyms were in use. + + - Fixed potential memory leak whereby previously pickled objects + placed back in a session would not be fully garbage collected + unless the Session were explicitly closed out. - Fixed Query being able to join() from individual columns of a joined-table subclass entity, i.e. diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index 1b7b3fbd54..10a0f43eeb 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -16,6 +16,7 @@ class InstanceState(object): load_path = () insert_order = None mutable_dict = None + _strong_obj = None def __init__(self, obj, manager): self.class_ = obj.__class__ @@ -139,7 +140,7 @@ class InstanceState(object): return d def __setstate__(self, state): - self.obj = weakref.ref(state['instance']) + self.obj = weakref.ref(state['instance'], self._cleanup) self.class_ = state['instance'].__class__ self.manager = manager_of_class(self.class_) @@ -150,6 +151,9 @@ class InstanceState(object): self.expired = state.get('expired', False) self.callables = state.get('callables', {}) + if self.modified: + self._strong_obj = state['instance'] + self.__dict__.update( (k, state[k]) for k in ( 'key', 'load_options', 'expired_attributes', 'mutable_dict' @@ -272,7 +276,8 @@ class InstanceState(object): instance_dict._modified.add(self) self.modified = True - self._strong_obj = self.obj() + if not self._strong_obj: + self._strong_obj = self.obj() def commit(self, dict_, keys): """Commit attributes. diff --git a/test/orm/test_session.py b/test/orm/test_session.py index a94b4e7df7..328cbee8ee 100644 --- a/test/orm/test_session.py +++ b/test/orm/test_session.py @@ -789,6 +789,36 @@ class SessionTest(_fixtures.FixtureTest): user = s.query(User).one() assert user.name == 'fred' assert s.identity_map + + @testing.resolve_artifact_names + def test_weak_ref_pickled(self): + s = create_session() + mapper(User, users) + + s.add(User(name='ed')) + s.flush() + assert not s.dirty + + user = s.query(User).one() + user.name = 'fred' + s.expunge(user) + + u2 = pickle.loads(pickle.dumps(user)) + + del user + s.add(u2) + + del u2 + gc.collect() + + assert len(s.identity_map) == 1 + assert len(s.dirty) == 1 + assert None not in s.dirty + s.flush() + gc.collect() + assert not s.dirty + + assert not s.identity_map @testing.resolve_artifact_names def test_weakref_with_cycles_o2m(self):