mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			401 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			401 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #----------------------------------------------------------------------
 | |
| #  Copyright (c) 1999-2001, Digital Creations, Fredericksburg, VA, USA
 | |
| #  and Andrew Kuchling. All rights reserved.
 | |
| #
 | |
| #  Redistribution and use in source and binary forms, with or without
 | |
| #  modification, are permitted provided that the following conditions are
 | |
| #  met:
 | |
| #
 | |
| #    o Redistributions of source code must retain the above copyright
 | |
| #      notice, this list of conditions, and the disclaimer that follows.
 | |
| #
 | |
| #    o Redistributions in binary form must reproduce the above copyright
 | |
| #      notice, this list of conditions, and the following disclaimer in
 | |
| #      the documentation and/or other materials provided with the
 | |
| #      distribution.
 | |
| #
 | |
| #    o Neither the name of Digital Creations nor the names of its
 | |
| #      contributors may be used to endorse or promote products derived
 | |
| #      from this software without specific prior written permission.
 | |
| #
 | |
| #  THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS
 | |
| #  IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 | |
| #  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 | |
| #  PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL
 | |
| #  CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 | |
| #  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 | |
| #  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 | |
| #  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | |
| #  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 | |
| #  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 | |
| #  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 | |
| #  DAMAGE.
 | |
| #----------------------------------------------------------------------
 | |
| 
 | |
| 
 | |
| """Support for BerkeleyDB 3.3 through 4.4 with a simple interface.
 | |
| 
 | |
| For the full featured object oriented interface use the bsddb.db module
 | |
| instead.  It mirrors the Sleepycat BerkeleyDB C API.
 | |
| """
 | |
| 
 | |
| try:
 | |
|     if __name__ == 'bsddb3':
 | |
|         # import _pybsddb binary as it should be the more recent version from
 | |
|         # a standalone pybsddb addon package than the version included with
 | |
|         # python as bsddb._bsddb.
 | |
|         import _pybsddb
 | |
|         _bsddb = _pybsddb
 | |
|         from bsddb3.dbutils import DeadlockWrap as _DeadlockWrap
 | |
|     else:
 | |
|         import _bsddb
 | |
|         from bsddb.dbutils import DeadlockWrap as _DeadlockWrap
 | |
| except ImportError:
 | |
|     # Remove ourselves from sys.modules
 | |
|     import sys
 | |
|     del sys.modules[__name__]
 | |
|     raise
 | |
| 
 | |
| # bsddb3 calls it db, but provide _db for backwards compatibility
 | |
| db = _db = _bsddb
 | |
| __version__ = db.__version__
 | |
| 
 | |
| error = db.DBError  # So bsddb.error will mean something...
 | |
| 
 | |
| #----------------------------------------------------------------------
 | |
| 
 | |
| import sys, os
 | |
| 
 | |
| # for backwards compatibility with python versions older than 2.3, the
 | |
| # iterator interface is dynamically defined and added using a mixin
 | |
| # class.  old python can't tokenize it due to the yield keyword.
 | |
| if sys.version >= '2.3':
 | |
|     import UserDict
 | |
|     from weakref import ref
 | |
