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