10c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi"""A dumb and slow but simple dbm clone.
20c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
30c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiFor database spam, spam.dir contains the index (a text file),
40c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yispam.bak *may* contain a backup of the index (also a text file),
50c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiwhile spam.dat contains the data (a binary file).
60c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
70c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiXXX TO DO:
80c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
90c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi- seems to contain a bug when updating...
100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi- reclaim free space (currently, space once occupied by deleted or expanded
120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiitems is never reused)
130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi- support concurrent access (currently, if two processes take turns making
150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiupdates, they can mess up the index)
160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi- support efficient access to large databases (currently, the whole index
180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiis read when the database is opened, and some updates rewrite the whole index)
190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi- support opening for read-only (flag = 'm')
210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi"""
230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport os as _os
250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport __builtin__
260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport UserDict
270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi_open = __builtin__.open
290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi_BLOCKSIZE = 512
310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yierror = IOError                         # For anydbm
330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass _Database(UserDict.DictMixin):
350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # The on-disk directory and data files can remain in mutually
370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # inconsistent states for an arbitrarily long time (see comments
380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # at the end of __setitem__).  This is only repaired when _commit()
390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # gets called.  One place _commit() gets called is from __del__(),
400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # and if that occurs at program shutdown time, module globals may
410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # already have gotten rebound to None.  Since it's crucial that
420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # _commit() finish successfully, we can't ignore shutdown races
430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # here, and _commit() must not reference any globals.
440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    _os = _os       # for _commit()
450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    _open = _open   # for _commit()
460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def __init__(self, filebasename, mode):
480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._mode = mode
490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # The directory file is a text file.  Each line looks like
510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        #    "%r, (%d, %d)\n" % (key, pos, siz)
520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # where key is the string key, pos is the offset into the dat
530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # file of the associated value's first byte, and siz is the number
540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # of bytes in the associated value.
550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._dirfile = filebasename + _os.extsep + 'dir'
560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # The data file is a binary file pointed into by the directory
580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # file, and holds the values associated with keys.  Each value
590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # begins at a _BLOCKSIZE-aligned byte offset, and is a raw
600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # binary 8-bit string value.
610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._datfile = filebasename + _os.extsep + 'dat'
620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._bakfile = filebasename + _os.extsep + 'bak'
630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # The index is an in-memory dict, mirroring the directory file.
650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._index = None  # maps keys to (pos, siz) pairs
660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Mod by Jack: create data file if needed
680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        try:
690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            f = _open(self._datfile, 'r')
700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        except IOError:
710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            f = _open(self._datfile, 'w')
720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self._chmod(self._datfile)
730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.close()
740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._update()
750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Read directory file into the in-memory index dict.
770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _update(self):
780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._index = {}
790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        try:
800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            f = _open(self._dirfile)
810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        except IOError:
820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            pass
830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for line in f:
850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                line = line.rstrip()
860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                key, pos_and_siz_pair = eval(line)
870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self._index[key] = pos_and_siz_pair
880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            f.close()
890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Write the index dict to the directory file.  The original directory
910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # file (if any) is renamed with a .bak extension first.  If a .bak
920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # file currently exists, it's deleted.
930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _commit(self):
940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # CAUTION:  It's vital that _commit() succeed, and _commit() can
950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # be called from __del__().  Therefore we must never reference a
960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # global in this routine.
970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self._index is None:
980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            return  # nothing to do
990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        try:
1010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self._os.unlink(self._bakfile)
1020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        except self._os.error:
1030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            pass
1040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        try:
1060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self._os.rename(self._dirfile, self._bakfile)
1070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        except self._os.error:
1080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            pass
1090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f = self._open(self._dirfile, 'w')
1110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._chmod(self._dirfile)
1120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for key, pos_and_siz_pair in self._index.iteritems():
1130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            f.write("%r, %r\n" % (key, pos_and_siz_pair))
1140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.close()
1150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    sync = _commit
1170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def __getitem__(self, key):
1190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        pos, siz = self._index[key]     # may raise KeyError
1200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f = _open(self._datfile, 'rb')
1210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.seek(pos)
1220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dat = f.read(siz)
1230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.close()
1240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return dat
1250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Append val to the data file, starting at a _BLOCKSIZE-aligned
1270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # offset.  The data file is first padded with NUL bytes (if needed)
1280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # to get to an aligned offset.  Return pair
1290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    #     (starting offset of val, len(val))
1300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _addval(self, val):
1310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f = _open(self._datfile, 'rb+')
1320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.seek(0, 2)
1330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        pos = int(f.tell())
1340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE
1350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.write('\0'*(npos-pos))
1360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        pos = npos
1370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.write(val)
1380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.close()
1390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return (pos, len(val))
1400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Write val to the data file, starting at offset pos.  The caller
1420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # is responsible for ensuring that there's enough room starting at
1430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # pos to hold val, without overwriting some other value.  Return
1440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # pair (pos, len(val)).
1450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _setval(self, pos, val):
1460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f = _open(self._datfile, 'rb+')
1470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.seek(pos)
1480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.write(val)
1490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.close()
1500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return (pos, len(val))
1510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # key is a new key whose associated value starts in the data file
1530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # at offset pos and with length siz.  Add an index record to
1540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # the in-memory index dict, and append one to the directory file.
1550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _addkey(self, key, pos_and_siz_pair):
1560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._index[key] = pos_and_siz_pair
1570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f = _open(self._dirfile, 'a')
1580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._chmod(self._dirfile)
1590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.write("%r, %r\n" % (key, pos_and_siz_pair))
1600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.close()
1610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def __setitem__(self, key, val):
1630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if not type(key) == type('') == type(val):
1640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            raise TypeError, "keys and values must be strings"
1650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if key not in self._index:
1660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self._addkey(key, self._addval(val))
1670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
1680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # See whether the new value is small enough to fit in the
1690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # (padded) space currently occupied by the old value.
1700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            pos, siz = self._index[key]
1710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE
1720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE
1730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if newblocks <= oldblocks:
1740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self._index[key] = self._setval(pos, val)
1750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
1760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # The new value doesn't fit in the (padded) space used
1770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # by the old value.  The blocks used by the old value are
1780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # forever lost.
1790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self._index[key] = self._addval(val)
1800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Note that _index may be out of synch with the directory
1820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # file now:  _setval() and _addval() don't update the directory
1830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # file.  This also means that the on-disk directory and data
1840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # files are in a mutually inconsistent state, and they'll
1850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # remain that way until _commit() is called.  Note that this
1860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # is a disaster (for the database) if the program crashes
1870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # (so that _commit() never gets called).
1880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def __delitem__(self, key):
1900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # The blocks used by the associated value are lost.
1910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        del self._index[key]
1920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # XXX It's unclear why we do a _commit() here (the code always
1930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # XXX has, so I'm not changing it).  _setitem__ doesn't try to
1940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # XXX keep the directory file in synch.  Why should we?  Or
1950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # XXX why shouldn't __setitem__?
1960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._commit()
1970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def keys(self):
1990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return self._index.keys()
2000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def has_key(self, key):
2020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return key in self._index
2030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def __contains__(self, key):
2050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return key in self._index
2060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def iterkeys(self):
2080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return self._index.iterkeys()
2090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    __iter__ = iterkeys
2100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def __len__(self):
2120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return len(self._index)
2130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def close(self):
2150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._commit()
2160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self._index = self._datfile = self._dirfile = self._bakfile = None
2170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    __del__ = close
2190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _chmod (self, file):
2210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if hasattr(self._os, 'chmod'):
2220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self._os.chmod(file, self._mode)
2230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidef open(file, flag=None, mode=0666):
2260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    """Open the database file, filename, and return corresponding object.
2270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    The flag argument, used to control how the database is opened in the
2290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    other DBM implementations, is ignored in the dumbdbm module; the
2300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    database is always opened for update, and will be created if it does
2310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    not exist.
2320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    The optional mode argument is the UNIX mode of the file, used only when
2340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    the database has to be created.  It defaults to octal code 0666 (and
2350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    will be modified by the prevailing umask).
2360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    """
2380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # flag argument is currently ignored
2390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Modify mode depending on the umask
2410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    try:
2420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        um = _os.umask(0)
2430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        _os.umask(um)
2440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    except AttributeError:
2450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        pass
2460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    else:
2470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Turn off any bits that are set in the umask
2480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        mode = mode & (~um)
2490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    return _Database(file, mode)
251