dbapi.py
brancholdstable
changeset 7074 e4580e5f0703
parent 7056 51f88f13d6f3
child 7061 bb2080547722
child 7083 b8e35cde46e9
equal deleted inserted replaced
6749:48f468f33704 7074:e4580e5f0703
    45 def _fake_property_value(self, name):
    45 def _fake_property_value(self, name):
    46     try:
    46     try:
    47         return super(DBAPIRequest, self).property_value(name)
    47         return super(DBAPIRequest, self).property_value(name)
    48     except KeyError:
    48     except KeyError:
    49         return ''
    49         return ''
       
    50 
       
    51 def fake(*args, **kwargs):
       
    52     return None
    50 
    53 
    51 def multiple_connections_fix():
    54 def multiple_connections_fix():
    52     """some monkey patching necessary when an application has to deal with
    55     """some monkey patching necessary when an application has to deal with
    53     several connections to different repositories. It tries to hide buggy class
    56     several connections to different repositories. It tries to hide buggy class
    54     attributes since classes are not designed to be shared among multiple
    57     attributes since classes are not designed to be shared among multiple
   163       flag telling if logging should be initialized. You usually don't want
   166       flag telling if logging should be initialized. You usually don't want
   164       logging initialization when establishing the connection from a process
   167       logging initialization when establishing the connection from a process
   165       where it's already initialized.
   168       where it's already initialized.
   166 
   169 
   167     :kwargs:
   170     :kwargs:
   168       there goes authentication tokens. You usually have to specify for
   171       there goes authentication tokens. You usually have to specify a password
   169       instance a password for the given user, using a named 'password' argument.
   172       for the given user, using a named 'password' argument.
   170     """
   173     """
   171     config = cwconfig.CubicWebNoAppConfiguration()
       
   172     if host:
       
   173         config.global_set_option('pyro-ns-host', host)
       
   174     if group:
       
   175         config.global_set_option('pyro-ns-group', group)
       
   176     cnxprops = cnxprops or ConnectionProperties()
   174     cnxprops = cnxprops or ConnectionProperties()
   177     method = cnxprops.cnxtype
   175     method = cnxprops.cnxtype
       
   176     if method == 'pyro':
       
   177         config = cwconfig.CubicWebNoAppConfiguration()
       
   178         if host:
       
   179             config.global_set_option('pyro-ns-host', host)
       
   180         if group:
       
   181             config.global_set_option('pyro-ns-group', group)
       
   182     else:
       
   183         assert database
       
   184         config = cwconfig.instance_configuration(database)
   178     repo = get_repository(method, database, config=config)
   185     repo = get_repository(method, database, config=config)
   179     if method == 'inmemory':
   186     if method == 'inmemory':
   180         vreg = repo.vreg
   187         vreg = repo.vreg
   181     elif setvreg:
   188     elif setvreg:
   182         if mulcnx:
   189         if mulcnx:
   192         vreg = None
   199         vreg = None
   193     cnx = repo_connect(repo, login, cnxprops=cnxprops, **kwargs)
   200     cnx = repo_connect(repo, login, cnxprops=cnxprops, **kwargs)
   194     cnx.vreg = vreg
   201     cnx.vreg = vreg
   195     return cnx
   202     return cnx
   196 
   203 
   197 def in_memory_cnx(config, login, **kwargs):
   204 def in_memory_repo(config):
   198     """usefull method for testing and scripting to get a dbapi.Connection
   205     """Return and in_memory Repository object from a config (or vreg)"""
   199     object connected to an in-memory repository instance
       
   200     """
       
   201     if isinstance(config, cwvreg.CubicWebVRegistry):
   206     if isinstance(config, cwvreg.CubicWebVRegistry):
   202         vreg = config
   207         vreg = config
   203         config = None
   208         config = None
   204     else:
   209     else:
   205         vreg = None
   210         vreg = None
   206     # get local access to the repository
   211     # get local access to the repository
   207     repo = get_repository('inmemory', config=config, vreg=vreg)
   212     return get_repository('inmemory', config=config, vreg=vreg)
       
   213 
       
   214 def in_memory_cnx(repo, login, **kwargs):
       
   215     """Establish a In memory connection to a <repo> for the user with <login>
       
   216 
       
   217     additionel credential might be required"""
       
   218     cnxprops = ConnectionProperties('inmemory')
       
   219     return repo_connect(repo, login, cnxprops=cnxprops, **kwargs)
       
   220 
       
   221 def in_memory_repo_cnx(config, login, **kwargs):
       
   222     """usefull method for testing and scripting to get a dbapi.Connection
       
   223     object connected to an in-memory repository instance
       
   224     """
   208     # connection to the CubicWeb repository
   225     # connection to the CubicWeb repository
   209     cnxprops = ConnectionProperties('inmemory')
   226     repo = in_memory_repo(config)
   210     cnx = repo_connect(repo, login, cnxprops=cnxprops, **kwargs)
   227     return repo, in_memory_cnx(repo, login, **kwargs)
   211     return repo, cnx
       
   212 
   228 
   213 class _NeedAuthAccessMock(object):
   229 class _NeedAuthAccessMock(object):
   214     def __getattribute__(self, attr):
   230     def __getattribute__(self, attr):
   215         raise AuthenticationError()
   231         raise AuthenticationError()
   216     def __nonzero__(self):
   232     def __nonzero__(self):
   311         else:
   327         else:
   312             del self._eid_cache[eid]
   328             del self._eid_cache[eid]
   313 
   329 
   314     # low level session data management #######################################
   330     # low level session data management #######################################
   315 
   331 
   316     def get_shared_data(self, key, default=None, pop=False):
   332     def get_shared_data(self, key, default=None, pop=False, txdata=False):
   317         """return value associated to `key` in shared data"""
   333         """see :meth:`Connection.get_shared_data`"""
   318         return self.cnx.get_shared_data(key, default, pop)
   334         return self.cnx.get_shared_data(key, default, pop, txdata)
   319 
   335 
   320     def set_shared_data(self, key, value, querydata=False):
   336     def set_shared_data(self, key, value, txdata=False, querydata=None):
   321         """set value associated to `key` in shared data
   337         """see :meth:`Connection.set_shared_data`"""
   322 
   338         if querydata is not None:
   323         if `querydata` is true, the value will be added to the repository
   339             txdata = querydata
   324         session's query data which are cleared on commit/rollback of the current
   340             warn('[3.10] querydata argument has been renamed to txdata',
   325         transaction, and won't be available through the connexion, only on the
   341                  DeprecationWarning, stacklevel=2)
   326         repository side.
   342         return self.cnx.set_shared_data(key, value, txdata)
   327         """
       
   328         return self.cnx.set_shared_data(key, value, querydata)
       
   329 
   343 
   330     # server session compat layer #############################################
   344     # server session compat layer #############################################
   331 
   345 
   332     def describe(self, eid):
   346     def describe(self, eid):
   333         """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
   347         """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
   480     def __init__(self, repo, cnxid, cnxprops=None):
   494     def __init__(self, repo, cnxid, cnxprops=None):
   481         self._repo = repo
   495         self._repo = repo
   482         self.sessionid = cnxid
   496         self.sessionid = cnxid
   483         self._close_on_del = getattr(cnxprops, 'close_on_del', True)
   497         self._close_on_del = getattr(cnxprops, 'close_on_del', True)
   484         self._cnxtype = getattr(cnxprops, 'cnxtype', 'pyro')
   498         self._cnxtype = getattr(cnxprops, 'cnxtype', 'pyro')
       
   499         self._web_request = False
   485         if cnxprops and cnxprops.log_queries:
   500         if cnxprops and cnxprops.log_queries:
   486             self.executed_queries = []
   501             self.executed_queries = []
   487             self.cursor_class = LogCursor
   502             self.cursor_class = LogCursor
   488         if self._cnxtype == 'pyro':
   503         if self._cnxtype == 'pyro':
   489             # check client/server compat
   504             # check client/server compat
   532             esubpath = subpath
   547             esubpath = subpath
   533         if 'views' in subpath:
   548         if 'views' in subpath:
   534             esubpath = list(subpath)
   549             esubpath = list(subpath)
   535             esubpath.remove('views')
   550             esubpath.remove('views')
   536             esubpath.append(join('web', 'views'))
   551             esubpath.append(join('web', 'views'))
   537         cubespath = [config.cube_dir(p) for p in cubes]
   552         config.init_cubes(cubes)
   538         config.load_site_cubicweb(cubespath)
   553         vpath = config.build_vregistry_path(reversed(config.cubes_path()),
   539         vpath = config.build_vregistry_path(reversed(cubespath),
       
   540                                             evobjpath=esubpath,
   554                                             evobjpath=esubpath,
   541                                             tvobjpath=subpath)
   555                                             tvobjpath=subpath)
   542         self.vreg.register_objects(vpath)
   556         self.vreg.register_objects(vpath)
   543 
   557 
   544     def use_web_compatible_requests(self, baseurl, sitetitle=None):
   558     def use_web_compatible_requests(self, baseurl, sitetitle=None):
   545         """monkey patch DBAPIRequest to fake a cw.web.request, so you should
   559         """monkey patch DBAPIRequest to fake a cw.web.request, so you should
   546         able to call html views using rset from a simple dbapi connection.
   560         able to call html views using rset from a simple dbapi connection.
   547 
   561 
   548         You should call `load_appobjects` at some point to register those views.
   562         You should call `load_appobjects` at some point to register those views.
   549         """
   563         """
   550         from cubicweb.web.request import CubicWebRequestBase as cwrb
       
   551         DBAPIRequest.build_ajax_replace_url = cwrb.build_ajax_replace_url.im_func
       
   552         DBAPIRequest.ajax_replace_url = cwrb.ajax_replace_url.im_func
       
   553         DBAPIRequest.list_form_param = cwrb.list_form_param.im_func
       
   554         DBAPIRequest.property_value = _fake_property_value
   564         DBAPIRequest.property_value = _fake_property_value
   555         DBAPIRequest.next_tabindex = count().next
   565         DBAPIRequest.next_tabindex = count().next
   556         DBAPIRequest.form = {}
       
   557         DBAPIRequest.data = {}
       
   558         fake = lambda *args, **kwargs: None
       
   559         DBAPIRequest.relative_path = fake
   566         DBAPIRequest.relative_path = fake
   560         DBAPIRequest.url = fake
   567         DBAPIRequest.url = fake
   561         DBAPIRequest.next_tabindex = fake
       
   562         DBAPIRequest.get_page_data = fake
   568         DBAPIRequest.get_page_data = fake
   563         DBAPIRequest.set_page_data = fake
   569         DBAPIRequest.set_page_data = fake
   564         DBAPIRequest.add_js = fake #cwrb.add_js.im_func
       
   565         DBAPIRequest.add_css = fake #cwrb.add_css.im_func
       
   566         # XXX could ask the repo for it's base-url configuration
   570         # XXX could ask the repo for it's base-url configuration
   567         self.vreg.config.set_option('base-url', baseurl)
   571         self.vreg.config.set_option('base-url', baseurl)
       
   572         self.vreg.config.uiprops = {}
       
   573         self.vreg.config.datadir_url = baseurl + '/data'
   568         # XXX why is this needed? if really needed, could be fetched by a query
   574         # XXX why is this needed? if really needed, could be fetched by a query
   569         if sitetitle is not None:
   575         if sitetitle is not None:
   570             self.vreg['propertydefs']['ui.site-title'] = {'default': sitetitle}
   576             self.vreg['propertydefs']['ui.site-title'] = {'default': sitetitle}
   571 
   577         self._web_request = True
   572     @check_not_closed
   578 
   573     def source_defs(self):
   579     def request(self):
   574         """Return the definition of sources used by the repository.
   580         if self._web_request:
   575 
   581             from cubicweb.web.request import CubicWebRequestBase
   576         This is NOT part of the DB-API.
   582             req = CubicWebRequestBase(self.vreg, False)
   577         """
   583             req.get_header = lambda x, default=None: default
   578         return self._repo.source_defs()
   584             req.set_session = lambda session, user=None: DBAPIRequest.set_session(
       
   585                 req, session, user)
       
   586             req.relative_path = lambda includeparams=True: ''
       
   587         else:
       
   588             req = DBAPIRequest(self.vreg)
       
   589         req.set_session(DBAPISession(self))
       
   590         return req
   579 
   591 
   580     @check_not_closed
   592     @check_not_closed
   581     def user(self, req=None, props=None):
   593     def user(self, req=None, props=None):
   582         """return the User object associated to this connection"""
   594         """return the User object associated to this connection"""
   583         # cnx validity is checked by the call to .user_info
   595         # cnx validity is checked by the call to .user_info
   591                                                              groups=groups,
   603                                                              groups=groups,
   592                                                              properties=properties)
   604                                                              properties=properties)
   593         else:
   605         else:
   594             from cubicweb.entity import Entity
   606             from cubicweb.entity import Entity
   595             user = Entity(req, rset, row=0)
   607             user = Entity(req, rset, row=0)
   596         user['login'] = login # cache login
   608         user.cw_attr_cache['login'] = login # cache login
   597         return user
   609         return user
   598 
   610 
   599     @check_not_closed
   611     @check_not_closed
   600     def check(self):
   612     def check(self):
   601         """raise `BadConnectionId` if the connection is no more valid"""
   613         """raise `BadConnectionId` if the connection is no more valid, else
   602         self._repo.check_session(self.sessionid)
   614         return its latest activity timestamp.
       
   615         """
       
   616         return self._repo.check_session(self.sessionid)
   603 
   617 
   604     def _txid(self, cursor=None): # XXX could now handle various isolation level!
   618     def _txid(self, cursor=None): # XXX could now handle various isolation level!
   605         # return a dict as bw compat trick
   619         # return a dict as bw compat trick
   606         return {'txid': currentThread().getName()}
   620         return {'txid': currentThread().getName()}
   607 
   621 
   608     def request(self):
       
   609         return DBAPIRequest(self.vreg, DBAPISession(self))
       
   610 
       
   611     # session data methods #####################################################
   622     # session data methods #####################################################
   612 
   623 
   613     @check_not_closed
   624     @check_not_closed
   614     def set_session_props(self, **props):
   625     def set_session_props(self, **props):
   615         """raise `BadConnectionId` if the connection is no more valid"""
   626         """raise `BadConnectionId` if the connection is no more valid"""
   616         self._repo.set_session_props(self.sessionid, props)
   627         self._repo.set_session_props(self.sessionid, props)
   617 
   628 
   618     @check_not_closed
   629     @check_not_closed
   619     def get_shared_data(self, key, default=None, pop=False):
   630     def get_shared_data(self, key, default=None, pop=False, txdata=False):
   620         """return value associated to `key` in shared data"""
   631         """return value associated to key in the session's data dictionary or
   621         return self._repo.get_shared_data(self.sessionid, key, default, pop)
   632         session's transaction's data if `txdata` is true.
   622 
   633 
   623     @check_not_closed
   634         If pop is True, value will be removed from the dictionnary.
   624     def set_shared_data(self, key, value, querydata=False):
   635 
       
   636         If key isn't defined in the dictionnary, value specified by the
       
   637         `default` argument will be returned.
       
   638         """
       
   639         return self._repo.get_shared_data(self.sessionid, key, default, pop, txdata)
       
   640 
       
   641     @check_not_closed
       
   642     def set_shared_data(self, key, value, txdata=False):
   625         """set value associated to `key` in shared data
   643         """set value associated to `key` in shared data
   626 
   644 
   627         if `querydata` is true, the value will be added to the repository
   645         if `txdata` is true, the value will be added to the repository
   628         session's query data which are cleared on commit/rollback of the current
   646         session's query data which are cleared on commit/rollback of the current
   629         transaction, and won't be available through the connexion, only on the
   647         transaction.
   630         repository side.
   648         """
   631         """
   649         return self._repo.set_shared_data(self.sessionid, key, value, txdata)
   632         return self._repo.set_shared_data(self.sessionid, key, value, querydata)
       
   633 
   650 
   634     # meta-data accessors ######################################################
   651     # meta-data accessors ######################################################
       
   652 
       
   653     @check_not_closed
       
   654     def source_defs(self):
       
   655         """Return the definition of sources used by the repository."""
       
   656         return self._repo.source_defs()
   635 
   657 
   636     @check_not_closed
   658     @check_not_closed
   637     def get_schema(self):
   659     def get_schema(self):
   638         """Return the schema currently used by the repository."""
   660         """Return the schema currently used by the repository."""
   639         return self._repo.get_schema()
   661         return self._repo.get_schema()