1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
from _gdbm_cffi import ffi, lib # generated by _gdbm_build.py
import sys, os, threading
_lock = threading.Lock()
class error(IOError):
pass
def _checkstr(key):
if isinstance(key, str):
key = key.encode("ascii")
if not isinstance(key, bytes):
raise TypeError("gdbm mappings have string indices only")
return key
def _fromstr(key):
if isinstance(key, str):
key = key.encode(sys.getdefaultencoding())
elif not isinstance(key, bytes):
msg = "gdbm mappings have bytes or string indices only, not {!r}"
raise TypeError(msg.format(type(key).__name__))
return {'dptr': ffi.new("char[]", key), 'dsize': len(key)}
class gdbm(object):
__ll_dbm = None
# All public methods need to acquire the lock; all private methods
# assume the lock is already held. Thus public methods cannot call
# other public methods.
def __init__(self, filename, iflags, mode):
with _lock:
res = lib.gdbm_open(filename, 0, iflags, mode, ffi.NULL)
self.__size = -1
if not res:
self.__raise_from_errno()
self.__ll_dbm = res
def close(self):
with _lock:
if self.__ll_dbm:
lib.gdbm_close(self.__ll_dbm)
self.__ll_dbm = None
def __raise_from_errno(self):
if ffi.errno:
raise error(ffi.errno, os.strerror(ffi.errno))
raise error(lib.gdbm_errno, lib.gdbm_strerror(lib.gdbm_errno))
def __len__(self):
with _lock:
if self.__size < 0:
self.__size = len(self.__keys())
return self.__size
def __setitem__(self, key, value):
with _lock:
self.__check_closed()
self.__size = -1
r = lib.gdbm_store(self.__ll_dbm, _fromstr(key), _fromstr(value),
lib.GDBM_REPLACE)
if r < 0:
self.__raise_from_errno()
def __delitem__(self, key):
with _lock:
self.__check_closed()
self.__size = -1
res = lib.gdbm_delete(self.__ll_dbm, _fromstr(key))
if res < 0:
raise KeyError(key)
def __contains__(self, key):
with _lock:
self.__check_closed()
key = _checkstr(key)
return lib.pygdbm_exists(self.__ll_dbm, key, len(key))
def get(self, key, default=None):
with _lock:
self.__check_closed()
key = _checkstr(key)
drec = lib.pygdbm_fetch(self.__ll_dbm, key, len(key))
if not drec.dptr:
return default
res = bytes(ffi.buffer(drec.dptr, drec.dsize))
lib.free(drec.dptr)
return res
def __getitem__(self, key):
value = self.get(key)
if value is None:
raise KeyError(key)
return value
def __keys(self):
self.__check_closed()
l = []
key = lib.gdbm_firstkey(self.__ll_dbm)
while key.dptr:
l.append(bytes(ffi.buffer(key.dptr, key.dsize)))
nextkey = lib.gdbm_nextkey(self.__ll_dbm, key)
lib.free(key.dptr)
key = nextkey
return l
def keys(self):
with _lock:
return self.__keys()
def firstkey(self):
with _lock:
self.__check_closed()
key = lib.gdbm_firstkey(self.__ll_dbm)
if key.dptr:
res = bytes(ffi.buffer(key.dptr, key.dsize))
lib.free(key.dptr)
return res
def nextkey(self, key):
with _lock:
self.__check_closed()
key = lib.gdbm_nextkey(self.__ll_dbm, _fromstr(key))
if key.dptr:
res = bytes(ffi.buffer(key.dptr, key.dsize))
lib.free(key.dptr)
return res
def reorganize(self):
with _lock:
self.__check_closed()
if lib.gdbm_reorganize(self.__ll_dbm) < 0:
self.__raise_from_errno()
def __check_closed(self):
if not self.__ll_dbm:
raise error(0, "GDBM object has already been closed")
__del__ = close
def sync(self):
with _lock:
self.__check_closed()
lib.gdbm_sync(self.__ll_dbm)
def setdefault(self, key, default=None):
value = self.get(key)
if value is not None:
return value
self[key] = default
return default
def __enter__(self):
return self
def __exit__(self, *exc_info):
self.close()
def open(filename, flags='r', mode=0o666):
if not isinstance(filename, str):
raise TypeError("must be str, not %s" % type(filename).__name__)
filename = filename.encode(sys.getdefaultencoding())
if flags[0] == 'r':
iflags = lib.GDBM_READER
elif flags[0] == 'w':
iflags = lib.GDBM_WRITER
elif flags[0] == 'c':
iflags = lib.GDBM_WRCREAT
elif flags[0] == 'n':
iflags = lib.GDBM_NEWDB
else:
raise error(0, "First flag must be one of 'r', 'w', 'c' or 'n'")
for flag in flags[1:]:
if flag == 'f':
iflags |= lib.GDBM_FAST
elif flag == 's':
iflags |= lib.GDBM_SYNC
elif flag == 'u':
iflags |= lib.GDBM_NOLOCK
else:
raise error(0, "Flag '%s' not supported" % flag)
return gdbm(filename, iflags, mode)
open_flags = "rwcnfsu"
|