[dbapi] remove the dbapi module and its immediate remaining users

We suppress toolsutils.config_connect, which has currently only one known user (the email cube), which is being patched.

It can be replaced with utils.admincnx function for a local access.

Next will come a series to:

  • remove the session backward compatibility
  • fold ClientConnection into Connection

Closes #3933480.

authorAurelien Campeas <aurelien.campeas@logilab.fr>
changeset6f25c7e4f19b
branchdefault
phasepublic
hiddenno
parent revision#6c12264b3f18 [web/views] remove dbapi usage from 'gc' view
child revision#d115c388e9cf [test] fix copy-pasta in doc string, #569324f890d7 [book] remove dbapi documentation
files modified by this revision
_gcdebug.py
dbapi.py
devtools/__init__.py
doc/3.21.rst
test/unittest_dbapi.py
toolsutils.py
web/request.py
web/test/unittest_application.py
web/views/authentication.py
web/views/sessions.py
# HG changeset patch
# User Aurelien Campeas <aurelien.campeas@logilab.fr>
# Date 1401807434 -7200
# Tue Jun 03 16:57:14 2014 +0200
# Node ID 6f25c7e4f19bb7be8bf4e30f3bb7f1b10eaa863b
# Parent 6c12264b3f1895f88c19c66846a61467c3ffc7b7
[dbapi] remove the dbapi module and its immediate remaining users

We suppress toolsutils.config_connect, which has currently
only one known user (the email cube), which is being patched.

It can be replaced with utils.admincnx function for a local
access.

Next will come a series to:

* remove the session backward compatibility
* fold ClientConnection into Connection

Closes #3933480.

