Commit 64ce5052 authored by Martin v. Löwis's avatar Martin v. Löwis

Make bsddb use bytes as keys and values. Add StringKeys

and StringValues classes. Fix test suite.
parent 33d2689f
...@@ -93,6 +93,17 @@ function. Consult the Berkeley DB documentation for their use and ...@@ -93,6 +93,17 @@ function. Consult the Berkeley DB documentation for their use and
interpretation. interpretation.
\end{funcdesc} \end{funcdesc}
\begin{classdesc}{StringKeys}{db}
Wrapper class around a DB object that supports string keys
(rather than bytes). All keys are encoded as UTF-8, then passed
to the underlying object. \versionadded{3.0}
\end{classdesc}
\begin{classdesc}{StringValues}{db}
Wrapper class around a DB object that supports string values
(rather than bytes). All values are encoded as UTF-8, then passed
to the underlying object. \versionadded{3.0}
\end{classdesc}
\begin{seealso} \begin{seealso}
\seemodule{dbhash}{DBM-style interface to the \module{bsddb}} \seemodule{dbhash}{DBM-style interface to the \module{bsddb}}
......
...@@ -64,15 +64,9 @@ error = db.DBError # So bsddb.error will mean something... ...@@ -64,15 +64,9 @@ error = db.DBError # So bsddb.error will mean something...
#---------------------------------------------------------------------- #----------------------------------------------------------------------
import sys, os import sys, os, UserDict
from weakref import ref
# 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): class _iter_mixin(UserDict.DictMixin):
def _make_iter_cursor(self): def _make_iter_cursor(self):
cur = _DeadlockWrap(self.db.cursor) cur = _DeadlockWrap(self.db.cursor)
...@@ -145,10 +139,6 @@ class _iter_mixin(UserDict.DictMixin): ...@@ -145,10 +139,6 @@ class _iter_mixin(UserDict.DictMixin):
except _bsddb.DBCursorClosedError: except _bsddb.DBCursorClosedError:
# the database was modified during iteration. abort. # the database was modified during iteration. abort.
return return
""")
else:
class _iter_mixin: pass
class _DBWithCursor(_iter_mixin): class _DBWithCursor(_iter_mixin):
""" """
...@@ -290,6 +280,138 @@ class _DBWithCursor(_iter_mixin): ...@@ -290,6 +280,138 @@ class _DBWithCursor(_iter_mixin):
self._checkOpen() self._checkOpen()
return _DeadlockWrap(self.db.sync) return _DeadlockWrap(self.db.sync)
class _ExposedProperties:
@property
def _cursor_refs(self):
return self.db._cursor_refs
class StringKeys(UserDict.DictMixin, _ExposedProperties):
"""Wrapper around DB object that automatically encodes
all keys as UTF-8; the keys must be strings."""
def __init__(self, db):
self.db = db
def __len__(self):
return len(self.db)
def __getitem__(self, key):
return self.db[key.encode("utf-8")]
def __setitem__(self, key, value):
self.db[key.encode("utf-8")] = value
def __delitem__(self, key):
del self.db[key.encode("utf-8")]
def __iter__(self):
for k in self.db:
yield k.decode("utf-8")
def close(self):
self.db.close()
def keys(self):
for k in self.db.keys():
yield k.decode("utf-8")
def has_key(self, key):
return self.db.has_key(key.encode("utf-8"))
__contains__ = has_key
def values(self):
return self.db.values()
def items(self):
for k,v in self.db.items():
yield k.decode("utf-8"), v
def set_location(self, key):
return self.db.set_location(key.encode("utf-8"))
def next(self):
key, value = self.db.next()
return key.decode("utf-8"), value
def previous(self):
key, value = self.db.previous()
return key.decode("utf-8"), value
def first(self):
key, value = self.db.first()
return key.decode("utf-8"), value
def last(self):
key, value = self.db.last()
return key.decode("utf-8"), value
def sync(self):
return self.db.sync()
class StringValues(UserDict.DictMixin, _ExposedProperties):
"""Wrapper around DB object that automatically encodes
all keys as UTF-8; the keys must be strings."""
def __init__(self, db):
self.db = db
def __len__(self):
return len(self.db)
def __getitem__(self, key):
return self.db[key].decode("utf-8")
def __setitem__(self, key, value):
self.db[key] = value.encode("utf-8")
def __delitem__(self, key):
del self.db[key]
def __iter__(self):
return iter(self.db)
def close(self):
self.db.close()
def keys(self):
return self.db.keys()
def has_key(self, key):
return self.db.has_key(key)
__contains__ = has_key
def values(self):
for v in self.db.values():
yield v.decode("utf-8")
def items(self):
for k,v in self.db.items():
yield k, v.decode("utf-8")
def set_location(self, key):
return self.db.set_location(key)
def next(self):
key, value = self.db.next()
return key, value.decode("utf-8")
def previous(self):
key, value = self.db.previous()
return key, value.decode("utf-8")
def first(self):
key, value = self.db.first()
return key, value.decode("utf-8")
def last(self):
key, value = self.db.last()
return key, value.decode("utf-8")
def sync(self):
return self.db.sync()
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Compatibility object factory functions # Compatibility object factory functions
...@@ -375,7 +497,7 @@ def _checkflag(flag, file): ...@@ -375,7 +497,7 @@ def _checkflag(flag, file):
if file is not None and os.path.isfile(file): if file is not None and os.path.isfile(file):
os.unlink(file) os.unlink(file)
else: else:
raise error, "flags should be one of 'r', 'w', 'c' or 'n'" raise error, "flags should be one of 'r', 'w', 'c' or 'n', not "+repr(flag)
return flags | db.DB_THREAD return flags | db.DB_THREAD
#---------------------------------------------------------------------- #----------------------------------------------------------------------
......
...@@ -12,8 +12,12 @@ from test import test_support ...@@ -12,8 +12,12 @@ from test import test_support
class TestBSDDB(unittest.TestCase): class TestBSDDB(unittest.TestCase):
openflag = 'c' openflag = 'c'
def do_open(self, *args, **kw):
# openmethod is a list so that it's not mistaken as an instance method
return bsddb.StringValues(bsddb.StringKeys(self.openmethod[0](*args, **kw)))
def setUp(self): def setUp(self):
self.f = self.openmethod[0](self.fname, self.openflag, cachesize=32768) self.f = self.do_open(self.fname, self.openflag, cachesize=32768)
self.d = dict(q='Guido', w='van', e='Rossum', r='invented', t='Python', y='') self.d = dict(q='Guido', w='van', e='Rossum', r='invented', t='Python', y='')
for k, v in self.d.items(): for k, v in self.d.items():
self.f[k] = v self.f[k] = v
...@@ -47,7 +51,7 @@ class TestBSDDB(unittest.TestCase): ...@@ -47,7 +51,7 @@ class TestBSDDB(unittest.TestCase):
# so finish here. # so finish here.
return return
self.f.close() self.f.close()
self.f = self.openmethod[0](self.fname, 'w') self.f = self.do_open(self.fname, 'w')
for k, v in self.d.items(): for k, v in self.d.items():
self.assertEqual(self.f[k], v) self.assertEqual(self.f[k], v)
......
...@@ -99,7 +99,7 @@ ...@@ -99,7 +99,7 @@
#endif #endif
#define PY_BSDDB_VERSION "4.5.0" #define PY_BSDDB_VERSION "4.5.0"
static char *rcs_id = "$Id$"; static char *svn_id = "$Id$";
#if (PY_VERSION_HEX < 0x02050000) #if (PY_VERSION_HEX < 0x02050000)
...@@ -413,7 +413,7 @@ make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags) ...@@ -413,7 +413,7 @@ make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags)
/* no need to do anything, the structure has already been zeroed */ /* no need to do anything, the structure has already been zeroed */
} }
else if (PyString_Check(keyobj)) { else if (PyBytes_Check(keyobj)) {
/* verify access method type */ /* verify access method type */
type = _DB_get_type(self); type = _DB_get_type(self);
if (type == -1) if (type == -1)
...@@ -425,8 +425,8 @@ make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags) ...@@ -425,8 +425,8 @@ make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags)
return 0; return 0;
} }
key->data = PyString_AS_STRING(keyobj); key->data = PyBytes_AS_STRING(keyobj);
key->size = PyString_GET_SIZE(keyobj); key->size = PyBytes_GET_SIZE(keyobj);
} }
else if (PyInt_Check(keyobj)) { else if (PyInt_Check(keyobj)) {
...@@ -460,7 +460,7 @@ make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags) ...@@ -460,7 +460,7 @@ make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags)
} }
else { else {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"String or Integer object expected for key, %s found", "Bytes or Integer object expected for key, %s found",
Py_Type(keyobj)->tp_name); Py_Type(keyobj)->tp_name);
return 0; return 0;
} }
...@@ -721,13 +721,13 @@ static PyObject* _DBCursor_get(DBCursorObject* self, int extra_flags, ...@@ -721,13 +721,13 @@ static PyObject* _DBCursor_get(DBCursorObject* self, int extra_flags,
case DB_RECNO: case DB_RECNO:
case DB_QUEUE: case DB_QUEUE:
retval = Py_BuildValue("is#", *((db_recno_t*)key.data), retval = Py_BuildValue("iy#", *((db_recno_t*)key.data),
data.data, data.size); data.data, data.size);
break; break;
case DB_HASH: case DB_HASH:
case DB_BTREE: case DB_BTREE:
default: default:
retval = Py_BuildValue("s#s#", key.data, key.size, retval = Py_BuildValue("y#y#", key.data, key.size,
data.data, data.size); data.data, data.size);
break; break;
} }
...@@ -1196,18 +1196,13 @@ _db_associateCallback(DB* db, const DBT* priKey, const DBT* priData, ...@@ -1196,18 +1196,13 @@ _db_associateCallback(DB* db, const DBT* priKey, const DBT* priData,
else if (PyInt_Check(result)) { else if (PyInt_Check(result)) {
retval = PyInt_AsLong(result); retval = PyInt_AsLong(result);
} }
else if (PyString_Check(result)) { else if (PyBytes_Check(result)) {
char* data; char* data;
Py_ssize_t size; Py_ssize_t size;
CLEAR_DBT(*secKey); CLEAR_DBT(*secKey);
#if PYTHON_API_VERSION <= 1007 size = PyBytes_Size(result);
/* 1.5 compatibility */ data = PyBytes_AsString(result);
size = PyString_Size(result);
data = PyString_AsString(result);
#else
PyString_AsStringAndSize(result, &data, &size);
#endif
secKey->flags = DB_DBT_APPMALLOC; /* DB will free */ secKey->flags = DB_DBT_APPMALLOC; /* DB will free */
secKey->data = malloc(size); /* TODO, check this */ secKey->data = malloc(size); /* TODO, check this */
if (secKey->data) { if (secKey->data) {
...@@ -1548,7 +1543,7 @@ DB_get(DBObject* self, PyObject* args, PyObject* kwargs) ...@@ -1548,7 +1543,7 @@ DB_get(DBObject* self, PyObject* args, PyObject* kwargs)
retval = Py_BuildValue("s#s#", key.data, key.size, data.data, retval = Py_BuildValue("s#s#", key.data, key.size, data.data,
data.size); data.size);
else /* return just the data */ else /* return just the data */
retval = PyString_FromStringAndSize((char*)data.data, data.size); retval = PyBytes_FromStringAndSize((char*)data.data, data.size);
FREE_DBT(data); FREE_DBT(data);
} }
FREE_DBT(key); FREE_DBT(key);
...@@ -1617,13 +1612,13 @@ DB_pget(DBObject* self, PyObject* args, PyObject* kwargs) ...@@ -1617,13 +1612,13 @@ DB_pget(DBObject* self, PyObject* args, PyObject* kwargs)
else if (!err) { else if (!err) {
PyObject *pkeyObj; PyObject *pkeyObj;
PyObject *dataObj; PyObject *dataObj;
dataObj = PyString_FromStringAndSize(data.data, data.size); dataObj = PyBytes_FromStringAndSize(data.data, data.size);
if (self->primaryDBType == DB_RECNO || if (self->primaryDBType == DB_RECNO ||
self->primaryDBType == DB_QUEUE) self->primaryDBType == DB_QUEUE)
pkeyObj = PyInt_FromLong(*(int *)pkey.data); pkeyObj = PyInt_FromLong(*(int *)pkey.data);
else else
pkeyObj = PyString_FromStringAndSize(pkey.data, pkey.size); pkeyObj = PyBytes_FromStringAndSize(pkey.data, pkey.size);
if (flags & DB_SET_RECNO) /* return key , pkey and data */ if (flags & DB_SET_RECNO) /* return key , pkey and data */
{ {
...@@ -1632,7 +1627,7 @@ DB_pget(DBObject* self, PyObject* args, PyObject* kwargs) ...@@ -1632,7 +1627,7 @@ DB_pget(DBObject* self, PyObject* args, PyObject* kwargs)
if (type == DB_RECNO || type == DB_QUEUE) if (type == DB_RECNO || type == DB_QUEUE)
keyObj = PyInt_FromLong(*(int *)key.data); keyObj = PyInt_FromLong(*(int *)key.data);
else else
keyObj = PyString_FromStringAndSize(key.data, key.size); keyObj = PyBytes_FromStringAndSize(key.data, key.size);
#if (PY_VERSION_HEX >= 0x02040000) #if (PY_VERSION_HEX >= 0x02040000)
retval = PyTuple_Pack(3, keyObj, pkeyObj, dataObj); retval = PyTuple_Pack(3, keyObj, pkeyObj, dataObj);
#else #else
...@@ -1753,7 +1748,7 @@ DB_get_both(DBObject* self, PyObject* args, PyObject* kwargs) ...@@ -1753,7 +1748,7 @@ DB_get_both(DBObject* self, PyObject* args, PyObject* kwargs)
} }
else if (!err) { else if (!err) {
/* XXX(nnorwitz): can we do: retval = dataobj; Py_INCREF(retval); */ /* XXX(nnorwitz): can we do: retval = dataobj; Py_INCREF(retval); */
retval = PyString_FromStringAndSize((char*)data.data, data.size); retval = PyBytes_FromStringAndSize((char*)data.data, data.size);
/* Even though the flags require DB_DBT_MALLOC, data is not always /* Even though the flags require DB_DBT_MALLOC, data is not always
allocated. 4.4: allocated, 4.5: *not* allocated. :-( */ allocated. 4.4: allocated, 4.5: *not* allocated. :-( */
...@@ -2801,7 +2796,7 @@ PyObject* DB_subscript(DBObject* self, PyObject* keyobj) ...@@ -2801,7 +2796,7 @@ PyObject* DB_subscript(DBObject* self, PyObject* keyobj)
retval = NULL; retval = NULL;
} }
else { else {
retval = PyString_FromStringAndSize((char*)data.data, data.size); retval = PyBytes_FromStringAndSize((char*)data.data, data.size);
FREE_DBT(data); FREE_DBT(data);
} }
...@@ -2952,7 +2947,7 @@ _DB_make_list(DBObject* self, DB_TXN* txn, int type) ...@@ -2952,7 +2947,7 @@ _DB_make_list(DBObject* self, DB_TXN* txn, int type)
case DB_BTREE: case DB_BTREE:
case DB_HASH: case DB_HASH:
default: default:
item = PyString_FromStringAndSize((char*)key.data, key.size); item = PyBytes_FromStringAndSize((char*)key.data, key.size);
break; break;
case DB_RECNO: case DB_RECNO:
case DB_QUEUE: case DB_QUEUE:
...@@ -2962,7 +2957,7 @@ _DB_make_list(DBObject* self, DB_TXN* txn, int type) ...@@ -2962,7 +2957,7 @@ _DB_make_list(DBObject* self, DB_TXN* txn, int type)
break; break;
case _VALUES_LIST: case _VALUES_LIST:
item = PyString_FromStringAndSize((char*)data.data, data.size); item = PyBytes_FromStringAndSize((char*)data.data, data.size);
break; break;
case _ITEMS_LIST: case _ITEMS_LIST:
...@@ -3303,13 +3298,13 @@ DBC_pget(DBCursorObject* self, PyObject* args, PyObject *kwargs) ...@@ -3303,13 +3298,13 @@ DBC_pget(DBCursorObject* self, PyObject* args, PyObject *kwargs)
else { else {
PyObject *pkeyObj; PyObject *pkeyObj;
PyObject *dataObj; PyObject *dataObj;
dataObj = PyString_FromStringAndSize(data.data, data.size); dataObj = PyBytes_FromStringAndSize(data.data, data.size);
if (self->mydb->primaryDBType == DB_RECNO || if (self->mydb->primaryDBType == DB_RECNO ||
self->mydb->primaryDBType == DB_QUEUE) self->mydb->primaryDBType == DB_QUEUE)
pkeyObj = PyInt_FromLong(*(int *)pkey.data); pkeyObj = PyInt_FromLong(*(int *)pkey.data);
else else
pkeyObj = PyString_FromStringAndSize(pkey.data, pkey.size); pkeyObj = PyBytes_FromStringAndSize(pkey.data, pkey.size);
if (key.data && key.size) /* return key, pkey and data */ if (key.data && key.size) /* return key, pkey and data */
{ {
...@@ -3318,7 +3313,7 @@ DBC_pget(DBCursorObject* self, PyObject* args, PyObject *kwargs) ...@@ -3318,7 +3313,7 @@ DBC_pget(DBCursorObject* self, PyObject* args, PyObject *kwargs)
if (type == DB_RECNO || type == DB_QUEUE) if (type == DB_RECNO || type == DB_QUEUE)
keyObj = PyInt_FromLong(*(int *)key.data); keyObj = PyInt_FromLong(*(int *)key.data);
else else
keyObj = PyString_FromStringAndSize(key.data, key.size); keyObj = PyBytes_FromStringAndSize(key.data, key.size);
#if (PY_VERSION_HEX >= 0x02040000) #if (PY_VERSION_HEX >= 0x02040000)
retval = PyTuple_Pack(3, keyObj, pkeyObj, dataObj); retval = PyTuple_Pack(3, keyObj, pkeyObj, dataObj);
#else #else
...@@ -4610,7 +4605,7 @@ DBEnv_log_archive(DBEnvObject* self, PyObject* args) ...@@ -4610,7 +4605,7 @@ DBEnv_log_archive(DBEnvObject* self, PyObject* args)
if (log_list) { if (log_list) {
char **log_list_start; char **log_list_start;
for (log_list_start = log_list; *log_list != NULL; ++log_list) { for (log_list_start = log_list; *log_list != NULL; ++log_list) {
item = PyString_FromString (*log_list); item = PyUnicode_FromString (*log_list);
if (item == NULL) { if (item == NULL) {
Py_DECREF(list); Py_DECREF(list);
list = NULL; list = NULL;
...@@ -4910,7 +4905,7 @@ DBSequence_get_key(DBSequenceObject* self, PyObject* args) ...@@ -4910,7 +4905,7 @@ DBSequence_get_key(DBSequenceObject* self, PyObject* args)
RETURN_IF_ERR(); RETURN_IF_ERR();
return PyString_FromStringAndSize(key.data, key.size); return PyBytes_FromStringAndSize(key.data, key.size);
} }
static PyObject* static PyObject*
...@@ -5335,7 +5330,7 @@ DBEnv_getattr(DBEnvObject* self, char *name) ...@@ -5335,7 +5330,7 @@ DBEnv_getattr(DBEnvObject* self, char *name)
if (self->db_env->db_home == NULL) { if (self->db_env->db_home == NULL) {
RETURN_NONE(); RETURN_NONE();
} }
return PyString_FromString(self->db_env->db_home); return PyUnicode_FromString(self->db_env->db_home);
} }
return Py_FindMethod(DBEnv_methods, (PyObject* )self, name); return Py_FindMethod(DBEnv_methods, (PyObject* )self, name);
...@@ -5654,9 +5649,9 @@ PyMODINIT_FUNC init_bsddb(void) ...@@ -5654,9 +5649,9 @@ PyMODINIT_FUNC init_bsddb(void)
{ {
PyObject* m; PyObject* m;
PyObject* d; PyObject* d;
PyObject* pybsddb_version_s = PyString_FromString( PY_BSDDB_VERSION ); PyObject* pybsddb_version_s = PyUnicode_FromString(PY_BSDDB_VERSION);
PyObject* db_version_s = PyString_FromString( DB_VERSION_STRING ); PyObject* db_version_s = PyUnicode_FromString(DB_VERSION_STRING);
PyObject* cvsid_s = PyString_FromString( rcs_id ); PyObject* svnid_s = PyUnicode_FromString(svn_id);
/* Initialize the type of the new type objects here; doing it here /* Initialize the type of the new type objects here; doing it here
is required for portability to Windows without requiring C++. */ is required for portability to Windows without requiring C++. */
...@@ -5683,12 +5678,12 @@ PyMODINIT_FUNC init_bsddb(void) ...@@ -5683,12 +5678,12 @@ PyMODINIT_FUNC init_bsddb(void)
/* Add some symbolic constants to the module */ /* Add some symbolic constants to the module */
d = PyModule_GetDict(m); d = PyModule_GetDict(m);
PyDict_SetItemString(d, "__version__", pybsddb_version_s); PyDict_SetItemString(d, "__version__", pybsddb_version_s);
PyDict_SetItemString(d, "cvsid", cvsid_s); PyDict_SetItemString(d, "cvsid", svnid_s);
PyDict_SetItemString(d, "DB_VERSION_STRING", db_version_s); PyDict_SetItemString(d, "DB_VERSION_STRING", db_version_s);
Py_DECREF(pybsddb_version_s); Py_DECREF(pybsddb_version_s);
pybsddb_version_s = NULL; pybsddb_version_s = NULL;
Py_DECREF(cvsid_s); Py_DECREF(svnid_s);
cvsid_s = NULL; svnid_s = NULL;
Py_DECREF(db_version_s); Py_DECREF(db_version_s);
db_version_s = NULL; db_version_s = NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment