[repoapi,session] remove all session-as-cnx backward compat
authorAurelien Campeas <aurelien.campeas@logilab.fr>
Fri, 06 Jun 2014 17:08:08 +0200
changeset 10346 b926ff4ef4a8
parent 10345 ef54ea75a642
child 10347 52a976c5d27a
[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.
devtools/testlib.py
server/migractions.py
server/repository.py
server/session.py
server/test/unittest_repository.py
server/test/unittest_session.py
--- 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()