[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.

Repository.describe and .entity_metas are adapted to always work with a cnx.

Related to #3933480.

authorAurelien Campeas <aurelien.campeas@logilab.fr>
changesetf66c657537ea
branchdefault
phasedraft
hiddenyes
parent revision#e01d4550ac91 [dbapi] retire repo.execute, which was used by the dbapi
child revision#4fc664064835 [connection] provide some missing documentation bits
files modified by this revision
devtools/testlib.py
server/migractions.py
server/repository.py
server/session.py
server/test/unittest_repository.py
server/test/unittest_session.py
# HG changeset patch
# User Aurelien Campeas <aurelien.campeas@logilab.fr>
# Date 1402067288 -7200
# Fri Jun 06 17:08:08 2014 +0200
# Node ID f66c657537eac39cb04fdb4931ab9cd42fcd7f83
# Parent e01d4550ac91dcb866b85304c41ee222a148997b
[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.

Repository.describe and .entity_metas are adapted to always work with
a cnx.

Related to #3933480.

diff --git a/devtools/testlib.py b/devtools/testlib.py
@@ -355,23 +355,13 @@
1 
2      @property
3      @deprecated('[3.19] explicitly use RepoAccess object in test instead')
4      def session(self):
5          """return current server side session"""
6 -        # XXX We want to use a srv_connection instead and deprecate this
7 -        # property
8          session = self._current_session
9          if session is None:
10              session = self._admin_session
11 -            # bypassing all sanity to use the same repo cnx in the session
12 -            #
13 -            # we can't call set_cnx as the Connection is not managed by the
14 -            # session.
15 -            session._Session__threaddata.cnx = self._admin_clt_cnx._cnx
16 -        else:
17 -            session._Session__threaddata.cnx = self.cnx._cnx
18 -        session.set_cnxset()
19          return session
20 
21      @property
22      @deprecated('[3.19] explicitly use RepoAccess object in test instead')
23      def websession(self):
@@ -539,12 +529,11 @@
24          if self._admin_clt_cnx is not None:
25              if self._admin_clt_cnx._open:
26                  self._admin_clt_cnx.close()
27              self._admin_clt_cnx = None
28          if self._admin_session is not None:
29 -            if not self._admin_session.closed:
30 -                self.repo.close(self._admin_session.sessionid)
31 +            self.repo.close(self._admin_session.sessionid)
32              self._admin_session = None
33          while self._cleanups:
34              cleanup, args, kwargs = self._cleanups.pop(-1)
35              cleanup(*args, **kwargs)
36          self.repo.turn_repo_off()
diff --git a/server/migractions.py b/server/migractions.py
@@ -95,11 +95,11 @@
37              self.cnx = cnx
38              self.repo = repo
39              self.session = cnx._session
40          elif connect:
41              self.repo_connect()
42 -            self.set_session()
43 +            self.set_cnx()
44          else:
45              self.session = None
46          # no config on shell to a remote instance
47          if config is not None and (cnx or connect):
48              repo = self.repo
@@ -123,11 +123,11 @@
49                  if insert_lperms:
50                      config._cubes = cubes
51          self.fs_schema = schema
52          self._synchronized = set()
53 
54 -    def set_session(self):
55 +    def set_cnx(self):
56          try:
57              login = self.repo.config.default_admin_config['login']
58              pwd = self.repo.config.default_admin_config['password']
59          except KeyError:
60              login, pwd = manager_userpasswd()
@@ -147,11 +147,10 @@
61                  login, pwd = manager_userpasswd()
62              except (KeyboardInterrupt, EOFError):
63                  print 'aborting...'
64                  sys.exit(0)
65          self.session = self.repo._get_session(self.cnx.sessionid)
66 -        self.session.keep_cnxset_mode('transaction')
67 
68      # overriden from base MigrationHelper ######################################
69 
70      @cached
71      def repo_connect(self):
diff --git a/server/repository.py b/server/repository.py
@@ -47,11 +47,11 @@
72                        UnknownEid, AuthenticationError, ExecutionError,
73                        BadConnectionId, Unauthorized, ValidationError,
74                        UniqueTogetherError, onevent)
75  from cubicweb import cwvreg, schema, server
76  from cubicweb.server import ShuttingDown, utils, hook, querier, sources
77 -from cubicweb.server.session import Session, InternalSession, InternalManager
78 +from cubicweb.server.session import Session, InternalManager
79  from cubicweb.server.ssplanner import EditedEntity
80 
81  NO_CACHE_RELATIONS = set( [('owned_by', 'object'),
82                             ('created_by', 'object'),
83                             ('cw_source', 'object'),
@@ -625,18 +625,18 @@
84                                       if not rschema.meta)
85          cwuserattrs = self._cwuser_attrs
86          for k in chain(fetch_attrs, query_attrs):
87              if k not in cwuserattrs:
88                  raise Exception('bad input for find_user')
89 -        with self.internal_session() as session:
90 +        with self.internal_cnx() as cnx:
91              varmaker = rqlvar_maker()
92              vars = [(attr, varmaker.next()) for attr in fetch_attrs]
93              rql = 'Any %s WHERE X is CWUser, ' % ','.join(var[1] for var in vars)
94              rql += ','.join('X %s %s' % (var[0], var[1]) for var in vars) + ','
95 -            rset = session.execute(rql + ','.join('X %s %%(%s)s' % (attr, attr)
96 -                                                  for attr in query_attrs),
97 -                                   query_attrs)
98 +            rset = cnx.execute(rql + ','.join('X %s %%(%s)s' % (attr, attr)
99 +                                              for attr in query_attrs),
100 +                               query_attrs)
101              return rset.rows
102 
103      def new_session(self, login, **kwargs):
104          """open a new session for a given user
105 
@@ -669,40 +669,35 @@
106          """return a tuple `(type, physical source uri, extid, actual source
107          uri)` for the entity of the given `eid`
108 
109          As of 3.19, physical source uri is always the system source.
110          """
111 -        session = self._get_session(sessionid, setcnxset=True, txid=txid)
112 -        try:
113 -            etype, extid, source = self.type_and_source_from_eid(eid, session)
114 -            return etype, source, extid, source
115 -        finally:
116 -            session.free_cnxset()
117 +        session = self._get_session(sessionid, txid=txid)
118 +        with session.new_cnx() as cnx:
119 +            with cnx.ensure_cnx_set:
120 +                etype, extid, source = self.type_and_source_from_eid(eid, cnx)
121 +                return etype, source, extid, source
122 
123 -    def entity_metas(self, sessionid, eid, txid=None):
124 +    def entity_metas(self, cnx, eid, txid=None):
125          """return a dictionary containing meta-datas for the entity of the given
126          `eid`. Available keys are:
127 
128          * 'type', the entity's type name,
129 
130          * 'source', the name of the source from which this entity's coming from,
131 
132          * 'extid', the identifierfor this entity in its originating source, as
133            an encoded string or `None` for entities from the 'system' source.
134          """
135 -        session = self._get_session(sessionid, setcnxset=True, txid=txid)
136 -        try:
137 -            etype, extid, source = self.type_and_source_from_eid(eid, session)
138 -            return {'type': etype, 'source': source, 'extid': extid}
139 -        finally:
140 -            session.free_cnxset()
141 +        etype, extid, source = self.type_and_source_from_eid(eid, cnx)
142 +        return {'type': etype, 'source': source, 'extid': extid}
143 
144      def check_session(self, sessionid):
145          """raise `BadConnectionId` if the connection is no more valid, else
146          return its latest activity timestamp.
147          """
148 -        return self._get_session(sessionid, setcnxset=False).timestamp
149 +        return self._get_session(sessionid).timestamp
150 
151      def commit(self, sessionid, txid=None):
152          """commit transaction for the session with the given id"""
153          self.debug('begin commit for session %s', sessionid)
154          try:
@@ -729,12 +724,10 @@
155      def close(self, sessionid, txid=None, checkshuttingdown=True):
156          """close the session with the given id"""
157          session = self._get_session(sessionid, txid=txid,
158                                      checkshuttingdown=checkshuttingdown)
159          # operation uncommited before close are rolled back before hook is called
160 -        if session._cnx._session_handled:
161 -            session._cnx.rollback(free_cnxset=False)
162          with session.new_cnx() as cnx:
163              self.hm.call_hooks('session_close', cnx)
164              # commit connection at this point in case write operation has been
165              # done during `session_close` hooks
166              cnx.commit()
@@ -755,11 +748,11 @@
167          """this method should be used by client to:
168          * check session id validity
169          * update user information on each user's request (i.e. groups and
170            custom properties)
171          """
172 -        user = self._get_session(sessionid, setcnxset=False).user
173 +        user = self._get_session(sessionid).user
174          return user.eid, user.login, user.groups, user.properties
175 
176      def undoable_transactions(self, sessionid, ueid=None, txid=None,
177                                **actionfilters):
178          """See :class:`cubicweb.dbapi.Connection.undoable_transactions`"""
@@ -816,57 +809,32 @@
179              if session.timestamp < mintime:
180                  self.close(session.sessionid)
181                  nbclosed += 1
182          return nbclosed
183 
184 -    @deprecated("[3.19] use internal_cnx now\n"
185 -                "(Beware that integrity hook are now enabled by default)")
186 -    def internal_session(self, cnxprops=None, safe=False):
187 -        """return a dbapi like connection/cursor using internal user which have
188 -        every rights on the repository. The `safe` argument is a boolean flag
189 -        telling if integrity hooks should be activated or not.
190 -
191 -        /!\ the safe argument is False by default.
192 -
193 -        *YOU HAVE TO* commit/rollback or close (rollback implicitly) the
194 -        session once the job's done, else you'll leak connections set up to the
195 -        time where no one is available, causing irremediable freeze...
196 -        """
197 -        session = InternalSession(self, cnxprops)
198 -        if not safe:
199 -            session.disable_hook_categories('integrity')
200 -        session.disable_hook_categories('security')
201 -        session._cnx.ctx_count += 1
202 -        session.set_cnxset()
203 -        return session
204 -
205      @contextmanager
206      def internal_cnx(self):
207          """Context manager returning a Connection using internal user which have
208          every access rights on the repository.
209 
210          Beware that unlike the older :meth:`internal_session`, internal
211          connections have all hooks beside security enabled.
212          """
213 -        with InternalSession(self) as session:
214 +        with Session(InternalManager(), self) as session:
215              with session.new_cnx() as cnx:
216                  with cnx.security_enabled(read=False, write=False):
217                      with cnx.ensure_cnx_set:
218                          yield cnx
219 
220 -    def _get_session(self, sessionid, setcnxset=False, txid=None,
221 -                     checkshuttingdown=True):
222 +    def _get_session(self, sessionid, txid=None, checkshuttingdown=True):
223          """return the session associated with the given session identifier"""
224          if checkshuttingdown and self.shutting_down:
225              raise ShuttingDown('Repository is shutting down')
226          try:
227              session = self._sessions[sessionid]
228          except KeyError:
229              raise BadConnectionId('No such session %s' % sessionid)
230 -        if setcnxset:
231 -            session.set_cnx(txid) # must be done before set_cnxset
232 -            session.set_cnxset()
233          return session
234 
235      # data sources handling ###################################################
236      # * correspondance between eid and (type, source)
237      # * correspondance between eid and local id (i.e. specific to a given source)
diff --git a/server/session.py b/server/session.py
@@ -32,11 +32,10 @@
238 
239  from cubicweb import QueryError, schema, server, ProgrammingError
240  from cubicweb.req import RequestSessionBase
241  from cubicweb.utils import make_uid
242  from cubicweb.rqlrewrite import RQLRewriter
243 -from cubicweb.server import ShuttingDown
244  from cubicweb.server.edition import EditedEntity
245 
246 
247  NO_UNDO_TYPES = schema.SCHEMA_TYPES.copy()
248  NO_UNDO_TYPES.add('CWCache')
@@ -438,10 +437,11 @@
249        read/write security is currently activated.
250 
251      """
252 
253      is_request = False
254 +    mode = 'read'
255 
256      def __init__(self, session, cnxid=None, session_handled=False):
257          # using super(Connection, self) confuse some test hack
258          RequestSessionBase.__init__(self, session.vreg)
259          # only the session provide explicite
@@ -472,20 +472,18 @@
260          self._execute = self.repo.querier.execute
261 
262          # other session utility
263          self._session_timestamp = session._timestamp
264 
265 -        #: connection handling mode
266 -        self.mode = session.default_mode
267          #: connection set used to execute queries on sources
268          self._cnxset = None
269          #: CnxSetTracker used to report cnxset usage
270 -        self._cnxset_tracker = session._cnxset_tracker
271 +        self._cnxset_tracker = CnxSetTracker()
272          #: is this connection from a client or internal to the repo
273          self.running_dbapi_query = True
274          # internal (root) session
275 -        self.is_internal_session = session.is_internal_session
276 +        self.is_internal_session = isinstance(session.user, InternalManager)
277 
278          #: dict containing arbitrary data cleared at the end of the transaction
279          self.transaction_data = {}
280          self._session_data = session.data
281          #: ordered list of operations to be processed on commit/rollback
@@ -504,11 +502,11 @@
282          self._read_security = DEFAULT_SECURITY # handled by a property
283          self.write_security = DEFAULT_SECURITY
284 
285          # undo control
286          config = session.repo.config
287 -        if config.creating or config.repairing or session.is_internal_session:
288 +        if config.creating or config.repairing or self.is_internal_session:
289              self.undo_actions = False
290          else:
291              self.undo_actions = config['undo-enabled']
292 
293          # RQLRewriter are not thread safe
@@ -1203,151 +1201,41 @@
294 
295      def __float__(self):
296          return float(self.value)
297 
298 
299 -class Session(RequestSessionBase): # XXX repoapi: stop being a
300 -                                   # RequestSessionBase at some point
301 +class Session(object):
302      """Repository user session
303 
304      This ties all together:
305       * session id,
306       * user,
307 -     * connections set,
308       * other session data.
309 -
310 -    **About session storage / transactions**
311 -
312 -    Here is a description of internal session attributes. Besides :attr:`data`
313 -    and :attr:`transaction_data`, you should not have to use attributes
314 -    described here but higher level APIs.
315 -
316 -      :attr:`data` is a dictionary containing shared data, used to communicate
317 -      extra information between the client and the repository
318 -
319 -      :attr:`_cnxs` is a dictionary of :class:`Connection` instance, one
320 -      for each running connection. The key is the connection id. By default
321 -      the connection id is the thread name but it can be otherwise (per dbapi
322 -      cursor for instance, or per thread name *from another process*).
323 -
324 -      :attr:`__threaddata` is a thread local storage whose `cnx` attribute
325 -      refers to the proper instance of :class:`Connection` according to the
326 -      connection.
327 -
328 -    You should not have to use neither :attr:`_cnx` nor :attr:`__threaddata`,
329 -    simply access connection data transparently through the :attr:`_cnx`
330 -    property. Also, you usually don't have to access it directly since current
331 -    connection's data may be accessed/modified through properties / methods:
332 -
333 -      :attr:`connection_data`, similarly to :attr:`data`, is a dictionary
334 -      containing some shared data that should be cleared at the end of the
335 -      connection. Hooks and operations may put arbitrary data in there, and
336 -      this may also be used as a communication channel between the client and
337 -      the repository.
338 -
339 -    .. automethod:: cubicweb.server.session.Session.added_in_transaction
340 -    .. automethod:: cubicweb.server.session.Session.deleted_in_transaction
341 -
342 -    Connection state information:
343 -
344 -      :attr:`running_dbapi_query`, boolean flag telling if the executing query
345 -      is coming from a dbapi connection or is a query from within the repository
346 -
347 -      :attr:`cnxset`, the connections set to use to execute queries on sources.
348 -      During a transaction, the connection set may be freed so that is may be
349 -      used by another session as long as no writing is done. This means we can
350 -      have multiple sessions with a reasonably low connections set pool size.
351 -
352 -      .. automethod:: cubicweb.server.session.Session.set_cnxset
353 -      .. automethod:: cubicweb.server.session.Session.free_cnxset
354 -
355 -      :attr:`mode`, string telling the connections set handling mode, may be one
356 -      of 'read' (connections set may be freed), 'write' (some write was done in
357 -      the connections set, it can't be freed before end of the transaction),
358 -      'transaction' (we want to keep the connections set during all the
359 -      transaction, with or without writing)
360 -
361 -      :attr:`pending_operations`, ordered list of operations to be processed on
362 -      commit/rollback
363 -
364 -      :attr:`commit_state`, describing the transaction commit state, may be one
365 -      of None (not yet committing), 'precommit' (calling precommit event on
366 -      operations), 'postcommit' (calling postcommit event on operations),
367 -      'uncommitable' (some :exc:`ValidationError` or :exc:`Unauthorized` error
368 -      has been raised during the transaction and so it must be rolled back).
369 -
370 -    .. automethod:: cubicweb.server.session.Session.commit
371 -    .. automethod:: cubicweb.server.session.Session.rollback
372 -    .. automethod:: cubicweb.server.session.Session.close
373 -    .. automethod:: cubicweb.server.session.Session.closed
374 -
375 -    Security level Management:
376 -
377 -      :attr:`read_security` and :attr:`write_security`, boolean flags telling if
378 -      read/write security is currently activated.
379 -
380 -    .. automethod:: cubicweb.server.session.Session.security_enabled
381 -
382 -    Hooks Management:
383 -
384 -      :attr:`hooks_mode`, may be either `HOOKS_ALLOW_ALL` or `HOOKS_DENY_ALL`.
385 -
386 -      :attr:`enabled_hook_categories`, when :attr:`hooks_mode` is
387 -      `HOOKS_DENY_ALL`, this set contains hooks categories that are enabled.
388 -
389 -      :attr:`disabled_hook_categories`, when :attr:`hooks_mode` is
390 -      `HOOKS_ALLOW_ALL`, this set contains hooks categories that are disabled.
391 -
392 -    .. automethod:: cubicweb.server.session.Session.deny_all_hooks_but
393 -    .. automethod:: cubicweb.server.session.Session.allow_all_hooks_but
394 -    .. automethod:: cubicweb.server.session.Session.is_hook_category_activated
395 -    .. automethod:: cubicweb.server.session.Session.is_hook_activated
396 -
397 -    Data manipulation:
398 -
399 -    .. automethod:: cubicweb.server.session.Session.add_relation
400 -    .. automethod:: cubicweb.server.session.Session.add_relations
401 -    .. automethod:: cubicweb.server.session.Session.delete_relation
402 -
403 -    Other:
404 -
405 -    .. automethod:: cubicweb.server.session.Session.call_service
406 -
407 -
408 -
409      """
410 -    is_request = False
411 -    is_internal_session = False
412 
413      def __init__(self, user, repo, cnxprops=None, _id=None):
414 -        super(Session, self).__init__(repo.vreg)
415          self.sessionid = _id or make_uid(unormalize(user.login).encode('UTF8'))
416          self.user = user # XXX repoapi: deprecated and store only a login.
417          self.repo = repo
418 +        self.vreg = repo.vreg
419          self._timestamp = Timestamp()
420 -        self.default_mode = 'read'
421 -        # short cut to querier .execute method
422 -        self._execute = repo.querier.execute
423 -        # shared data, used to communicate extra information between the client
424 -        # and the rql server
425          self.data = {}
426 -        # i18n initialization
427 -        self.set_language(user.prefered_language())
428 -        ### internals
429 -        # Connection of this section
430 -        self._cnxs = {} # XXX repoapi: remove this when nobody use the session
431 -                        # as a Connection
432 -        # Data local to the thread
433 -        self.__threaddata = threading.local() # XXX repoapi: remove this when
434 -                                              # nobody use the session as a Connection
435 -        self._cnxset_tracker = CnxSetTracker()
436 -        self._closed = False
437 -        self._lock = threading.RLock()
438 +        self.closed = False
439 +
440 +    def close(self):
441 +        self.closed = True
442 +
443 +    def __enter__(self):
444 +        return self
445 +
446 +    def __exit__(self, *args):
447 +        pass
448 
449      def __unicode__(self):
450          return '<session %s (%s 0x%x)>' % (
451              unicode(self.user.login), self.sessionid, id(self))
452 +
453      @property
454      def timestamp(self):
455          return float(self._timestamp)
456 
457      @property
@@ -1364,168 +1252,17 @@
458 
459          The returned Connection will *not* be managed by the Session.
460          """
461          return Connection(self)
462 
463 -    def _get_cnx(self, cnxid):
464 -        """return the <cnxid> connection attached to this session
465 -
466 -        Connection is created if necessary"""
467 -        with self._lock: # no connection exist with the same id
468 -            try:
469 -                if self.closed:
470 -                    raise SessionClosedError('try to access connections set on'
471 -                                             ' a closed session %s' % self.id)
472 -                cnx = self._cnxs[cnxid]
473 -                assert cnx._session_handled
474 -            except KeyError:
475 -                cnx = Connection(self, cnxid=cnxid, session_handled=True)
476 -                self._cnxs[cnxid] = cnx
477 -                cnx.__enter__()
478 -        return cnx
479 -
480 -    def _close_cnx(self, cnx):
481 -        """Close a Connection related to a session"""
482 -        assert cnx._session_handled
483 -        cnx.__exit__()
484 -        self._cnxs.pop(cnx.connectionid, None)
485 -        try:
486 -            if self.__threaddata.cnx is cnx:
487 -                del self.__threaddata.cnx
488 -        except AttributeError:
489 -            pass
490 -
491 -    def set_cnx(self, cnxid=None):
492 -        # XXX repoapi: remove this when nobody use the session as a Connection
493 -        """set the default connection of the current thread to <cnxid>
494 -
495 -        Connection is created if necessary"""
496 -        if cnxid is None:
497 -            cnxid = threading.currentThread().getName()
498 -        cnx = self._get_cnx(cnxid)
499 -        # New style session should not be accesed through the session.
500 -        assert cnx._session_handled
501 -        self.__threaddata.cnx = cnx
502 -
503 -    @property
504 -    def _cnx(self):
505 -        """default connection for current session in current thread"""
506 -        try:
507 -            return self.__threaddata.cnx
508 -        except AttributeError:
509 -            self.set_cnx()
510 -            return self.__threaddata.cnx
511 -
512      @deprecated('[3.19] use a Connection object instead')
513      def get_option_value(self, option, foreid=None):
514          if foreid is not None:
515              warn('[3.19] foreid argument is deprecated', DeprecationWarning,
516                   stacklevel=2)
517          return self.repo.get_option_value(option)
518 
519 -    @deprecated('[3.19] use a Connection object instead')
520 -    def transaction(self, free_cnxset=True):
521 -        """return context manager to enter a transaction for the session: when
522 -        exiting the `with` block on exception, call `session.rollback()`, else
523 -        call `session.commit()` on normal exit.
524 -
525 -        The `free_cnxset` will be given to rollback/commit methods to indicate
526 -        whether the connections set should be freed or not.
527 -        """
528 -        return transaction(self, free_cnxset)
529 -
530 -    add_relation = cnx_meth('add_relation')
531 -    add_relations = cnx_meth('add_relations')
532 -    delete_relation = cnx_meth('delete_relation')
533 -
534 -    # relations cache handling #################################################
535 -
536 -    update_rel_cache_add = cnx_meth('update_rel_cache_add')
537 -    update_rel_cache_del = cnx_meth('update_rel_cache_del')
538 -
539 -    # resource accessors ######################################################
540 -
541 -    system_sql = cnx_meth('system_sql')
542 -    deleted_in_transaction = cnx_meth('deleted_in_transaction')
543 -    added_in_transaction = cnx_meth('added_in_transaction')
544 -    rtype_eids_rdef = cnx_meth('rtype_eids_rdef')
545 -
546 -    # security control #########################################################
547 -
548 -    @deprecated('[3.19] use a Connection object instead')
549 -    def security_enabled(self, read=None, write=None):
550 -        return _session_security_enabled(self, read=read, write=write)
551 -
552 -    read_security = cnx_attr('read_security', writable=True)
553 -    write_security = cnx_attr('write_security', writable=True)
554 -    running_dbapi_query = cnx_attr('running_dbapi_query')
555 -
556 -    # hooks activation control #################################################
557 -    # all hooks should be activated during normal execution
558 -
559 -
560 -    @deprecated('[3.19] use a Connection object instead')
561 -    def allow_all_hooks_but(self, *categories):
562 -        return _session_hooks_control(self, HOOKS_ALLOW_ALL, *categories)
563 -    @deprecated('[3.19] use a Connection object instead')
564 -    def deny_all_hooks_but(self, *categories):
565 -        return _session_hooks_control(self, HOOKS_DENY_ALL, *categories)
566 -
567 -    hooks_mode = cnx_attr('hooks_mode')
568 -
569 -    disabled_hook_categories = cnx_attr('disabled_hook_cats')
570 -    enabled_hook_categories = cnx_attr('enabled_hook_cats')
571 -    disable_hook_categories = cnx_meth('disable_hook_categories')
572 -    enable_hook_categories = cnx_meth('enable_hook_categories')
573 -    is_hook_category_activated = cnx_meth('is_hook_category_activated')
574 -    is_hook_activated = cnx_meth('is_hook_activated')
575 -
576 -    # connection management ###################################################
577 -
578 -    @deprecated('[3.19] use a Connection object instead')
579 -    def keep_cnxset_mode(self, mode):
580 -        """set `mode`, e.g. how the session will keep its connections set:
581 -
582 -        * if mode == 'write', the connections set is freed after each read
583 -          query, but kept until the transaction's end (eg commit or rollback)
584 -          when a write query is detected (eg INSERT/SET/DELETE queries)
585 -
586 -        * if mode == 'transaction', the connections set is only freed after the
587 -          transaction's end
588 -
589 -        notice that a repository has a limited set of connections sets, and a
590 -        session has to wait for a free connections set to run any rql query
591 -        (unless it already has one set).
592 -        """
593 -        assert mode in ('transaction', 'write')
594 -        if mode == 'transaction':
595 -            self.default_mode = 'transaction'
596 -        else: # mode == 'write'
597 -            self.default_mode = 'read'
598 -
599 -    mode = cnx_attr('mode', writable=True)
600 -    commit_state = cnx_attr('commit_state', writable=True)
601 -
602 -    @property
603 -    @deprecated('[3.19] use a Connection object instead')
604 -    def cnxset(self):
605 -        """connections set, set according to transaction mode for each query"""
606 -        if self._closed:
607 -            self.free_cnxset(True)
608 -            raise SessionClosedError('try to access connections set on a closed session %s' % self.id)
609 -        return self._cnx.cnxset
610 -
611 -    def set_cnxset(self):
612 -        """the session need a connections set to execute some queries"""
613 -        with self._lock: # can probably be removed
614 -            if self._closed:
615 -                self.free_cnxset(True)
616 -                raise SessionClosedError('try to set connections set on a closed session %s' % self.id)
617 -            return self._cnx.set_cnxset()
618 -    free_cnxset = cnx_meth('free_cnxset')
619 -    ensure_cnx_set = cnx_attr('ensure_cnx_set')
620 -
621      def _touch(self):
622          """update latest session usage timestamp and reset mode to read"""
623          self._timestamp.touch()
624 
625      local_perm_cache = cnx_attr('local_perm_cache')
@@ -1533,138 +1270,10 @@
626      def local_perm_cache(self, value):
627          #base class assign an empty dict:-(
628          assert value == {}
629          pass
630 
631 -    # server-side service call #################################################
632 -
633 -    call_service = cnx_meth('call_service')
634 -
635 -    # request interface #######################################################
636 -
637 -    @property
638 -    @deprecated('[3.19] use a Connection object instead')
639 -    def cursor(self):
640 -        """return a rql cursor"""
641 -        return self
642 -
643 -    set_entity_cache  = cnx_meth('set_entity_cache')
644 -    entity_cache      = cnx_meth('entity_cache')
645 -    cache_entities    = cnx_meth('cached_entities')
646 -    drop_entity_cache = cnx_meth('drop_entity_cache')
647 -
648 -    source_defs = cnx_meth('source_defs')
649 -    entity_metas = cnx_meth('entity_metas')
650 -    describe = cnx_meth('describe') # XXX deprecated in 3.19
651 -
652 -
653 -    @deprecated('[3.19] use a Connection object instead')
654 -    def execute(self, *args, **kwargs):
655 -        """db-api like method directly linked to the querier execute method.
656 -
657 -        See :meth:`cubicweb.dbapi.Cursor.execute` documentation.
658 -        """
659 -        rset = self._cnx.execute(*args, **kwargs)
660 -        rset.req = self
661 -        return rset
662 -
663 -    def _clear_thread_data(self, free_cnxset=True):
664 -        """remove everything from the thread local storage, except connections set
665 -        which is explicitly removed by free_cnxset, and mode which is set anyway
666 -        by _touch
667 -        """
668 -        try:
669 -            cnx = self.__threaddata.cnx
670 -        except AttributeError:
671 -            pass
672 -        else:
673 -            if free_cnxset:
674 -                cnx._free_cnxset()
675 -                if cnx.ctx_count == 0:
676 -                    self._close_cnx(cnx)
677 -                else:
678 -                    cnx.clear()
679 -            else:
680 -                cnx.clear()
681 -
682 -    @deprecated('[3.19] use a Connection object instead')
683 -    def commit(self, free_cnxset=True, reset_pool=None):
684 -        """commit the current session's transaction"""
685 -        cstate = self._cnx.commit_state
686 -        if cstate == 'uncommitable':
687 -            raise QueryError('transaction must be rolled back')
688 -        try:
689 -            return self._cnx.commit(free_cnxset, reset_pool)
690 -        finally:
691 -            self._clear_thread_data(free_cnxset)
692 -
693 -    @deprecated('[3.19] use a Connection object instead')
694 -    def rollback(self, *args, **kwargs):
695 -        """rollback the current session's transaction"""
696 -        return self._rollback(*args, **kwargs)
697 -
698 -    def _rollback(self, free_cnxset=True, **kwargs):
699 -        try:
700 -            return self._cnx.rollback(free_cnxset, **kwargs)
701 -        finally:
702 -            self._clear_thread_data(free_cnxset)
703 -
704 -    def close(self):
705 -        # do not close connections set on session close, since they are shared now
706 -        tracker = self._cnxset_tracker
707 -        with self._lock:
708 -            self._closed = True
709 -        tracker.close()
710 -        if self._cnx._session_handled:
711 -            self._rollback()
712 -        self.debug('waiting for open connection of session: %s', self)
713 -        timeout = 10
714 -        pendings = tracker.wait(timeout)
715 -        if pendings:
716 -            self.error('%i connection still alive after 10 seconds, will close '
717 -                       'session anyway', len(pendings))
718 -            for cnxid in pendings:
719 -                cnx = self._cnxs.get(cnxid)
720 -                if cnx is not None:
721 -                    # drop cnx.cnxset
722 -                    with tracker:
723 -                        try:
724 -                            cnxset = cnx.cnxset
725 -                            if cnxset is None:
726 -                                continue
727 -                            cnx.cnxset = None
728 -                        except RuntimeError:
729 -                            msg = 'issue while force free of cnxset in %s'
730 -                            self.error(msg, cnx)
731 -                    # cnxset.reconnect() do an hard reset of the cnxset
732 -                    # it force it to be freed
733 -                    cnxset.reconnect()
734 -                    self.repo._free_cnxset(cnxset)
735 -        del self.__threaddata
736 -        del self._cnxs
737 -
738 -    @property
739 -    def closed(self):
740 -        return not hasattr(self, '_cnxs')
741 -
742 -    # transaction data/operations management ##################################
743 -
744 -    transaction_data = cnx_attr('transaction_data')
745 -    pending_operations = cnx_attr('pending_operations')
746 -    pruned_hooks_cache = cnx_attr('pruned_hooks_cache')
747 -    add_operation      = cnx_meth('add_operation')
748 -
749 -    # undo support ############################################################
750 -
751 -    ertype_supports_undo = cnx_meth('ertype_supports_undo')
752 -    transaction_inc_action_counter = cnx_meth('transaction_inc_action_counter')
753 -    transaction_uuid = cnx_meth('transaction_uuid')
754 -
755 -    # querier helpers #########################################################
756 -
757 -    rql_rewriter = cnx_attr('_rewriter')
758 -
759      # deprecated ###############################################################
760 
761      @property
762      def anonymous_session(self):
763          # XXX for now, anonymous_user only exists in webconfig (and testconfig).
@@ -1677,63 +1286,22 @@
764 
765      @deprecated('[3.13] use getattr(session.rtype_eids_rdef(rtype, eidfrom, eidto), prop)')
766      def schema_rproperty(self, rtype, eidfrom, eidto, rprop):
767          return getattr(self.rtype_eids_rdef(rtype, eidfrom, eidto), rprop)
768 
769 -    @property
770 -    @deprecated("[3.13] use .cnxset attribute instead of .pool")
771 -    def pool(self):
772 -        return self.cnxset
773 -
774 -    @deprecated("[3.13] use .set_cnxset() method instead of .set_pool()")
775 -    def set_pool(self):
776 -        return self.set_cnxset()
777 -
778 -    @deprecated("[3.13] use .free_cnxset() method instead of .reset_pool()")
779 -    def reset_pool(self):
780 -        return self.free_cnxset()
781 -
782      # these are overridden by set_log_methods below
783      # only defining here to prevent pylint from complaining
784      info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
785 
786 -Session.HOOKS_ALLOW_ALL = HOOKS_ALLOW_ALL
787 -Session.HOOKS_DENY_ALL = HOOKS_DENY_ALL
788 -Session.DEFAULT_SECURITY = DEFAULT_SECURITY
789 -
790 -
791 -
792 -class InternalSession(Session):
793 -    """special session created internally by the repository"""
794 -    is_internal_session = True
795 -    running_dbapi_query = False
796 -
797 -    def __init__(self, repo, cnxprops=None, safe=False):
798 -        super(InternalSession, self).__init__(InternalManager(), repo, cnxprops,
799 -                                              _id='internal')
800 -        self.user._cw = self # XXX remove when "vreg = user._cw.vreg" hack in entity.py is gone
801 -
802 -    def __enter__(self):
803 -        return self
804 -
805 -    def __exit__(self, exctype, excvalue, tb):
806 -        self.close()
807 -
808 -    @property
809 -    def cnxset(self):
810 -        """connections set, set according to transaction mode for each query"""
811 -        if self.repo.shutting_down:
812 -            self.free_cnxset(True)
813 -            raise ShuttingDown('repository is shutting down')
814 -        return self._cnx.cnxset
815 
816 
817  class InternalManager(object):
818      """a manager user with all access rights used internally for task such as
819      bootstrapping the repository or creating regular users according to
820      repository content
821      """
822 +
823      def __init__(self, lang='en'):
824          self.eid = -1
825          self.login = u'__internal_manager__'
826          self.properties = {}
827          self.groups = set(['managers'])
diff --git a/server/test/unittest_repository.py b/server/test/unittest_repository.py
@@ -199,14 +199,16 @@
828          self.assertEqual(ownedby.objects('CWEType'), ('CWUser',))
829 
830      def test_internal_api(self):
831          repo = self.repo
832          cnxid = repo.connect(self.admlogin, password=self.admpassword)
833 -        session = repo._get_session(cnxid, setcnxset=True)
834 -        self.assertEqual(repo.type_and_source_from_eid(2, session),
835 -                         ('CWGroup', None, 'system'))
836 -        self.assertEqual(repo.type_from_eid(2, session), 'CWGroup')
837 +        session = repo._get_session(cnxid)
838 +        with session.new_cnx() as cnx:
839 +            with cnx.ensure_cnx_set:
840 +                self.assertEqual(repo.type_and_source_from_eid(2, cnx),
841 +                                 ('CWGroup', None, 'system'))
842 +                self.assertEqual(repo.type_from_eid(2, cnx), 'CWGroup')
843          repo.close(cnxid)
844 
845      def test_public_api(self):
846          self.assertEqual(self.repo.get_schema(), self.repo.schema)
847          self.assertEqual(self.repo.source_defs(), {'system': {'type': 'native',
@@ -218,13 +220,15 @@
848 
849      def test_session_api(self):
850          repo = self.repo
851          cnxid = repo.connect(self.admlogin, password=self.admpassword)
852          self.assertEqual(repo.user_info(cnxid), (6, 'admin', set([u'managers']), {}))
853 -        self.assertEqual({'type': u'CWGroup', 'extid': None, 'source': 'system'},
854 -                         repo.entity_metas(cnxid, 2))
855          self.assertEqual(repo.describe(cnxid, 2), (u'CWGroup', 'system', None, 'system'))
856 +        with self.admin_access.repo_cnx() as cnx:
857 +            with cnx.ensure_cnx_set:
858 +                self.assertEqual({'type': u'CWGroup', 'extid': None, 'source': 'system'},
859 +                                 repo.entity_metas(cnx, 2))
860          repo.close(cnxid)
861          self.assertRaises(BadConnectionId, repo.user_info, cnxid)
862          self.assertRaises(BadConnectionId, repo.describe, cnxid, 1)
863 
864      def test_schema_is_relation(self):
diff --git a/server/test/unittest_session.py b/server/test/unittest_session.py
@@ -1,124 +0,0 @@
865 -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
866 -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
867 -#
868 -# This file is part of CubicWeb.
869 -#
870 -# CubicWeb is free software: you can redistribute it and/or modify it under the
871 -# terms of the GNU Lesser General Public License as published by the Free
872 -# Software Foundation, either version 2.1 of the License, or (at your option)
873 -# any later version.
874 -#
875 -# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
876 -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
877 -# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
878 -# details.
879 -#
880 -# You should have received a copy of the GNU Lesser General Public License along
881 -# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
882 -
883 -from cubicweb.devtools.testlib import CubicWebTC
884 -from cubicweb.server.session import HOOKS_ALLOW_ALL, HOOKS_DENY_ALL
885 -from cubicweb.server import hook
886 -from cubicweb.predicates import is_instance
887 -
888 -class InternalSessionTC(CubicWebTC):
889 -    def test_dbapi_query(self):
890 -        session = self.repo.internal_session()
891 -        self.assertFalse(session.running_dbapi_query)
892 -        session.close()
893 -
894 -    def test_integrity_hooks(self):
895 -        with self.repo.internal_session() as session:
896 -            self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
897 -            self.assertEqual(set(('integrity', 'security')), session.disabled_hook_categories)
898 -            self.assertEqual(set(), session.enabled_hook_categories)
899 -            session.commit()
900 -            self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
901 -            self.assertEqual(set(('integrity', 'security')), session.disabled_hook_categories)
902 -            self.assertEqual(set(), session.enabled_hook_categories)
903 -
904 -class SessionTC(CubicWebTC):
905 -
906 -    def test_hooks_control(self):
907 -        session = self.session
908 -        # this test check the "old" behavior of session with automatic connection management
909 -        # close the default cnx, we do nto want it to interfer with the test
910 -        self.cnx.close()
911 -        # open a dedicated one
912 -        session.set_cnx('Some-random-cnx-unrelated-to-the-default-one')
913 -        # go test go
914 -        self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
915 -        self.assertEqual(set(), session.disabled_hook_categories)
916 -        self.assertEqual(set(), session.enabled_hook_categories)
917 -        self.assertEqual(1, len(session._cnxs))
918 -        with session.deny_all_hooks_but('metadata'):
919 -            self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
920 -            self.assertEqual(set(), session.disabled_hook_categories)
921 -            self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
922 -            session.commit()
923 -            self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
924 -            self.assertEqual(set(), session.disabled_hook_categories)
925 -            self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
926 -            session.rollback()
927 -            self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
928 -            self.assertEqual(set(), session.disabled_hook_categories)
929 -            self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
930 -            with session.allow_all_hooks_but('integrity'):
931 -                self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
932 -                self.assertEqual(set(('integrity',)), session.disabled_hook_categories)
933 -                self.assertEqual(set(('metadata',)), session.enabled_hook_categories) # not changed in such case
934 -            self.assertEqual(HOOKS_DENY_ALL, session.hooks_mode)
935 -            self.assertEqual(set(), session.disabled_hook_categories)
936 -            self.assertEqual(set(('metadata',)), session.enabled_hook_categories)
937 -        # leaving context manager with no transaction running should reset the
938 -        # transaction local storage (and associated cnxset)
939 -        self.assertEqual({}, session._cnxs)
940 -        self.assertEqual(None, session.cnxset)
941 -        self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode, session.HOOKS_ALLOW_ALL)
942 -        self.assertEqual(set(), session.disabled_hook_categories)
943 -        self.assertEqual(set(), session.enabled_hook_categories)
944 -
945 -    def test_explicit_connection(self):
946 -        with self.session.new_cnx() as cnx:
947 -            rset = cnx.execute('Any X LIMIT 1 WHERE X is CWUser')
948 -            self.assertEqual(1, len(rset))
949 -            user = rset.get_entity(0, 0)
950 -            user.cw_delete()
951 -            cnx.rollback()
952 -            new_user = cnx.entity_from_eid(user.eid)
953 -            self.assertIsNotNone(new_user.login)
954 -        self.assertFalse(cnx._open)
955 -
956 -    def test_internal_cnx(self):
957 -        with self.repo.internal_cnx() as cnx:
958 -            rset = cnx.execute('Any X LIMIT 1 WHERE X is CWUser')
959 -            self.assertEqual(1, len(rset))
960 -            user = rset.get_entity(0, 0)
961 -            user.cw_delete()
962 -            cnx.rollback()
963 -            new_user = cnx.entity_from_eid(user.eid)
964 -            self.assertIsNotNone(new_user.login)
965 -        self.assertFalse(cnx._open)
966 -
967 -    def test_connection_exit(self):
968 -        """exiting a connection should roll back the transaction, including any
969 -        pending operations"""
970 -        self.rollbacked = False
971 -        class RollbackOp(hook.Operation):
972 -            _test = self
973 -            def rollback_event(self):
974 -                self._test.rollbacked = True
975 -        class RollbackHook(hook.Hook):
976 -            __regid__ = 'rollback'
977 -            events = ('after_update_entity',)
978 -            __select__ = hook.Hook.__select__ & is_instance('CWGroup')
979 -            def __call__(self):
980 -                RollbackOp(self._cw)
981 -        with self.temporary_appobjects(RollbackHook):
982 -            with self.admin_access.client_cnx() as cnx:
983 -                cnx.execute('SET G name "foo" WHERE G is CWGroup, G name "managers"')
984 -            self.assertTrue(self.rollbacked)
985 -
986 -if __name__ == '__main__':
987 -    from logilab.common.testlib import unittest_main
988 -    unittest_main()