server/session.py
branchtls-sprint
changeset 1660 d1030dd9730b
parent 1263 01152fffd593
child 1880 293fe4b49e28
equal deleted inserted replaced
1659:838eaecaa458 1660:d1030dd9730b
    11 from time import time
    11 from time import time
    12 
    12 
    13 from rql.nodes import VariableRef, Function, ETYPE_PYOBJ_MAP, etype_from_pyobj
    13 from rql.nodes import VariableRef, Function, ETYPE_PYOBJ_MAP, etype_from_pyobj
    14 from yams import BASE_TYPES
    14 from yams import BASE_TYPES
    15 
    15 
    16 from cubicweb import RequestSessionMixIn, Binary
    16 from cubicweb import RequestSessionMixIn, Binary, UnknownEid
    17 from cubicweb.dbapi import ConnectionProperties
    17 from cubicweb.dbapi import ConnectionProperties
    18 from cubicweb.utils import make_uid
    18 from cubicweb.utils import make_uid
    19 from cubicweb.server.rqlrewrite import RQLRewriter
    19 from cubicweb.server.rqlrewrite import RQLRewriter
    20 
    20 
    21 ETYPE_PYOBJ_MAP[Binary] = 'Bytes'
    21 ETYPE_PYOBJ_MAP[Binary] = 'Bytes'
    27             etype = variable.get_type(sol, args)
    27             etype = variable.get_type(sol, args)
    28             if etype is None:
    28             if etype is None:
    29                 continue
    29                 continue
    30             if etype in BASE_TYPES:
    30             if etype in BASE_TYPES:
    31                 return True
    31                 return True
    32             return False   
    32             return False
    33 
    33 
    34 def _make_description(selected, args, solution):
    34 def _make_description(selected, args, solution):
    35     """return a description for a result set"""
    35     """return a description for a result set"""
    36     description = []
    36     description = []
    37     for term in selected:
    37     for term in selected:
    43 
    43 
    44 class Session(RequestSessionMixIn):
    44 class Session(RequestSessionMixIn):
    45     """tie session id, user, connections pool and other session data all
    45     """tie session id, user, connections pool and other session data all
    46     together
    46     together
    47     """
    47     """
    48     
    48 
    49     def __init__(self, user, repo, cnxprops=None, _id=None):
    49     def __init__(self, user, repo, cnxprops=None, _id=None):
    50         super(Session, self).__init__(repo.vreg)
    50         super(Session, self).__init__(repo.vreg)
    51         self.id = _id or make_uid(user.login.encode('UTF8'))
    51         self.id = _id or make_uid(user.login.encode('UTF8'))
    52         cnxprops = cnxprops or ConnectionProperties('inmemory')
    52         cnxprops = cnxprops or ConnectionProperties('inmemory')
    53         self.user = user
    53         self.user = user
    63         # and the rql server
    63         # and the rql server
    64         self.data = {}
    64         self.data = {}
    65         # i18n initialization
    65         # i18n initialization
    66         self.set_language(cnxprops.lang)
    66         self.set_language(cnxprops.lang)
    67         self._threaddata = threading.local()
    67         self._threaddata = threading.local()
    68         
    68 
    69     def get_mode(self):
    69     def get_mode(self):
    70         return getattr(self._threaddata, 'mode', 'read')
    70         return getattr(self._threaddata, 'mode', 'read')
    71     def set_mode(self, value):
    71     def set_mode(self, value):
    72         self._threaddata.mode = value
    72         self._threaddata.mode = value
    73     # transaction mode (read/write), resetted to read on commit / rollback
    73     # transaction mode (read/write), resetted to read on commit / rollback
    76     def get_commit_state(self):
    76     def get_commit_state(self):
    77         return getattr(self._threaddata, 'commit_state', None)
    77         return getattr(self._threaddata, 'commit_state', None)
    78     def set_commit_state(self, value):
    78     def set_commit_state(self, value):
    79         self._threaddata.commit_state = value
    79         self._threaddata.commit_state = value
    80     commit_state = property(get_commit_state, set_commit_state)
    80     commit_state = property(get_commit_state, set_commit_state)
    81     
    81 
    82     # set according to transaction mode for each query
    82     # set according to transaction mode for each query
    83     @property
    83     @property
    84     def pool(self):
    84     def pool(self):
    85         return getattr(self._threaddata, 'pool', None)
    85         return getattr(self._threaddata, 'pool', None)
    86     
    86 
    87     # pending transaction operations
    87     # pending transaction operations
    88     @property
    88     @property
    89     def pending_operations(self):
    89     def pending_operations(self):
    90         try:
    90         try:
    91             return self._threaddata.pending_operations
    91             return self._threaddata.pending_operations
    92         except AttributeError:
    92         except AttributeError:
    93             self._threaddata.pending_operations = []
    93             self._threaddata.pending_operations = []
    94             return self._threaddata.pending_operations
    94             return self._threaddata.pending_operations
    95     
    95 
    96     # rql rewriter
    96     # rql rewriter
    97     @property
    97     @property
    98     def rql_rewriter(self):
    98     def rql_rewriter(self):
    99         try:
    99         try:
   100             return self._threaddata._rewriter
   100             return self._threaddata._rewriter
   101         except AttributeError:
   101         except AttributeError:
   102             self._threaddata._rewriter = RQLRewriter(self.repo.querier, self)
   102             self._threaddata._rewriter = RQLRewriter(self.repo.querier, self)
   103             return self._threaddata._rewriter
   103             return self._threaddata._rewriter
   104     
   104 
   105     # transaction queries data
   105     # transaction queries data
   106     @property
   106     @property
   107     def _query_data(self):
   107     def _query_data(self):
   108         try:
   108         try:
   109             return self._threaddata._query_data
   109             return self._threaddata._query_data
   110         except AttributeError:
   110         except AttributeError:
   111             self._threaddata._query_data = {}
   111             self._threaddata._query_data = {}
   112             return self._threaddata._query_data
   112             return self._threaddata._query_data
   113     
   113 
   114     def set_language(self, language):
   114     def set_language(self, language):
   115         """i18n configuration for translation"""
   115         """i18n configuration for translation"""
   116         vreg = self.vreg
   116         vreg = self.vreg
   117         language = language or self.user.property_value('ui.language')
   117         language = language or self.user.property_value('ui.language')
   118         try:
   118         try:
   122             try:
   122             try:
   123                 self._ = self.__ = vreg.config.translations[language]
   123                 self._ = self.__ = vreg.config.translations[language]
   124             except KeyError:
   124             except KeyError:
   125                 self._ = self.__ = unicode
   125                 self._ = self.__ = unicode
   126         self.lang = language
   126         self.lang = language
   127         
   127 
   128     def change_property(self, prop, value):
   128     def change_property(self, prop, value):
   129         assert prop == 'lang' # this is the only one changeable property for now
   129         assert prop == 'lang' # this is the only one changeable property for now
   130         self.set_language(value)
   130         self.set_language(value)
   131 
   131 
   132     def __str__(self):
   132     def __str__(self):
   133         return '<%ssession %s (%s 0x%x)>' % (self.cnxtype, self.user.login, 
   133         return '<%ssession %s (%s 0x%x)>' % (self.cnxtype, self.user.login,
   134                                              self.id, id(self))
   134                                              self.id, id(self))
   135 
   135 
   136     def etype_class(self, etype):
   136     def etype_class(self, etype):
   137         """return an entity class for the given entity type"""
   137         """return an entity class for the given entity type"""
   138         return self.vreg.etype_class(etype)
   138         return self.vreg.etype_class(etype)
   139     
   139 
   140     def entity(self, eid):
   140     def entity(self, eid):
   141         """return a result set for the given eid"""
   141         """return a result set for the given eid"""
   142         return self.eid_rset(eid).get_entity(0, 0)
   142         return self.eid_rset(eid).get_entity(0, 0)
   143         
   143 
   144     def _touch(self):
   144     def _touch(self):
   145         """update latest session usage timestamp and reset mode to read
   145         """update latest session usage timestamp and reset mode to read
   146         """
   146         """
   147         self.timestamp = time()
   147         self.timestamp = time()
   148         self.local_perm_cache.clear()
   148         self.local_perm_cache.clear()
   149         self._threaddata.mode = 'read'
   149         self._threaddata.mode = 'read'
   150         
   150 
   151     def set_pool(self):
   151     def set_pool(self):
   152         """the session need a pool to execute some queries"""
   152         """the session need a pool to execute some queries"""
   153         if self.pool is None:
   153         if self.pool is None:
   154             self._threaddata.pool = self.repo._get_pool()
   154             self._threaddata.pool = self.repo._get_pool()
   155             try:                
   155             try:
   156                 self._threaddata.pool.pool_set(self)
   156                 self._threaddata.pool.pool_set(self)
   157             except:
   157             except:
   158                 self.repo._free_pool(self.pool)
   158                 self.repo._free_pool(self.pool)
   159                 self._threaddata.pool = None
   159                 self._threaddata.pool = None
   160                 raise
   160                 raise
   161         return self._threaddata.pool
   161         return self._threaddata.pool
   162             
   162 
   163     def reset_pool(self):
   163     def reset_pool(self):
   164         """the session has no longer using its pool, at least for some time
   164         """the session has no longer using its pool, at least for some time
   165         """
   165         """
   166         # pool may be none if no operation has been done since last commit
   166         # pool may be none if no operation has been done since last commit
   167         # or rollback
   167         # or rollback
   168         if self.pool is not None and self.mode == 'read':
   168         if self.pool is not None and self.mode == 'read':
   169             # even in read mode, we must release the current transaction
   169             # even in read mode, we must release the current transaction
   170             self.repo._free_pool(self.pool)
   170             self.repo._free_pool(self.pool)
   171             self.pool.pool_reset(self)
   171             self.pool.pool_reset(self)
   172             self._threaddata.pool = None
   172             self._threaddata.pool = None
   173             
   173 
   174     def system_sql(self, sql, args=None):
   174     def system_sql(self, sql, args=None):
   175         """return a sql cursor on the system database"""
   175         """return a sql cursor on the system database"""
   176         if not sql.split(None, 1)[0].upper() == 'SELECT':
   176         if not sql.split(None, 1)[0].upper() == 'SELECT':
   177             self.mode = 'write'
   177             self.mode = 'write'
   178         cursor = self.pool['system']
   178         cursor = self.pool['system']
   179         self.pool.source('system').doexec(cursor, sql, args)
   179         self.pool.source('system').doexec(cursor, sql, args)
   180         return cursor
   180         return cursor
   181 
   181 
   182     def actual_session(self):
   182     def actual_session(self):
   183         """return the original parent session if any, else self"""
   183         """return the original parent session if any, else self"""
   184         return self        
   184         return self
   185 
   185 
   186     # shared data handling ###################################################
   186     # shared data handling ###################################################
   187     
   187 
   188     def get_shared_data(self, key, default=None, pop=False):
   188     def get_shared_data(self, key, default=None, pop=False):
   189         """return value associated to `key` in session data"""
   189         """return value associated to `key` in session data"""
   190         if pop:
   190         if pop:
   191             return self.data.pop(key, default)
   191             return self.data.pop(key, default)
   192         else:
   192         else:
   193             return self.data.get(key, default)
   193             return self.data.get(key, default)
   194         
   194 
   195     def set_shared_data(self, key, value, querydata=False):
   195     def set_shared_data(self, key, value, querydata=False):
   196         """set value associated to `key` in session data"""
   196         """set value associated to `key` in session data"""
   197         if querydata:
   197         if querydata:
   198             self.set_query_data(key, value)
   198             self.set_query_data(key, value)
   199         else:
   199         else:
   200             self.data[key] = value
   200             self.data[key] = value
   201         
   201 
   202     # request interface #######################################################
   202     # request interface #######################################################
   203     
   203 
   204     def set_entity_cache(self, entity):
   204     def set_entity_cache(self, entity):
   205         # no entity cache in the server, too high risk of inconsistency
   205         # no entity cache in the server, too high risk of inconsistency
   206         # between pre/post hooks
   206         # between pre/post hooks
   207         pass
   207         pass
   208 
   208 
   209     def entity_cache(self, eid):
   209     def entity_cache(self, eid):
   210         raise KeyError(eid)
   210         raise KeyError(eid)
   211 
   211 
   212     def base_url(self):
   212     def base_url(self):
   213         return self.repo.config['base-url'] or u''
   213         return self.repo.config['base-url'] or u''
   214         
   214 
   215     def from_controller(self):
   215     def from_controller(self):
   216         """return the id (string) of the controller issuing the request (no
   216         """return the id (string) of the controller issuing the request (no
   217         sense here, always return 'view')
   217         sense here, always return 'view')
   218         """
   218         """
   219         return 'view'
   219         return 'view'
   220     
   220 
   221     def source_defs(self):
   221     def source_defs(self):
   222         return self.repo.source_defs()
   222         return self.repo.source_defs()
   223 
   223 
   224     def describe(self, eid):
   224     def describe(self, eid):
   225         """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
   225         """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
   226         return self.repo.type_and_source_from_eid(eid, self)
   226         return self.repo.type_and_source_from_eid(eid, self)
   227     
   227 
   228     # db-api like interface ###################################################
   228     # db-api like interface ###################################################
   229 
   229 
   230     def source_from_eid(self, eid):
   230     def source_from_eid(self, eid):
   231         """return the source where the entity with id <eid> is located"""
   231         """return the source where the entity with id <eid> is located"""
   232         return self.repo.source_from_eid(eid, self)
   232         return self.repo.source_from_eid(eid, self)
   247                 csession = ChildSession(self)
   247                 csession = ChildSession(self)
   248             self._threaddata.childsession = csession
   248             self._threaddata.childsession = csession
   249         # need shared pool set
   249         # need shared pool set
   250         self.set_pool()
   250         self.set_pool()
   251         return csession
   251         return csession
   252         
   252 
   253     def unsafe_execute(self, rql, kwargs=None, eid_key=None, build_descr=False,
   253     def unsafe_execute(self, rql, kwargs=None, eid_key=None, build_descr=False,
   254                        propagate=False):
   254                        propagate=False):
   255         """like .execute but with security checking disabled (this method is
   255         """like .execute but with security checking disabled (this method is
   256         internal to the server, it's not part of the db-api)
   256         internal to the server, it's not part of the db-api)
   257 
   257 
   264 
   264 
   265     @property
   265     @property
   266     def cursor(self):
   266     def cursor(self):
   267         """return a rql cursor"""
   267         """return a rql cursor"""
   268         return self
   268         return self
   269     
   269 
   270     def execute(self, rql, kwargs=None, eid_key=None, build_descr=True,
   270     def execute(self, rql, kwargs=None, eid_key=None, build_descr=True,
   271                 propagate=False):
   271                 propagate=False):
   272         """db-api like method directly linked to the querier execute method
   272         """db-api like method directly linked to the querier execute method
   273 
   273 
   274         Becare that unlike actual cursor.execute, `build_descr` default to
   274         Becare that unlike actual cursor.execute, `build_descr` default to
   275         false
   275         false
   276         """
   276         """
   277         rset = self._execute(self, rql, kwargs, eid_key, build_descr)
   277         rset = self._execute(self, rql, kwargs, eid_key, build_descr)
   278         return self.decorate_rset(rset, propagate)
   278         return self.decorate_rset(rset, propagate)
   279     
   279 
   280     def commit(self, reset_pool=True):
   280     def commit(self, reset_pool=True):
   281         """commit the current session's transaction"""
   281         """commit the current session's transaction"""
   282         if self.pool is None:
   282         if self.pool is None:
   283             assert not self.pending_operations
   283             assert not self.pending_operations
   284             self._query_data.clear()
   284             self._query_data.clear()
   315             self.commit_state = None
   315             self.commit_state = None
   316             self.pending_operations[:] = []
   316             self.pending_operations[:] = []
   317             self._query_data.clear()
   317             self._query_data.clear()
   318             if reset_pool:
   318             if reset_pool:
   319                 self.reset_pool()
   319                 self.reset_pool()
   320                         
   320 
   321     def rollback(self, reset_pool=True):
   321     def rollback(self, reset_pool=True):
   322         """rollback the current session's transaction"""
   322         """rollback the current session's transaction"""
   323         if self.pool is None:
   323         if self.pool is None:
   324             assert not self.pending_operations
   324             assert not self.pending_operations
   325             self._query_data.clear()
   325             self._query_data.clear()
   338             self._touch()
   338             self._touch()
   339             self.pending_operations[:] = []
   339             self.pending_operations[:] = []
   340             self._query_data.clear()
   340             self._query_data.clear()
   341             if reset_pool:
   341             if reset_pool:
   342                 self.reset_pool()
   342                 self.reset_pool()
   343         
   343 
   344     def close(self):
   344     def close(self):
   345         """do not close pool on session close, since they are shared now"""
   345         """do not close pool on session close, since they are shared now"""
   346         self.rollback()
   346         self.rollback()
   347         
   347 
   348     # transaction data/operations management ##################################
   348     # transaction data/operations management ##################################
   349     
   349 
   350     def add_query_data(self, key, value):
   350     def add_query_data(self, key, value):
   351         self._query_data.setdefault(key, []).append(value)
   351         self._query_data.setdefault(key, []).append(value)
   352     
   352 
   353     def set_query_data(self, key, value):
   353     def set_query_data(self, key, value):
   354         self._query_data[key] = value
   354         self._query_data[key] = value
   355         
   355 
   356     def query_data(self, key, default=None, setdefault=False, pop=False):
   356     def query_data(self, key, default=None, setdefault=False, pop=False):
   357         if setdefault:
   357         if setdefault:
   358             assert not pop
   358             assert not pop
   359             return self._query_data.setdefault(key, default)
   359             return self._query_data.setdefault(key, default)
   360         if pop:
   360         if pop:
   361             return self._query_data.pop(key, default)
   361             return self._query_data.pop(key, default)
   362         else:
   362         else:
   363             return self._query_data.get(key, default)
   363             return self._query_data.get(key, default)
   364         
   364 
   365     def add_operation(self, operation, index=None):
   365     def add_operation(self, operation, index=None):
   366         """add an observer"""
   366         """add an observer"""
   367         assert self.commit_state != 'commit'
   367         assert self.commit_state != 'commit'
   368         if index is not None:
   368         if index is not None:
   369             self.pending_operations.insert(index, operation)
   369             self.pending_operations.insert(index, operation)
   370         else:
   370         else:
   371             self.pending_operations.append(operation)
   371             self.pending_operations.append(operation)
   372             
   372 
   373     # querier helpers #########################################################
   373     # querier helpers #########################################################
   374     
   374 
   375     def build_description(self, rqlst, args, result):
   375     def build_description(self, rqlst, args, result):
   376         """build a description for a given result"""
   376         """build a description for a given result"""
   377         if len(rqlst.children) == 1 and len(rqlst.children[0].solutions) == 1:
   377         if len(rqlst.children) == 1 and len(rqlst.children[0].solutions) == 1:
   378             # easy, all lines are identical
   378             # easy, all lines are identical
   379             selected = rqlst.children[0].selection
   379             selected = rqlst.children[0].selection
   383         # hard, delegate the work :o)
   383         # hard, delegate the work :o)
   384         return self.manual_build_descr(rqlst, args, result)
   384         return self.manual_build_descr(rqlst, args, result)
   385 
   385 
   386     def manual_build_descr(self, rqlst, args, result):
   386     def manual_build_descr(self, rqlst, args, result):
   387         """build a description for a given result by analysing each row
   387         """build a description for a given result by analysing each row
   388         
   388 
   389         XXX could probably be done more efficiently during execution of query
   389         XXX could probably be done more efficiently during execution of query
   390         """
   390         """
   391         # not so easy, looks for variable which changes from one solution
   391         # not so easy, looks for variable which changes from one solution
   392         # to another
   392         # to another
   393         unstables = rqlst.get_variable_variables()
   393         unstables = rqlst.get_variable_variables()
   408                 etype = rqlst.children[0].solutions[0]
   408                 etype = rqlst.children[0].solutions[0]
   409                 basedescription.append(term.get_type(etype, args))
   409                 basedescription.append(term.get_type(etype, args))
   410         if not todetermine:
   410         if not todetermine:
   411             return [tuple(basedescription)] * len(result)
   411             return [tuple(basedescription)] * len(result)
   412         return self._build_descr(result, basedescription, todetermine)
   412         return self._build_descr(result, basedescription, todetermine)
   413     
   413 
   414     def _build_descr(self, result, basedescription, todetermine):
   414     def _build_descr(self, result, basedescription, todetermine):
   415         description = []
   415         description = []
   416         etype_from_eid = self.describe
   416         etype_from_eid = self.describe
   417         for row in result:
   417         for row in result:
   418             row_descr = basedescription
   418             row_descr = basedescription
   420                 value = row[index]
   420                 value = row[index]
   421                 if value is None:
   421                 if value is None:
   422                     # None value inserted by an outer join, no type
   422                     # None value inserted by an outer join, no type
   423                     row_descr[index] = None
   423                     row_descr[index] = None
   424                     continue
   424                     continue
   425                 try:
   425                 if isfinal:
   426                     if isfinal:
   426                     row_descr[index] = etype_from_pyobj(value)
   427                         row_descr[index] = etype_from_pyobj(value)
   427                 else:
   428                     else:
   428                     try:
   429                         row_descr[index] = etype_from_eid(value)[0]
   429                         row_descr[index] = etype_from_eid(value)[0]
   430                 except UnknownEid:
   430                     except UnknownEid:
   431                     self.critical('wrong eid in repository, should check database')
   431                         self.critical('wrong eid %s in repository, should check database' % value)
   432                     row_descr[index] = row[index] = None
   432                         row_descr[index] = row[index] = None
   433             description.append(tuple(row_descr))
   433             description.append(tuple(row_descr))
   434         return description
   434         return description
   435 
   435 
   436     
   436 
   437 class ChildSession(Session):
   437 class ChildSession(Session):
   438     """child (or internal) session are used to hijack the security system
   438     """child (or internal) session are used to hijack the security system
   439     """
   439     """
   440     cnxtype = 'inmemory'
   440     cnxtype = 'inmemory'
   441     
   441 
   442     def __init__(self, parent_session):
   442     def __init__(self, parent_session):
   443         self.id = None
   443         self.id = None
   444         self.is_internal_session = False
   444         self.is_internal_session = False
   445         self.is_super_session = True
   445         self.is_super_session = True
   446         # session which has created this one
   446         # session which has created this one
   452         self.encoding = parent_session.encoding
   452         self.encoding = parent_session.encoding
   453         self.lang = parent_session.lang
   453         self.lang = parent_session.lang
   454         self._ = self.__ = parent_session._
   454         self._ = self.__ = parent_session._
   455         # short cut to querier .execute method
   455         # short cut to querier .execute method
   456         self._execute = self.repo.querier.execute
   456         self._execute = self.repo.querier.execute
   457     
   457 
   458     @property
   458     @property
   459     def super_session(self):
   459     def super_session(self):
   460         return self
   460         return self
   461 
   461 
   462     def get_mode(self):
   462     def get_mode(self):
   468     def get_commit_state(self):
   468     def get_commit_state(self):
   469         return self.parent_session.commit_state
   469         return self.parent_session.commit_state
   470     def set_commit_state(self, value):
   470     def set_commit_state(self, value):
   471         self.parent_session.set_commit_state(value)
   471         self.parent_session.set_commit_state(value)
   472     commit_state = property(get_commit_state, set_commit_state)
   472     commit_state = property(get_commit_state, set_commit_state)
   473     
   473 
   474     @property
   474     @property
   475     def pool(self):
   475     def pool(self):
   476         return self.parent_session.pool
   476         return self.parent_session.pool
   477     @property
   477     @property
   478     def pending_operations(self):
   478     def pending_operations(self):
   479         return self.parent_session.pending_operations
   479         return self.parent_session.pending_operations
   480     @property
   480     @property
   481     def _query_data(self):
   481     def _query_data(self):
   482         return self.parent_session._query_data
   482         return self.parent_session._query_data
   483         
   483 
   484     def set_pool(self):
   484     def set_pool(self):
   485         """the session need a pool to execute some queries"""
   485         """the session need a pool to execute some queries"""
   486         self.parent_session.set_pool()
   486         self.parent_session.set_pool()
   487             
   487 
   488     def reset_pool(self):
   488     def reset_pool(self):
   489         """the session has no longer using its pool, at least for some time
   489         """the session has no longer using its pool, at least for some time
   490         """
   490         """
   491         self.parent_session.reset_pool()
   491         self.parent_session.reset_pool()
   492 
   492 
   493     def actual_session(self):
   493     def actual_session(self):
   494         """return the original parent session if any, else self"""
   494         """return the original parent session if any, else self"""
   495         return self.parent_session
   495         return self.parent_session
   496         
   496 
   497     def commit(self, reset_pool=True):
   497     def commit(self, reset_pool=True):
   498         """commit the current session's transaction"""
   498         """commit the current session's transaction"""
   499         self.parent_session.commit(reset_pool)
   499         self.parent_session.commit(reset_pool)
   500         
   500 
   501     def rollback(self, reset_pool=True):
   501     def rollback(self, reset_pool=True):
   502         """rollback the current session's transaction"""
   502         """rollback the current session's transaction"""
   503         self.parent_session.rollback(reset_pool)
   503         self.parent_session.rollback(reset_pool)
   504         
   504 
   505     def close(self):
   505     def close(self):
   506         """do not close pool on session close, since they are shared now"""
   506         """do not close pool on session close, since they are shared now"""
   507         self.rollback()
   507         self.rollback()
   508         
   508 
   509     def user_data(self):
   509     def user_data(self):
   510         """returns a dictionnary with this user's information"""
   510         """returns a dictionnary with this user's information"""
   511         return self.parent_session.user_data()
   511         return self.parent_session.user_data()
   512 
   512 
   513 
   513 
   514 class InternalSession(Session):
   514 class InternalSession(Session):
   515     """special session created internaly by the repository"""
   515     """special session created internaly by the repository"""
   516     
   516 
   517     def __init__(self, repo, cnxprops=None):
   517     def __init__(self, repo, cnxprops=None):
   518         super(InternalSession, self).__init__(_IMANAGER, repo, cnxprops,
   518         super(InternalSession, self).__init__(_IMANAGER, repo, cnxprops,
   519                                               _id='internal')
   519                                               _id='internal')
   520         self.cnxtype = 'inmemory'
   520         self.cnxtype = 'inmemory'
   521         self.is_internal_session = True
   521         self.is_internal_session = True
   522         self.is_super_session = True
   522         self.is_super_session = True
   523     
   523 
   524     @property
   524     @property
   525     def super_session(self):
   525     def super_session(self):
   526         return self
   526         return self
   527 
   527 
   528 
   528 
   542     def is_in_group(self, group):
   542     def is_in_group(self, group):
   543         return True
   543         return True
   544 
   544 
   545     def owns(self, eid):
   545     def owns(self, eid):
   546         return True
   546         return True
   547     
   547 
   548     def has_permission(self, pname, contexteid=None):
   548     def has_permission(self, pname, contexteid=None):
   549         return True
   549         return True
   550 
   550 
   551     def property_value(self, key):
   551     def property_value(self, key):
   552         if key == 'ui.language':
   552         if key == 'ui.language':