|     exec """
 | |
| class _iter_mixin(UserDict.DictMixin):
 | |
|     def _make_iter_cursor(self):
 | |
|         cur = _DeadlockWrap(self.db.cursor)
 | |
|         key = id(cur)
 | |
|         self._cursor_refs[key] = ref(cur, self._gen_cref_cleaner(key))
 | |
|         return cur
 | |
| 
 | |
|     def _gen_cref_cleaner(self, key):
 | |
|         # use generate the function for the weakref callback here
 | |
|         # to ensure that we do not hold a strict reference to cur
 | |
|         # in the callback.
 | |
|         return lambda ref: self._cursor_refs.pop(key, None)
 | |
| 
 | |
|     def __iter__(self):
 | |
|         try:
 | |
|             cur = self._make_iter_cursor()
 | |
| 
 | |
|             # FIXME-20031102-greg: race condition.  cursor could
 | |
|             # be closed by another thread before this call.
 | |
| 
 | |
|             # since we're only returning keys, we call the cursor
 | |
|             # methods with flags=0, dlen=0, dofs=0
 | |
|             key = _DeadlockWrap(cur.first, 0,0,0)[0]
 | |
|             yield key
 | |
| 
 | |
|             next = cur.next
 | |
|             while 1:
 | |
|                 try:
 | |
|                     key = _DeadlockWrap(next, 0,0,0)[0]
 | |
|                     yield key
 | |
|                 except _bsddb.DBCursorClosedError:
 | |
|                     cur = self._make_iter_cursor()
 | |
|                     # FIXME-20031101-greg: race condition.  cursor could
 | |
|                     # be closed by another thread before this call.
 | |
|                     _DeadlockWrap(cur.set, key,0,0,0)
 | |
|                     next = cur.next
 | |
|         except _bsddb.DBNotFoundError:
 | |
|             return
 | |
|         except _bsddb.DBCursorClosedError:
 | |
|             # the database was modified during iteration.  abort.
 | |
|             return
 | |
| 
 | |
|     def iteritems(self):
 | |
|         if not self.db:
 | |
|             return
 | |
|         try:
 | |
|             cur = self._make_iter_cursor()
 | |
| 
 | |
|             # FIXME-20031102-greg: race condition.  cursor could
 | |
|             # be closed by another thread before this call.
 | |
| 
 | |
|             kv = _DeadlockWrap(cur.first)
 | |
|             key = kv[0]
 | |
|             yield kv
 | |
| 
 | |
|             next = cur.next
 | |
|             while 1:
 | |
|                 try:
 | |
|                     kv = _DeadlockWrap(next)
 | |
|                     key = kv[0]
 | |
|                     yield kv
 | |
|                 except _bsddb.DBCursorClosedError:
 | |
|                     cur = self._make_iter_cursor()
 | |
|                     # FIXME-20031101-greg: race condition.  cursor could
 | |
|                     # be closed by another thread before this call.
 | |
|                     _DeadlockWrap(cur.set, key,0,0,0)
 | |
|                     next = cur.next
 | |
|         except _bsddb.DBNotFoundError:
 | |
|             return
 | |
|         except _bsddb.DBCursorClosedError:
 | |
|             # the database was modified during iteration.  abort.
 | |
|             return
 | |
| """
 | |
| else:
 | |
|     class _iter_mixin: pass
 | |
| 
 | |
| 
 | |
| class _DBWithCursor(_iter_mixin):
 | |
|     """
 | |
|     A simple wrapper around DB that makes it look like the bsddbobject in
 | |
|     the old module.  It uses a cursor as needed to provide DB traversal.
 | |
|     """
 | |
|     def __init__(self, db):
 | |
|         self.db = db
 | |
|         self.db.set_get_returns_none(0)
 | |
| 
 | |
|         # FIXME-20031101-greg: I believe there is still the potential
 | |
|         # for deadlocks in a multithreaded environment if someone
 | |
|         # attempts to use the any of the cursor interfaces in one
 | |
|         # thread while doing a put or delete in another thread.  The
 | |
|         # reason is that _checkCursor and _closeCursors are not atomic
 | |
|         # operations.  Doing our own locking around self.dbc,
 | |
|         # self.saved_dbc_key and self._cursor_refs could prevent this.
 | |
|         # TODO: A test case demonstrating the problem needs to be written.
 | |
| 
 | |
|         # self.dbc is a DBCursor object used to implement the
 | |
|         # first/next/previous/last/set_location methods.
 | |
|         self.dbc = None
 | |
|         self.saved_dbc_key = None
 | |
| 
 | |
|         # a collection of all DBCursor objects currently allocated
 | |
|         # by the _iter_mixin interface.
 | |
|         self._cursor_refs = {}
 | |
| 
 | |
|     def __del__(self):
 | |
|         self.close()
 | |
| 
 | |
|     def _checkCursor(self):
 | |
|         if self.dbc is None:
 | |
|             self.dbc = _DeadlockWrap(self.db.cursor)
 | |
|             if self.saved_dbc_key is not None:
 | |
|                 _DeadlockWrap(self.dbc.set, self.saved_dbc_key)
 | |
|                 self.saved_dbc_key = None
 | |
| 
 | |
|     # This method is needed for all non-cursor DB calls to avoid
 | |
|     # BerkeleyDB deadlocks (due to being opened with DB_INIT_LOCK
 | |
|     # and DB_THREAD to be thread safe) when intermixing database
 | |
|     # operations that use the cursor internally with those that don't.
 | |
|     def _closeCursors(self, save=1):
 | |
|         if self.dbc:
 | |
|             c = self.dbc
 | |
|             self.dbc = None
 | |
|             if save:
 | |
|                 try:
 | |
|                     self.saved_dbc_key = _DeadlockWrap(c.current, 0,0,0)[0]
 | |
|                 except db.DBError:
 | |
