Kaydet (Commit) 64ce5052 authored tarafından 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.
üst 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