server/session.py
changeset 4835 13b0b96d7982
parent 4834 b718626a0e60
child 4843 5f7363416765
equal deleted inserted replaced
4834:b718626a0e60 4835:13b0b96d7982
     3 :organization: Logilab
     3 :organization: Logilab
     4 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     4 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     7 """
     7 """
       
     8 from __future__ import with_statement
       
     9 
     8 __docformat__ = "restructuredtext en"
    10 __docformat__ = "restructuredtext en"
     9 
    11 
    10 import sys
    12 import sys
    11 import threading
    13 import threading
    12 from time import time
    14 from time import time
    69                 self.session.disable_hooks_category(*self.changes)
    71                 self.session.disable_hooks_category(*self.changes)
    70             else:
    72             else:
    71                 self.session.enable_hooks_category(*self.changes)
    73                 self.session.enable_hooks_category(*self.changes)
    72         self.session.set_hooks_mode(self.oldmode)
    74         self.session.set_hooks_mode(self.oldmode)
    73 
    75 
       
    76 INDENT = ''
       
    77 class security_enabled(object):
       
    78     """context manager to control security w/ session.execute, since by
       
    79     default security is disabled on queries executed on the repository
       
    80     side.
       
    81     """
       
    82     def __init__(self, session, read=None, write=None):
       
    83         self.session = session
       
    84         self.read = read
       
    85         self.write = write
       
    86 
       
    87     def __enter__(self):
       
    88 #        global INDENT
       
    89         if self.read is not None:
       
    90             self.oldread = self.session.set_read_security(self.read)
       
    91 #            print INDENT + 'read', self.read, self.oldread
       
    92         if self.write is not None:
       
    93             self.oldwrite = self.session.set_write_security(self.write)
       
    94 #            print INDENT + 'write', self.write, self.oldwrite
       
    95 #        INDENT += '  '
       
    96 
       
    97     def __exit__(self, exctype, exc, traceback):
       
    98 #        global INDENT
       
    99 #        INDENT = INDENT[:-2]
       
   100         if self.read is not None:
       
   101             self.session.set_read_security(self.oldread)
       
   102 #            print INDENT + 'reset read to', self.oldread
       
   103         if self.write is not None:
       
   104             self.session.set_write_security(self.oldwrite)
       
   105 #            print INDENT + 'reset write to', self.oldwrite
       
   106 
    74 
   107 
    75 
   108 
    76 class Session(RequestSessionBase):
   109 class Session(RequestSessionBase):
    77     """tie session id, user, connections pool and other session data all
   110     """tie session id, user, connections pool and other session data all
    78     together
   111     together
    86         self.repo = repo
   119         self.repo = repo
    87         self.cnxtype = cnxprops.cnxtype
   120         self.cnxtype = cnxprops.cnxtype
    88         self.creation = time()
   121         self.creation = time()
    89         self.timestamp = self.creation
   122         self.timestamp = self.creation
    90         self.is_internal_session = False
   123         self.is_internal_session = False
    91         self.is_super_session = False
       
    92         self.default_mode = 'read'
   124         self.default_mode = 'read'
    93         # short cut to querier .execute method
   125         # short cut to querier .execute method
    94         self._execute = repo.querier.execute
   126         self._execute = repo.querier.execute
    95         # shared data, used to communicate extra information between the client
   127         # shared data, used to communicate extra information between the client
    96         # and the rql server
   128         # and the rql server
   107             self.cnxtype, unicode(self.user.login), self.id, id(self))
   139             self.cnxtype, unicode(self.user.login), self.id, id(self))
   108 
   140 
   109     def hijack_user(self, user):
   141     def hijack_user(self, user):
   110         """return a fake request/session using specified user"""
   142         """return a fake request/session using specified user"""
   111         session = Session(user, self.repo)
   143         session = Session(user, self.repo)
   112         session._threaddata = self.actual_session()._threaddata
   144         session._threaddata.pool = pool
   113         return session
   145         return session
   114 
       
   115     def _super_call(self, __cb, *args, **kwargs):
       
   116         if self.is_super_session:
       
   117             __cb(self, *args, **kwargs)
       
   118             return
       
   119         self.is_super_session = True
       
   120         try:
       
   121             __cb(self, *args, **kwargs)
       
   122         finally:
       
   123             self.is_super_session = False
       
   124 
   146 
   125     def add_relation(self, fromeid, rtype, toeid):
   147     def add_relation(self, fromeid, rtype, toeid):
   126         """provide direct access to the repository method to add a relation.
   148         """provide direct access to the repository method to add a relation.
   127 
   149 
   128         This is equivalent to the following rql query:
   150         This is equivalent to the following rql query:
   131 
   153 
   132         without read security check but also all the burden of rql execution.
   154         without read security check but also all the burden of rql execution.
   133         You may use this in hooks when you know both eids of the relation you
   155         You may use this in hooks when you know both eids of the relation you
   134         want to add.
   156         want to add.
   135         """
   157         """
   136         if self.vreg.schema[rtype].inlined:
   158         with security_enabled(self, False, False):
   137             entity = self.entity_from_eid(fromeid)
   159             if self.vreg.schema[rtype].inlined:
   138             entity[rtype] = toeid
   160                 entity = self.entity_from_eid(fromeid)
   139             self._super_call(self.repo.glob_update_entity,
   161                 entity[rtype] = toeid
   140                              entity, set((rtype,)))
   162                 self.repo.glob_update_entity(self, entity, set((rtype,)))
   141         else:
   163             else:
   142             self._super_call(self.repo.glob_add_relation,
   164                 self.repo.glob_add_relation(self, fromeid, rtype, toeid)
   143                              fromeid, rtype, toeid)
       
   144 
   165 
   145     def delete_relation(self, fromeid, rtype, toeid):
   166     def delete_relation(self, fromeid, rtype, toeid):
   146         """provide direct access to the repository method to delete a relation.
   167         """provide direct access to the repository method to delete a relation.
   147 
   168 
   148         This is equivalent to the following rql query:
   169         This is equivalent to the following rql query:
   151 
   172 
   152         without read security check but also all the burden of rql execution.
   173         without read security check but also all the burden of rql execution.
   153         You may use this in hooks when you know both eids of the relation you
   174         You may use this in hooks when you know both eids of the relation you
   154         want to delete.
   175         want to delete.
   155         """
   176         """
   156         if self.vreg.schema[rtype].inlined:
   177         with security_enabled(self, False, False):
   157             entity = self.entity_from_eid(fromeid)
   178             if self.vreg.schema[rtype].inlined:
   158             entity[rtype] = None
   179                 entity = self.entity_from_eid(fromeid)
   159             self._super_call(self.repo.glob_update_entity,
   180                 entity[rtype] = None
   160                              entity, set((rtype,)))
   181                 self.repo.glob_update_entity(self, entity, set((rtype,)))
   161         else:
   182             else:
   162             self._super_call(self.repo.glob_delete_relation,
   183                 self.repo.glob_delete_relation(self, fromeid, rtype, toeid)
   163                              fromeid, rtype, toeid)
       
   164 
   184 
   165     # relations cache handling #################################################
   185     # relations cache handling #################################################
   166 
   186 
   167     def update_rel_cache_add(self, subject, rtype, object, symmetric=False):
   187     def update_rel_cache_add(self, subject, rtype, object, symmetric=False):
   168         self._update_entity_rel_cache_add(subject, rtype, 'subject', object)
   188         self._update_entity_rel_cache_add(subject, rtype, 'subject', object)
   227             rset.rowcount -= 1
   247             rset.rowcount -= 1
   228             entity._related_cache['%s_%s' % (rtype, role)] = (rset, tuple(entities))
   248             entity._related_cache['%s_%s' % (rtype, role)] = (rset, tuple(entities))
   229 
   249 
   230     # resource accessors ######################################################
   250     # resource accessors ######################################################
   231 
   251 
   232     def actual_session(self):
       
   233         """return the original parent session if any, else self"""
       
   234         return self
       
   235 
       
   236     def system_sql(self, sql, args=None, rollback_on_failure=True):
   252     def system_sql(self, sql, args=None, rollback_on_failure=True):
   237         """return a sql cursor on the system database"""
   253         """return a sql cursor on the system database"""
   238         if not sql.split(None, 1)[0].upper() == 'SELECT':
   254         if not sql.split(None, 1)[0].upper() == 'SELECT':
   239             self.mode = 'write'
   255             self.mode = 'write'
   240         return self.pool.source('system').doexec(self, sql, args,
   256         return self.pool.source('system').doexec(self, sql, args,
   273         rschema = self.repo.schema[rtype]
   289         rschema = self.repo.schema[rtype]
   274         subjtype = self.describe(eidfrom)[0]
   290         subjtype = self.describe(eidfrom)[0]
   275         objtype = self.describe(eidto)[0]
   291         objtype = self.describe(eidto)[0]
   276         rdef = rschema.rdef(subjtype, objtype)
   292         rdef = rschema.rdef(subjtype, objtype)
   277         return rdef.get(rprop)
   293         return rdef.get(rprop)
       
   294 
       
   295     # security control #########################################################
       
   296 
       
   297     DEFAULT_SECURITY = object() # evaluated to true by design
       
   298 
       
   299     @property
       
   300     def read_security(self):
       
   301         """return a boolean telling if read security is activated or not"""
       
   302         try:
       
   303             return self._threaddata.read_security
       
   304         except AttributeError:
       
   305             self._threaddata.read_security = self.DEFAULT_SECURITY
       
   306             return self._threaddata.read_security
       
   307 
       
   308     def set_read_security(self, activated):
       
   309         """[de]activate read security, returning the previous value set for
       
   310         later restoration.
       
   311 
       
   312         you should usually use the `security_enabled` context manager instead
       
   313         of this to change security settings.
       
   314         """
       
   315         oldmode = self.read_security
       
   316         self._threaddata.read_security = activated
       
   317         # dbapi_query used to detect hooks triggered by a 'dbapi' query (eg not
       
   318         # issued on the session). This is tricky since we the execution model of
       
   319         # a (write) user query is:
       
   320         #
       
   321         # repository.execute (security enabled)
       
   322         #  \-> querier.execute
       
   323         #       \-> repo.glob_xxx (add/update/delete entity/relation)
       
   324         #            \-> deactivate security before calling hooks
       
   325         #                 \-> WE WANT TO CHECK QUERY NATURE HERE
       
   326         #                      \-> potentially, other calls to querier.execute
       
   327         #
       
   328         # so we can't rely on simply checking session.read_security, but
       
   329         # recalling the first transition from DEFAULT_SECURITY to something
       
   330         # else (False actually) is not perfect but should be enough
       
   331         self._threaddata.dbapi_query = oldmode is self.DEFAULT_SECURITY
       
   332         return oldmode
       
   333 
       
   334     @property
       
   335     def write_security(self):
       
   336         """return a boolean telling if write security is activated or not"""
       
   337         try:
       
   338             return self._threaddata.write_security
       
   339         except:
       
   340             self._threaddata.write_security = self.DEFAULT_SECURITY
       
   341             return self._threaddata.write_security
       
   342 
       
   343     def set_write_security(self, activated):
       
   344         """[de]activate write security, returning the previous value set for
       
   345         later restoration.
       
   346 
       
   347         you should usually use the `security_enabled` context manager instead
       
   348         of this to change security settings.
       
   349         """
       
   350         oldmode = self.write_security
       
   351         self._threaddata.write_security = activated
       
   352         return oldmode
       
   353 
       
   354     @property
       
   355     def running_dbapi_query(self):
       
   356         """return a boolean telling if it's triggered by a db-api query or by
       
   357         a session query.
       
   358 
       
   359         To be used in hooks, else may have a wrong value.
       
   360         """
       
   361         return getattr(self._threaddata, 'dbapi_query', True)
   278 
   362 
   279     # hooks activation control #################################################
   363     # hooks activation control #################################################
   280     # all hooks should be activated during normal execution
   364     # all hooks should be activated during normal execution
   281 
   365 
   282     HOOKS_ALLOW_ALL = object()
   366     HOOKS_ALLOW_ALL = object()
   503 
   587 
   504     def source_from_eid(self, eid):
   588     def source_from_eid(self, eid):
   505         """return the source where the entity with id <eid> is located"""
   589         """return the source where the entity with id <eid> is located"""
   506         return self.repo.source_from_eid(eid, self)
   590         return self.repo.source_from_eid(eid, self)
   507 
   591 
   508     def decorate_rset(self, rset, propagate=False):
   592     def decorate_rset(self, rset):
   509         rset.vreg = self.vreg
   593         rset.vreg = self.vreg
   510         rset.req = propagate and self or self.actual_session()
   594         rset.req = self
   511         return rset
   595         return rset
   512 
   596 
   513     @property
   597     def execute(self, rql, kwargs=None, eid_key=None, build_descr=True):
   514     def super_session(self):
   598         """db-api like method directly linked to the querier execute method"""
   515         try:
       
   516             csession = self.childsession
       
   517         except AttributeError:
       
   518             if isinstance(self, (ChildSession, InternalSession)):
       
   519                 csession = self
       
   520             else:
       
   521                 csession = ChildSession(self)
       
   522             self.childsession = csession
       
   523         # need shared pool set
       
   524         self.set_pool(checkclosed=False)
       
   525         return csession
       
   526 
       
   527     def unsafe_execute(self, rql, kwargs=None, eid_key=None, build_descr=True,
       
   528                        propagate=False):
       
   529         """like .execute but with security checking disabled (this method is
       
   530         internal to the server, it's not part of the db-api)
       
   531 
       
   532         if `propagate` is true, the super_session will be attached to the result
       
   533         set instead of the parent session, hence further query done through
       
   534         entities fetched from this result set will bypass security as well
       
   535         """
       
   536         return self.super_session.execute(rql, kwargs, eid_key, build_descr,
       
   537                                           propagate)
       
   538 
       
   539     def execute(self, rql, kwargs=None, eid_key=None, build_descr=True,
       
   540                 propagate=False):
       
   541         """db-api like method directly linked to the querier execute method
       
   542 
       
   543         Becare that unlike actual cursor.execute, `build_descr` default to
       
   544         false
       
   545         """
       
   546         rset = self._execute(self, rql, kwargs, eid_key, build_descr)
   599         rset = self._execute(self, rql, kwargs, eid_key, build_descr)
   547         return self.decorate_rset(rset, propagate)
   600         return self.decorate_rset(rset)
   548 
   601 
   549     def _clear_thread_data(self):
   602     def _clear_thread_data(self):
   550         """remove everything from the thread local storage, except pool
   603         """remove everything from the thread local storage, except pool
   551         which is explicitly removed by reset_pool, and mode which is set anyway
   604         which is explicitly removed by reset_pool, and mode which is set anyway
   552         by _touch
   605         by _touch
   567             self._touch()
   620             self._touch()
   568             self.debug('commit session %s done (no db activity)', self.id)
   621             self.debug('commit session %s done (no db activity)', self.id)
   569             return
   622             return
   570         if self.commit_state:
   623         if self.commit_state:
   571             return
   624             return
   572         # on rollback, an operation should have the following state
   625         # by default, operations are executed with security turned off
   573         # information:
   626         with security_enabled(self, False, False):
   574         # - processed by the precommit/commit event or not
   627             # on rollback, an operation should have the following state
   575         # - if processed, is it the failed operation
   628             # information:
   576         try:
   629             # - processed by the precommit/commit event or not
   577             for trstate in ('precommit', 'commit'):
   630             # - if processed, is it the failed operation
   578                 processed = []
   631             try:
   579                 self.commit_state = trstate
   632                 for trstate in ('precommit', 'commit'):
   580                 try:
   633                     processed = []
   581                     while self.pending_operations:
   634                     self.commit_state = trstate
   582                         operation = self.pending_operations.pop(0)
   635                     try:
   583                         operation.processed = trstate
   636                         while self.pending_operations:
   584                         processed.append(operation)
   637                             operation = self.pending_operations.pop(0)
       
   638                             operation.processed = trstate
       
   639                             processed.append(operation)
       
   640                             operation.handle_event('%s_event' % trstate)
       
   641                         self.pending_operations[:] = processed
       
   642                         self.debug('%s session %s done', trstate, self.id)
       
   643                     except:
       
   644                         self.exception('error while %sing', trstate)
       
   645                         # if error on [pre]commit:
       
   646                         #
       
   647                         # * set .failed = True on the operation causing the failure
       
   648                         # * call revert<event>_event on processed operations
       
   649                         # * call rollback_event on *all* operations
       
   650                         #
       
   651                         # that seems more natural than not calling rollback_event
       
   652                         # for processed operations, and allow generic rollback
       
   653                         # instead of having to implements rollback, revertprecommit
       
   654                         # and revertcommit, that will be enough in mont case.
       
   655                         operation.failed = True
       
   656                         for operation in processed:
       
   657                             operation.handle_event('revert%s_event' % trstate)
       
   658                         # XXX use slice notation since self.pending_operations is a
       
   659                         # read-only property.
       
   660                         self.pending_operations[:] = processed + self.pending_operations
       
   661                         self.rollback(reset_pool)
       
   662                         raise
       
   663                 self.pool.commit()
       
   664                 self.commit_state = trstate = 'postcommit'
       
   665                 while self.pending_operations:
       
   666                     operation = self.pending_operations.pop(0)
       
   667                     operation.processed = trstate
       
   668                     try:
   585                         operation.handle_event('%s_event' % trstate)
   669                         operation.handle_event('%s_event' % trstate)
   586                     self.pending_operations[:] = processed
   670                     except:
   587                     self.debug('%s session %s done', trstate, self.id)
   671                         self.critical('error while %sing', trstate,
   588                 except:
   672                                       exc_info=sys.exc_info())
   589                     self.exception('error while %sing', trstate)
   673                 self.info('%s session %s done', trstate, self.id)
   590                     # if error on [pre]commit:
   674             finally:
   591                     #
   675                 self._clear_thread_data()
   592                     # * set .failed = True on the operation causing the failure
   676                 self._touch()
   593                     # * call revert<event>_event on processed operations
   677                 if reset_pool:
   594                     # * call rollback_event on *all* operations
   678                     self.reset_pool(ignoremode=True)
   595                     #
       
   596                     # that seems more natural than not calling rollback_event
       
   597                     # for processed operations, and allow generic rollback
       
   598                     # instead of having to implements rollback, revertprecommit
       
   599                     # and revertcommit, that will be enough in mont case.
       
   600                     operation.failed = True
       
   601                     for operation in processed:
       
   602                         operation.handle_event('revert%s_event' % trstate)
       
   603                     # XXX use slice notation since self.pending_operations is a
       
   604                     # read-only property.
       
   605                     self.pending_operations[:] = processed + self.pending_operations
       
   606                     self.rollback(reset_pool)
       
   607                     raise
       
   608             self.pool.commit()
       
   609             self.commit_state = trstate = 'postcommit'
       
   610             while self.pending_operations:
       
   611                 operation = self.pending_operations.pop(0)
       
   612                 operation.processed = trstate
       
   613                 try:
       
   614                     operation.handle_event('%s_event' % trstate)
       
   615                 except:
       
   616                     self.critical('error while %sing', trstate,
       
   617                                   exc_info=sys.exc_info())
       
   618             self.info('%s session %s done', trstate, self.id)
       
   619         finally:
       
   620             self._clear_thread_data()
       
   621             self._touch()
       
   622             if reset_pool:
       
   623                 self.reset_pool(ignoremode=True)
       
   624 
   679 
   625     def rollback(self, reset_pool=True):
   680     def rollback(self, reset_pool=True):
   626         """rollback the current session's transaction"""
   681         """rollback the current session's transaction"""
   627         if self.pool is None:
   682         if self.pool is None:
   628             assert not self.pending_operations
   683             assert not self.pending_operations
   629             self._clear_thread_data()
   684             self._clear_thread_data()
   630             self._touch()
   685             self._touch()
   631             self.debug('rollback session %s done (no db activity)', self.id)
   686             self.debug('rollback session %s done (no db activity)', self.id)
   632             return
   687             return
   633         try:
   688         # by default, operations are executed with security turned off
   634             while self.pending_operations:
   689         with security_enabled(self, False, False):
   635                 try:
   690             try:
   636                     operation = self.pending_operations.pop(0)
   691                 while self.pending_operations:
   637                     operation.handle_event('rollback_event')
   692                     try:
   638                 except:
   693                         operation = self.pending_operations.pop(0)
   639                     self.critical('rollback error', exc_info=sys.exc_info())
   694                         operation.handle_event('rollback_event')
   640                     continue
   695                     except:
   641             self.pool.rollback()
   696                         self.critical('rollback error', exc_info=sys.exc_info())
   642             self.debug('rollback for session %s done', self.id)
   697                         continue
   643         finally:
   698                 self.pool.rollback()
   644             self._clear_thread_data()
   699                 self.debug('rollback for session %s done', self.id)
   645             self._touch()
   700             finally:
   646             if reset_pool:
   701                 self._clear_thread_data()
   647                 self.reset_pool(ignoremode=True)
   702                 self._touch()
       
   703                 if reset_pool:
       
   704                     self.reset_pool(ignoremode=True)
   648 
   705 
   649     def close(self):
   706     def close(self):
   650         """do not close pool on session close, since they are shared now"""
   707         """do not close pool on session close, since they are shared now"""
   651         self._closed = True
   708         self._closed = True
   652         # copy since _threads_in_transaction maybe modified while waiting
   709         # copy since _threads_in_transaction maybe modified while waiting
   765             description.append(tuple(row_descr))
   822             description.append(tuple(row_descr))
   766         return description
   823         return description
   767 
   824 
   768     # deprecated ###############################################################
   825     # deprecated ###############################################################
   769 
   826 
       
   827     @deprecated("[3.7] control security with session.[read|write]_security")
       
   828     def unsafe_execute(self, rql, kwargs=None, eid_key=None, build_descr=True,
       
   829                        propagate=False):
       
   830         """like .execute but with security checking disabled (this method is
       
   831         internal to the server, it's not part of the db-api)
       
   832         """
       
   833         return self.execute(rql, kwargs, eid_key, build_descr)
       
   834 
       
   835     @property
       
   836     @deprecated("[3.7] is_super_session is deprecated, test "
       
   837                 "session.read_security and or session.write_security")
       
   838     def is_super_session(self):
       
   839         return not self.read_security or not self.write_security
       
   840 
       
   841     @deprecated("[3.7] session is actual session")
       
   842     def actual_session(self):
       
   843         """return the original parent session if any, else self"""
       
   844         return self
       
   845 
   770     @property
   846     @property
   771     @deprecated("[3.6] use session.vreg.schema")
   847     @deprecated("[3.6] use session.vreg.schema")
   772     def schema(self):
   848     def schema(self):
   773         return self.repo.schema
   849         return self.repo.schema
   774 
   850 
   791     def entity(self, eid):
   867     def entity(self, eid):
   792         """return a result set for the given eid"""
   868         """return a result set for the given eid"""
   793         return self.entity_from_eid(eid)
   869         return self.entity_from_eid(eid)
   794 
   870 
   795 
   871 
   796 class ChildSession(Session):
       
   797     """child (or internal) session are used to hijack the security system
       
   798     """
       
   799     cnxtype = 'inmemory'
       
   800 
       
   801     def __init__(self, parent_session):
       
   802         self.id = None
       
   803         self.is_internal_session = False
       
   804         self.is_super_session = True
       
   805         # session which has created this one
       
   806         self.parent_session = parent_session
       
   807         self.user = InternalManager()
       
   808         self.user.req = self # XXX remove when "vreg = user.req.vreg" hack in entity.py is gone
       
   809         self.repo = parent_session.repo
       
   810         self.vreg = parent_session.vreg
       
   811         self.data = parent_session.data
       
   812         self.encoding = parent_session.encoding
       
   813         self.lang = parent_session.lang
       
   814         self._ = self.__ = parent_session._
       
   815         # short cut to querier .execute method
       
   816         self._execute = self.repo.querier.execute
       
   817 
       
   818     @property
       
   819     def super_session(self):
       
   820         return self
       
   821 
       
   822     @property
       
   823     def hooks_mode(self):
       
   824         return self.parent_session.hooks_mode
       
   825     def set_hooks_mode(self, mode):
       
   826         return self.parent_session.set_hooks_mode(mode)
       
   827 
       
   828     @property
       
   829     def disabled_hooks_categories(self):
       
   830         return self.parent_session.disabled_hooks_categories
       
   831 
       
   832     @property
       
   833     def enabled_hooks_categories(self):
       
   834         return self.parent_session.enabled_hooks_categories
       
   835 
       
   836 
       
   837     def get_mode(self):
       
   838         return self.parent_session.mode
       
   839     def set_mode(self, value):
       
   840         self.parent_session.set_mode(value)
       
   841     mode = property(get_mode, set_mode)
       
   842 
       
   843     def get_commit_state(self):
       
   844         return self.parent_session.commit_state
       
   845     def set_commit_state(self, value):
       
   846         self.parent_session.set_commit_state(value)
       
   847     commit_state = property(get_commit_state, set_commit_state)
       
   848 
       
   849     @property
       
   850     def pool(self):
       
   851         return self.parent_session.pool
       
   852     @property
       
   853     def pending_operations(self):
       
   854         return self.parent_session.pending_operations
       
   855     @property
       
   856     def transaction_data(self):
       
   857         return self.parent_session.transaction_data
       
   858 
       
   859     def set_pool(self):
       
   860         """the session need a pool to execute some queries"""
       
   861         self.parent_session.set_pool()
       
   862 
       
   863     def reset_pool(self):
       
   864         """the session has no longer using its pool, at least for some time
       
   865         """
       
   866         self.parent_session.reset_pool()
       
   867 
       
   868     def actual_session(self):
       
   869         """return the original parent session if any, else self"""
       
   870         return self.parent_session
       
   871 
       
   872     def commit(self, reset_pool=True):
       
   873         """commit the current session's transaction"""
       
   874         self.parent_session.commit(reset_pool)
       
   875 
       
   876     def rollback(self, reset_pool=True):
       
   877         """rollback the current session's transaction"""
       
   878         self.parent_session.rollback(reset_pool)
       
   879 
       
   880     def close(self):
       
   881         """do not close pool on session close, since they are shared now"""
       
   882         self.rollback()
       
   883 
       
   884     def user_data(self):
       
   885         """returns a dictionnary with this user's information"""
       
   886         return self.parent_session.user_data()
       
   887 
       
   888 
       
   889 class InternalSession(Session):
   872 class InternalSession(Session):
   890     """special session created internaly by the repository"""
   873     """special session created internaly by the repository"""
   891 
   874 
   892     def __init__(self, repo, cnxprops=None):
   875     def __init__(self, repo, cnxprops=None):
   893         super(InternalSession, self).__init__(InternalManager(), repo, cnxprops,
   876         super(InternalSession, self).__init__(InternalManager(), repo, cnxprops,
   894                                               _id='internal')
   877                                               _id='internal')
   895         self.user.req = self # XXX remove when "vreg = user.req.vreg" hack in entity.py is gone
   878         self.user.req = self # XXX remove when "vreg = user.req.vreg" hack in entity.py is gone
   896         self.cnxtype = 'inmemory'
   879         self.cnxtype = 'inmemory'
   897         self.is_internal_session = True
   880         self.is_internal_session = True
   898         self.is_super_session = True
   881         self.disable_hooks_category('integrity')
   899 
       
   900     @property
       
   901     def super_session(self):
       
   902         return self
       
   903 
   882 
   904 
   883 
   905 class InternalManager(object):
   884 class InternalManager(object):
   906     """a manager user with all access rights used internally for task such as
   885     """a manager user with all access rights used internally for task such as
   907     bootstrapping the repository or creating regular users according to
   886     bootstrapping the repository or creating regular users according to