14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""A dumb and slow but simple dbm clone.
24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmFor database spam, spam.dir contains the index (a text file),
44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmspam.bak *may* contain a backup of the index (also a text file),
54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmwhile spam.dat contains the data (a binary file).
64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmXXX TO DO:
84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm- seems to contain a bug when updating...
104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm- reclaim free space (currently, space once occupied by deleted or expanded
124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmitems is never reused)
134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm- support concurrent access (currently, if two processes take turns making
154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmupdates, they can mess up the index)
164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm- support efficient access to large databases (currently, the whole index
184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmis read when the database is opened, and some updates rewrite the whole index)
194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm- support opening for read-only (flag = 'm')
214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""
234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport os as _os
254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport __builtin__
264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport UserDict
274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm_open = __builtin__.open
294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm_BLOCKSIZE = 512
314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmerror = IOError                         # For anydbm
334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass _Database(UserDict.DictMixin):
354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # The on-disk directory and data files can remain in mutually
374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # inconsistent states for an arbitrarily long time (see comments
384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # at the end of __setitem__).  This is only repaired when _commit()
394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # gets called.  One place _commit() gets called is from __del__(),
404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # and if that occurs at program shutdown time, module globals may
414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # already have gotten rebound to None.  Since it's crucial that
424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # _commit() finish successfully, we can't ignore shutdown races
434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # here, and _commit() must not reference any globals.
444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    _os = _os       # for _commit()
454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    _open = _open   # for _commit()
464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, filebasename, mode):
484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._mode = mode
494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # The directory file is a text file.  Each line looks like
514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        #    "%r, (%d, %d)\n" % (key, pos, siz)
524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # where key is the string key, pos is the offset into the dat
534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # file of the associated value's first byte, and siz is the number
544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # of bytes in the associated value.
554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._dirfile = filebasename + _os.extsep + 'dir'
564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # The data file is a binary file pointed into by the directory
584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # file, and holds the values associated with keys.  Each value
594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # begins at a _BLOCKSIZE-aligned byte offset, and is a raw
604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # binary 8-bit string value.
614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._datfile = filebasename + _os.extsep + 'dat'
624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._bakfile = filebasename + _os.extsep + 'bak'
634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # The index is an in-memory dict, mirroring the directory file.
654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._index = None  # maps keys to (pos, siz) pairs
664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # Mod by Jack: create data file if needed
684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        try:
694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            f = _open(self._datfile, 'r')
704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        except IOError:
714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            f = _open(self._datfile, 'w')
724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self._chmod(self._datfile)
734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.close()
744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._update()
754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Read directory file into the in-memory index dict.
774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _update(self):
784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._index = {}
794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        try:
804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            f = _open(self._dirfile)
814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        except IOError:
824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            pass
834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            for line in f:
854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                line = line.rstrip()
864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                key, pos_and_siz_pair = eval(line)
874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self._index[key] = pos_and_siz_pair
884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            f.close()
894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Write the index dict to the directory file.  The original directory
914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # file (if any) is renamed with a .bak extension first.  If a .bak
924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # file currently exists, it's deleted.
934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _commit(self):
944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # CAUTION:  It's vital that _commit() succeed, and _commit() can
954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # be called from __del__().  Therefore we must never reference a
964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # global in this routine.
974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self._index is None:
984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return  # nothing to do
994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        try:
1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self._os.unlink(self._bakfile)
1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        except self._os.error:
1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            pass
1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        try:
1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self._os.rename(self._dirfile, self._bakfile)
1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        except self._os.error:
1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            pass
1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f = self._open(self._dirfile, 'w')
1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._chmod(self._dirfile)
1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for key, pos_and_siz_pair in self._index.iteritems():
1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            f.write("%r, %r\n" % (key, pos_and_siz_pair))
1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.close()
1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    sync = _commit
1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __getitem__(self, key):
1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        pos, siz = self._index[key]     # may raise KeyError
1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f = _open(self._datfile, 'rb')
1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.seek(pos)
1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        dat = f.read(siz)
1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.close()
1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return dat
1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Append val to the data file, starting at a _BLOCKSIZE-aligned
1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # offset.  The data file is first padded with NUL bytes (if needed)
1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # to get to an aligned offset.  Return pair
1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    #     (starting offset of val, len(val))
1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _addval(self, val):
1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f = _open(self._datfile, 'rb+')
1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.seek(0, 2)
1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        pos = int(f.tell())
1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE
1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.write('\0'*(npos-pos))
1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        pos = npos
1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.write(val)
1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.close()
1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return (pos, len(val))
1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Write val to the data file, starting at offset pos.  The caller
1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # is responsible for ensuring that there's enough room starting at
1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # pos to hold val, without overwriting some other value.  Return
1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # pair (pos, len(val)).
1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _setval(self, pos, val):
1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f = _open(self._datfile, 'rb+')
1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.seek(pos)
1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.write(val)
1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.close()
1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return (pos, len(val))
1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # key is a new key whose associated value starts in the data file
1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # at offset pos and with length siz.  Add an index record to
1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # the in-memory index dict, and append one to the directory file.
1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _addkey(self, key, pos_and_siz_pair):
1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._index[key] = pos_and_siz_pair
1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f = _open(self._dirfile, 'a')
1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._chmod(self._dirfile)
1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.write("%r, %r\n" % (key, pos_and_siz_pair))
1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.close()
1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __setitem__(self, key, val):
1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not type(key) == type('') == type(val):
1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise TypeError, "keys and values must be strings"
1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if key not in self._index:
1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self._addkey(key, self._addval(val))
1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # See whether the new value is small enough to fit in the
1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # (padded) space currently occupied by the old value.
1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            pos, siz = self._index[key]
1714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE
1724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE
1734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if newblocks <= oldblocks:
1744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self._index[key] = self._setval(pos, val)
1754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            else:
1764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                # The new value doesn't fit in the (padded) space used
1774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                # by the old value.  The blocks used by the old value are
1784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                # forever lost.
1794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self._index[key] = self._addval(val)
1804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # Note that _index may be out of synch with the directory
1824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # file now:  _setval() and _addval() don't update the directory
1834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # file.  This also means that the on-disk directory and data
1844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # files are in a mutually inconsistent state, and they'll
1854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # remain that way until _commit() is called.  Note that this
1864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # is a disaster (for the database) if the program crashes
1874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            # (so that _commit() never gets called).
1884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __delitem__(self, key):
1904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # The blocks used by the associated value are lost.
1914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        del self._index[key]
1924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # XXX It's unclear why we do a _commit() here (the code always
1934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # XXX has, so I'm not changing it).  _setitem__ doesn't try to
1944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # XXX keep the directory file in synch.  Why should we?  Or
1954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # XXX why shouldn't __setitem__?
1964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._commit()
1974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def keys(self):
1994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self._index.keys()
2004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def has_key(self, key):
2024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return key in self._index
2034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __contains__(self, key):
2054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return key in self._index
2064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def iterkeys(self):
2084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self._index.iterkeys()
2094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    __iter__ = iterkeys
2104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __len__(self):
2124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return len(self._index)
2134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def close(self):
2154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._commit()
2164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self._index = self._datfile = self._dirfile = self._bakfile = None
2174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    __del__ = close
2194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def _chmod (self, file):
2214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if hasattr(self._os, 'chmod'):
2224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self._os.chmod(file, self._mode)
2234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef open(file, flag=None, mode=0666):
2264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """Open the database file, filename, and return corresponding object.
2274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    The flag argument, used to control how the database is opened in the
2294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    other DBM implementations, is ignored in the dumbdbm module; the
2304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    database is always opened for update, and will be created if it does
2314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    not exist.
2324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    The optional mode argument is the UNIX mode of the file, used only when
2344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    the database has to be created.  It defaults to octal code 0666 (and
2354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    will be modified by the prevailing umask).
2364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """
2384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # flag argument is currently ignored
2394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    # Modify mode depending on the umask
2414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    try:
2424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        um = _os.umask(0)
2434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _os.umask(um)
2444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    except AttributeError:
2454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        pass
2464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    else:
2474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # Turn off any bits that are set in the umask
2484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        mode = mode & (~um)
2494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    return _Database(file, mode)
251