def test_conn_reusable(self): conn = db.connect() conn.execute(select([1])) assert len(dbapi.connections) == 1 dbapi.shutdown() # raises error try: conn.execute(select([1])) assert False except tsa.exc.DBAPIError: pass assert not conn.closed assert conn.invalidated # ensure all connections closed (pool was recycled) gc_collect() assert len(dbapi.connections) == 0 # test reconnects conn.execute(select([1])) assert not conn.invalidated assert len(dbapi.connections) == 1
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)
def test_invalidate_trans(self): conn = db.connect() trans = conn.begin() dbapi.shutdown() try: conn.execute(select([1])) assert False except tsa.exc.DBAPIError: pass # assert was invalidated gc_collect() assert len(dbapi.connections) == 0 assert not conn.closed assert conn.invalidated assert trans.is_active assert_raises_message( tsa.exc.StatementError, "Can't reconnect until invalid transaction is rolled back", conn.execute, select([1]), ) assert trans.is_active try: trans.commit() assert False except tsa.exc.InvalidRequestError, e: assert str(e) == "Can't reconnect until invalid transaction is " "rolled back"
def test_gced_delete_on_rollback(self): User, users = self.classes.User, self.tables.users s = self.session() u1 = User(name='ed') s.add(u1) s.commit() s.delete(u1) u1_state = attributes.instance_state(u1) assert u1_state in s.identity_map.all_states() assert u1_state in s._deleted s.flush() assert u1_state not in s.identity_map.all_states() assert u1_state not in s._deleted del u1 gc_collect() assert u1_state.obj() is None s.rollback() assert u1_state in s.identity_map.all_states() u1 = s.query(User).filter_by(name='ed').one() assert u1_state not in s.identity_map.all_states() assert s.scalar(users.count()) == 1 s.delete(u1) s.flush() assert s.scalar(users.count()) == 0 s.commit()
def _test_overflow(self, thread_count, max_overflow): gc_collect() dbapi = MockDBAPI() def creator(): time.sleep(.05) return dbapi.connect() p = pool.QueuePool(creator=creator, pool_size=3, timeout=2, max_overflow=max_overflow) peaks = [] def whammy(): for i in range(10): try: con = p.connect() time.sleep(.005) peaks.append(p.overflow()) con.close() del con except tsa.exc.TimeoutError: pass threads = [] for i in xrange(thread_count): th = threading.Thread(target=whammy) th.start() threads.append(th) for th in threads: th.join() self.assert_(max(peaks) <= max_overflow) lazy_gc() assert not pool._refs
def profile(*args): gc_collect() samples = [0 for x in range(0, 50)] for x in range(0, 50): func(*args) gc_collect() samples[x] = len(gc.get_objects()) print "sample gc sizes:", samples assert len(_sessions) == 0 for x in samples[-4:]: if x != samples[-5]: flatline = False break else: flatline = True # object count is bigger than when it started if not flatline and samples[-1] > samples[0]: for x in samples[1:-2]: # see if a spike bigger than the endpoint exists if x > samples[-1]: break else: assert False, repr(samples) + " " + repr(flatline)
def test_expire_all(self): users, Address, addresses, User = ( self.tables.users, self.classes.Address, self.tables.addresses, self.classes.User, ) mapper( User, users, properties={"addresses": relationship(Address, backref="user", lazy="joined", order_by=addresses.c.id)}, ) mapper(Address, addresses) sess = create_session() userlist = sess.query(User).order_by(User.id).all() assert self.static.user_address_result == userlist assert len(list(sess)) == 9 sess.expire_all() gc_collect() assert len(list(sess)) == 4 # since addresses were gc'ed userlist = sess.query(User).order_by(User.id).all() u = userlist[1] eq_(self.static.user_address_result, userlist) assert len(list(sess)) == 9
def test_invalidate_trans(self): conn = db.connect() trans = conn.begin() dbapi.shutdown() try: conn.execute(select([1])) assert False except tsa.exc.DBAPIError: pass # assert was invalidated gc_collect() assert len(dbapi.connections) == 0 assert not conn.closed assert conn.invalidated assert trans.is_active assert_raises_message( tsa.exc.StatementError, "Can't reconnect until invalid transaction is rolled back", conn.execute, select([1])) assert trans.is_active try: trans.commit() assert False except tsa.exc.InvalidRequestError, e: assert str(e) \ == "Can't reconnect until invalid transaction is "\ "rolled back"
def test_weakref_with_cycles_o2o(self): Address, addresses, users, User = ( self.classes.Address, self.tables.addresses, self.tables.users, self.classes.User, ) s = sessionmaker()() mapper(User, users, properties={"address": relationship(Address, backref="user", uselist=False)}) mapper(Address, addresses) s.add(User(name="ed", address=Address(email_address="ed1"))) s.commit() user = s.query(User).options(joinedload(User.address)).one() user.address.user eq_(user, User(name="ed", address=Address(email_address="ed1"))) del user gc_collect() assert len(s.identity_map) == 0 user = s.query(User).options(joinedload(User.address)).one() user.address.email_address = "ed2" user.address.user # lazyload del user gc_collect() assert len(s.identity_map) == 2 s.commit() user = s.query(User).options(joinedload(User.address)).one() eq_(user, User(name="ed", address=Address(email_address="ed2")))
def test_weak_ref_pickled(self): users, User = self.tables.users, pickleable.User 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
def test_auto_detach_on_gc_session(self): users, User = self.tables.users, self.classes.User mapper(User, users) sess = Session() u1 = User(name='u1') sess.add(u1) sess.commit() # can't add u1 to Session, # already belongs to u2 s2 = Session() assert_raises_message( sa.exc.InvalidRequestError, r".*is already attached to session", s2.add, u1 ) # garbage collect sess del sess gc_collect() # s2 lets it in now despite u1 having # session_key s2.add(u1) assert u1 in s2
def all(): setup() try: t, t2 = 0, 0 def usage(label): now = resource.getrusage(resource.RUSAGE_SELF) print "%s: %0.3fs real, %0.3fs user, %0.3fs sys" % ( label, t2 - t, now.ru_utime - usage.last.ru_utime, now.ru_stime - usage.last.ru_stime) usage.snap(now) usage.snap = lambda stats=None: setattr( usage, 'last', stats or resource.getrusage(resource.RUSAGE_SELF)) session = create_session() gc_collect() usage.snap() t = time.clock() people = orm_select(session) t2 = time.clock() usage('load objects') gc_collect() usage.snap() t = time.clock() update_and_flush(session, people) t2 = time.clock() usage('update and flush') finally: metadata.drop_all()
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)
def test_autoflush_expressions(self): """test that an expression which is dependent on object state is evaluated after the session autoflushes. This is the lambda inside of strategies.py lazy_clause. """ users, Address, addresses, User = ( self.tables.users, self.classes.Address, self.tables.addresses, self.classes.User, ) mapper(User, users, properties={"addresses": relationship(Address, backref="user")}) mapper(Address, addresses) sess = create_session(autoflush=True, autocommit=False) u = User(name="ed", addresses=[Address(email_address="foo")]) sess.add(u) eq_(sess.query(Address).filter(Address.user == u).one(), Address(email_address="foo")) # still works after "u" is garbage collected sess.commit() sess.close() u = sess.query(User).get(u.id) q = sess.query(Address).filter(Address.user == u) del u gc_collect() eq_(q.one(), Address(email_address="foo"))
def test_copy(self): mapper(Parent, self.parents, properties=dict(children=relationship(Child))) mapper(Child, self.children) p = Parent('p1') p.kids.extend(['c1', 'c2']) p_copy = copy.copy(p) del p gc_collect() assert set(p_copy.kids) == set(['c1', 'c2']), p.kids
def test_subclass(self): class SubTarget(self.Target): pass st = SubTarget() st.dispatch.some_event(1, 2) del st del SubTarget gc_collect() eq_(self.Target.__subclasses__(), [])
def test_weak_single(self): data, wim = self._fixture() assert len(data) == len(wim) == len(wim.by_id) oid = id(data[0]) del data[0] gc_collect() assert len(data) == len(wim) == len(wim.by_id) assert oid not in wim.by_id
def test_weak_clear(self): data, wim = self._fixture() assert len(data) == len(wim) == len(wim.by_id) del data[:] gc_collect() eq_(wim, {}) eq_(wim.by_id, {}) eq_(wim._weakrefs, {})
def test_stale_state_positive_gc(self): User = self.classes.User s, u1, a1 = self._fixture() s.expunge(u1) del u1 gc_collect() u1 = s.query(User).first() u1.addresses.remove(a1) self._assert_not_hasparent(a1)
def _profile_cProfile(filename, fn, *args, **kw): import cProfile, pstats, time load_stats = lambda: pstats.Stats(filename) gc_collect() began = time.time() cProfile.runctx('result = fn(*args, **kw)', globals(), locals(), filename=filename) ended = time.time() return ended - began, load_stats, locals()['result']
def test_reconnect(self): """test that an 'is_disconnect' condition will invalidate the connection, and additionally dispose the previous connection pool and recreate.""" pid = id(db.pool) # make a connection conn = db.connect() # connection works conn.execute(select([1])) # create a second connection within the pool, which we'll ensure # also goes away conn2 = db.connect() conn2.close() # two connections opened total now assert len(dbapi.connections) == 2 # set it to fail dbapi.shutdown() try: conn.execute(select([1])) assert False except tsa.exc.DBAPIError: pass # assert was invalidated assert not conn.closed assert conn.invalidated # close shouldnt break conn.close() assert id(db.pool) != pid # ensure all connections closed (pool was recycled) gc_collect() assert len(dbapi.connections) == 0 conn = db.connect() conn.execute(select([1])) conn.close() assert len(dbapi.connections) == 1
def test_weakref_kaboom(self): p = self._queuepool_fixture(pool_size=3, max_overflow=-1, use_threadlocal=True) c1 = p.connect() c2 = p.connect() c1.close() c2 = None del c1 del c2 gc_collect() assert p.checkedout() == 0 c3 = p.connect() assert c3 is not None
def test_prune(self): users, User = self.tables.users, self.classes.User s = create_session(weak_identity_map=False) mapper(User, users) for o in [User(name='u%s' % x) for x in xrange(10)]: s.add(o) # o is still live after this loop... self.assert_(len(s.identity_map) == 0) self.assert_(s.prune() == 0) s.flush() gc_collect() self.assert_(s.prune() == 9) self.assert_(len(s.identity_map) == 1) id = o.id del o self.assert_(s.prune() == 1) self.assert_(len(s.identity_map) == 0) u = s.query(User).get(id) self.assert_(s.prune() == 0) self.assert_(len(s.identity_map) == 1) u.name = 'squiznart' del u self.assert_(s.prune() == 0) self.assert_(len(s.identity_map) == 1) s.flush() self.assert_(s.prune() == 1) self.assert_(len(s.identity_map) == 0) s.add(User(name='x')) self.assert_(s.prune() == 0) self.assert_(len(s.identity_map) == 0) s.flush() self.assert_(len(s.identity_map) == 1) self.assert_(s.prune() == 1) self.assert_(len(s.identity_map) == 0) u = s.query(User).get(id) s.delete(u) del u self.assert_(s.prune() == 0) self.assert_(len(s.identity_map) == 1) s.flush() self.assert_(s.prune() == 0) self.assert_(len(s.identity_map) == 0)
def test_prune(self): users, User = self.tables.users, self.classes.User s = create_session(weak_identity_map=False) mapper(User, users) for o in [User(name="u%s" % x) for x in xrange(10)]: s.add(o) # o is still live after this loop... self.assert_(len(s.identity_map) == 0) self.assert_(s.prune() == 0) s.flush() gc_collect() self.assert_(s.prune() == 9) self.assert_(len(s.identity_map) == 1) id = o.id del o self.assert_(s.prune() == 1) self.assert_(len(s.identity_map) == 0) u = s.query(User).get(id) self.assert_(s.prune() == 0) self.assert_(len(s.identity_map) == 1) u.name = "squiznart" del u self.assert_(s.prune() == 0) self.assert_(len(s.identity_map) == 1) s.flush() self.assert_(s.prune() == 1) self.assert_(len(s.identity_map) == 0) s.add(User(name="x")) self.assert_(s.prune() == 0) self.assert_(len(s.identity_map) == 0) s.flush() self.assert_(len(s.identity_map) == 1) self.assert_(s.prune() == 1) self.assert_(len(s.identity_map) == 0) u = s.query(User).get(id) s.delete(u) del u self.assert_(s.prune() == 0) self.assert_(len(s.identity_map) == 1) s.flush() self.assert_(s.prune() == 0) self.assert_(len(s.identity_map) == 0)
def test_identity_map_mutate(self): users, User = self.tables.users, self.classes.User mapper(User, users) sess = Session() sess.add_all([User(name="u1"), User(name="u2"), User(name="u3")]) sess.commit() u1, u2, u3 = sess.query(User).all() for i, (key, value) in enumerate(sess.identity_map.iteritems()): if i == 2: del u3 gc_collect()
def test_identity_map_mutate(self): users, User = self.tables.users, self.classes.User mapper(User, users) sess = Session() sess.add_all([User(name='u1'), User(name='u2'), User(name='u3')]) sess.commit() u1, u2, u3 = sess.query(User).all() for i, (key, value) in enumerate(sess.identity_map.iteritems()): if i == 2: del u3 gc_collect()
def _profile_hotshot(filename, fn, *args, **kw): import hotshot, hotshot.stats, time load_stats = lambda: hotshot.stats.load(filename) gc_collect() prof = hotshot.Profile(filename) began = time.time() prof.start() try: result = fn(*args, **kw) finally: prof.stop() ended = time.time() prof.close() return ended - began, load_stats, result
def wrap(*args, **kw): if cProfile is None: raise SkipTest("cProfile is not installed") if not _profile_stats.has_stats() and not _profile_stats.write: raise SkipTest("No profiling stats available on this " "platform for this function. Run tests with " "--write-profiles to add statistics to %s for " "this platform." % _profile_stats.short_fname) gc_collect() timespent, load_stats, fn_result = _profile( fn, *args, **kw ) stats = load_stats() callcount = stats.total_calls expected = _profile_stats.result(callcount) if expected is None: expected_count = None else: line_no, expected_count = expected print("Pstats calls: %d Expected %s" % ( callcount, expected_count ) ) stats.print_stats() #stats.print_callers() if expected_count: deviance = int(callcount * variance) if abs(callcount - expected_count) > deviance: raise AssertionError( "Adjusted function call count %s not within %s%% " "of expected %s. (Delete line %d of file %s to regenerate " "this callcount, when tests are run with --write-profiles.)" % ( callcount, (variance * 100), expected_count, line_no, _profile_stats.fname)) return fn_result
def test_non_mutated_state_not_resurrected(self): Foo = self.classes.Foo f1 = Foo(data=pickleable.Bar(4, 5)) session = Session() session.add(f1) session.commit() session = Session() f1 = session.query(Foo).first() del f1 gc_collect() assert len(session.identity_map) == 0 f1 = session.query(Foo).first() assert not attributes.instance_state(f1).modified
def test_weak_identity_map(self): mapper(Parent, self.parents, properties=dict(children=relationship(Child))) mapper(Child, self.children) session = create_session(weak_identity_map=True) def add_child(parent_name, child_name): parent = \ session.query(Parent).filter_by(name=parent_name).one() parent.kids.append(child_name) add_child('p1', 'c1') gc_collect() add_child('p1', 'c2') session.flush() p = session.query(Parent).filter_by(name='p1').one() assert set(p.kids) == set(['c1', 'c2']), p.kids
def test_stale_state_negative(self): User = self.classes.User s, u1, a1 = self._fixture() u2 = User(addresses=[a1]) s.add(u2) s.flush() s._expunge_state(attributes.instance_state(u2)) del u2 gc_collect() assert_raises_message(orm_exc.StaleDataError, "can't be sure this is the most recent parent.", u1.addresses.remove, a1) s.flush() self._assert_hasparent(a1)
def test_mutated_state_resurrected(self): Foo = self.classes.Foo f1 = Foo(data=pickleable.Bar(4, 5), val=u"hi") session = Session() session.add(f1) session.commit() f1.data.y = 19 del f1 gc_collect() assert len(session.identity_map) == 1 session.commit() assert session.query(Foo).one().data == pickleable.Bar(4, 19)
def test_mutated_state_resurrected(self): Foo = self.classes.Foo f1 = Foo(data=pickleable.Bar(4, 5), val=u'hi') session = Session() session.add(f1) session.commit() f1.data.y = 19 del f1 gc_collect() assert len(session.identity_map) == 1 session.commit() assert session.query(Foo).one().data == pickleable.Bar(4, 19)
def test_stale_state_negative(self): User = self.classes.User s, u1, a1 = self._fixture() u2 = User(addresses=[a1]) s.add(u2) s.flush() s._expunge_state(attributes.instance_state(u2)) del u2 gc_collect() assert_raises_message( orm_exc.StaleDataError, "can't be sure this is the most recent parent.", u1.addresses.remove, a1 ) s.flush() self._assert_hasparent(a1)
def test_strong_ref(self): users, User = self.tables.users, self.classes.User s = create_session(weak_identity_map=False) mapper(User, users) # save user s.add(User(name='u1')) s.flush() user = s.query(User).one() user = None print s.identity_map gc_collect() assert len(s.identity_map) == 1 user = s.query(User).one() assert not s.identity_map._modified user.name = 'u2' assert s.identity_map._modified s.flush() eq_(users.select().execute().fetchall(), [(user.id, 'u2')])
def test_strong_ref(self): users, User = self.tables.users, self.classes.User s = create_session(weak_identity_map=False) mapper(User, users) # save user s.add(User(name="u1")) s.flush() user = s.query(User).one() user = None print s.identity_map gc_collect() assert len(s.identity_map) == 1 user = s.query(User).one() assert not s.identity_map._modified user.name = "u2" assert s.identity_map._modified s.flush() eq_(users.select().execute().fetchall(), [(user.id, "u2")])
def test_weak_ref(self): """test the weak-referencing identity map, which strongly- references modified items.""" users, User = self.tables.users, self.classes.User s = create_session() mapper(User, users) s.add(User(name='ed')) s.flush() assert not s.dirty user = s.query(User).one() del user gc_collect() assert len(s.identity_map) == 0 user = s.query(User).one() user.name = 'fred' del user 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 user = s.query(User).one() assert user.name == 'fred' assert s.identity_map
def test_weak_ref(self): """test the weak-referencing identity map, which strongly- references modified items.""" users, User = self.tables.users, self.classes.User s = create_session() mapper(User, users) s.add(User(name="ed")) s.flush() assert not s.dirty user = s.query(User).one() del user gc_collect() assert len(s.identity_map) == 0 user = s.query(User).one() user.name = "fred" del user 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 user = s.query(User).one() assert user.name == "fred" assert s.identity_map
def test_stale_state_positive_pk_change(self): """Illustrate that we can't easily link a stale state to a fresh one if the fresh one has a PK change (unless we a. tracked all the previous PKs, wasteful, or b. recycled states - time consuming, breaks lots of edge cases, destabilizes the code) """ User = self.classes.User s, u1, a1 = self._fixture() s._expunge_state(attributes.instance_state(u1)) del u1 gc_collect() u1 = s.query(User).first() # primary key change. now we # can't rely on state.key as the # identifier. u1.id = 5 a1.user_id = 5 s.flush() assert_raises_message(orm_exc.StaleDataError, "can't be sure this is the most recent parent.", u1.addresses.remove, a1) # unfortunately, u1.addresses was impacted # here assert u1.addresses == [] # expire all and we can continue s.expire_all() u1.addresses.remove(a1) self._assert_not_hasparent(a1)
def test_weak_threadhop(self): data, wim = self._fixture() data = set(data) cv = threading.Condition() def empty(obj): cv.acquire() obj.clear() cv.notify() cv.release() th = threading.Thread(target=empty, args=(data, )) cv.acquire() th.start() cv.wait() cv.release() gc_collect() eq_(wim, {}) eq_(wim.by_id, {}) eq_(wim._weakrefs, {})
def wrap(*args, **kw): if cProfile is None: raise SkipTest("cProfile is not installed") if not _profile_stats.has_stats() and not _profile_stats.write: raise SkipTest("No profiling stats available on this " "platform for this function. Run tests with " "--write-profiles to add statistics to %s for " "this platform." % _profile_stats.short_fname) gc_collect() timespent, load_stats, fn_result = _profile(fn, *args, **kw) stats = load_stats() callcount = stats.total_calls expected = _profile_stats.result(callcount) if expected is None: expected_count = None else: line_no, expected_count = expected print("Pstats calls: %d Expected %s" % (callcount, expected_count)) stats.print_stats() #stats.print_callers() if expected_count: deviance = int(callcount * variance) if abs(callcount - expected_count) > deviance: raise AssertionError( "Adjusted function call count %s not within %s%% " "of expected %s. (Delete line %d of file %s to regenerate " "this callcount, when tests are run with --write-profiles.)" % (callcount, (variance * 100), expected_count, line_no, _profile_stats.fname)) return fn_result
def test_weakref_with_cycles_o2o(self): Address, addresses, users, User = (self.classes.Address, self.tables.addresses, self.tables.users, self.classes.User) s = sessionmaker()() mapper(User, users, properties={ "address": relationship(Address, backref="user", uselist=False) }) mapper(Address, addresses) s.add(User(name="ed", address=Address(email_address="ed1"))) s.commit() user = s.query(User).options(joinedload(User.address)).one() user.address.user eq_(user, User(name="ed", address=Address(email_address="ed1"))) del user gc_collect() assert len(s.identity_map) == 0 user = s.query(User).options(joinedload(User.address)).one() user.address.email_address = 'ed2' user.address.user # lazyload del user gc_collect() assert len(s.identity_map) == 2 s.commit() user = s.query(User).options(joinedload(User.address)).one() eq_(user, User(name="ed", address=Address(email_address="ed2")))
def assert_no_mappers(): clear_mappers() gc_collect() assert len(_mapper_registry) == 0
def all(): setup() try: t, t2 = 0, 0 def usage(label): now = resource.getrusage(resource.RUSAGE_SELF) print "%s: %0.3fs real, %0.3fs user, %0.3fs sys" % ( label, t2 - t, now.ru_utime - usage.last.ru_utime, now.ru_stime - usage.last.ru_stime) usage.snap(now) usage.snap = lambda stats=None: setattr( usage, 'last', stats or resource.getrusage(resource.RUSAGE_SELF)) gc_collect() usage.snap() t = time.clock() sqlite_select(RawPerson) t2 = time.clock() usage('sqlite select/native') gc_collect() usage.snap() t = time.clock() sqlite_select(Person) t2 = time.clock() usage('sqlite select/instrumented') gc_collect() usage.snap() t = time.clock() sql_select(RawPerson) t2 = time.clock() usage('sqlalchemy.sql select/native') gc_collect() usage.snap() t = time.clock() sql_select(Person) t2 = time.clock() usage('sqlalchemy.sql select/instrumented') gc_collect() usage.snap() t = time.clock() orm_select() t2 = time.clock() usage('sqlalchemy.orm fetch') gc_collect() usage.snap() t = time.clock() joined_orm_select() t2 = time.clock() usage('sqlalchemy.orm "joined" fetch') finally: metadata.drop_all()