183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""A dumb and slow but simple dbm clone.
283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehFor database spam, spam.dir contains the index (a text file),
483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehspam.bak *may* contain a backup of the index (also a text file),
583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehwhile spam.dat contains the data (a binary file).
683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehXXX TO DO:
883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh- seems to contain a bug when updating...
1083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
1183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh- reclaim free space (currently, space once occupied by deleted or expanded
1283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehitems is never reused)
1383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
1483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh- support concurrent access (currently, if two processes take turns making
1583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehupdates, they can mess up the index)
1683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
1783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh- support efficient access to large databases (currently, the whole index
1883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehis read when the database is opened, and some updates rewrite the whole index)
1983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh- support opening for read-only (flag = 'm')
2183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""
2383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport os as _os
2583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport __builtin__
2683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport UserDict
2783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh_open = __builtin__.open
2983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh_BLOCKSIZE = 512
3183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieherror = IOError                         # For anydbm
3383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass _Database(UserDict.DictMixin):
3583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
3683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # The on-disk directory and data files can remain in mutually
3783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # inconsistent states for an arbitrarily long time (see comments
3883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # at the end of __setitem__).  This is only repaired when _commit()
3983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # gets called.  One place _commit() gets called is from __del__(),
4083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # and if that occurs at program shutdown time, module globals may
4183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # already have gotten rebound to None.  Since it's crucial that
4283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _commit() finish successfully, we can't ignore shutdown races
4383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # here, and _commit() must not reference any globals.
4483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _os = _os       # for _commit()
4583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _open = _open   # for _commit()
4683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
4783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, filebasename, mode):
4883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._mode = mode
4983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # The directory file is a text file.  Each line looks like
5183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        #    "%r, (%d, %d)\n" % (key, pos, siz)
5283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # where key is the string key, pos is the offset into the dat
5383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # file of the associated value's first byte, and siz is the number
5483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # of bytes in the associated value.
5583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._dirfile = filebasename + _os.extsep + 'dir'
5683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # The data file is a binary file pointed into by the directory
5883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # file, and holds the values associated with keys.  Each value
5983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # begins at a _BLOCKSIZE-aligned byte offset, and is a raw
6083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # binary 8-bit string value.
6183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._datfile = filebasename + _os.extsep + 'dat'
6283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._bakfile = filebasename + _os.extsep + 'bak'
6383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # The index is an in-memory dict, mirroring the directory file.
6583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._index = None  # maps keys to (pos, siz) pairs
6683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Mod by Jack: create data file if needed
6883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
6983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = _open(self._datfile, 'r')
7083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except IOError:
7183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = _open(self._datfile, 'w')
7283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._chmod(self._datfile)
7383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.close()
7483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._update()
7583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
7683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Read directory file into the in-memory index dict.
7783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _update(self):
7883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._index = {}
7983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
8083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = _open(self._dirfile)
8183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except IOError:
8283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            pass
8383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
8483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for line in f:
8583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                line = line.rstrip()
8683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                key, pos_and_siz_pair = eval(line)
8783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._index[key] = pos_and_siz_pair
8883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f.close()
8983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Write the index dict to the directory file.  The original directory
9183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # file (if any) is renamed with a .bak extension first.  If a .bak
9283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # file currently exists, it's deleted.
9383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _commit(self):
9483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # CAUTION:  It's vital that _commit() succeed, and _commit() can
9583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # be called from __del__().  Therefore we must never reference a
9683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # global in this routine.
9783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._index is None:
9883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return  # nothing to do
9983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
10183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._os.unlink(self._bakfile)
10283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except self._os.error:
10383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            pass
10483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
10683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._os.rename(self._dirfile, self._bakfile)
10783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except self._os.error:
10883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            pass
10983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = self._open(self._dirfile, 'w')
11183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._chmod(self._dirfile)
11283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for key, pos_and_siz_pair in self._index.iteritems():
11383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f.write("%r, %r\n" % (key, pos_and_siz_pair))
11483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.close()
11583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    sync = _commit
11783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __getitem__(self, key):
11983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        pos, siz = self._index[key]     # may raise KeyError
12083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = _open(self._datfile, 'rb')
12183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.seek(pos)
12283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dat = f.read(siz)
12383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.close()
12483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return dat
12583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Append val to the data file, starting at a _BLOCKSIZE-aligned
12783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # offset.  The data file is first padded with NUL bytes (if needed)
12883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # to get to an aligned offset.  Return pair
12983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #     (starting offset of val, len(val))
13083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _addval(self, val):
13183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = _open(self._datfile, 'rb+')
13283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.seek(0, 2)
13383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        pos = int(f.tell())
13483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        npos = ((pos + _BLOCKSIZE - 1) // _BLOCKSIZE) * _BLOCKSIZE
13583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.write('\0'*(npos-pos))
13683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        pos = npos
13783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.write(val)
13883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.close()
13983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return (pos, len(val))
14083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
14183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Write val to the data file, starting at offset pos.  The caller
14283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # is responsible for ensuring that there's enough room starting at
14383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # pos to hold val, without overwriting some other value.  Return
14483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # pair (pos, len(val)).
14583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _setval(self, pos, val):
14683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = _open(self._datfile, 'rb+')
14783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.seek(pos)
14883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.write(val)
14983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.close()
15083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return (pos, len(val))
15183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
15283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # key is a new key whose associated value starts in the data file
15383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # at offset pos and with length siz.  Add an index record to
15483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # the in-memory index dict, and append one to the directory file.
15583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _addkey(self, key, pos_and_siz_pair):
15683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._index[key] = pos_and_siz_pair
15783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = _open(self._dirfile, 'a')
15883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._chmod(self._dirfile)
15983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.write("%r, %r\n" % (key, pos_and_siz_pair))
16083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.close()
16183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
16283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __setitem__(self, key, val):
16383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not type(key) == type('') == type(val):
16483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise TypeError, "keys and values must be strings"
16583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if key not in self._index:
16683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._addkey(key, self._addval(val))
16783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
16883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # See whether the new value is small enough to fit in the
16983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # (padded) space currently occupied by the old value.
17083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            pos, siz = self._index[key]
17183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE
17283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE
17383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if newblocks <= oldblocks:
17483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._index[key] = self._setval(pos, val)
17583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else:
17683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                # The new value doesn't fit in the (padded) space used
17783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                # by the old value.  The blocks used by the old value are
17883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                # forever lost.
17983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._index[key] = self._addval(val)
18083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
18183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # Note that _index may be out of synch with the directory
18283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # file now:  _setval() and _addval() don't update the directory
18383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # file.  This also means that the on-disk directory and data
18483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # files are in a mutually inconsistent state, and they'll
18583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # remain that way until _commit() is called.  Note that this
18683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # is a disaster (for the database) if the program crashes
18783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # (so that _commit() never gets called).
18883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
18983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __delitem__(self, key):
19083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # The blocks used by the associated value are lost.
19183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        del self._index[key]
19283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # XXX It's unclear why we do a _commit() here (the code always
19383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # XXX has, so I'm not changing it).  _setitem__ doesn't try to
19483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # XXX keep the directory file in synch.  Why should we?  Or
19583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # XXX why shouldn't __setitem__?
19683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._commit()
19783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def keys(self):
19983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._index.keys()
20083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def has_key(self, key):
20283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return key in self._index
20383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __contains__(self, key):
20583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return key in self._index
20683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def iterkeys(self):
20883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._index.iterkeys()
20983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    __iter__ = iterkeys
21083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
21183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __len__(self):
21283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return len(self._index)
21383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
21483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def close(self):
21583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._commit()
21683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._index = self._datfile = self._dirfile = self._bakfile = None
21783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
21883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    __del__ = close
21983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
22083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _chmod (self, file):
22183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if hasattr(self._os, 'chmod'):
22283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._os.chmod(file, self._mode)
22383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
22483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
22583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef open(file, flag=None, mode=0666):
22683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Open the database file, filename, and return corresponding object.
22783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
22883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    The flag argument, used to control how the database is opened in the
22983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    other DBM implementations, is ignored in the dumbdbm module; the
23083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    database is always opened for update, and will be created if it does
23183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    not exist.
23283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
23383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    The optional mode argument is the UNIX mode of the file, used only when
23483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    the database has to be created.  It defaults to octal code 0666 (and
23583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    will be modified by the prevailing umask).
23683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
23783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
23883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # flag argument is currently ignored
23983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
24083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Modify mode depending on the umask
24183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
24283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        um = _os.umask(0)
24383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _os.umask(um)
24483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except AttributeError:
24583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        pass
24683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
24783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Turn off any bits that are set in the umask
24883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        mode = mode & (~um)
24983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
25083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return _Database(file, mode)
251