[repoapi,session] remove all session-as-cnx backward compat
The `dbapi` being gone, we now can drop the session object
bw-compatibility layer. This will allow further simplifications, such
as folding ClientConnection and Connection (without too much pain),
and then having persistent sessions.
Related to #3933480.
--- a/devtools/testlib.py Wed Apr 22 18:28:58 2015 +0200
+++ b/devtools/testlib.py Fri Jun 06 17:08:08 2014 +0200
@@ -358,19 +358,9 @@
@deprecated('[3.19] explicitly use RepoAccess object in test instead')
def session(self):
"""return current server side session"""
- # XXX We want to use a srv_connection instead and deprecate this
- # property
session = self._current_session
if session is None:
session = self._admin_session
- # bypassing all sanity to use the same repo cnx in the session
- #
- # we can't call set_cnx as the Connection is not managed by the
- # session.
- session._Session__threaddata.cnx = self._admin_clt_cnx._cnx
- else:
- session._Session__threaddata.cnx = self.cnx._cnx
- session.set_cnxset()
return session
@property
@@ -542,8 +532,7 @@
self._admin_clt_cnx.close()
self._admin_clt_cnx = None
if self._admin_session is not None:
- if not self._admin_session.closed:
- self.repo.close(self._admin_session.sessionid)
+ self.repo.close(self._admin_session.sessionid)
self._admin_session = None
while self._cleanups:
cleanup, args, kwargs = self._cleanups.pop(-1)
--- a/server/migractions.py Wed Apr 22 18:28:58 2015 +0200
+++ b/server/migractions.py Fri Jun 06 17:08:08 2014 +0200
@@ -97,7 +97,7 @@
self.session = cnx._session
elif connect:
self.repo_connect()
- self.set_session()
+ self.set_cnx()
else:
self.session = None
# no config on shell to a remote instance
@@ -125,7 +125,7 @@
self.fs_schema = schema
self._synchronized = set()
- def set_session(self):
+ def set_cnx(self):
try:
login = self.repo.config.default_admin_config['login']
pwd = self.repo.config.default_admin_config['password']
@@ -149,7 +149,6 @@
print 'aborting...'
sys.exit(0)
self.session = self.repo._get_session(self.cnx.sessionid)
- self.session.keep_cnxset_mode('transaction')
# overriden from base MigrationHelper ######################################
--- a/server/repository.py Wed Apr 22 18:28:58 2015 +0200
+++ b/server/repository.py Fri Jun 06 17:08:08 2014 +0200
@@ -668,15 +668,13 @@
"""raise `BadConnectionId` if the connection is no more valid, else
return its latest activity timestamp.
"""
- return self._get_session(sessionid, setcnxset=False).timestamp
+ return self._get_session(sessionid).timestamp
def close(self, sessionid, txid=None, checkshuttingdown=True):
"""close the session with the given id"""
session = self._get_session(sessionid, txid=txid,
checkshuttingdown=checkshuttingdown)
# operation uncommited before close are rolled back before hook is called
- if session._cnx._session_handled:
- session._cnx.rollback(free_cnxset=False)
with session.new_cnx() as cnx:
self.hm.call_hooks('session_close', cnx)
# commit connection at this point in case write operation has been
@@ -724,8 +722,7 @@
with cnx.ensure_cnx_set:
yield cnx
- def _get_session(self, sessionid, setcnxset=False, txid=None,
- checkshuttingdown=True):
+ def _get_session(self, sessionid, txid=None, checkshuttingdown=True):
"""return the session associated with the given session identifier"""
if checkshuttingdown and self.shutting_down:
raise ShuttingDown('Repository is shutting down')
@@ -733,9 +730,6 @@
session = self._sessions[sessionid]
except KeyError:
raise BadConnectionId('No such session %s' % sessionid)
- if setcnxset:
- session.set_cnx(txid) # must be done before set_cnxset
- session.set_cnxset()
return session
# data sources handling ###################################################
--- a/server/session.py Wed Apr 22 18:28:58 2015 +0200
+++ b/server/session.py Fri Jun 06 17:08:08 2014 +0200
@@ -439,6 +439,7 @@
"""
is_request = False
+ mode = 'read'
def __init__(self, session, cnxid=None, session_handled=False):
# using super(Connection, self) confuse some test hack
@@ -473,12 +474,10 @@
# other session utility
self._session_timestamp = session._timestamp
- #: connection handling mode
- self.mode = session.default_mode
#: connection set used to execute queries on sources
self._cnxset = None
#: CnxSetTracker used to report cnxset usage
- self._cnxset_tracker = session._cnxset_tracker
+ self._cnxset_tracker = CnxSetTracker()
#: is this connection from a client or internal to the repo
self.running_dbapi_query = True
# internal (root) session
@@ -1226,148 +1225,37 @@
return float(self.value)
-class Session(RequestSessionBase): # XXX repoapi: stop being a
- # RequestSessionBase at some point
+class Session(object):
"""Repository user session
This ties all together:
* session id,
* user,
- * connections set,
* other session data.
-
- **About session storage / transactions**
-
- Here is a description of internal session attributes. Besides :attr:`data`
- and :attr:`transaction_data`, you should not have to use attributes
- described here but higher level APIs.
-
- :attr:`data` is a dictionary containing shared data, used to communicate
- extra information between the client and the repository
-
- :attr:`_cnxs` is a dictionary of :class:`Connection` instance, one
- for each running connection. The key is the connection id. By default
- the connection id is the thread name but it can be otherwise (per dbapi
- cursor for instance, or per thread name *from another process*).
-
- :attr:`__threaddata` is a thread local storage whose `cnx` attribute
- refers to the proper instance of :class:`Connection` according to the
- connection.
-
- You should not have to use neither :attr:`_cnx` nor :attr:`__threaddata`,
- simply access connection data transparently through the :attr:`_cnx`
- property. Also, you usually don't have to access it directly since current
- connection's data may be accessed/modified through properties / methods:
-
- :attr:`connection_data`, similarly to :attr:`data`, is a dictionary
- containing some shared data that should be cleared at the end of the
- connection. Hooks and operations may put arbitrary data in there, and
- this may also be used as a communication channel between the client and
- the repository.
-
- .. automethod:: cubicweb.server.session.Session.get_shared_data
- .. automethod:: cubicweb.server.session.Session.set_shared_data
- .. automethod:: cubicweb.server.session.Session.added_in_transaction
- .. automethod:: cubicweb.server.session.Session.deleted_in_transaction
-
- Connection state information:
-
- :attr:`running_dbapi_query`, boolean flag telling if the executing query
- is coming from a dbapi connection or is a query from within the repository
-
- :attr:`cnxset`, the connections set to use to execute queries on sources.
- During a transaction, the connection set may be freed so that is may be
- used by another session as long as no writing is done. This means we can
- have multiple sessions with a reasonably low connections set pool size.
-
- .. automethod:: cubicweb.server.session.Session.set_cnxset
- .. automethod:: cubicweb.server.session.Session.free_cnxset
-
- :attr:`mode`, string telling the connections set handling mode, may be one
- of 'read' (connections set may be freed), 'write' (some write was done in
- the connections set, it can't be freed before end of the transaction),
- 'transaction' (we want to keep the connections set during all the
- transaction, with or without writing)
-
- :attr:`pending_operations`, ordered list of operations to be processed on
- commit/rollback
-
- :attr:`commit_state`, describing the transaction commit state, may be one
- of None (not yet committing), 'precommit' (calling precommit event on
- operations), 'postcommit' (calling postcommit event on operations),
- 'uncommitable' (some :exc:`ValidationError` or :exc:`Unauthorized` error
- has been raised during the transaction and so it must be rolled back).
-
- .. automethod:: cubicweb.server.session.Session.commit
- .. automethod:: cubicweb.server.session.Session.rollback
- .. automethod:: cubicweb.server.session.Session.close
- .. automethod:: cubicweb.server.session.Session.closed
-
- Security level Management:
-
- :attr:`read_security` and :attr:`write_security`, boolean flags telling if
- read/write security is currently activated.
-
- .. automethod:: cubicweb.server.session.Session.security_enabled
-
- Hooks Management:
-
- :attr:`hooks_mode`, may be either `HOOKS_ALLOW_ALL` or `HOOKS_DENY_ALL`.
-
- :attr:`enabled_hook_categories`, when :attr:`hooks_mode` is
- `HOOKS_DENY_ALL`, this set contains hooks categories that are enabled.
-
- :attr:`disabled_hook_categories`, when :attr:`hooks_mode` is
- `HOOKS_ALLOW_ALL`, this set contains hooks categories that are disabled.
-
- .. automethod:: cubicweb.server.session.Session.deny_all_hooks_but
- .. automethod:: cubicweb.server.session.Session.allow_all_hooks_but
- .. automethod:: cubicweb.server.session.Session.is_hook_category_activated
- .. automethod:: cubicweb.server.session.Session.is_hook_activated
-
- Data manipulation:
-
- .. automethod:: cubicweb.server.session.Session.add_relation
- .. automethod:: cubicweb.server.session.Session.add_relations
- .. automethod:: cubicweb.server.session.Session.delete_relation
-
- Other:
-
- .. automethod:: cubicweb.server.session.Session.call_service
-
-
-
"""
- is_request = False
def __init__(self, user, repo, cnxprops=None, _id=None):
- super(Session, self).__init__(repo.vreg)
self.sessionid = _id or make_uid(unormalize(user.login).encode('UTF8'))
self.user = user # XXX repoapi: deprecated and store only a login.
self.repo = repo
+ self.vreg = repo.vreg
self._timestamp = Timestamp()
- self.default_mode = 'read'
- # short cut to querier .execute method
- self._execute = repo.querier.execute
- # shared data, used to communicate extra information between the client
- # and the rql server
self.data = {}
- # i18n initialization
- self.set_language(user.prefered_language())
- ### internals
- # Connection of this section
- self._cnxs = {} # XXX repoapi: remove this when nobody use the session
- # as a Connection
- # Data local to the thread
- self.__threaddata = threading.local() # XXX repoapi: remove this when
- # nobody use the session as a Connection
- self._cnxset_tracker = CnxSetTracker()
- self._closed = False
- self._lock = threading.RLock()
+ self.closed = False
+
+ def close(self):
+ self.closed = True
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ pass
def __unicode__(self):
return '<session %s (%s 0x%x)>' % (
unicode(self.user.login), self.sessionid, id(self))
+
@property
def timestamp(self):
return float(self._timestamp)
@@ -1388,55 +1276,6 @@
"""
return Connection(self)
- def _get_cnx(self, cnxid):
- """return the <cnxid> connection attached to this session
-
- Connection is created if necessary"""
- with self._lock: # no connection exist with the same id
- try:
- if self.closed:
- raise SessionClosedError('try to access connections set on'
- ' a closed session %s' % self.id)
- cnx = self._cnxs[cnxid]
- assert cnx._session_handled
- except KeyError:
- cnx = Connection(self, cnxid=cnxid, session_handled=True)
- self._cnxs[cnxid] = cnx
- cnx.__enter__()
- return cnx
-
- def _close_cnx(self, cnx):
- """Close a Connection related to a session"""
- assert cnx._session_handled
- cnx.__exit__()
- self._cnxs.pop(cnx.connectionid, None)
- try:
- if self.__threaddata.cnx is cnx:
- del self.__threaddata.cnx
- except AttributeError:
- pass
-
- def set_cnx(self, cnxid=None):
- # XXX repoapi: remove this when nobody use the session as a Connection
- """set the default connection of the current thread to <cnxid>
-
- Connection is created if necessary"""
- if cnxid is None:
- cnxid = threading.currentThread().getName()
- cnx = self._get_cnx(cnxid)
- # New style session should not be accesed through the session.
- assert cnx._session_handled
- self.__threaddata.cnx = cnx
-
- @property
- def _cnx(self):
- """default connection for current session in current thread"""
- try:
- return self.__threaddata.cnx
- except AttributeError:
- self.set_cnx()
- return self.__threaddata.cnx
-
@deprecated('[3.19] use a Connection object instead')
def get_option_value(self, option, foreid=None):
if foreid is not None:
@@ -1444,108 +1283,6 @@
stacklevel=2)
return self.repo.get_option_value(option)
- @deprecated('[3.19] use a Connection object instead')
- def transaction(self, free_cnxset=True):
- """return context manager to enter a transaction for the session: when
- exiting the `with` block on exception, call `session.rollback()`, else
- call `session.commit()` on normal exit.
-
- The `free_cnxset` will be given to rollback/commit methods to indicate
- whether the connections set should be freed or not.
- """
- return transaction(self, free_cnxset)
-
- add_relation = cnx_meth('add_relation')
- add_relations = cnx_meth('add_relations')
- delete_relation = cnx_meth('delete_relation')
-
- # relations cache handling #################################################
-
- update_rel_cache_add = cnx_meth('update_rel_cache_add')
- update_rel_cache_del = cnx_meth('update_rel_cache_del')
-
- # resource accessors ######################################################
-
- system_sql = cnx_meth('system_sql')
- deleted_in_transaction = cnx_meth('deleted_in_transaction')
- added_in_transaction = cnx_meth('added_in_transaction')
- rtype_eids_rdef = cnx_meth('rtype_eids_rdef')
-
- # security control #########################################################
-
- @deprecated('[3.19] use a Connection object instead')
- def security_enabled(self, read=None, write=None):
- return _session_security_enabled(self, read=read, write=write)
-
- read_security = cnx_attr('read_security', writable=True)
- write_security = cnx_attr('write_security', writable=True)
- running_dbapi_query = cnx_attr('running_dbapi_query')
-
- # hooks activation control #################################################
- # all hooks should be activated during normal execution
-
-
- @deprecated('[3.19] use a Connection object instead')
- def allow_all_hooks_but(self, *categories):
- return _session_hooks_control(self, HOOKS_ALLOW_ALL, *categories)
- @deprecated('[3.19] use a Connection object instead')
- def deny_all_hooks_but(self, *categories):
- return _session_hooks_control(self, HOOKS_DENY_ALL, *categories)
-
- hooks_mode = cnx_attr('hooks_mode')
-
- disabled_hook_categories = cnx_attr('disabled_hook_cats')
- enabled_hook_categories = cnx_attr('enabled_hook_cats')
- disable_hook_categories = cnx_meth('disable_hook_categories')
- enable_hook_categories = cnx_meth('enable_hook_categories')
- is_hook_category_activated = cnx_meth('is_hook_category_activated')
- is_hook_activated = cnx_meth('is_hook_activated')
-
- # connection management ###################################################
-
- @deprecated('[3.19] use a Connection object instead')
- def keep_cnxset_mode(self, mode):
- """set `mode`, e.g. how the session will keep its connections set:
-
- * if mode == 'write', the connections set is freed after each read
- query, but kept until the transaction's end (eg commit or rollback)
- when a write query is detected (eg INSERT/SET/DELETE queries)
-
- * if mode == 'transaction', the connections set is only freed after the
- transaction's end
-
- notice that a repository has a limited set of connections sets, and a
- session has to wait for a free connections set to run any rql query
- (unless it already has one set).
- """
- assert mode in ('transaction', 'write')
- if mode == 'transaction':
- self.default_mode = 'transaction'
- else: # mode == 'write'
- self.default_mode = 'read'
-
- mode = cnx_attr('mode', writable=True)
- commit_state = cnx_attr('commit_state', writable=True)
-
- @property
- @deprecated('[3.19] use a Connection object instead')
- def cnxset(self):
- """connections set, set according to transaction mode for each query"""
- if self._closed:
- self.free_cnxset(True)
- raise SessionClosedError('try to access connections set on a closed session %s' % self.id)
- return self._cnx.cnxset
-
- def set_cnxset(self):
- """the session need a connections set to execute some queries"""
- with self._lock: # can probably be removed
- if self._closed:
- self.free_cnxset(True)
- raise SessionClosedError('try to set connections set on a closed session %s' % self.id)
- return self._cnx.set_cnxset()
- free_cnxset = cnx_meth('free_cnxset')
- ensure_cnx_set = cnx_attr('ensure_cnx_set')
-
def _touch(self):
"""update latest session usage timestamp and reset mode to read"""
self._timestamp.touch()
@@ -1557,156 +1294,6 @@
assert value == {}
pass
- # shared data handling ###################################################
-
- @deprecated('[3.19] use session or transaction data')
- def get_shared_data(self, key, default=None, pop=False, txdata=False):
- """return value associated to `key` in session data"""
- if txdata:
- return self._cnx.get_shared_data(key, default, pop, txdata=True)
- else:
- data = self.data
- if pop:
- return data.pop(key, default)
- else:
- return data.get(key, default)
-
- @deprecated('[3.19] use session or transaction data')
- def set_shared_data(self, key, value, txdata=False):
- """set value associated to `key` in session data"""
- if txdata:
- return self._cnx.set_shared_data(key, value, txdata=True)
- else:
- self.data[key] = value
-
- # server-side service call #################################################
-
- call_service = cnx_meth('call_service')
-
- # request interface #######################################################
-
- @property
- @deprecated('[3.19] use a Connection object instead')
- def cursor(self):
- """return a rql cursor"""
- return self
-
- set_entity_cache = cnx_meth('set_entity_cache')
- entity_cache = cnx_meth('entity_cache')
- cache_entities = cnx_meth('cached_entities')
- drop_entity_cache = cnx_meth('drop_entity_cache')
-
- source_defs = cnx_meth('source_defs')
- entity_metas = cnx_meth('entity_metas')
- describe = cnx_meth('describe') # XXX deprecated in 3.19
-
-
- @deprecated('[3.19] use a Connection object instead')
- def execute(self, *args, **kwargs):
- """db-api like method directly linked to the querier execute method.
-
- See :meth:`cubicweb.dbapi.Cursor.execute` documentation.
- """
- rset = self._cnx.execute(*args, **kwargs)
- rset.req = self
- return rset
-
- def _clear_thread_data(self, free_cnxset=True):
- """remove everything from the thread local storage, except connections set
- which is explicitly removed by free_cnxset, and mode which is set anyway
- by _touch
- """
- try:
- cnx = self.__threaddata.cnx
- except AttributeError:
- pass
- else:
- if free_cnxset:
- cnx._free_cnxset()
- if cnx.ctx_count == 0:
- self._close_cnx(cnx)
- else:
- cnx.clear()
- else:
- cnx.clear()
-
- @deprecated('[3.19] use a Connection object instead')
- def commit(self, free_cnxset=True, reset_pool=None):
- """commit the current session's transaction"""
- cstate = self._cnx.commit_state
- if cstate == 'uncommitable':
- raise QueryError('transaction must be rolled back')
- try:
- return self._cnx.commit(free_cnxset, reset_pool)
- finally:
- self._clear_thread_data(free_cnxset)
-
- @deprecated('[3.19] use a Connection object instead')
- def rollback(self, *args, **kwargs):
- """rollback the current session's transaction"""
- return self._rollback(*args, **kwargs)
-
- def _rollback(self, free_cnxset=True, **kwargs):
- try:
- return self._cnx.rollback(free_cnxset, **kwargs)
- finally:
- self._clear_thread_data(free_cnxset)
-
- def close(self):
- # do not close connections set on session close, since they are shared now
- tracker = self._cnxset_tracker
- with self._lock:
- self._closed = True
- tracker.close()
- if self._cnx._session_handled:
- self._rollback()
- self.debug('waiting for open connection of session: %s', self)
- timeout = 10
- pendings = tracker.wait(timeout)
- if pendings:
- self.error('%i connection still alive after 10 seconds, will close '
- 'session anyway', len(pendings))
- for cnxid in pendings:
- cnx = self._cnxs.get(cnxid)
- if cnx is not None:
- # drop cnx.cnxset
- with tracker:
- try:
- cnxset = cnx.cnxset
- if cnxset is None:
- continue
- cnx.cnxset = None
- except RuntimeError:
- msg = 'issue while force free of cnxset in %s'
- self.error(msg, cnx)
- # cnxset.reconnect() do an hard reset of the cnxset
- # it force it to be freed
- cnxset.reconnect()
- self.repo._free_cnxset(cnxset)
- del self.__threaddata
- del self._cnxs
-
- @property
- def closed(self):
- return not hasattr(self, '_cnxs')
-
- # transaction data/operations management ##################################
-
- transaction_data = cnx_attr('transaction_data')
- pending_operations = cnx_attr('pending_operations')
- pruned_hooks_cache = cnx_attr('pruned_hooks_cache')
- add_operation = cnx_meth('add_operation')
-
- # undo support ############################################################
-
- ertype_supports_undo = cnx_meth('ertype_supports_undo')
- transaction_inc_action_counter = cnx_meth('transaction_inc_action_counter')
- transaction_uuid = cnx_meth('transaction_uuid')
-
- # querier helpers #########################################################
-
- rql_rewriter = cnx_attr('_rewriter')
-
# deprecated ###############################################################
@property
@@ -1723,26 +1310,10 @@
def schema_rproperty(self, rtype, eidfrom, eidto, rprop):
return getattr(self.rtype_eids_rdef(rtype, eidfrom, eidto), rprop)
- @property
- @deprecated("[3.13] use .cnxset attribute instead of .pool")
- def pool(self):
- return self.cnxset
-
- @deprecated("[3.13] use .set_cnxset() method instead of .set_pool()")
- def set_pool(self):
- return self.set_cnxset()
-
- @deprecated("[3.13] use .free_cnxset() method instead of .reset_pool()")
- def reset_pool(self):
- return self.free_cnxset()
-
# these are overridden by set_log_methods below
# only defining here to prevent pylint from complaining
info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
-Session.HOOKS_ALLOW_ALL = HOOKS_ALLOW_ALL
-Session.HOOKS_DENY_ALL = HOOKS_DENY_ALL
-Session.DEFAULT_SECURITY = DEFAULT_SECURITY
class InternalManager(object):
--- a/server/test/unittest_repository.py Wed Apr 22 18:28:58 2015 +0200
+++ b/server/test/unittest_repository.py Fri Jun 06 17:08:08 2014 +0200
@@ -201,10 +201,12 @@
def test_internal_api(self):
repo = self.repo
cnxid = repo.connect(self.admlogin, password=self.admpassword)
- session = repo._get_session(cnxid, setcnxset=True)
- self.assertEqual(repo.type_and_source_from_eid(2, session),
- ('CWGroup', None, 'system'))
- self.assertEqual(repo.type_from_eid(2, session), 'CWGroup')
+ session = repo._get_session(cnxid)
+ with session.new_cnx() as cnx:
+ with cnx.ensure_cnx_set:
+ self.assertEqual(repo.type_and_source_from_eid(2, cnx),
+ ('CWGroup', None, 'system'))
+ self.assertEqual(repo.type_from_eid(2, cnx), 'CWGroup')
repo.close(cnxid)
def test_public_api(self):
--- a/server/test/unittest_session.py Wed Apr 22 18:28:58 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-
-from cubicweb.devtools.testlib import CubicWebTC
-from cubicweb.server.session import HOOKS_ALLOW_ALL, HOOKS_DENY_ALL
-from cubicweb.server import hook
-from cubicweb.predicates import is_instance
-
-class SessionTC(CubicWebTC):
-
- def test_hooks_control(self):
- session = self.session
- # this test check the "old" behavior of session with automatic connection management
- # close the default cnx, we do nto want it to interfer with the test
- self.cnx.close()
- # open a dedicated one
- session.set_cnx('Some-random-cnx-unrelated-to-the-default-one')
- # go test go
- self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
- self.assertEqual(set(), session.disabled_hook_categories)
- self.assertEqual(set(), session.enabled_hook_categories)
- self.assertEqual(1, len(session._cnxs))
- with session.deny_all_hooks_but('metadata'):
- self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
- self.assertEqual(set(), session.disabled_hook_categories)
- self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
- session.commit()
- self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
- self.assertEqual(set(), session.disabled_hook_categories)
- self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
- session.rollback()
- self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
- self.assertEqual(set(), session.disabled_hook_categories)
- self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
- with session.allow_all_hooks_but('integrity'):
- self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
- self.assertEqual(set(('integrity',)), session.disabled_hook_categories)
- self.assertEqual(set(('metadata',)), session.enabled_hook_categories) # not changed in such case
- self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
- self.assertEqual(set(), session.disabled_hook_categories)
- self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
- # leaving context manager with no transaction running should reset the
- # transaction local storage (and associated cnxset)
- self.assertEqual({}, session._cnxs)
- self.assertEqual(None, session.cnxset)
- self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode, session.HOOKS_ALLOW_ALL)
- self.assertEqual(set(), session.disabled_hook_categories)
- self.assertEqual(set(), session.enabled_hook_categories)
-
- def test_explicit_connection(self):
- with self.session.new_cnx() as cnx:
- rset = cnx.execute('Any X LIMIT 1 WHERE X is CWUser')
- self.assertEqual(1, len(rset))
- user = rset.get_entity(0, 0)
- user.cw_delete()
- cnx.rollback()
- new_user = cnx.entity_from_eid(user.eid)
- self.assertIsNotNone(new_user.login)
- self.assertFalse(cnx._open)
-
- def test_internal_cnx(self):
- with self.repo.internal_cnx() as cnx:
- rset = cnx.execute('Any X LIMIT 1 WHERE X is CWUser')
- self.assertEqual(1, len(rset))
- user = rset.get_entity(0, 0)
- user.cw_delete()
- cnx.rollback()
- new_user = cnx.entity_from_eid(user.eid)
- self.assertIsNotNone(new_user.login)
- self.assertFalse(cnx._open)
-
- def test_connection_exit(self):
- """exiting a connection should roll back the transaction, including any
- pending operations"""
- self.rollbacked = False
- class RollbackOp(hook.Operation):
- _test = self
- def rollback_event(self):
- self._test.rollbacked = True
- class RollbackHook(hook.Hook):
- __regid__ = 'rollback'
- events = ('after_update_entity',)
- __select__ = hook.Hook.__select__ & is_instance('CWGroup')
- def __call__(self):
- RollbackOp(self._cw)
- with self.temporary_appobjects(RollbackHook):
- with self.admin_access.client_cnx() as cnx:
- cnx.execute('SET G name "foo" WHERE G is CWGroup, G name "managers"')
- self.assertTrue(self.rollbacked)
-
-if __name__ == '__main__':
- from logilab.common.testlib import unittest_main
- unittest_main()