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