diff --git a/_gcdebug.py b/_gcdebug.py
@@ -17,23 +17,27 @@
1  # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
2 
3  import gc, types, weakref
4 
5  from cubicweb.schema import CubicWebRelationSchema, CubicWebEntitySchema
6 -from cubicweb.dbapi import _NeedAuthAccessMock
7 +try:
8 +    from cubicweb.web.request import _NeedAuthAccessMock
9 +except ImportError:
10 +    _NeedAuthAccessMock = None
11 
12  listiterator = type(iter([]))
13 
14  IGNORE_CLASSES = (
15      type, tuple, dict, list, set, frozenset, type(len),
16      weakref.ref, weakref.WeakKeyDictionary,
17      listiterator,
18      property, classmethod,
19      types.ModuleType, types.FunctionType, types.MethodType,
20      types.MemberDescriptorType, types.GetSetDescriptorType,
21 -    _NeedAuthAccessMock,
22      )
23 +if _NeedAuthAccessMock is not None:
24 +    IGNORE_CLASSES = IGNORE_CLASSES + (_NeedAuthAccessMock,)
25 
26  def _get_counted_class(obj, classes):
27      for cls in classes:
28          if isinstance(obj, cls):
29              return cls
diff --git a/dbapi.py b/dbapi.py
@@ -1,796 +0,0 @@
30 -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
31 -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
32 -#
33 -# This file is part of CubicWeb.
34 -#
35 -# CubicWeb is free software: you can redistribute it and/or modify it under the
36 -# terms of the GNU Lesser General Public License as published by the Free
37 -# Software Foundation, either version 2.1 of the License, or (at your option)
38 -# any later version.
39 -#
40 -# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
41 -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42 -# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
43 -# details.
44 -#
45 -# You should have received a copy of the GNU Lesser General Public License along
46 -# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
47 -"""DB-API 2.0 compliant module
48 -
49 -Take a look at http://www.python.org/peps/pep-0249.html
50 -
51 -(most parts of this document are reported here in docstrings)
52 -"""
53 -
54 -__docformat__ = "restructuredtext en"
55 -
56 -from threading import currentThread
57 -from logging import getLogger
58 -from time import time, clock
59 -from itertools import count
60 -from warnings import warn
61 -from os.path import join
62 -from uuid import uuid4
63 -from urlparse import  urlparse
64 -
65 -from logilab.common.logging_ext import set_log_methods
66 -from logilab.common.decorators import monkeypatch, cachedproperty
67 -from logilab.common.deprecation import deprecated
68 -
69 -from cubicweb import (ETYPE_NAME_MAP, AuthenticationError, ProgrammingError,
70 -                      cwvreg, cwconfig)
71 -from cubicweb.repoapi import get_repository
72 -from cubicweb.req import RequestSessionBase
73 -
74 -
75 -_MARKER = object()
76 -
77 -def _fake_property_value(self, name):
78 -    try:
79 -        return super(DBAPIRequest, self).property_value(name)
80 -    except KeyError:
81 -        return ''
82 -
83 -def fake(*args, **kwargs):
84 -    return None
85 -
86 -def multiple_connections_fix():
87 -    """some monkey patching necessary when an application has to deal with
88 -    several connections to different repositories. It tries to hide buggy class
89 -    attributes since classes are not designed to be shared among multiple
90 -    registries.
91 -    """
92 -    defaultcls = cwvreg.CWRegistryStore.REGISTRY_FACTORY[None]
93 -
94 -    etypescls = cwvreg.CWRegistryStore.REGISTRY_FACTORY['etypes']
95 -    orig_etype_class = etypescls.orig_etype_class = etypescls.etype_class
96 -    @monkeypatch(defaultcls)
97 -    def etype_class(self, etype):
98 -        """return an entity class for the given entity type.
99 -        Try to find out a specific class for this kind of entity or
100 -        default to a dump of the class registered for 'Any'
101 -        """
102 -        usercls = orig_etype_class(self, etype)
103 -        if etype == 'Any':
104 -            return usercls
105 -        usercls.e_schema = self.schema.eschema(etype)
106 -        return usercls
107 -
108 -def multiple_connections_unfix():
109 -    etypescls = cwvreg.CWRegistryStore.REGISTRY_FACTORY['etypes']
110 -    etypescls.etype_class = etypescls.orig_etype_class
111 -
112 -
113 -class ConnectionProperties(object):
114 -    def __init__(self, cnxtype=None, close=True, log=False):
115 -        if cnxtype is not None:
116 -            warn('[3.16] cnxtype argument is deprecated', DeprecationWarning,
117 -                 stacklevel=2)
118 -        self.cnxtype = cnxtype
119 -        self.log_queries = log
120 -        self.close_on_del = close
121 -
122 -
123 -@deprecated('[3.19] the dbapi is deprecated. Have a look at the new repoapi.')
124 -def _repo_connect(repo, login, **kwargs):
125 -    """Constructor to create a new connection to the given CubicWeb repository.
126 -
127 -    Returns a Connection instance.
128 -
129 -    Raises AuthenticationError if authentication failed
130 -    """
131 -    cnxid = repo.connect(unicode(login), **kwargs)
132 -    cnx = Connection(repo, cnxid, kwargs.get('cnxprops'))
133 -    if cnx.is_repo_in_memory:
134 -        cnx.vreg = repo.vreg
135 -    return cnx
136 -
137 -def connect(database, login=None,
138 -            cnxprops=None, setvreg=True, mulcnx=True, initlog=True, **kwargs):
139 -    """Constructor for creating a connection to the CubicWeb repository.
140 -    Returns a :class:`Connection` object.
141 -
142 -    Typical usage::
143 -
144 -      cnx = connect('myinstance', login='me', password='toto')
145 -
146 -    `database` may be:
147 -
148 -    * a simple instance id for in-memory connection
149 -
150 -    * a uri like scheme://host:port/instanceid where scheme must be
151 -      'inmemory'
152 -
153 -    Other arguments:
154 -
155 -    :login:
156 -      the user login to use to authenticate.
157 -
158 -    :cnxprops:
159 -      a :class:`ConnectionProperties` instance, allowing to specify
160 -      the connection method (eg in memory).
161 -
162 -    :setvreg:
163 -      flag telling if a registry should be initialized for the connection.
164 -      Don't change this unless you know what you're doing.
165 -
166 -    :mulcnx:
167 -      Will disappear at some point. Try to deal with connections to differents
168 -      instances in the same process unless specified otherwise by setting this
169 -      flag to False. Don't change this unless you know what you're doing.
170 -
171 -    :initlog:
172 -      flag telling if logging should be initialized. You usually don't want
173 -      logging initialization when establishing the connection from a process
174 -      where it's already initialized.
175 -
176 -    :kwargs:
177 -      there goes authentication tokens. You usually have to specify a password
178 -      for the given user, using a named 'password' argument.
179 -
180 -    """
181 -    if not urlparse(database).scheme:
182 -        warn('[3.16] give an qualified URI as database instead of using '
183 -             'host/cnxprops to specify the connection method',
184 -             DeprecationWarning, stacklevel=2)
185 -    puri = urlparse(database)
186 -    method = puri.scheme.lower()
187 -    assert method == 'inmemory'
188 -    config = cwconfig.instance_configuration(puri.netloc)
189 -    repo = get_repository(database, config=config)
190 -    vreg = repo.vreg
191 -    cnx = _repo_connect(repo, login, cnxprops=cnxprops, **kwargs)
192 -    cnx.vreg = vreg
193 -    return cnx
194 -
195 -def in_memory_repo(config):
196 -    """Return and in_memory Repository object from a config (or vreg)"""
197 -    if isinstance(config, cwvreg.CWRegistryStore):
198 -        vreg = config
199 -        config = None
200 -    else:
201 -        vreg = None
202 -    # get local access to the repository
203 -    return get_repository('inmemory://', config=config, vreg=vreg)
204 -
205 -def in_memory_repo_cnx(config, login, **kwargs):
206 -    """useful method for testing and scripting to get a dbapi.Connection
207 -    object connected to an in-memory repository instance
208 -    """
209 -    # connection to the CubicWeb repository
210 -    repo = in_memory_repo(config)
211 -    return repo, _repo_connect(repo, login, **kwargs)
212 -
213 -# XXX web only method, move to webconfig?
214 -def anonymous_session(vreg):
215 -    """return a new anonymous session
216 -
217 -    raises an AuthenticationError if anonymous usage is not allowed
218 -    """
219 -    anoninfo = vreg.config.anonymous_user()
220 -    if anoninfo[0] is None: # no anonymous user
221 -        raise AuthenticationError('anonymous access is not authorized')
222 -    anon_login, anon_password = anoninfo
223 -    # use vreg's repository cache
224 -    repo = vreg.config.repository(vreg)
225 -    anon_cnx = _repo_connect(repo, anon_login, password=anon_password)
226 -    anon_cnx.vreg = vreg
227 -    return DBAPISession(anon_cnx, anon_login)
228 -
229 -
230 -class _NeedAuthAccessMock(object):
231 -    def __getattribute__(self, attr):
232 -        raise AuthenticationError()
233 -    def __nonzero__(self):
234 -        return False
235 -
236 -class DBAPISession(object):
237 -    def __init__(self, cnx, login=None):
238 -        self.cnx = cnx
239 -        self.data = {}
240 -        self.login = login
241 -        # dbapi session identifier is the same as the first connection
242 -        # identifier, but may later differ in case of auto-reconnection as done
243 -        # by the web authentication manager (in cw.web.views.authentication)
244 -        if cnx is not None:
245 -            self.sessionid = cnx.sessionid
246 -        else:
247 -            self.sessionid = uuid4().hex
248 -
249 -    @property
250 -    def anonymous_session(self):
251 -        return not self.cnx or self.cnx.anonymous_connection
252 -
253 -    def __repr__(self):
254 -        return '<DBAPISession %r>' % self.sessionid
255 -
256 -
257 -class DBAPIRequest(RequestSessionBase):
258 -    #: Request language identifier eg: 'en'
259 -    lang = None
260 -
261 -    def __init__(self, vreg, session=None):
262 -        super(DBAPIRequest, self).__init__(vreg)
263 -        #: 'language' => translation_function() mapping
264 -        try:
265 -            # no vreg or config which doesn't handle translations
266 -            self.translations = vreg.config.translations
267 -        except AttributeError:
268 -            self.translations = {}
269 -        #: cache entities built during the request
270 -        self._eid_cache = {}
271 -        if session is not None:
272 -            self.set_session(session)
273 -        else:
274 -            # these args are initialized after a connection is
275 -            # established
276 -            self.session = DBAPISession(None)
277 -            self.cnx = self.user = _NeedAuthAccessMock()
278 -        self.set_default_language(vreg)
279 -
280 -    def get_option_value(self, option, foreid=None):
281 -        if foreid is not None:
282 -            warn('[3.19] foreid argument is deprecated', DeprecationWarning,
283 -                 stacklevel=2)
284 -        return self.cnx.get_option_value(option)
285 -
286 -    def set_session(self, session):
287 -        """method called by the session handler when the user is authenticated
288 -        or an anonymous connection is open
289 -        """
290 -        self.session = session
291 -        if session.cnx:
292 -            self.cnx = session.cnx
293 -            self.execute = session.cnx.cursor(self).execute
294 -            self.user = self.cnx.user(self)
295 -            self.set_entity_cache(self.user)
296 -
297 -    def execute(self, *args, **kwargs): # pylint: disable=E0202
298 -        """overriden when session is set. By default raise authentication error
299 -        so authentication is requested.
300 -        """
301 -        raise AuthenticationError()
302 -
303 -    def set_default_language(self, vreg):
304 -        try:
305 -            lang = vreg.property_value('ui.language')
306 -        except Exception: # property may not be registered
307 -            lang = 'en'
308 -        try:
309 -            self.set_language(lang)
310 -        except KeyError:
311 -            # this occurs usually during test execution
312 -            self._ = self.__ = unicode
313 -            self.pgettext = lambda x, y: unicode(y)
314 -
315 -    # server-side service call #################################################
316 -
317 -    def call_service(self, regid, **kwargs):
318 -        return self.cnx.call_service(regid, **kwargs)
319 -
320 -    # entities cache management ###############################################
321 -
322 -    def entity_cache(self, eid):
323 -        return self._eid_cache[eid]
324 -
325 -    def set_entity_cache(self, entity):
326 -        self._eid_cache[entity.eid] = entity
327 -
328 -    def cached_entities(self):
329 -        return self._eid_cache.values()
330 -
331 -    def drop_entity_cache(self, eid=None):
332 -        if eid is None:
333 -            self._eid_cache = {}
334 -        else:
335 -            del self._eid_cache[eid]
336 -
337 -    # low level session data management #######################################
338 -
339 -    @deprecated('[3.19] use session or transaction data')
340 -    def get_shared_data(self, key, default=None, pop=False, txdata=False):
341 -        """see :meth:`Connection.get_shared_data`"""
342 -        return self.cnx.get_shared_data(key, default, pop, txdata)
343 -
344 -    @deprecated('[3.19] use session or transaction data')
345 -    def set_shared_data(self, key, value, txdata=False, querydata=None):
346 -        """see :meth:`Connection.set_shared_data`"""
347 -        if querydata is not None:
348 -            txdata = querydata
349 -            warn('[3.10] querydata argument has been renamed to txdata',
350 -                 DeprecationWarning, stacklevel=2)
351 -        return self.cnx.set_shared_data(key, value, txdata)
352 -
353 -    # server session compat layer #############################################
354 -
355 -    def entity_metas(self, eid):
356 -        """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
357 -        return self.cnx.entity_metas(eid)
358 -
359 -    def source_defs(self):
360 -        """return the definition of sources used by the repository."""
361 -        return self.cnx.source_defs()
362 -
363 -    @deprecated('[3.19] use .entity_metas(eid) instead')
364 -    def describe(self, eid, asdict=False):
365 -        """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
366 -        return self.cnx.describe(eid, asdict)
367 -
368 -    # these are overridden by set_log_methods below
369 -    # only defining here to prevent pylint from complaining
370 -    info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
371 -
372 -set_log_methods(DBAPIRequest, getLogger('cubicweb.dbapi'))
373 -
374 -
375 -
376 -# cursor / connection objects ##################################################
377 -
378 -class Cursor(object):
379 -    """These objects represent a database cursor, which is used to manage the
380 -    context of a fetch operation. Cursors created from the same connection are
381 -    not isolated, i.e., any changes done to the database by a cursor are
382 -    immediately visible by the other cursors. Cursors created from different
383 -    connections are isolated.
384 -    """
385 -
386 -    def __init__(self, connection, repo, req=None):
387 -        """This read-only attribute return a reference to the Connection
388 -        object on which the cursor was created.
389 -        """
390 -        self.connection = connection
391 -        """optionnal issuing request instance"""
392 -        self.req = req
393 -        self._repo = repo
394 -        self._sessid = connection.sessionid
395 -
396 -    def close(self):
397 -        """no effect"""
398 -        pass
399 -
400 -    def _txid(self):
401 -        return self.connection._txid(self)
402 -
403 -    def execute(self, rql, args=None, build_descr=True):
404 -        """execute a rql query, return resulting rows and their description in
405 -        a :class:`~cubicweb.rset.ResultSet` object
406 -
407 -        * `rql` should be a Unicode string or a plain ASCII string, containing
408 -          the rql query
409 -
410 -        * `args` the optional args dictionary associated to the query, with key
411 -          matching named substitution in `rql`
412 -
413 -        * `build_descr` is a boolean flag indicating if the description should
414 -          be built on select queries (if false, the description will be en empty
415 -          list)
416 -
417 -        on INSERT queries, there will be one row for each inserted entity,
418 -        containing its eid
419 -
420 -        on SET queries, XXX describe
421 -
422 -        DELETE queries returns no result.
423 -
424 -        .. Note::
425 -          to maximize the rql parsing/analyzing cache performance, you should
426 -          always use substitute arguments in queries, i.e. avoid query such as::
427 -
428 -            execute('Any X WHERE X eid 123')
429 -
430 -          use::
431 -
432 -            execute('Any X WHERE X eid %(x)s', {'x': 123})
433 -        """
434 -        rset = self._repo.execute(self._sessid, rql, args,
435 -                                  build_descr=build_descr, **self._txid())
436 -        rset.req = self.req
437 -        return rset
438 -
439 -
440 -class LogCursor(Cursor):
441 -    """override the standard cursor to log executed queries"""
442 -
443 -    def execute(self, operation, parameters=None, build_descr=True):
444 -        """override the standard cursor to log executed queries"""
445 -        tstart, cstart = time(), clock()
446 -        rset = Cursor.execute(self, operation, parameters, build_descr=build_descr)
447 -        self.connection.executed_queries.append((operation, parameters,
448 -                                                 time() - tstart, clock() - cstart))
449 -        return rset
450 -
451 -def check_not_closed(func):
452 -    def decorator(self, *args, **kwargs):
453 -        if self._closed is not None:
454 -            raise ProgrammingError('Closed connection %s' % self.sessionid)
455 -        return func(self, *args, **kwargs)
456 -    return decorator
457 -
458 -class Connection(object):
459 -    """DB-API 2.0 compatible Connection object for CubicWeb
460 -    """
461 -    # make exceptions available through the connection object
462 -    ProgrammingError = ProgrammingError
463 -    # attributes that may be overriden per connection instance
464 -    cursor_class = Cursor
465 -    vreg = None
466 -    _closed = None
467 -
468 -    def __init__(self, repo, cnxid, cnxprops=None):
469 -        self._repo = repo
470 -        self.sessionid = cnxid
471 -        self._close_on_del = getattr(cnxprops, 'close_on_del', True)
472 -        self._web_request = False
473 -        if cnxprops and cnxprops.log_queries:
474 -            self.executed_queries = []
475 -            self.cursor_class = LogCursor
476 -
477 -    @property
478 -    def is_repo_in_memory(self):
479 -        """return True if this is a local, aka in-memory, connection to the
480 -        repository
481 -        """
482 -        try:
483 -            from cubicweb.server.repository import Repository
484 -        except ImportError:
485 -            # code not available, no way
486 -            return False
487 -        return isinstance(self._repo, Repository)
488 -
489 -    @property # could be a cached property but we want to prevent assigment to
490 -              # catch potential programming error.
491 -    def anonymous_connection(self):
492 -        login = self._repo.user_info(self.sessionid)[1]
493 -        anon_login = self.vreg.config.get('anonymous-user')
494 -        return login == anon_login
495 -
496 -    def __repr__(self):
497 -        if self.anonymous_connection:
498 -            return '<Connection %s (anonymous)>' % self.sessionid
499 -        return '<Connection %s>' % self.sessionid
500 -
501 -    def __enter__(self):
502 -        return self.cursor()
503 -
504 -    def __exit__(self, exc_type, exc_val, exc_tb):
505 -        if exc_type is None:
506 -            self.commit()
507 -        else:
508 -            self.rollback()
509 -            return False #propagate the exception
510 -
511 -    def __del__(self):
512 -        """close the remote connection if necessary"""
513 -        if self._closed is None and self._close_on_del:
514 -            try:
515 -                self.close()
516 -            except Exception:
517 -                pass
518 -
519 -    # server-side service call #################################################
520 -
521 -    @check_not_closed
522 -    def call_service(self, regid, **kwargs):
523 -        return self._repo.call_service(self.sessionid, regid, **kwargs)
524 -
525 -    # connection initialization methods ########################################
526 -
527 -    def load_appobjects(self, cubes=_MARKER, subpath=None, expand=True):
528 -        config = self.vreg.config
529 -        if cubes is _MARKER:
530 -            cubes = self._repo.get_cubes()
531 -        elif cubes is None:
532 -            cubes = ()
533 -        else:
534 -            if not isinstance(cubes, (list, tuple)):
535 -                cubes = (cubes,)
536 -            if expand:
537 -                cubes = config.expand_cubes(cubes)
538 -        if subpath is None:
539 -            subpath = esubpath = ('entities', 'views')
540 -        else:
541 -            esubpath = subpath
542 -        if 'views' in subpath:
543 -            esubpath = list(subpath)
544 -            esubpath.remove('views')
545 -            esubpath.append(join('web', 'views'))
546 -        # first load available configs, necessary for proper persistent
547 -        # properties initialization
548 -        config.load_available_configs()
549 -        # then init cubes
550 -        config.init_cubes(cubes)
551 -        # then load appobjects into the registry
552 -        vpath = config.build_appobjects_path(reversed(config.cubes_path()),
553 -                                             evobjpath=esubpath,
554 -                                             tvobjpath=subpath)
555 -        self.vreg.register_objects(vpath)
556 -
557 -    def use_web_compatible_requests(self, baseurl, sitetitle=None):
558 -        """monkey patch DBAPIRequest to fake a cw.web.request, so you should
559 -        able to call html views using rset from a simple dbapi connection.
560 -
561 -        You should call `load_appobjects` at some point to register those views.
562 -        """
563 -        DBAPIRequest.property_value = _fake_property_value
564 -        DBAPIRequest.next_tabindex = count().next
565 -        DBAPIRequest.relative_path = fake
566 -        DBAPIRequest.url = fake
567 -        DBAPIRequest.get_page_data = fake
568 -        DBAPIRequest.set_page_data = fake
569 -        # XXX could ask the repo for it's base-url configuration
570 -        self.vreg.config.set_option('base-url', baseurl)
571 -        self.vreg.config.uiprops = {}
572 -        self.vreg.config.datadir_url = baseurl + '/data'
573 -        # XXX why is this needed? if really needed, could be fetched by a query
574 -        if sitetitle is not None:
575 -            self.vreg['propertydefs']['ui.site-title'] = {'default': sitetitle}
576 -        self._web_request = True
577 -
578 -    def request(self):
579 -        if self._web_request:
580 -            from cubicweb.web.request import DBAPICubicWebRequestBase
581 -            req = DBAPICubicWebRequestBase(self.vreg, False)
582 -            req.get_header = lambda x, default=None: default
583 -            req.set_session = lambda session: DBAPIRequest.set_session(
584 -                req, session)
585 -            req.relative_path = lambda includeparams=True: ''
586 -        else:
587 -            req = DBAPIRequest(self.vreg)
588 -        req.set_session(DBAPISession(self))
589 -        return req
590 -
591 -    @check_not_closed
592 -    def user(self, req=None, props=None):
593 -        """return the User object associated to this connection"""
594 -        # cnx validity is checked by the call to .user_info
595 -        eid, login, groups, properties = self._repo.user_info(self.sessionid,
596 -                                                              props)
597 -        if req is None:
598 -            req = self.request()
599 -        rset = req.eid_rset(eid, 'CWUser')
600 -        if self.vreg is not None and 'etypes' in self.vreg:
601 -            user = self.vreg['etypes'].etype_class('CWUser')(
602 -                req, rset, row=0, groups=groups, properties=properties)
603 -        else:
604 -            from cubicweb.entity import Entity
605 -            user = Entity(req, rset, row=0)
606 -        user.cw_attr_cache['login'] = login # cache login
607 -        return user
608 -
609 -    @check_not_closed
610 -    def check(self):
611 -        """raise `BadConnectionId` if the connection is no more valid, else
612 -        return its latest activity timestamp.
613 -        """
614 -        return self._repo.check_session(self.sessionid)
615 -
616 -    def _txid(self, cursor=None): # pylint: disable=E0202
617 -        # XXX could now handle various isolation level!
618 -        # return a dict as bw compat trick
619 -        return {'txid': currentThread().getName()}
620 -
621 -    # session data methods #####################################################
622 -
623 -    @check_not_closed
624 -    def get_shared_data(self, key, default=None, pop=False, txdata=False):
625 -        """return value associated to key in the session's data dictionary or
626 -        session's transaction's data if `txdata` is true.
627 -
628 -        If pop is True, value will be removed from the dictionary.
629 -
630 -        If key isn't defined in the dictionary, value specified by the
631 -        `default` argument will be returned.
632 -        """
633 -        return self._repo.get_shared_data(self.sessionid, key, default, pop, txdata)
634 -
635 -    @check_not_closed
636 -    def set_shared_data(self, key, value, txdata=False):
637 -        """set value associated to `key` in shared data
638 -
639 -        if `txdata` is true, the value will be added to the repository
640 -        session's query data which are cleared on commit/rollback of the current
641 -        transaction.
642 -        """
643 -        return self._repo.set_shared_data(self.sessionid, key, value, txdata)
644 -
645 -    # meta-data accessors ######################################################
646 -
647 -    @check_not_closed
648 -    def source_defs(self):
649 -        """Return the definition of sources used by the repository."""
650 -        return self._repo.source_defs()
651 -
652 -    @check_not_closed
653 -    def get_schema(self):
654 -        """Return the schema currently used by the repository."""
655 -        return self._repo.get_schema()
656 -
657 -    @check_not_closed
658 -    def get_option_value(self, option, foreid=None):
659 -        """Return the value for `option` in the configuration.
660 -
661 -        `foreid` argument is deprecated and now useless (as of 3.19).
662 -        """
663 -        if foreid is not None:
664 -            warn('[3.19] foreid argument is deprecated', DeprecationWarning,
665 -                 stacklevel=2)
666 -        return self._repo.get_option_value(option)
667 -
668 -
669 -    @check_not_closed
670 -    def entity_metas(self, eid):
671 -        """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
672 -        try:
673 -            return self._repo.entity_metas(self.sessionid, eid, **self._txid())
674 -        except AttributeError:
675 -            # talking to pre 3.19 repository
676 -            metas = self._repo.describe(self.sessionid, eid, **self._txid())
677 -            if len(metas) == 3: # even older backward compat
678 -                metas = list(metas)
679 -                metas.append(metas[1])
680 -            return dict(zip(('type', 'source', 'extid', 'asource'), metas))
681 -
682 -
683 -    @deprecated('[3.19] use .entity_metas(eid) instead')
684 -    @check_not_closed
685 -    def describe(self, eid, asdict=False):
686 -        try:
687 -            metas = self._repo.entity_metas(self.sessionid, eid, **self._txid())
688 -        except AttributeError:
689 -            metas = self._repo.describe(self.sessionid, eid, **self._txid())
690 -            # talking to pre 3.19 repository
691 -            if len(metas) == 3: # even older backward compat
692 -                metas = list(metas)
693 -                metas.append(metas[1])
694 -            if asdict:
695 -                return dict(zip(('type', 'source', 'extid', 'asource'), metas))
696 -            return metas[:-1]
697 -        if asdict:
698 -            metas['asource'] = meta['source'] # XXX pre 3.19 client compat
699 -            return metas
700 -        return metas['type'], metas['source'], metas['extid']
701 -
702 -
703 -    # db-api like interface ####################################################
704 -
705 -    @check_not_closed
706 -    def commit(self):
707 -        """Commit pending transaction for this connection to the repository.
708 -
709 -        may raises `Unauthorized` or `ValidationError` if we attempted to do
710 -        something we're not allowed to for security or integrity reason.
711 -
712 -        If the transaction is undoable, a transaction id will be returned.
713 -        """
714 -        return self._repo.commit(self.sessionid, **self._txid())
715 -
716 -    @check_not_closed
717 -    def rollback(self):
718 -        """This method is optional since not all databases provide transaction
719 -        support.
720 -
721 -        In case a database does provide transactions this method causes the the
722 -        database to roll back to the start of any pending transaction.  Closing
723 -        a connection without committing the changes first will cause an implicit
724 -        rollback to be performed.
725 -        """
726 -        self._repo.rollback(self.sessionid, **self._txid())
727 -
728 -    @check_not_closed
729 -    def cursor(self, req=None):
730 -        """Return a new Cursor Object using the connection.
731 -        """
732 -        if req is None:
733 -            req = self.request()
734 -        return self.cursor_class(self, self._repo, req=req)
735 -
736 -    @check_not_closed
737 -    def close(self):
738 -        """Close the connection now (rather than whenever __del__ is called).
739 -
740 -        The connection will be unusable from this point forward; an Error (or
741 -        subclass) exception will be raised if any operation is attempted with
742 -        the connection. The same applies to all cursor objects trying to use the
743 -        connection.  Note that closing a connection without committing the
744 -        changes first will cause an implicit rollback to be performed.
745 -        """
746 -        self._repo.close(self.sessionid, **self._txid())
747 -        del self._repo # necessary for proper garbage collection
748 -        self._closed = 1
749 -
750 -    # undo support ############################################################
751 -
752 -    @check_not_closed
753 -    def undoable_transactions(self, ueid=None, req=None, **actionfilters):
754 -        """Return a list of undoable transaction objects by the connection's
755 -        user, ordered by descendant transaction time.
756 -
757 -        Managers may filter according to user (eid) who has done the transaction
758 -        using the `ueid` argument. Others will only see their own transactions.
759 -
760 -        Additional filtering capabilities is provided by using the following
761 -        named arguments:
762 -
763 -        * `etype` to get only transactions creating/updating/deleting entities
764 -          of the given type
765 -
766 -        * `eid` to get only transactions applied to entity of the given eid
767 -
768 -        * `action` to get only transactions doing the given action (action in
769 -          'C', 'U', 'D', 'A', 'R'). If `etype`, action can only be 'C', 'U' or
770 -          'D'.
771 -
772 -        * `public`: when additional filtering is provided, their are by default
773 -          only searched in 'public' actions, unless a `public` argument is given
774 -          and set to false.
775 -        """
776 -        actionfilters.update(self._txid())
777 -        txinfos = self._repo.undoable_transactions(self.sessionid, ueid,
778 -                                                   **actionfilters)
779 -        if req is None:
780 -            req = self.request()
781 -        for txinfo in txinfos:
782 -            txinfo.req = req
783 -        return txinfos
784 -
785 -    @check_not_closed
786 -    def transaction_info(self, txuuid, req=None):
787 -        """Return transaction object for the given uid.
788 -
789 -        raise `NoSuchTransaction` if not found or if session's user is not
790 -        allowed (eg not in managers group and the transaction doesn't belong to
791 -        him).
792 -        """
793 -        txinfo = self._repo.transaction_info(self.sessionid, txuuid,
794 -                                             **self._txid())
795 -        if req is None:
796 -            req = self.request()
797 -        txinfo.req = req
798 -        return txinfo
799 -
800 -    @check_not_closed
801 -    def transaction_actions(self, txuuid, public=True):
802 -        """Return an ordered list of action effectued during that transaction.
803 -
804 -        If public is true, return only 'public' actions, eg not ones triggered
805 -        under the cover by hooks, else return all actions.
806 -
807 -        raise `NoSuchTransaction` if the transaction is not found or if
808 -        session's user is not allowed (eg not in managers group and the
809 -        transaction doesn't belong to him).
810 -        """
811 -        return self._repo.transaction_actions(self.sessionid, txuuid, public,
812 -                                              **self._txid())
813 -
814 -    @check_not_closed
815 -    def undo_transaction(self, txuuid):
816 -        """Undo the given transaction. Return potential restoration errors.
817 -
818 -        raise `NoSuchTransaction` if not found or if session's user is not
819 -        allowed (eg not in managers group and the transaction doesn't belong to
820 -        him).
821 -        """
822 -        return self._repo.undo_transaction(self.sessionid, txuuid,
823 -                                           **self._txid())
824 -
825 -in_memory_cnx = deprecated('[3.16] use _repo_connect instead)')(_repo_connect)
diff --git a/devtools/__init__.py b/devtools/__init__.py
@@ -392,13 +392,13 @@
826              repo._has_started = True
827          return repo
828 
829      def _new_repo(self, config):
830          """Factory method to create a new Repository Instance"""
831 -        from cubicweb.dbapi import in_memory_repo
832 +        from cubicweb.repoapi import _get_inmemory_repo
833          config._cubes = None
834 -        repo = in_memory_repo(config)
835 +        repo = _get_inmemory_repo(config)
836          # extending Repository class
837          repo._has_started = False
838          repo._needs_refresh = False
839          repo.turn_repo_on = partial(turn_repo_on, repo)
840          repo.turn_repo_off = partial(turn_repo_off, repo)
diff --git a/doc/3.21.rst b/doc/3.21.rst
@@ -35,5 +35,6 @@
841    "repository-only" instances (i.e. without a http component) are no
842    longer possible.  If you have any such instances, you will need to
843    rename the configuration file from repository.conf to all-in-one.conf
844    and run ``cubicweb-ctl upgrade`` to update it.
845 
846 +* the old (deprecated since 3.19) `DBAPI` api is completely removed
diff --git a/test/unittest_dbapi.py b/test/unittest_dbapi.py
@@ -1,103 +0,0 @@
847 -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
848 -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
849 -#
850 -# This file is part of CubicWeb.
851 -#
852 -# CubicWeb is free software: you can redistribute it and/or modify it under the
853 -# terms of the GNU Lesser General Public License as published by the Free
854 -# Software Foundation, either version 2.1 of the License, or (at your option)
855 -# any later version.
856 -#
857 -# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
858 -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
859 -# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
860 -# details.
861 -#
862 -# You should have received a copy of the GNU Lesser General Public License along
863 -# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
864 -"""unittest for cubicweb.dbapi"""
865 -
866 -from copy import copy
867 -
868 -from logilab.common import tempattr
869 -
870 -from cubicweb import ConnectionError, cwconfig, NoSelectableObject
871 -from cubicweb.dbapi import ProgrammingError, _repo_connect
872 -from cubicweb.devtools.testlib import CubicWebTC
873 -
874 -
875 -class DBAPITC(CubicWebTC):
876 -
877 -    def test_public_repo_api(self):
878 -        cnx = _repo_connect(self.repo, login='anon', password='anon')
879 -        self.assertEqual(cnx.get_schema(), self.repo.schema)
880 -        self.assertEqual(cnx.source_defs(), {'system': {'type': 'native', 'uri': 'system',
881 -                                                        'use-cwuri-as-url': False}})
882 -        cnx.close()
883 -        self.assertRaises(ProgrammingError, cnx.get_schema)
884 -        self.assertRaises(ProgrammingError, cnx.source_defs)
885 -
886 -    def test_db_api(self):
887 -        cnx = _repo_connect(self.repo, login='anon', password='anon')
888 -        self.assertEqual(cnx.rollback(), None)
889 -        self.assertEqual(cnx.commit(), None)
890 -        cnx.close()
891 -        self.assertRaises(ProgrammingError, cnx.rollback)
892 -        self.assertRaises(ProgrammingError, cnx.commit)
893 -        self.assertRaises(ProgrammingError, cnx.close)
894 -
895 -    def test_api(self):
896 -        cnx = _repo_connect(self.repo, login='anon', password='anon')
897 -        self.assertEqual(cnx.user(None).login, 'anon')
898 -        self.assertEqual({'type': u'CWSource', 'source': u'system', 'extid': None},
899 -                         cnx.entity_metas(1))
900 -        self.assertEqual(cnx.describe(1), (u'CWSource', u'system', None))
901 -        cnx.close()
902 -        self.assertRaises(ProgrammingError, cnx.user, None)
903 -        self.assertRaises(ProgrammingError, cnx.entity_metas, 1)
904 -        self.assertRaises(ProgrammingError, cnx.describe, 1)
905 -
906 -    def test_shared_data_api(self):
907 -        cnx = _repo_connect(self.repo, login='anon', password='anon')
908 -        self.assertEqual(cnx.get_shared_data('data'), None)
909 -        cnx.set_shared_data('data', 4)
910 -        self.assertEqual(cnx.get_shared_data('data'), 4)
911 -        cnx.get_shared_data('data', pop=True)
912 -        cnx.get_shared_data('whatever', pop=True)
913 -        self.assertEqual(cnx.get_shared_data('data'), None)
914 -        cnx.set_shared_data('data', 4)
915 -        self.assertEqual(cnx.get_shared_data('data'), 4)
916 -        cnx.close()
917 -        self.assertRaises(ProgrammingError, cnx.check)
918 -        self.assertRaises(ProgrammingError, cnx.set_shared_data, 'data', 0)
919 -        self.assertRaises(ProgrammingError, cnx.get_shared_data, 'data')
920 -
921 -    def test_web_compatible_request(self):
922 -        config = cwconfig.CubicWebNoAppConfiguration()
923 -        cnx = _repo_connect(self.repo, login='admin', password='gingkow')
924 -        with tempattr(cnx.vreg, 'config', config):
925 -            cnx.use_web_compatible_requests('http://perdu.com')
926 -            req = cnx.request()
927 -            self.assertEqual(req.base_url(), 'http://perdu.com/')
928 -            self.assertEqual(req.from_controller(), 'view')
929 -            self.assertEqual(req.relative_path(), '')
930 -            req.ajax_replace_url('domid') # don't crash
931 -            req.user.cw_adapt_to('IBreadCrumbs') # don't crash
932 -
933 -    def test_call_service(self):
934 -        ServiceClass = self.vreg['services']['test_service'][0]
935 -        for _cw in (self.request(), self.session):
936 -            ret_value = _cw.call_service('test_service', msg='coucou')
937 -            self.assertEqual('coucou', ServiceClass.passed_here.pop())
938 -            self.assertEqual('babar', ret_value)
939 -        with self.login('anon') as ctm:
940 -            for _cw in (self.request(), self.session):
941 -                with self.assertRaises(NoSelectableObject):
942 -                    _cw.call_service('test_service', msg='toto')
943 -                self.rollback()
944 -                self.assertEqual([], ServiceClass.passed_here)
945 -
946 -
947 -if __name__ == '__main__':
948 -    from logilab.common.testlib import unittest_main
949 -    unittest_main()
diff --git a/toolsutils.py b/toolsutils.py
@@ -255,22 +255,10 @@
950        'help': 'specify the name server\'s host name. Will be detected by \
951  broadcast if not provided.',
952        }),
953      )
954 
955 -def config_connect(appid, optconfig):
956 -    from cubicweb.dbapi import connect
957 -    from getpass import getpass
958 -    user = optconfig.user
959 -    if not user:
960 -        user = raw_input('login: ')
961 -    password = optconfig.password
962 -    if not password:
963 -        password = getpass('password: ')
964 -    return connect(login=user, password=password, host=optconfig.host, database=appid)
965 -
966 -
967  ## cwshell helpers #############################################################
968 
969  class AbstractMatcher(object):
970      """Abstract class for CWShellCompleter's matchers.
971 
diff --git a/web/request.py b/web/request.py
@@ -36,12 +36,12 @@
972 
973  from logilab.common.decorators import cached
974  from logilab.common.deprecation import deprecated
975  from logilab.mtconverter import xml_escape
976 
977 +from cubicweb import AuthenticationError
978  from cubicweb.req import RequestSessionBase
979 -from cubicweb.dbapi import DBAPIRequest
980  from cubicweb.uilib import remove_html_tags, js
981  from cubicweb.utils import SizeConstrainedList, HTMLHead, make_uid
982  from cubicweb.view import TRANSITIONAL_DOCTYPE_NOEXT
983  from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit,
984                            RequestError, StatusResponse)
@@ -954,26 +954,33 @@
985                      return
986          # 3. site's default language
987          self.set_default_language(vreg)
988 
989 
990 -class DBAPICubicWebRequestBase(_CubicWebRequestBase, DBAPIRequest):
991 -
992 -    def set_session(self, session):
993 -        """method called by the session handler when the user is authenticated
994 -        or an anonymous connection is open
995 -        """
996 -        super(CubicWebRequestBase, self).set_session(session)
997 -        # set request language
998 -        self.set_user_language(session.user)
999 -
1000 -
1001  def _cnx_func(name):
1002      def proxy(req, *args, **kwargs):
1003          return getattr(req.cnx, name)(*args, **kwargs)
1004      return proxy
1005 
1006 +class _NeedAuthAccessMock(object):
1007 +
1008 +    def __getattribute__(self, attr):
1009 +        raise AuthenticationError()
1010 +
1011 +    def __nonzero__(self):
1012 +        return False
1013 +
1014 +class _MockAnonymousSession(object):
1015 +    sessionid = 'thisisnotarealsession'
1016 +
1017 +    @property
1018 +    def data(self):
1019 +        return {}
1020 +
1021 +    @property
1022 +    def anonymous_session(self):
1023 +        return True
1024 
1025  class ConnectionCubicWebRequestBase(_CubicWebRequestBase):
1026 
1027      def __init__(self, vreg, https=False, form=None, headers={}):
1028          """"""
@@ -985,12 +992,11 @@
1029              self.translations = vreg.config.translations
1030          except AttributeError:
1031              self.translations = {}
1032          super(ConnectionCubicWebRequestBase, self).__init__(vreg, https=https,
1033                                                         form=form, headers=headers)
1034 -        from cubicweb.dbapi import DBAPISession, _NeedAuthAccessMock
1035 -        self.session = DBAPISession(None)
1036 +        self.session = _MockAnonymousSession()
1037          self.cnx = self.user = _NeedAuthAccessMock()
1038 
1039      @property
1040      def transaction_data(self):
1041          return self.cnx.transaction_data
@@ -1005,11 +1011,10 @@
1042          rset = self.cnx.execute(*args, **kwargs)
1043          rset.req = self
1044          return rset
1045 
1046      def set_default_language(self, vreg):
1047 -        # XXX copy from dbapi
1048          try:
1049              lang = vreg.property_value('ui.language')
1050          except Exception: # property may not be registered
1051              lang = 'en'
1052          try:
diff --git a/web/test/unittest_application.py b/web/test/unittest_application.py
@@ -28,11 +28,10 @@
1053  from cubicweb.devtools.testlib import CubicWebTC, real_error_handling
1054  from cubicweb.devtools.fake import FakeRequest
1055  from cubicweb.web import LogOut, Redirect, INTERNAL_FIELD_VALUE
1056  from cubicweb.web.views.basecontrollers import ViewController
1057  from cubicweb.web.application import anonymized_request
1058 -from cubicweb.dbapi import DBAPISession, _NeedAuthAccessMock
1059  from cubicweb import repoapi
1060 
1061  class FakeMapping:
1062      """emulates a mapping module"""
1063      def __init__(self):
@@ -366,14 +365,10 @@
1064          sessioncookie = self.app.session_handler.session_cookie(req)
1065          cookie[sessioncookie] = req.session.sessionid
1066          req.set_request_header('Cookie', cookie[sessioncookie].OutputString(),
1067                                 raw=True)
1068          clear_cache(req, 'get_authorization')
1069 -        # reset session as if it was a new incoming request
1070 -        req.session = DBAPISession(None)
1071 -        req.user = req.cnx = _NeedAuthAccessMock
1072 -        
1073 
1074      def _test_auth_anon(self, req):
1075          asession = self.app.get_session(req)
1076          # important otherwise _reset_cookie will not use the right session
1077          req.set_cnx(repoapi.ClientConnection(asession))
diff --git a/web/views/authentication.py b/web/views/authentication.py
@@ -1,6 +1,6 @@
1078 -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
1079 +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
1080  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
1081  #
1082  # This file is part of CubicWeb.
1083  #
1084  # CubicWeb is free software: you can redistribute it and/or modify it under the
@@ -24,11 +24,10 @@
1085  from logilab.common.decorators import clear_cache
1086  from logilab.common.deprecation import class_renamed
1087 
1088  from cubicweb import AuthenticationError, BadConnectionId
1089  from cubicweb.view import Component
1090 -from cubicweb.dbapi import _repo_connect, ConnectionProperties
1091  from cubicweb.web import InvalidSession
1092  from cubicweb.web.application import AbstractAuthenticationManager
1093 
1094  class NoAuthInfo(Exception): pass
1095 
diff --git a/web/views/sessions.py b/web/views/sessions.py
@@ -25,11 +25,10 @@
1096 
1097  from cubicweb import (RepositoryError, Unauthorized, AuthenticationError,
1098                        BadConnectionId)
1099  from cubicweb.web import InvalidSession, Redirect
1100  from cubicweb.web.application import AbstractSessionManager
1101 -from cubicweb.dbapi import ProgrammingError, DBAPISession
1102  from cubicweb import repoapi
1103 
1104 
1105  class InMemoryRepositorySessionManager(AbstractSessionManager):
1106      """manage session data associated to a session identifier"""