[Checkins] SVN: zope.sqlalchemy/trunk/ Make life-time of sessions configurable. Specify `keep_session=True` when
Christian Theune
cvs-admin at zope.org
Sun Jan 27 19:04:22 UTC 2013
Log message for revision 129103:
Make life-time of sessions configurable. Specify `keep_session=True` when
setting up the SA extension.
Changed:
U zope.sqlalchemy/trunk/CHANGES.txt
U zope.sqlalchemy/trunk/src/zope/sqlalchemy/README.txt
U zope.sqlalchemy/trunk/src/zope/sqlalchemy/datamanager.py
-=-
Modified: zope.sqlalchemy/trunk/CHANGES.txt
===================================================================
--- zope.sqlalchemy/trunk/CHANGES.txt 2013-01-24 03:42:08 UTC (rev 129102)
+++ zope.sqlalchemy/trunk/CHANGES.txt 2013-01-27 19:04:21 UTC (rev 129103)
@@ -4,7 +4,8 @@
0.7.2 (unreleased)
------------------
-* ...
+* Make life-time of sessions configurable. Specify `keep_session=True` when
+ setting up the SA extension.
0.7.1 (2012-05-19)
------------------
Modified: zope.sqlalchemy/trunk/src/zope/sqlalchemy/README.txt
===================================================================
--- zope.sqlalchemy/trunk/src/zope/sqlalchemy/README.txt 2013-01-24 03:42:08 UTC (rev 129102)
+++ zope.sqlalchemy/trunk/src/zope/sqlalchemy/README.txt 2013-01-27 19:04:21 UTC (rev 129103)
@@ -104,7 +104,7 @@
[]
We can now create a new user and commit the changes using Zope's transaction
-machinary, just as Zope's publisher would.
+machinery, just as Zope's publisher would.
>>> session.add(User(id=1, name='bob'))
>>> transaction.commit()
@@ -130,7 +130,7 @@
[<Address object at ...>]
>>> str(bob.addresses[0].email)
'bob at bob.bob'
- >>> bob.addresses[0].email = 'wrong at wrong'
+ >>> bob.addresses[0].email = 'wrong at wrong'
To rollback a transaction, use transaction.abort().
@@ -153,7 +153,7 @@
>>> conn = session.connection()
>>> users = Base.metadata.tables['test_users']
>>> conn.execute(users.update(users.c.name=='bob'), name='ben')
- <sqlalchemy.engine.base.ResultProxy object at ...>
+ <sqlalchemy.engine.result.ResultProxy object at ...>
>>> from zope.sqlalchemy import mark_changed
>>> mark_changed(session)
>>> transaction.commit()
@@ -170,13 +170,39 @@
>>> session = Session()
>>> conn = session.connection()
>>> conn.execute(users.update(users.c.name=='ben'), name='bob')
- <sqlalchemy.engine.base.ResultProxy object at ...>
+ <sqlalchemy.engine.result.ResultProxy object at ...>
>>> transaction.commit()
>>> session = Session()
>>> str(session.query(User).all()[0].name)
'bob'
>>> transaction.abort()
+Long-lasting session scopes
+---------------------------
+
+The default behaviour of the transaction integration is to close the session
+after a commit. You can tell by trying to access an object after committing:
+
+ >>> bob = session.query(User).all()[0]
+ >>> transaction.commit()
+ >>> bob.name
+ Traceback (most recent call last):
+ DetachedInstanceError: Instance <User at ...> is not bound to a Session; attribute refresh operation cannot proceed
+
+To support cases where a session needs to last longer than a transaction
+(useful in test suites) you can specify to keep a session when creating the
+transaction extension:
+
+ >>> Session = scoped_session(sessionmaker(bind=engine,
+ ... twophase=TEST_TWOPHASE, extension=ZopeTransactionExtension(keep_session=True)))
+
+ >>> session = Session()
+ >>> bob = session.query(User).all()[0]
+ >>> bob.name = 'bobby'
+ >>> transaction.commit()
+ >>> bob.name
+ u'bobby'
+
Development version
===================
Modified: zope.sqlalchemy/trunk/src/zope/sqlalchemy/datamanager.py
===================================================================
--- zope.sqlalchemy/trunk/src/zope/sqlalchemy/datamanager.py 2013-01-24 03:42:08 UTC (rev 129102)
+++ zope.sqlalchemy/trunk/src/zope/sqlalchemy/datamanager.py 2013-01-27 19:04:21 UTC (rev 129103)
@@ -61,12 +61,13 @@
One phase variant.
"""
- def __init__(self, session, status, transaction_manager):
+ def __init__(self, session, status, transaction_manager, keep_session=False):
self.transaction_manager = transaction_manager
self.tx = session.transaction._iterate_parents()[-1]
self.session = session
_SESSION_STATE[id(session)] = status
self.state = 'init'
+ self.keep_session = keep_session
def _finish(self, final_state):
assert self.tx is not None
@@ -76,7 +77,10 @@
self.state = final_state
# closing the session is the last thing we do. If it fails the
# transactions don't get wedged and the error propagates
- session.close()
+ if not self.keep_session:
+ session.close()
+ else:
+ session.expire_all()
def abort(self, trans):
if self.tx is not None: # there may have been no work to do
@@ -176,7 +180,7 @@
self.transaction.rollback()
-def join_transaction(session, initial_state=STATUS_ACTIVE, transaction_manager=zope_transaction.manager):
+def join_transaction(session, initial_state=STATUS_ACTIVE, transaction_manager=zope_transaction.manager, keep_session=False):
"""Join a session to a transaction using the appropriate datamanager.
It is safe to call this multiple times, if the session is already joined
@@ -195,7 +199,7 @@
DataManager = TwoPhaseSessionDataManager
else:
DataManager = SessionDataManager
- transaction_manager.get().join(DataManager(session, initial_state, transaction_manager))
+ transaction_manager.get().join(DataManager(session, initial_state, transaction_manager, keep_session=keep_session))
def mark_changed(session, transaction_manager=zope_transaction.manager):
"""Mark a session as needing to be committed.
@@ -211,17 +215,18 @@
the DataManager to rollback rather than commit on read only transactions.
"""
- def __init__(self, initial_state=STATUS_ACTIVE, transaction_manager=zope_transaction.manager):
+ def __init__(self, initial_state=STATUS_ACTIVE, transaction_manager=zope_transaction.manager, keep_session=False):
if initial_state=='invalidated': initial_state = STATUS_CHANGED #BBB
SessionExtension.__init__(self)
self.initial_state = initial_state
self.transaction_manager = transaction_manager
+ self.keep_session = keep_session
def after_begin(self, session, transaction, connection):
- join_transaction(session, self.initial_state, self.transaction_manager)
+ join_transaction(session, self.initial_state, self.transaction_manager, self.keep_session)
def after_attach(self, session, instance):
- join_transaction(session, self.initial_state, self.transaction_manager)
+ join_transaction(session, self.initial_state, self.transaction_manager, self.keep_session)
def after_flush(self, session, flush_context):
mark_changed(session, self.transaction_manager)
More information about the checkins
mailing list