"Fossies" - the Fresh Open Source Software Archive

Member "roundup-2.0.0/roundup/backends/sessions_dbm.py" (29 Feb 2020, 5527 Bytes) of package /linux/www/roundup-2.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "sessions_dbm.py": 1.6.1_vs_2.0.0.

    1 """This module defines a very basic store that's used by the CGI interface
    2 to store session and one-time-key information.
    3 
    4 Yes, it's called "sessions" - because originally it only defined a session
    5 class. It's now also used for One Time Key handling too.
    6 """
    7 __docformat__ = 'restructuredtext'
    8 
    9 import os, marshal, time
   10 
   11 from roundup.anypy.html import html_escape as escape
   12 
   13 from roundup import hyperdb
   14 from roundup.i18n import _
   15 from roundup.anypy.dbm_ import anydbm, whichdb
   16 
   17 
   18 class BasicDatabase:
   19     ''' Provide a nice encapsulation of an anydbm store.
   20 
   21         Keys are id strings, values are automatically marshalled data.
   22     '''
   23     _db_type = None
   24     name = None
   25 
   26     def __init__(self, db):
   27         self.config = db.config
   28         self.dir = db.config.DATABASE
   29         os.umask(db.config.UMASK)
   30 
   31     def exists(self, infoid):
   32         db = self.opendb('c')
   33         try:
   34             return infoid in db
   35         finally:
   36             db.close()
   37 
   38     def clear(self):
   39         path = os.path.join(self.dir, self.name)
   40         if os.path.exists(path):
   41             os.remove(path)
   42         elif os.path.exists(path+'.db'):    # dbm appends .db
   43             os.remove(path+'.db')
   44 
   45     def cache_db_type(self, path):
   46         ''' determine which DB wrote the class file, and cache it as an
   47             attribute of __class__ (to allow for subclassed DBs to be
   48             different sorts)
   49         '''
   50         db_type = ''
   51         if os.path.exists(path):
   52             db_type = whichdb(path)
   53             if not db_type:
   54                 raise hyperdb.DatabaseError(
   55                     _("Couldn't identify database type"))
   56         elif os.path.exists(path+'.db'):
   57             # if the path ends in '.db', it's a dbm database, whether
   58             # anydbm says it's dbhash or not!
   59             db_type = 'dbm'
   60         self.__class__._db_type = db_type
   61 
   62     _marker = []
   63 
   64     def get(self, infoid, value, default=_marker):
   65         db = self.opendb('c')
   66         try:
   67             if infoid in db:
   68                 values = marshal.loads(db[infoid])
   69             else:
   70                 if default != self._marker:
   71                     return default
   72                 raise KeyError('No such %s "%s"' % (self.name, escape(infoid)))
   73             return values.get(value, None)
   74         finally:
   75             db.close()
   76 
   77     def getall(self, infoid):
   78         db = self.opendb('c')
   79         try:
   80             try:
   81                 d = marshal.loads(db[infoid])
   82                 del d['__timestamp']
   83                 return d
   84             except KeyError:
   85                 raise KeyError('No such %s "%s"' % (self.name, escape(infoid)))
   86         finally:
   87             db.close()
   88 
   89     def set(self, infoid, **newvalues):
   90         db = self.opendb('c')
   91         try:
   92             if infoid in db:
   93                 values = marshal.loads(db[infoid])
   94             else:
   95                 values = {'__timestamp': time.time()}
   96             values.update(newvalues)
   97             db[infoid] = marshal.dumps(values)
   98         finally:
   99             db.close()
  100 
  101     def list(self):
  102         db = self.opendb('r')
  103         try:
  104             return list(db.keys())
  105         finally:
  106             db.close()
  107 
  108     def destroy(self, infoid):
  109         db = self.opendb('c')
  110         try:
  111             if infoid in db:
  112                 del db[infoid]
  113         finally:
  114             db.close()
  115 
  116     def opendb(self, mode):
  117         '''Low-level database opener that gets around anydbm/dbm
  118            eccentricities.
  119         '''
  120         # figure the class db type
  121         path = os.path.join(os.getcwd(), self.dir, self.name)
  122         if self._db_type is None:
  123             self.cache_db_type(path)
  124 
  125         db_type = self._db_type
  126 
  127         # new database? let anydbm pick the best dbm
  128         if not db_type:
  129             return anydbm.open(path, 'c')
  130 
  131         # open the database with the correct module
  132         dbm = __import__(db_type)
  133 
  134         retries_left = 15
  135         while True:
  136             try:
  137                 handle = dbm.open(path, mode)
  138                 break
  139             except OSError:
  140                 # Primarily we want to catch and retry:
  141                 #   [Errno 11] Resource temporarily unavailable retry
  142                 # FIXME: make this more specific
  143                 if retries_left < 0:
  144                     # We have used up the retries. Reraise the exception
  145                     # that got us here.
  146                     raise
  147                 else:
  148                     # delay retry a bit
  149                     time.sleep(0.01)
  150                     retries_left = retries_left - 1
  151                     continue  # the while loop
  152         return handle
  153 
  154     def commit(self):
  155         pass
  156 
  157     def close(self):
  158         pass
  159 
  160     def updateTimestamp(self, sessid):
  161         ''' don't update every hit - once a minute should be OK '''
  162         sess = self.get(sessid, '__timestamp', None)
  163         now = time.time()
  164         if sess is None or now > sess + 60:
  165             self.set(sessid, __timestamp=now)
  166 
  167     def clean(self):
  168         ''' Remove session records that haven't been used for a week. '''
  169         now = time.time()
  170         week = 60*60*24*7
  171         for sessid in self.list():
  172             sess = self.get(sessid, '__timestamp', None)
  173             if sess is None:
  174                 self.updateTimestamp(sessid)
  175                 continue
  176             interval = now - sess
  177             if interval > week:
  178                 self.destroy(sessid)
  179 
  180 
  181 class Sessions(BasicDatabase):
  182     name = 'sessions'
  183 
  184 
  185 class OneTimeKeys(BasicDatabase):
  186     name = 'otks'
  187 
  188 # vim: set sts ts=4 sw=4 et si :