|                     pass
 | |
|             _DeadlockWrap(c.close)
 | |
|             del c
 | |
|         for cref in self._cursor_refs.values():
 | |
|             c = cref()
 | |
|             if c is not None:
 | |
|                 _DeadlockWrap(c.close)
 | |
| 
 | |
|     def _checkOpen(self):
 | |
|         if self.db is None:
 | |
|             raise error, "BSDDB object has already been closed"
 | |
| 
 | |
|     def isOpen(self):
 | |
|         return self.db is not None
 | |
| 
 | |
|     def __len__(self):
 | |
|         self._checkOpen()
 | |
|         return _DeadlockWrap(lambda: len(self.db))  # len(self.db)
 | |
| 
 | |
|     def __getitem__(self, key):
 | |
|         self._checkOpen()
 | |
|         return _DeadlockWrap(lambda: self.db[key])  # self.db[key]
 | |
| 
 | |
|     def __setitem__(self, key, value):
 | |
|         self._checkOpen()
 | |
|         self._closeCursors()
 | |
|         def wrapF():
 | |
|             self.db[key] = value
 | |
|         _DeadlockWrap(wrapF)  # self.db[key] = value
 | |
| 
 | |
|     def __delitem__(self, key):
 | |
|         self._checkOpen()
 | |
|         self._closeCursors()
 | |
|         def wrapF():
 | |
|             del self.db[key]
 | |
|         _DeadlockWrap(wrapF)  # del self.db[key]
 | |
| 
 | |
|     def close(self):
 | |
|         self._closeCursors(save=0)
 | |
|         if self.dbc is not None:
 | |
|             _DeadlockWrap(self.dbc.close)
 | |
|         v = 0
 | |
|         if self.db is not None:
 | |
|             v = _DeadlockWrap(self.db.close)
 | |
|         self.dbc = None
 | |
|         self.db = None
 | |
|         return v
 | |
| 
 | |
|     def keys(self):
 | |
|         self._checkOpen()
 | |
|         return _DeadlockWrap(self.db.keys)
 | |
| 
 | |
|     def has_key(self, key):
 | |
|         self._checkOpen()
 | |
|         return _DeadlockWrap(self.db.has_key, key)
 | |
| 
 | |
|     def set_location(self, key):
 | |
|         self._checkOpen()
 | |
|         self._checkCursor()
 | |
|         return _DeadlockWrap(self.dbc.set_range, key)
 | |
| 
 | |
|     def next(self):
 | |
|         self._checkOpen()
 | |
|         self._checkCursor()
 | |
|         rv = _DeadlockWrap(self.dbc.next)
 | |
|         return rv
 | |
| 
 | |
|     def previous(self):
 | |
|         self._checkOpen()
 | |
|         self._checkCursor()
 | |
|         rv = _DeadlockWrap(self.dbc.prev)
 | |
|         return rv
 | |
| 
 | |
|     def first(self):
 | |
|         self._checkOpen()
 | |
|         # fix 1725856: don't needlessly try to restore our cursor position
 | |
|         self.saved_dbc_key = None
 | |
|         self._checkCursor()
 | |
|         rv = _DeadlockWrap(self.dbc.first)
 | |
|         return rv
 | |
| 
 | |
|     def last(self):
 | |
|         self._checkOpen()
 | |
|         # fix 1725856: don't needlessly try to restore our cursor position
 | |
|         self.saved_dbc_key = None
 | |
|         self._checkCursor()
 | |
|         rv = _DeadlockWrap(self.dbc.last)
 | |
|         return rv
 | |
| 
 | |
|     def sync(self):
 | |
|         self._checkOpen()
 | |
|         return _DeadlockWrap(self.db.sync)
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------
 | |
| # Compatibility object factory functions
 | |
| 
 | |
| def hashopen(file, flag='c', mode=0666, pgsize=None, ffactor=None, nelem=None,
 | |
|             cachesize=None, lorder=None, hflags=0):
 | |
| 
 | |
|     flags = _checkflag(flag, file)
 | |
|     e = _openDBEnv(cachesize)
 | |
|     d = db.DB(e)
 | |
|     d.set_flags(hflags)
 | |
|     if pgsize is not None:    d.set_pagesize(pgsize)
 | |
|     if lorder is not None:    d.set_lorder(lorder)
 | |
|     if ffactor is not None:   d.set_h_ffactor(ffactor)
 | |
|     if nelem is not None:     d.set_h_nelem(nelem)
 | |
|     d.open(file, db.DB_HASH, flags, mode)
 | |
