SCons.dblite

Package SCons :: Module dblite |
[hide private]
[frames] | no frames]

# Source Code for Module SCons.dblite

      1  # dblite.py module contributed by Ralf W. Grosse-Kunstleve.
      2  # Extended for Unicode by Steven Knight.
      3
      4  import cPickle
      5  import time
      6  import shutil
      7  import os
      8  import os.path
      9  import types
     10  import __builtin__
     11
     12  _open = __builtin__.open # avoid name clash
     13
     14  keep_all_files = 00000
     15  ignore_corrupt_dbfiles = 0
     16
     17 -def corruption_warning(filename):
     18      print "Warning: Discarding corrupt database:", filename
     19
     20  if hasattr(types, 'UnicodeType'):
     21 -    def is_string(s):
     22          t = type(s)
     23          return t is types.StringType or t is types.UnicodeType
     24  else:
     25 -    def is_string(s):
     26          return type(s) is types.StringType
     27
     28  try:
     29      unicode('a')
     30  except NameError:
     31 -    def unicode(s): return s
     32
     33  dblite_suffix = '.dblite'
     34  tmp_suffix = '.tmp'
     35
     36 -class dblite:
     37
     38 -  def __init__(self, file_base_name, flag, mode):
     39      assert flag in (None, "r", "w", "c", "n")
     40      if (flag is None): flag = "r"
     41      base, ext = os.path.splitext(file_base_name)
     42      if ext == dblite_suffix:
     43        # There's already a suffix on the file name, don't add one.
     44        self._file_name = file_base_name
     45        self._tmp_name = base + tmp_suffix
     46      else:
     47        self._file_name = file_base_name + dblite_suffix
     48        self._tmp_name = file_base_name + tmp_suffix
     49      self._flag = flag
     50      self._mode = mode
     51      self._dict = {}
     52      self._needs_sync = 00000
     53      if (self._flag == "n"):
     54        _open(self._file_name, "wb", self._mode)
     55      else:
     56        try:
     57          f = _open(self._file_name, "rb")
     58        except IOError, e:
     59          if (self._flag != "c"):
     60            raise e
     61          _open(self._file_name, "wb", self._mode)
     62        else:
     63          p = f.read()
     64          if (len(p) > 0):
     65            try:
     66              self._dict = cPickle.loads(p)
     67            except (cPickle.UnpicklingError, EOFError):
     68              if (ignore_corrupt_dbfiles == 0): raise
     69              if (ignore_corrupt_dbfiles == 1):
     70                corruption_warning(self._file_name)
     71
     72 -  def __del__(self):
     73      if (self._needs_sync):
     74        self.sync()
     75
     76 -  def sync(self):
     77      self._check_writable()
     78      f = _open(self._tmp_name, "wb", self._mode)
     79      cPickle.dump(self._dict, f, 1)
     80      f.close()
     81      # Windows doesn't allow renaming if the file exists, so unlink
     82      # it first, chmod'ing it to make sure we can do so.  On UNIX, we
     83      # may not be able to chmod the file if it's owned by someone else
     84      # (e.g. from a previous run as root).  We should still be able to
     85      # unlink() the file if the directory's writable, though, so ignore
     86      # any OSError exception  thrown by the chmod() call.
     87      try: os.chmod(self._file_name, 0777)
     88      except OSError: pass
     89      os.unlink(self._file_name)
     90      os.rename(self._tmp_name, self._file_name)
     91      self._needs_sync = 00000
     92      if (keep_all_files):
     93        shutil.copyfile(
     94          self._file_name,
     95          self._file_name + "_" + str(int(time.time())))
     96
     97 -  def _check_writable(self):
     98      if (self._flag == "r"):
     99        raise IOError("Read-only database: %s" % self._file_name)
    100
    101 -  def __getitem__(self, key):
    102      return self._dict[key]
    103
    104 -  def __setitem__(self, key, value):
    105      self._check_writable()
    106      if (not is_string(key)):
    107        raise TypeError, "key `%s' must be a string but is %s" % (key, type(key))
    108      if (not is_string(value)):
    109        raise TypeError, "value `%s' must be a string but is %s" % (value, type(value))
    110      self._dict[key] = value
    111      self._needs_sync = 0001
    112
    113 -  def keys(self):
    114      return self._dict.keys()
    115
    116 -  def has_key(self, key):
    117      return key in self._dict
    118
    119 -  def __contains__(self, key):
    120      return key in self._dict
    121
    122 -  def iterkeys(self):
    123      return self._dict.iterkeys()
    124
    125    __iter__ = iterkeys
    126
    127 -  def __len__(self):
    128      return len(self._dict)
    129
    130 -def open(file, flag=None, mode=0666):
    131    return dblite(file, flag, mode)
    132
    133 -def _exercise():
    134    db = open("tmp", "n")
    135    assert len(db) == 0
    136    db["foo"] = "bar"
    137    assert db["foo"] == "bar"
    138    db[unicode("ufoo")] = unicode("ubar")
    139    assert db[unicode("ufoo")] == unicode("ubar")
    140    db.sync()
    141    db = open("tmp", "c")
    142    assert len(db) == 2, len(db)
    143    assert db["foo"] == "bar"
    144    db["bar"] = "foo"
    145    assert db["bar"] == "foo"
    146    db[unicode("ubar")] = unicode("ufoo")
    147    assert db[unicode("ubar")] == unicode("ufoo")
    148    db.sync()
    149    db = open("tmp", "r")
    150    assert len(db) == 4, len(db)
    151    assert db["foo"] == "bar"
    152    assert db["bar"] == "foo"
    153    assert db[unicode("ufoo")] == unicode("ubar")
    154    assert db[unicode("ubar")] == unicode("ufoo")
    155    try:
    156      db.sync()
    157    except IOError, e:
    158      assert str(e) == "Read-only database: tmp.dblite"
    159    else:
    160      raise RuntimeError, "IOError expected."
    161    db = open("tmp", "w")
    162    assert len(db) == 4
    163    db["ping"] = "pong"
    164    db.sync()
    165    try:
    166      db[(1,2)] = "tuple"
    167    except TypeError, e:
    168      assert str(e) == "key `(1, 2)' must be a string but is <type 'tuple'>", str(e)
    169    else:
    170      raise RuntimeError, "TypeError exception expected"
    171    try:
    172      db["list"] = [1,2]
    173    except TypeError, e:
    174      assert str(e) == "value `[1, 2]' must be a string but is <type 'list'>", str(e)
    175    else:
    176      raise RuntimeError, "TypeError exception expected"
    177    db = open("tmp", "r")
    178    assert len(db) == 5
    179    db = open("tmp", "n")
    180    assert len(db) == 0
    181    _open("tmp.dblite", "w")
    182    db = open("tmp", "r")
    183    _open("tmp.dblite", "w").write("x")
    184    try:
    185      db = open("tmp", "r")
    186    except cPickle.UnpicklingError:
    187      pass
    188    else:
    189      raise RuntimeError, "cPickle exception expected."
    190    global ignore_corrupt_dbfiles
    191    ignore_corrupt_dbfiles = 2
    192    db = open("tmp", "r")
    193    assert len(db) == 0
    194    os.unlink("tmp.dblite")
    195    try:
    196      db = open("tmp", "w")
    197    except IOError, e:
    198      assert str(e) == "[Errno 2] No such file or directory: 'tmp.dblite'", str(e)
    199    else:
    200      raise RuntimeError, "IOError expected."
    201    print "OK"
    202
    203  if (__name__ == "__main__"):
    204    _exercise()
    205