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