[server/test] delete test that doesn't make sense on python3
XXX if PY2?
# copyright 2003-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of CubicWeb.## CubicWeb is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## CubicWeb is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with CubicWeb. If not, see <http://www.gnu.org/licenses/>."""Repository users' and internal' sessions."""from__future__importprint_function__docformat__="restructuredtext en"importsysfromtimeimporttimefromuuidimportuuid4fromwarningsimportwarnimportfunctoolsfromcontextlibimportcontextmanagerfromsiximporttext_typefromlogilab.common.deprecationimportdeprecatedfromlogilab.common.textutilsimportunormalizefromlogilab.common.registryimportobjectify_predicatefromcubicwebimportQueryError,schema,server,ProgrammingErrorfromcubicweb.reqimportRequestSessionBasefromcubicweb.utilsimportmake_uidfromcubicweb.rqlrewriteimportRQLRewriterfromcubicweb.server.editionimportEditedEntityNO_UNDO_TYPES=schema.SCHEMA_TYPES.copy()NO_UNDO_TYPES.add('CWCache')# is / is_instance_of are usually added by sql hooks except when using# dataimport.NoHookRQLObjectStore, and we don't want to record them# anyway in the later caseNO_UNDO_TYPES.add('is')NO_UNDO_TYPES.add('is_instance_of')NO_UNDO_TYPES.add('cw_source')# XXX rememberme,forgotpwd,apycot,vcsfile@objectify_predicatedefis_user_session(cls,req,**kwargs):"""return 1 when session is not internal. This predicate can only be used repository side only. """returnnotreq.is_internal_session@objectify_predicatedefis_internal_session(cls,req,**kwargs):"""return 1 when session is not internal. This predicate can only be used repository side only. """returnreq.is_internal_session@objectify_predicatedefrepairing(cls,req,**kwargs):"""return 1 when repository is running in repair mode"""returnreq.vreg.config.repairing@deprecated('[3.17] use <object>.allow/deny_all_hooks_but instead')defhooks_control(obj,mode,*categories):assertmodein(HOOKS_ALLOW_ALL,HOOKS_DENY_ALL)ifmode==HOOKS_ALLOW_ALL:returnobj.allow_all_hooks_but(*categories)elifmode==HOOKS_DENY_ALL:returnobj.deny_all_hooks_but(*categories)class_hooks_control(object):"""context manager to control activated hooks categories. If mode is `HOOKS_DENY_ALL`, given hooks categories will be enabled. If mode is `HOOKS_ALLOW_ALL`, given hooks categories will be disabled. .. sourcecode:: python with _hooks_control(cnx, HOOKS_ALLOW_ALL, 'integrity'): # ... do stuff with all but 'integrity' hooks activated with _hooks_control(cnx, HOOKS_DENY_ALL, 'integrity'): # ... do stuff with none but 'integrity' hooks activated This is an internal API, you should rather use :meth:`~cubicweb.server.session.Connection.deny_all_hooks_but` or :meth:`~cubicweb.server.session.Connection.allow_all_hooks_but` Connection methods. """def__init__(self,cnx,mode,*categories):assertmodein(HOOKS_ALLOW_ALL,HOOKS_DENY_ALL)self.cnx=cnxself.mode=modeself.categories=categoriesself.oldmode=Noneself.changes=()def__enter__(self):self.oldmode=self.cnx.hooks_modeself.cnx.hooks_mode=self.modeifself.modeisHOOKS_DENY_ALL:self.changes=self.cnx.enable_hook_categories(*self.categories)else:self.changes=self.cnx.disable_hook_categories(*self.categories)self.cnx.ctx_count+=1def__exit__(self,exctype,exc,traceback):self.cnx.ctx_count-=1try:ifself.categories:ifself.modeisHOOKS_DENY_ALL:self.cnx.disable_hook_categories(*self.categories)else:self.cnx.enable_hook_categories(*self.categories)finally:self.cnx.hooks_mode=self.oldmode@deprecated('[3.17] use <object>.security_enabled instead')defsecurity_enabled(obj,*args,**kwargs):returnobj.security_enabled(*args,**kwargs)class_security_enabled(object):"""context manager to control security w/ session.execute, By default security is disabled on queries executed on the repository side. """def__init__(self,cnx,read=None,write=None):self.cnx=cnxself.read=readself.write=writeself.oldread=Noneself.oldwrite=Nonedef__enter__(self):ifself.readisNone:self.oldread=Noneelse:self.oldread=self.cnx.read_securityself.cnx.read_security=self.readifself.writeisNone:self.oldwrite=Noneelse:self.oldwrite=self.cnx.write_securityself.cnx.write_security=self.writeself.cnx.ctx_count+=1def__exit__(self,exctype,exc,traceback):self.cnx.ctx_count-=1ifself.oldreadisnotNone:self.cnx.read_security=self.oldreadifself.oldwriteisnotNone:self.cnx.write_security=self.oldwriteHOOKS_ALLOW_ALL=object()HOOKS_DENY_ALL=object()DEFAULT_SECURITY=object()# evaluated to true by designclassSessionClosedError(RuntimeError):passdef_open_only(func):"""decorator for Connection method that check it is open"""@functools.wraps(func)defcheck_open(cnx,*args,**kwargs):ifnotcnx._open:raiseProgrammingError('Closed Connection: %s'%cnx.connectionid)returnfunc(cnx,*args,**kwargs)returncheck_openclassConnection(RequestSessionBase):"""Repository Connection Holds all connection related data Database connection resources: :attr:`hooks_in_progress`, boolean flag telling if the executing query is coming from a repoapi connection or is a query from within the repository (e.g. started by hooks) :attr:`cnxset`, the connections set to use to execute queries on sources. If the transaction is read only, the connection set may be freed between actual queries. This allows multiple connections with a reasonably low connection set pool size. Control mechanism is detailed below. .. automethod:: cubicweb.server.session.Connection.set_cnxset .. automethod:: cubicweb.server.session.Connection.free_cnxset :attr:`mode`, string telling the connections set handling mode, may be one of 'read' (connections set may be freed), 'write' (some write was done in the connections set, it can't be freed before end of the transaction), 'transaction' (we want to keep the connections set during all the transaction, with or without writing) Shared data: :attr:`data` is a dictionary bound to the underlying session, who will be present for the life time of the session. This may be useful for web clients that rely on the server for managing bits of session-scoped data. :attr:`transaction_data` is a dictionary cleared at the end of the transaction. Hooks and operations may put arbitrary data in there. Internal state: :attr:`pending_operations`, ordered list of operations to be processed on commit/rollback :attr:`commit_state`, describing the transaction commit state, may be one of None (not yet committing), 'precommit' (calling precommit event on operations), 'postcommit' (calling postcommit event on operations), 'uncommitable' (some :exc:`ValidationError` or :exc:`Unauthorized` error has been raised during the transaction and so it must be rolled back). Hooks controls: :attr:`hooks_mode`, may be either `HOOKS_ALLOW_ALL` or `HOOKS_DENY_ALL`. :attr:`enabled_hook_cats`, when :attr:`hooks_mode` is `HOOKS_DENY_ALL`, this set contains hooks categories that are enabled. :attr:`disabled_hook_cats`, when :attr:`hooks_mode` is `HOOKS_ALLOW_ALL`, this set contains hooks categories that are disabled. Security level Management: :attr:`read_security` and :attr:`write_security`, boolean flags telling if read/write security is currently activated. """is_request=Falsehooks_in_progress=Falseis_repo_in_memory=True# bw compatdef__init__(self,session):# using super(Connection, self) confuse some test hackRequestSessionBase.__init__(self,session.vreg)#: connection unique idself._open=Noneself.connectionid='%s-%s'%(session.sessionid,uuid4().hex)self.session=sessionself.sessionid=session.sessionid#: reentrance handlingself.ctx_count=0#: server.Repository objectself.repo=session.repoself.vreg=self.repo.vregself._execute=self.repo.querier.execute# other session utilityself._session_timestamp=session._timestamp# internal (root) sessionself.is_internal_session=isinstance(session.user,InternalManager)#: dict containing arbitrary data cleared at the end of the transactionself.transaction_data={}self._session_data=session.data#: ordered list of operations to be processed on commit/rollbackself.pending_operations=[]#: (None, 'precommit', 'postcommit', 'uncommitable')self.commit_state=None### hook control attributeself.hooks_mode=HOOKS_ALLOW_ALLself.disabled_hook_cats=set()self.enabled_hook_cats=set()self.pruned_hooks_cache={}### security control attributesself._read_security=DEFAULT_SECURITY# handled by a propertyself.write_security=DEFAULT_SECURITY# undo controlconfig=session.repo.configifconfig.creatingorconfig.repairingorself.is_internal_session:self.undo_actions=Falseelse:self.undo_actions=config['undo-enabled']# RQLRewriter are not thread safeself._rewriter=RQLRewriter(self)# other session utilityifsession.user.login=='__internal_manager__':self.user=session.userself.set_language(self.user.prefered_language())else:self._set_user(session.user)@_open_onlydefsource_defs(self):"""Return the definition of sources used by the repository."""returnself.session.repo.source_defs()@_open_onlydefget_schema(self):"""Return the schema currently used by the repository."""returnself.session.repo.source_defs()@_open_onlydefget_option_value(self,option):"""Return the value for `option` in the configuration."""returnself.session.repo.get_option_value(option)# transaction api@_open_onlydefundoable_transactions(self,ueid=None,**actionfilters):"""Return a list of undoable transaction objects by the connection's user, ordered by descendant transaction time. Managers may filter according to user (eid) who has done the transaction using the `ueid` argument. Others will only see their own transactions. Additional filtering capabilities is provided by using the following named arguments: * `etype` to get only transactions creating/updating/deleting entities of the given type * `eid` to get only transactions applied to entity of the given eid * `action` to get only transactions doing the given action (action in 'C', 'U', 'D', 'A', 'R'). If `etype`, action can only be 'C', 'U' or 'D'. * `public`: when additional filtering is provided, they are by default only searched in 'public' actions, unless a `public` argument is given and set to false. """returnself.repo.system_source.undoable_transactions(self,ueid,**actionfilters)@_open_onlydeftransaction_info(self,txuuid):"""Return transaction object for the given uid. raise `NoSuchTransaction` if not found or if session's user is not allowed (eg not in managers group and the transaction doesn't belong to him). """returnself.repo.system_source.tx_info(self,txuuid)@_open_onlydeftransaction_actions(self,txuuid,public=True):"""Return an ordered list of actions effectued during that transaction. If public is true, return only 'public' actions, i.e. not ones triggered under the cover by hooks, else return all actions. raise `NoSuchTransaction` if the transaction is not found or if the user is not allowed (eg not in managers group). """returnself.repo.system_source.tx_actions(self,txuuid,public)@_open_onlydefundo_transaction(self,txuuid):"""Undo the given transaction. Return potential restoration errors. raise `NoSuchTransaction` if not found or if user is not allowed (eg not in managers group). """returnself.repo.system_source.undo_transaction(self,txuuid)# life cycle handling ####################################################def__enter__(self):assertself._openisNone# first openingself._open=Trueself.cnxset=self.repo._get_cnxset()returnselfdef__exit__(self,exctype=None,excvalue=None,tb=None):assertself._open# actually already openself.rollback()self._open=Falseself.cnxset.cnxset_freed()self.repo._free_cnxset(self.cnxset)self.cnxset=None@contextmanagerdefrunning_hooks_ops(self):"""this context manager should be called whenever hooks or operations are about to be run (but after hook selection) It will help the undo logic record pertinent metadata or some hooks to run (or not) depending on who/what issued the query. """prevmode=self.hooks_in_progressself.hooks_in_progress=Trueyieldself.hooks_in_progress=prevmode# shared data handling ###################################################@propertydefdata(self):returnself._session_data@propertydefrql_rewriter(self):returnself._rewriter@_open_only@deprecated('[3.19] use session or transaction data',stacklevel=3)defget_shared_data(self,key,default=None,pop=False,txdata=False):"""return value associated to `key` in session data"""iftxdata:data=self.transaction_dataelse:data=self._session_dataifpop:returndata.pop(key,default)else:returndata.get(key,default)@_open_only@deprecated('[3.19] use session or transaction data',stacklevel=3)defset_shared_data(self,key,value,txdata=False):"""set value associated to `key` in session data"""iftxdata:self.transaction_data[key]=valueelse:self._session_data[key]=valuedefclear(self):"""reset internal data"""self.transaction_data={}#: ordered list of operations to be processed on commit/rollbackself.pending_operations=[]#: (None, 'precommit', 'postcommit', 'uncommitable')self.commit_state=Noneself.pruned_hooks_cache={}self.local_perm_cache.clear()self.rewriter=RQLRewriter(self)@deprecated('[3.19] cnxset are automatically managed now.'' stop using explicit set and free.')defset_cnxset(self):pass@deprecated('[3.19] cnxset are automatically managed now.'' stop using explicit set and free.')deffree_cnxset(self,ignoremode=False):pass@property@contextmanager@_open_only@deprecated('[3.21] a cnxset is automatically set on __enter__ call now.'' stop using .ensure_cnx_set')defensure_cnx_set(self):yield@propertydefanonymous_connection(self):returnself.session.anonymous_session# Entity cache management ################################################### The connection entity cache as held in cnx.transaction_data is removed at the# end of the connection (commit and rollback)## XXX connection level caching may be a pb with multiple repository# instances, but 1. this is probably not the only one :$ and 2. it may be# an acceptable risk. Anyway we could activate it or not according to a# configuration optiondefset_entity_cache(self,entity):"""Add `entity` to the connection entity cache"""# XXX not using _open_only because before at creation time. _set_user# call this function to cache the Connection user.ifentity.cw_etype!='CWUser'andnotself._open:raiseProgrammingError('Closed Connection: %s'%self.connectionid)ecache=self.transaction_data.setdefault('ecache',{})ecache.setdefault(entity.eid,entity)@_open_onlydefentity_cache(self,eid):"""get cache entity for `eid`"""returnself.transaction_data['ecache'][eid]@_open_onlydefcached_entities(self):"""return the whole entity cache"""returnself.transaction_data.get('ecache',{}).values()@_open_onlydefdrop_entity_cache(self,eid=None):"""drop entity from the cache If eid is None, the whole cache is dropped"""ifeidisNone:self.transaction_data.pop('ecache',None)else:delself.transaction_data['ecache'][eid]# relations handling #######################################################@_open_onlydefadd_relation(self,fromeid,rtype,toeid):"""provide direct access to the repository method to add a relation. This is equivalent to the following rql query: SET X rtype Y WHERE X eid fromeid, T eid toeid without read security check but also all the burden of rql execution. You may use this in hooks when you know both eids of the relation you want to add. """self.add_relations([(rtype,[(fromeid,toeid)])])@_open_onlydefadd_relations(self,relations):'''set many relation using a shortcut similar to the one in add_relation relations is a list of 2-uples, the first element of each 2-uple is the rtype, and the second is a list of (fromeid, toeid) tuples '''edited_entities={}relations_dict={}withself.security_enabled(False,False):forrtype,eidsinrelations:ifself.vreg.schema[rtype].inlined:forfromeid,toeidineids:iffromeidnotinedited_entities:entity=self.entity_from_eid(fromeid)edited=EditedEntity(entity)edited_entities[fromeid]=editedelse:edited=edited_entities[fromeid]edited.edited_attribute(rtype,toeid)else:relations_dict[rtype]=eidsself.repo.glob_add_relations(self,relations_dict)foreditedinedited_entities.values():self.repo.glob_update_entity(self,edited)@_open_onlydefdelete_relation(self,fromeid,rtype,toeid):"""provide direct access to the repository method to delete a relation. This is equivalent to the following rql query: DELETE X rtype Y WHERE X eid fromeid, T eid toeid without read security check but also all the burden of rql execution. You may use this in hooks when you know both eids of the relation you want to delete. """withself.security_enabled(False,False):ifself.vreg.schema[rtype].inlined:entity=self.entity_from_eid(fromeid)entity.cw_attr_cache[rtype]=Noneself.repo.glob_update_entity(self,entity,set((rtype,)))else:self.repo.glob_delete_relation(self,fromeid,rtype,toeid)# relations cache handling #################################################@_open_onlydefupdate_rel_cache_add(self,subject,rtype,object,symmetric=False):self._update_entity_rel_cache_add(subject,rtype,'subject',object)ifsymmetric:self._update_entity_rel_cache_add(object,rtype,'subject',subject)else:self._update_entity_rel_cache_add(object,rtype,'object',subject)@_open_onlydefupdate_rel_cache_del(self,subject,rtype,object,symmetric=False):self._update_entity_rel_cache_del(subject,rtype,'subject',object)ifsymmetric:self._update_entity_rel_cache_del(object,rtype,'object',object)else:self._update_entity_rel_cache_del(object,rtype,'object',subject)@_open_onlydef_update_entity_rel_cache_add(self,eid,rtype,role,targeteid):try:entity=self.entity_cache(eid)exceptKeyError:returnrcache=entity.cw_relation_cached(rtype,role)ifrcacheisnotNone:rset,entities=rcacherset=rset.copy()entities=list(entities)rset.rows.append([targeteid])ifnotisinstance(rset.description,list):# else description not setrset.description=list(rset.description)rset.description.append([self.entity_metas(targeteid)['type']])targetentity=self.entity_from_eid(targeteid)iftargetentity.cw_rsetisNone:targetentity.cw_rset=rsettargetentity.cw_row=rset.rowcounttargetentity.cw_col=0rset.rowcount+=1entities.append(targetentity)entity._cw_related_cache['%s_%s'%(rtype,role)]=(rset,tuple(entities))@_open_onlydef_update_entity_rel_cache_del(self,eid,rtype,role,targeteid):try:entity=self.entity_cache(eid)exceptKeyError:returnrcache=entity.cw_relation_cached(rtype,role)ifrcacheisnotNone:rset,entities=rcacheforidx,rowinenumerate(rset.rows):ifrow[0]==targeteid:breakelse:# this may occurs if the cache has been filed by a hook# after the database updateself.debug('cache inconsistency for %s%s%s%s',eid,rtype,role,targeteid)returnrset=rset.copy()entities=list(entities)delrset.rows[idx]ifisinstance(rset.description,list):# else description not setdelrset.description[idx]delentities[idx]rset.rowcount-=1entity._cw_related_cache['%s_%s'%(rtype,role)]=(rset,tuple(entities))# Tracking of entities added of removed in the transaction ##################@_open_onlydefdeleted_in_transaction(self,eid):"""return True if the entity of the given eid is being deleted in the current transaction """returneidinself.transaction_data.get('pendingeids',())@_open_onlydefadded_in_transaction(self,eid):"""return True if the entity of the given eid is being created in the current transaction """returneidinself.transaction_data.get('neweids',())# Operation management ####################################################@_open_onlydefadd_operation(self,operation,index=None):"""add an operation to be executed at the end of the transaction"""ifindexisNone:self.pending_operations.append(operation)else:self.pending_operations.insert(index,operation)# Hooks control ###########################################################@_open_onlydefallow_all_hooks_but(self,*categories):return_hooks_control(self,HOOKS_ALLOW_ALL,*categories)@_open_onlydefdeny_all_hooks_but(self,*categories):return_hooks_control(self,HOOKS_DENY_ALL,*categories)@_open_onlydefdisable_hook_categories(self,*categories):"""disable the given hook categories: - on HOOKS_DENY_ALL mode, ensure those categories are not enabled - on HOOKS_ALLOW_ALL mode, ensure those categories are disabled """changes=set()self.pruned_hooks_cache.clear()categories=set(categories)ifself.hooks_modeisHOOKS_DENY_ALL:enabledcats=self.enabled_hook_catschanges=enabledcats&categoriesenabledcats-=changes# changes is small hence fasterelse:disabledcats=self.disabled_hook_catschanges=categories-disabledcatsdisabledcats|=changes# changes is small hence fasterreturntuple(changes)@_open_onlydefenable_hook_categories(self,*categories):"""enable the given hook categories: - on HOOKS_DENY_ALL mode, ensure those categories are enabled - on HOOKS_ALLOW_ALL mode, ensure those categories are not disabled """changes=set()self.pruned_hooks_cache.clear()categories=set(categories)ifself.hooks_modeisHOOKS_DENY_ALL:enabledcats=self.enabled_hook_catschanges=categories-enabledcatsenabledcats|=changes# changes is small hence fasterelse:disabledcats=self.disabled_hook_catschanges=disabledcats&categoriesdisabledcats-=changes# changes is small hence fasterreturntuple(changes)@_open_onlydefis_hook_category_activated(self,category):"""return a boolean telling if the given category is currently activated or not """ifself.hooks_modeisHOOKS_DENY_ALL:returncategoryinself.enabled_hook_catsreturncategorynotinself.disabled_hook_cats@_open_onlydefis_hook_activated(self,hook):"""return a boolean telling if the given hook class is currently activated or not """returnself.is_hook_category_activated(hook.category)# Security management #####################################################@_open_onlydefsecurity_enabled(self,read=None,write=None):return_security_enabled(self,read=read,write=write)@property@_open_onlydefread_security(self):returnself._read_security@read_security.setter@_open_onlydefread_security(self,activated):self._read_security=activated# undo support ############################################################@_open_onlydefertype_supports_undo(self,ertype):returnself.undo_actionsandertypenotinNO_UNDO_TYPES@_open_onlydeftransaction_uuid(self,set=True):uuid=self.transaction_data.get('tx_uuid')ifsetanduuidisNone:self.transaction_data['tx_uuid']=uuid=text_type(uuid4().hex)self.repo.system_source.start_undoable_transaction(self,uuid)returnuuid@_open_onlydeftransaction_inc_action_counter(self):num=self.transaction_data.setdefault('tx_action_count',0)+1self.transaction_data['tx_action_count']=numreturnnum# db-api like interface ###################################################@_open_onlydefsource_defs(self):returnself.repo.source_defs()@deprecated('[3.19] use .entity_metas(eid) instead')@_open_onlydefdescribe(self,eid,asdict=False):"""return a tuple (type, sourceuri, extid) for the entity with id <eid>"""etype,extid,source=self.repo.type_and_source_from_eid(eid,self)metas={'type':etype,'source':source,'extid':extid}ifasdict:metas['asource']=metas['source']# XXX pre 3.19 client compatreturnmetasreturnetype,source,extid@_open_onlydefentity_metas(self,eid):"""return a tuple (type, sourceuri, extid) for the entity with id <eid>"""etype,extid,source=self.repo.type_and_source_from_eid(eid,self)return{'type':etype,'source':source,'extid':extid}# core method #############################################################@_open_onlydefexecute(self,rql,kwargs=None,build_descr=True):"""db-api like method directly linked to the querier execute method. See :meth:`cubicweb.dbapi.Cursor.execute` documentation. """self._session_timestamp.touch()rset=self._execute(self,rql,kwargs,build_descr)rset.req=selfself._session_timestamp.touch()returnrset@_open_onlydefrollback(self,free_cnxset=None,reset_pool=None):"""rollback the current transaction"""iffree_cnxsetisnotNone:warn('[3.21] free_cnxset is now unneeded',DeprecationWarning,stacklevel=2)ifreset_poolisnotNone:warn('[3.13] reset_pool is now unneeded',DeprecationWarning,stacklevel=2)cnxset=self.cnxsetassertcnxsetisnotNonetry:# by default, operations are executed with security turned offwithself.security_enabled(False,False):whileself.pending_operations:try:operation=self.pending_operations.pop(0)operation.handle_event('rollback_event')exceptBaseException:self.critical('rollback error',exc_info=sys.exc_info())continuecnxset.rollback()self.debug('rollback for transaction %s done',self.connectionid)finally:self._session_timestamp.touch()self.clear()@_open_onlydefcommit(self,free_cnxset=None,reset_pool=None):"""commit the current session's transaction"""iffree_cnxsetisnotNone:warn('[3.21] free_cnxset is now unneeded',DeprecationWarning,stacklevel=2)ifreset_poolisnotNone:warn('[3.13] reset_pool is now unneeded',DeprecationWarning,stacklevel=2)assertself.cnxsetisnotNonecstate=self.commit_stateifcstate=='uncommitable':raiseQueryError('transaction must be rolled back')ifcstate=='precommit':self.warn('calling commit in precommit makes no sense; ignoring commit')returnifcstate=='postcommit':self.critical('postcommit phase is not allowed to write to the db; ignoring commit')returnassertcstateisNone# on rollback, an operation should have the following state# information:# - processed by the precommit/commit event or not# - if processed, is it the failed operationdebug=server.DEBUG&server.DBG_OPStry:# by default, operations are executed with security turned offwithself.security_enabled(False,False):processed=[]self.commit_state='precommit'ifdebug:print(self.commit_state,'*'*20)try:withself.running_hooks_ops():whileself.pending_operations:operation=self.pending_operations.pop(0)operation.processed='precommit'processed.append(operation)ifdebug:print(operation)operation.handle_event('precommit_event')self.pending_operations[:]=processedself.debug('precommit transaction %s done',self.connectionid)exceptBaseException:# if error on [pre]commit:## * set .failed = True on the operation causing the failure# * call revert<event>_event on processed operations# * call rollback_event on *all* operations## that seems more natural than not calling rollback_event# for processed operations, and allow generic rollback# instead of having to implements rollback, revertprecommit# and revertcommit, that will be enough in mont case.operation.failed=Trueifdebug:print(self.commit_state,'*'*20)withself.running_hooks_ops():foroperationinreversed(processed):ifdebug:print(operation)try:operation.handle_event('revertprecommit_event')exceptBaseException:self.critical('error while reverting precommit',exc_info=True)# XXX use slice notation since self.pending_operations is a# read-only property.self.pending_operations[:]=processed+self.pending_operationsself.rollback()raiseself.cnxset.commit()self.commit_state='postcommit'ifdebug:print(self.commit_state,'*'*20)withself.running_hooks_ops():whileself.pending_operations:operation=self.pending_operations.pop(0)ifdebug:print(operation)operation.processed='postcommit'try:operation.handle_event('postcommit_event')exceptBaseException:self.critical('error while postcommit',exc_info=sys.exc_info())self.debug('postcommit transaction %s done',self.connectionid)returnself.transaction_uuid(set=False)finally:self._session_timestamp.touch()self.clear()# resource accessors ######################################################@_open_onlydefcall_service(self,regid,**kwargs):self.debug('calling service %s',regid)service=self.vreg['services'].select(regid,self,**kwargs)returnservice.call(**kwargs)@_open_onlydefsystem_sql(self,sql,args=None,rollback_on_failure=True):"""return a sql cursor on the system database"""source=self.repo.system_sourcetry:returnsource.doexec(self,sql,args,rollback=rollback_on_failure)except(source.OperationalError,source.InterfaceError):ifnotrollback_on_failure:raisesource.warning("trying to reconnect")self.cnxset.reconnect()returnsource.doexec(self,sql,args,rollback=rollback_on_failure)@_open_onlydefrtype_eids_rdef(self,rtype,eidfrom,eidto):# use type_and_source_from_eid instead of type_from_eid for optimization# (avoid two extra methods call)subjtype=self.repo.type_and_source_from_eid(eidfrom,self)[0]objtype=self.repo.type_and_source_from_eid(eidto,self)[0]returnself.vreg.schema.rschema(rtype).rdefs[(subjtype,objtype)]defcnx_attr(attr_name,writable=False):"""return a property to forward attribute access to connection. This is to be used by session"""args={}@deprecated('[3.19] use a Connection object instead')defattr_from_cnx(session):returngetattr(session._cnx,attr_name)args['fget']=attr_from_cnxifwritable:@deprecated('[3.19] use a Connection object instead')defwrite_attr(session,value):returnsetattr(session._cnx,attr_name,value)args['fset']=write_attrreturnproperty(**args)classTimestamp(object):def__init__(self):self.value=time()deftouch(self):self.value=time()def__float__(self):returnfloat(self.value)classSession(object):"""Repository user session This ties all together: * session id, * user, * other session data. """def__init__(self,user,repo,cnxprops=None,_id=None):self.sessionid=_idormake_uid(unormalize(user.login))self.user=user# XXX repoapi: deprecated and store only a login.self.repo=repoself.vreg=repo.vregself._timestamp=Timestamp()self.data={}self.closed=Falsedefclose(self):self.closed=Truedef__enter__(self):returnselfdef__exit__(self,*args):passdef__unicode__(self):return'<session %s (%s 0x%x)>'%(unicode(self.user.login),self.sessionid,id(self))@propertydeftimestamp(self):returnfloat(self._timestamp)@property@deprecated('[3.19] session.id is deprecated, use session.sessionid')defid(self):returnself.sessionid@propertydeflogin(self):returnself.user.logindefnew_cnx(self):"""Return a new Connection object linked to the session The returned Connection will *not* be managed by the Session. """returnConnection(self)@deprecated('[3.19] use a Connection object instead')defget_option_value(self,option,foreid=None):ifforeidisnotNone:warn('[3.19] foreid argument is deprecated',DeprecationWarning,stacklevel=2)returnself.repo.get_option_value(option)def_touch(self):"""update latest session usage timestamp and reset mode to read"""self._timestamp.touch()local_perm_cache=cnx_attr('local_perm_cache')@local_perm_cache.setterdeflocal_perm_cache(self,value):#base class assign an empty dict:-(assertvalue=={}pass# deprecated ###############################################################@propertydefanonymous_session(self):# XXX for now, anonymous_user only exists in webconfig (and testconfig).# It will only be present inside all-in-one instance.# there is plan to move it down to global config.ifnothasattr(self.repo.config,'anonymous_user'):# not a web or test config, no anonymous userreturnFalsereturnself.user.login==self.repo.config.anonymous_user()[0]@deprecated('[3.13] use getattr(session.rtype_eids_rdef(rtype, eidfrom, eidto), prop)')defschema_rproperty(self,rtype,eidfrom,eidto,rprop):returngetattr(self.rtype_eids_rdef(rtype,eidfrom,eidto),rprop)# these are overridden by set_log_methods below# only defining here to prevent pylint from complaininginfo=warning=error=critical=exception=debug=lambdamsg,*a,**kw:NoneclassInternalManager(object):"""a manager user with all access rights used internally for task such as bootstrapping the repository or creating regular users according to repository content """def__init__(self,lang='en'):self.eid=-1self.login=u'__internal_manager__'self.properties={}self.groups=set(['managers'])self.lang=langdefmatching_groups(self,groups):return1defis_in_group(self,group):returnTruedefowns(self,eid):returnTruedefproperty_value(self,key):ifkey=='ui.language':returnself.langreturnNonedefprefered_language(self,language=None):# mock CWUser.prefered_language, mainly for testing purposereturnself.property_value('ui.language')# CWUser compat for notification ###########################################defname(self):return'cubicweb'class_IEmailable:@staticmethoddefget_email():return''defcw_adapt_to(self,iface):ififace=='IEmailable':returnself._IEmailablereturnNonefromloggingimportgetLoggerfromcubicwebimportset_log_methodsset_log_methods(Session,getLogger('cubicweb.session'))set_log_methods(Connection,getLogger('cubicweb.session'))