diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-07-18 17:42:11 +0000 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-07-18 17:42:11 +0000 |
commit | 5e7f90a1d62b04a0f866d8cd170c66e7b1f7ae7d (patch) | |
tree | 82d964efbcc1992067325e6c1cca45b6c61ce02f /test/orm/session.py | |
parent | 5af88c5df1f26d60becc8c60f7d99b3278e7c220 (diff) | |
download | sqlalchemy-5e7f90a1d62b04a0f866d8cd170c66e7b1f7ae7d.tar.gz |
- Fixed a series of potential race conditions in
Session whereby asynchronous GC could remove unmodified,
no longer referenced items from the session as they were
present in a list of items to be processed, typically
during session.expunge_all() and dependent methods.
Diffstat (limited to 'test/orm/session.py')
-rw-r--r-- | test/orm/session.py | 82 |
1 files changed, 81 insertions, 1 deletions
diff --git a/test/orm/session.py b/test/orm/session.py index df9536650..9d3ab6610 100644 --- a/test/orm/session.py +++ b/test/orm/session.py @@ -2,7 +2,7 @@ import testenv; testenv.configure_for_tests() import gc import inspect import pickle -from sqlalchemy.orm import create_session, sessionmaker +from sqlalchemy.orm import create_session, sessionmaker, attributes from testlib import engines, sa, testing, config from testlib.sa import Table, Column, Integer, String from testlib.sa.orm import mapper, relation, backref @@ -990,7 +990,87 @@ class SessionTest(_fixtures.FixtureTest): assert b in sess assert len(list(sess)) == 1 +class DisposedStates(testing.ORMTest): + keep_mappers = True + keep_tables = True + + def define_tables(self, metadata): + global t1 + t1 = Table('t1', metadata, + Column('id', Integer, primary_key=True), + Column('data', String(50)) + ) + + def setup_mappers(self): + global T + class T(object): + def __init__(self, data): + self.data = data + mapper(T, t1) + + def tearDown(self): + from sqlalchemy.orm.session import _sessions + _sessions.clear() + super(DisposedStates, self).tearDown() + + def _set_imap_in_disposal(self, sess, *objs): + """remove selected objects from the given session, as though they + were dereferenced and removed from WeakIdentityMap. + + Hardcodes the identity map's "all_states()" method to return the full list + of states. This simulates the all_states() method returning results, afterwhich + some of the states get garbage collected (this normally only happens during + asynchronous gc). The Session now has one or more + InstanceState's which have been removed from the identity map and disposed. + + Will the Session not trip over this ??? Stay tuned. + + """ + all_states = sess.identity_map.all_states() + sess.identity_map.all_states = lambda: all_states + for obj in objs: + state = attributes.instance_state(obj) + sess.identity_map.remove(state) + state.dispose() + + def _test_session(self, **kwargs): + global sess + sess = create_session(**kwargs) + + data = o1, o2, o3, o4, o5 = [T('t1'), T('t2'), T('t3'), T('t4'), T('t5')] + + sess.add_all(data) + + sess.flush() + o1.data = 't1modified' + o5.data = 't5modified' + + self._set_imap_in_disposal(sess, o2, o4, o5) + return sess + + def test_flush(self): + self._test_session().flush() + + def test_clear(self): + self._test_session().clear() + + def test_close(self): + self._test_session().close() + + def test_expunge_all(self): + self._test_session().expunge_all() + + def test_expire_all(self): + self._test_session().expire_all() + + def test_rollback(self): + sess = self._test_session(autocommit=False, autoexpire=True) + sess.commit() + + sess.rollback() + + class SessionInterface(testing.TestBase): """Bogus args to Session methods produce actionable exceptions.""" |