|     return _DBWithCursor(d)
 | |
| 
 | |
| #----------------------------------------------------------------------
 | |
| 
 | |
| def btopen(file, flag='c', mode=0666,
 | |
|             btflags=0, cachesize=None, maxkeypage=None, minkeypage=None,
 | |
|             pgsize=None, lorder=None):
 | |
| 
 | |
|     flags = _checkflag(flag, file)
 | |
|     e = _openDBEnv(cachesize)
 | |
|     d = db.DB(e)
 | |
|     if pgsize is not None: d.set_pagesize(pgsize)
 | |
|     if lorder is not None: d.set_lorder(lorder)
 | |
|     d.set_flags(btflags)
 | |
|     if minkeypage is not None: d.set_bt_minkey(minkeypage)
 | |
|     if maxkeypage is not None: d.set_bt_maxkey(maxkeypage)
 | |
|     d.open(file, db.DB_BTREE, flags, mode)
 | |
|     return _DBWithCursor(d)
 | |
| 
 | |
| #----------------------------------------------------------------------
 | |
| 
 | |
| 
 | |
| def rnopen(file, flag='c', mode=0666,
 | |
|             rnflags=0, cachesize=None, pgsize=None, lorder=None,
 | |
|             rlen=None, delim=None, source=None, pad=None):
 | |
| 
 | |
|     flags = _checkflag(flag, file)
 | |
|     e = _openDBEnv(cachesize)
 | |
|     d = db.DB(e)
 | |
|     if pgsize is not None: d.set_pagesize(pgsize)
 | |
|     if lorder is not None: d.set_lorder(lorder)
 | |
|     d.set_flags(rnflags)
 | |
|     if delim is not None: d.set_re_delim(delim)
 | |
|     if rlen is not None: d.set_re_len(rlen)
 | |
|     if source is not None: d.set_re_source(source)
 | |
|     if pad is not None: d.set_re_pad(pad)
 | |
|     d.open(file, db.DB_RECNO, flags, mode)
 | |
|     return _DBWithCursor(d)
 | |
| 
 | |
| #----------------------------------------------------------------------
 | |
| 
 | |
| def _openDBEnv(cachesize):
 | |
|     e = db.DBEnv()
 | |
|     if cachesize is not None:
 | |
|         if cachesize >= 20480:
 | |
|             e.set_cachesize(0, cachesize)
 | |
|         else:
 | |
|             raise error, "cachesize must be >= 20480"
 | |
|     e.set_lk_detect(db.DB_LOCK_DEFAULT)
 | |
|     e.open('.', db.DB_PRIVATE | db.DB_CREATE | db.DB_THREAD | db.DB_INIT_LOCK | db.DB_INIT_MPOOL)
 | |
|     return e
 | |
| 
 | |
| def _checkflag(flag, file):
 | |
|     if flag == 'r':
 | |
|         flags = db.DB_RDONLY
 | |
|     elif flag == 'rw':
 | |
|         flags = 0
 | |
|     elif flag == 'w':
 | |
|         flags =  db.DB_CREATE
 | |
|     elif flag == 'c':
 | |
|         flags =  db.DB_CREATE
 | |
|     elif flag == 'n':
 | |
|         flags = db.DB_CREATE
 | |
|         #flags = db.DB_CREATE | db.DB_TRUNCATE
 | |
|         # we used db.DB_TRUNCATE flag for this before but BerkeleyDB
 | |
|         # 4.2.52 changed to disallowed truncate with txn environments.
 | |
|         if file is not None and os.path.isfile(file):
 | |
|             os.unlink(file)
 | |
|     else:
 | |
|         raise error, "flags should be one of 'r', 'w', 'c' or 'n'"
 | |
|     return flags | db.DB_THREAD
 | |
| 
 | |
| #----------------------------------------------------------------------
 | |
| 
 | |
| 
 | |
| # This is a silly little hack that allows apps to continue to use the
 | |
| # DB_THREAD flag even on systems without threads without freaking out
 | |
| # BerkeleyDB.
 | |
| #
 | |
| # This assumes that if Python was built with thread support then
 | |
| # BerkeleyDB was too.
 | |
| 
 | |
| try:
 | |
|     import thread
 | |
|     del thread
 | |
|     if db.version() < (3, 3, 0):
 | |
|         db.DB_THREAD = 0
 | |
| except ImportError:
 | |
|     db.DB_THREAD = 0
 | |
| 
 | |
| #----------------------------------------------------------------------
 | 
