[pypy-svn] r79439 - pypy/branch/gdbm/lib_pypy

dstromberg at codespeak.net dstromberg at codespeak.net
Wed Nov 24 00:51:18 CET 2010


Author: dstromberg
Date: Wed Nov 24 00:51:16 2010
New Revision: 79439

Added:
   pypy/branch/gdbm/lib_pypy/gdbm.py
Log:
Initial checkin: gdbm.py

Added: pypy/branch/gdbm/lib_pypy/gdbm.py
==============================================================================
--- (empty file)
+++ pypy/branch/gdbm/lib_pypy/gdbm.py	Wed Nov 24 00:51:16 2010
@@ -0,0 +1,245 @@
+
+'''libgdbm wrapper using ctypes'''
+
+from __future__ import with_statement
+
+#mport sys
+import ctypes
+import ctypes.util
+
+class error(Exception):
+    '''Exception for gdbm errors'''
+    def __init__(self, msg):
+        Exception.__init__(self)
+        self.msg = msg  
+
+    def __str__(self):
+        return self.msg
+
+class datum(ctypes.Structure):
+    # pylint: disable=R0903
+    # R0903: We don't need a lot of public methods
+    '''Hold a c string (with explicit length - not null terminated) describing a key or value'''
+
+    _fields_ = [
+        ('dptr', ctypes.POINTER(ctypes.c_char)),
+        ('dsize', ctypes.c_int),
+        ]
+
+    def __init__(self, text=None):
+        #sys.stderr.write('blee creating a datum from %s\n' % text)
+        if text is None:
+            self.free_when_done = True
+            self.dptr = None
+            self.dsize = -1
+            print 'No text, dptr is', bool(self.dptr)
+        else:
+            self.free_when_done = False
+            if isinstance(text, str):
+                ctypes.Structure.__init__(self, ctypes.create_string_buffer(text), len(text))
+            else:
+                raise TypeError, "text not None or str"
+
+    def __nonzero__(self):
+        if self.dptr:
+            return True
+        else:
+            return False
+
+    __bool__ = __nonzero__
+
+    def __str__(self):
+        char_star = ctypes.cast(self.dptr, ctypes.POINTER(ctypes.c_char))
+        return char_star[:self.dsize]
+
+    def __enter__(self):
+        self.free_when_done = True
+        return self
+
+    def free(self):
+        '''Free, in the style of C, the malloc'd memory associated with this datum (key or value)'''
+        address = ctypes.cast(self.dptr, ctypes.POINTER(ctypes.c_char))
+        FREE(address)
+        
+    def __exit__(self, *args):
+        # pylint: disable=W0212
+        # W0212: We seem to need _b_needsfree_
+
+        # if dptr isn't a NULL pointer, free what it points at
+        #sys.stderr.write('blee: self.dptr %s\n' % self.dptr)
+        #sys.stderr.write('blee: dir(self.dptr) %s\n' % dir(self.dptr))
+        # sys.stderr.write('blee: self.dptr.getcontents() %s\n' % self.dptr.getcontents())
+        #sys.stderr.write('blee: ctypes.addressof(self.dptr) %s\n' % hex(ctypes.addressof(self.dptr)))
+        #sys.stderr.write('blee: ctypes.byref(self.dptr) %s\n' % ctypes.byref(self.dptr))
+        if self.free_when_done and self.dptr:
+            self.free()
+
+class dbm(object):
+    '''A database object - providing access to gdbm tables'''
+    def __init__(self, dbmobj):
+        self._aobj = dbmobj
+
+    def close(self):
+        '''Close a table'''
+        if not self._aobj:
+            raise error('gdbm object has already been closed')
+        # Note that gdbm_close will free the memory malloc'd for the open database (but not for keys or values)
+        getattr(GDBM_LIB, FUNCS['close'])(self._aobj)
+        self._aobj = None
+
+    def __del__(self):
+        if self._aobj:
+            self.close()
+
+    def keys(self):
+        '''Get a list of the keys in the database'''
+        if not self._aobj:
+            raise error('gdbm object has already been closed')
+        allkeys = []
+        prior_key = getattr(GDBM_LIB, FUNCS['firstkey'])(self._aobj)
+        while prior_key:
+            key = str(prior_key)
+            allkeys.append(key)
+            new_key = getattr(GDBM_LIB, FUNCS['nextkey'])(self._aobj, prior_key)
+            prior_key.free()
+            prior_key = new_key
+        return allkeys
+
+    def get(self, key, default=None):
+        '''Get a key from the table'''
+        if not self._aobj:
+            raise error('gdbm object has already been closed')
+        key_datum = datum(key)
+        with getattr(GDBM_LIB, FUNCS['fetch'])(self._aobj, key_datum) as value_datum:
+            if value_datum:
+                result = str(value_datum)
+                return result
+        return default
+
+    def __len__(self):
+        return len(self.keys())
+
+    def __getitem__(self, key):
+        # It's OK to use self.get here, because gdbm itself can never return a None;
+        # It can only return strings (including the empty string), so None is a distinct sentinel value
+        value = self.get(key)
+        if value is None:
+            raise KeyError(key)
+        return value
+
+    def __setitem__(self, key, value):
+        if not self._aobj: 
+            raise error('gdbm object has already been closed')
+        key_datum = datum(key)
+        value = datum(value)
+        status = getattr(GDBM_LIB, FUNCS['store'])(self._aobj, key_datum, value, GDBM_LIB.DBM_REPLACE)
+        return status
+
+    def setdefault(self, key, default=''):
+        '''Get and maybe set a default'''
+        if not self._aobj:
+            raise error('gdbm object has already been closed')
+        value = self.get(key)
+        if value is not None:
+            return value
+        status = self[key] = default
+        if status < 0:
+            raise error("cannot add item to database")
+        return default
+
+    def has_key(self, key):
+        '''Does the table contain the requested key?'''
+        if not self._aobj:
+            raise error('gdbm object has already been closed')
+        value = self.get(key)
+        if value is None:
+            return False
+        else:
+            return True
+
+    def __delitem__(self, key):
+        if not self._aobj:
+            raise error('gdbm object has already been closed')
+        key_datum = datum(key)
+        status = getattr(GDBM_LIB, FUNCS['delete'])(self._aobj, key_datum)
+        if status < 0:
+            raise KeyError(key)
+
+### initialization
+
+def _init_func(name, argtypes=None, restype=None):
+    '''Extract functions from libgdbm'''
+    try:
+        func = getattr(GDBM_LIB, '_gdbm_' + name)
+        FUNCS[name] = '_gdbm_' + name
+    except AttributeError:
+        func = getattr(GDBM_LIB, 'gdbm_' + name)
+        FUNCS[name] = 'gdbm_' + name
+    if argtypes is not None:
+        func.argtypes = argtypes
+    if restype is not None:
+        func.restype = restype
+
+GDBM_LIBPATH = ctypes.util.find_library('gdbm')
+if not GDBM_LIBPATH:
+    raise ImportError("Cannot find gdbm library")
+GDBM_LIB = ctypes.CDLL(GDBM_LIBPATH) # Linux
+
+FUNCS = {}
+_init_func('open', argtypes=(ctypes.c_char_p, ctypes.c_int, ctypes.c_int))
+_init_func('close', restype=ctypes.c_void_p)
+_init_func('firstkey', restype=datum)
+_init_func('nextkey', argtypes=(ctypes.c_void_p, datum), restype=datum)
+_init_func('fetch', restype=datum)
+_init_func('store', restype=ctypes.c_int)
+_init_func('delete', restype=ctypes.c_int)
+
+C_LIBPATH = ctypes.util.find_library('c')
+if not C_LIBPATH:
+    raise ImportError("Cannot find c library")
+C_LIB = ctypes.CDLL(C_LIBPATH) # Linux
+
+try:
+    FREE = getattr(C_LIB, '_free')
+except AttributeError:
+    FREE = getattr(C_LIB, 'free')
+#FREE.argtypes = [ ctypes.POINTER(ctypes.c_char) ]
+FREE.argtypes = [ ctypes.c_void_p ]
+FREE.restype = None
+
+GDBM_LIB.DBM_INSERT = 0
+GDBM_LIB.DBM_REPLACE = 1
+
+# pylint: disable=W0622
+# W0622: We need to redefine open this time - it's part of the API
+def open(filename, flag='r', mode=0666):
+    "open a gdbm database"
+    if not isinstance(filename, str):
+        raise TypeError("expected string")
+
+    openflag = 0
+
+    gdbm_reader  = 0        # A reader.
+    gdbm_writer  = 1        # A writer.
+    gdbm_wrcreat = 2        # A writer.  Create the db if needed.
+    gdbm_newdb   = 3        # A writer.  Always create a new db.
+
+    try:
+        openflag = {
+            'r': gdbm_reader,
+            'rw': gdbm_writer,
+            'w': gdbm_writer,
+            'c': gdbm_wrcreat,
+            'n': gdbm_newdb,
+            }[flag]
+    except KeyError:
+        raise error("arg 2 to open should be 'r', 'w', 'rw', 'c', or 'n'")
+
+    #                                                filename, block_size, read_write, mode, fatal_func
+    a_db = getattr(GDBM_LIB, FUNCS['open'])(filename, 2**18,         openflag,   mode, 0)
+    if a_db == 0:
+        raise error("Could not open file %s" % filename)
+    return dbm(a_db)
+
+__all__ = ('datum', 'dbm', 'error', 'open')
+



More information about the Pypy-commit mailing list