From abe17984fb608f62674737803ec0ea1012d5b176 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 2 Oct 2008 02:02:51 +0000 Subject: [PATCH] - identity_map._mutable_attrs is a plain dict since we manage weakref removal explicitly - call list() around iteration of _mutable_attrs to guard against async gc.collect() while check_modified() is running --- CHANGES | 4 +++ lib/sqlalchemy/orm/identity.py | 4 +-- test/orm/memusage.py | 53 ++++++++++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 6ce7d07c7a..c129c06529 100644 --- a/CHANGES +++ b/CHANGES @@ -43,6 +43,10 @@ CHANGES - Fixed session.transaction.commit() on a autocommit=False session not starting a new transaction. + + - Some adjustments to Session.identity_map's weak referencing + behavior to reduce asynchronous GC side effects. + - sql - column.in_(someselect) can now be used as a columns-clause expression without the subquery bleeding into the FROM clause diff --git a/lib/sqlalchemy/orm/identity.py b/lib/sqlalchemy/orm/identity.py index 6369d5a3c2..4232821612 100644 --- a/lib/sqlalchemy/orm/identity.py +++ b/lib/sqlalchemy/orm/identity.py @@ -12,7 +12,7 @@ from sqlalchemy.orm import attributes class IdentityMap(dict): def __init__(self): - self._mutable_attrs = weakref.WeakKeyDictionary() + self._mutable_attrs = {} self.modified = False def add(self, state): @@ -41,7 +41,7 @@ class IdentityMap(dict): """return True if any InstanceStates present have been marked as 'modified'.""" if not self.modified: - for state in self._mutable_attrs: + for state in list(self._mutable_attrs): if state.check_modified(): return True else: diff --git a/test/orm/memusage.py b/test/orm/memusage.py index 6348f22371..ab6a5e2c0a 100644 --- a/test/orm/memusage.py +++ b/test/orm/memusage.py @@ -1,11 +1,11 @@ import testenv; testenv.configure_for_tests() import gc -from sqlalchemy.orm import mapper, relation, create_session, clear_mappers +from sqlalchemy.orm import mapper, relation, create_session, clear_mappers, sessionmaker from sqlalchemy.orm.mapper import _mapper_registry from sqlalchemy.orm.session import _sessions - +import operator from testlib import testing -from testlib.sa import MetaData, Table, Column, Integer, String, ForeignKey +from testlib.sa import MetaData, Table, Column, Integer, String, ForeignKey, PickleType from orm import _base @@ -300,6 +300,53 @@ class MemUsageTest(EnsureZeroed): metadata.drop_all() assert_no_mappers() + def test_mutable_identity(self): + metadata = MetaData(testing.db) + table1 = Table("mytable", metadata, + Column('col1', Integer, primary_key=True), + Column('col2', PickleType(comparator=operator.eq)) + ) + + class Foo(object): + def __init__(self, col2): + self.col2 = col2 + + mapper(Foo, table1) + metadata.create_all() + + session = sessionmaker()() + + def go(): + obj = [ + Foo({'a':1}), + Foo({'b':1}), + Foo({'c':1}), + Foo({'d':1}), + Foo({'e':1}), + Foo({'f':1}), + Foo({'g':1}), + Foo({'h':1}), + Foo({'i':1}), + Foo({'j':1}), + Foo({'k':1}), + Foo({'l':1}), + ] + + session.add_all(obj) + session.commit() + + testing.eq_(len(session.identity_map._mutable_attrs), 12) + testing.eq_(len(session.identity_map), 12) + obj = None + gc.collect() + testing.eq_(len(session.identity_map._mutable_attrs), 0) + testing.eq_(len(session.identity_map), 0) + + try: + go() + finally: + metadata.drop_all() + if __name__ == '__main__': testenv.main() -- 2.47.3