diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -34,29 +34,64 @@ DBM *di_dbm; } dbmobject; -static PyTypeObject Dbmtype; +static struct PyModuleDef _dbmmodule; -#define is_dbmobject(v) (Py_TYPE(v) == &Dbmtype) -#define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \ - { PyErr_SetString(DbmError, "DBM object has already been closed"); \ - return NULL; } +typedef struct { + PyObject *Dbmtype; + PyObject *DbmError; +} _dbmmodulestate; -static PyObject *DbmError; +#define STATE(o) ((_dbmmodulestate *)PyModule_GetState(o)) + +static int +_dbmmodule_traverse(PyObject *m, visitproc visit, void *arg) +{ + Py_VISIT(STATE(m)->DbmError); + Py_VISIT(STATE(m)->Dbmtype); + return 0; +} + +static int +_dbmmodule_clear(PyObject *m) +{ + Py_CLEAR(STATE(m)->DbmError); + Py_CLEAR(STATE(m)->Dbmtype); + return 0; +} + +static void +_dbmmodule_free(void *m) +{ + _dbmmodule_clear((PyObject *)m); +} + +#define GSTATE \ + ((_dbmmodulestate *) PyModule_GetState(PyState_FindModule(&_dbmmodule))) + +#define is_dbmobject(v) (Py_TYPE(v) == GSTATE->Dbmtype) +#define check_dbmobject_open(v) \ + if ((v)->di_dbm == NULL) { \ + PyErr_SetString(GSTATE->DbmError, \ + "DBM object has already been closed"); \ + return NULL; \ + } static PyObject * newdbmobject(char *file, int flags, int mode) { dbmobject *dp; + _dbmmodulestate *state = GSTATE; - dp = PyObject_New(dbmobject, &Dbmtype); + dp = PyObject_New(dbmobject, (PyTypeObject *)state->Dbmtype); if (dp == NULL) return NULL; dp->di_size = -1; - if ( (dp->di_dbm = dbm_open(file, flags, mode)) == 0 ) { - PyErr_SetFromErrno(DbmError); + if ((dp->di_dbm = dbm_open(file, flags, mode)) == 0 ) { + PyErr_SetFromErrno(state->DbmError); Py_DECREF(dp); return NULL; } + Py_INCREF(state->Dbmtype); return (PyObject *)dp; } @@ -65,25 +100,27 @@ static void dbm_dealloc(register dbmobject *dp) { - if ( dp->di_dbm ) + PyTypeObject *type = Py_TYPE(dp); + if (dp->di_dbm) dbm_close(dp->di_dbm); PyObject_Del(dp); + Py_DECREF(type); } static Py_ssize_t dbm_length(dbmobject *dp) { if (dp->di_dbm == NULL) { - PyErr_SetString(DbmError, "DBM object has already been closed"); - return -1; + PyErr_SetString(GSTATE->DbmError, "DBM object has already been closed"); + return -1; } - if ( dp->di_size < 0 ) { + if (dp->di_size < 0) { datum key; int size; size = 0; - for ( key=dbm_firstkey(dp->di_dbm); key.dptr; - key = dbm_nextkey(dp->di_dbm)) + for (key=dbm_firstkey(dp->di_dbm); key.dptr; + key = dbm_nextkey(dp->di_dbm)) size++; dp->di_size = size; } @@ -102,13 +139,13 @@ krec.dsize = tmp_size; check_dbmobject_open(dp); drec = dbm_fetch(dp->di_dbm, krec); - if ( drec.dptr == 0 ) { + if (drec.dptr == 0) { PyErr_SetObject(PyExc_KeyError, key); return NULL; } - if ( dbm_error(dp->di_dbm) ) { + if (dbm_error(dp->di_dbm) ) { dbm_clearerr(dp->di_dbm); - PyErr_SetString(DbmError, ""); + PyErr_SetString(GSTATE->DbmError, ""); return NULL; } return PyBytes_FromStringAndSize(drec.dptr, drec.dsize); @@ -120,51 +157,44 @@ datum krec, drec; Py_ssize_t tmp_size; - if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) { + if (!PyArg_Parse(v, "s#", &krec.dptr, &tmp_size)) { PyErr_SetString(PyExc_TypeError, "dbm mappings have bytes or string keys only"); return -1; } krec.dsize = tmp_size; if (dp->di_dbm == NULL) { - PyErr_SetString(DbmError, "DBM object has already been closed"); - return -1; + PyErr_SetString(GSTATE->DbmError, "DBM object has already been closed"); + return -1; } dp->di_size = -1; if (w == NULL) { - if ( dbm_delete(dp->di_dbm, krec) < 0 ) { + if (dbm_delete(dp->di_dbm, krec) < 0) { dbm_clearerr(dp->di_dbm); PyErr_SetObject(PyExc_KeyError, v); return -1; } } else { - if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) { + if (!PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) { PyErr_SetString(PyExc_TypeError, "dbm mappings have byte or string elements only"); return -1; } drec.dsize = tmp_size; - if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { + if (dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { dbm_clearerr(dp->di_dbm); - PyErr_SetString(DbmError, - "cannot add item to database"); + PyErr_SetString(GSTATE->DbmError, "cannot add item to database"); return -1; } } - if ( dbm_error(dp->di_dbm) ) { + if (dbm_error(dp->di_dbm) ) { dbm_clearerr(dp->di_dbm); - PyErr_SetString(DbmError, ""); + PyErr_SetString(GSTATE->DbmError, ""); return -1; } return 0; } -static PyMappingMethods dbm_as_mapping = { - (lenfunc)dbm_length, /*mp_length*/ - (binaryfunc)dbm_subscript, /*mp_subscript*/ - (objobjargproc)dbm_ass_sub, /*mp_ass_subscript*/ -}; - static PyObject * dbm__close(register dbmobject *dp, PyObject *unused) { @@ -211,8 +241,7 @@ Py_ssize_t size; if ((dp)->di_dbm == NULL) { - PyErr_SetString(DbmError, - "DBM object has already been closed"); + PyErr_SetString(GSTATE->DbmError, "DBM object has already been closed"); return -1; } if (PyUnicode_Check(arg)) { @@ -235,19 +264,6 @@ return val.dptr != NULL; } -static PySequenceMethods dbm_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - dbm_contains, /* sq_contains */ - 0, /* sq_inplace_concat */ - 0, /* sq_inplace_repeat */ -}; - static PyObject * dbm_get(register dbmobject *dp, PyObject *args) { @@ -296,7 +312,7 @@ val.dsize = 0; } else { - if ( !PyArg_Parse(defvalue, "s#", &val.dptr, &tmp_size) ) { + if (!PyArg_Parse(defvalue, "s#", &val.dptr, &tmp_size) ) { PyErr_SetString(PyExc_TypeError, "dbm mappings have byte string elements only"); return NULL; @@ -306,7 +322,7 @@ } if (dbm_store(dp->di_dbm, key, val, DBM_INSERT) < 0) { dbm_clearerr(dp->di_dbm); - PyErr_SetString(DbmError, "cannot add item to database"); + PyErr_SetString(GSTATE->DbmError, "cannot add item to database"); Py_DECREF(defvalue); return NULL; } @@ -328,37 +344,26 @@ {NULL, NULL} /* sentinel */ }; -static PyTypeObject Dbmtype = { - PyVarObject_HEAD_INIT(NULL, 0) +static PyType_Slot Dbmtype_slots[] = { + {Py_tp_dealloc, (destructor)dbm_dealloc}, + {Py_sq_contains, dbm_contains}, + {Py_mp_length, (lenfunc)dbm_length}, + {Py_mp_subscript, (binaryfunc)dbm_subscript}, + {Py_mp_ass_subscript, (objobjargproc)dbm_ass_sub}, + {Py_tp_methods, dbm_methods}, + {Py_tp_members, }, + {0, 0} +}; + +static PyType_Spec Dbmtype_spec = { "_dbm.dbm", sizeof(dbmobject), 0, - (destructor)dbm_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_reserved*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - &dbm_as_sequence, /*tp_as_sequence*/ - &dbm_as_mapping, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - dbm_methods, /*tp_methods*/ + Py_TPFLAGS_DEFAULT, + Dbmtype_slots, }; + /* ----------------------------------------------------------------- */ static PyObject * @@ -369,20 +374,20 @@ int iflags; int mode = 0666; - if ( !PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode) ) + if (!PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode)) return NULL; - if ( strcmp(flags, "r") == 0 ) + if (strcmp(flags, "r") == 0 ) iflags = O_RDONLY; - else if ( strcmp(flags, "w") == 0 ) + else if (strcmp(flags, "w") == 0) iflags = O_RDWR; - else if ( strcmp(flags, "rw") == 0 ) /* B/W compat */ + else if (strcmp(flags, "rw") == 0) /* B/W compat */ iflags = O_RDWR|O_CREAT; - else if ( strcmp(flags, "c") == 0 ) + else if (strcmp(flags, "c") == 0) iflags = O_RDWR|O_CREAT; - else if ( strcmp(flags, "n") == 0 ) + else if (strcmp(flags, "n") == 0) iflags = O_RDWR|O_CREAT|O_TRUNC; else { - PyErr_SetString(DbmError, + PyErr_SetString(GSTATE->DbmError, "arg 2 to open should be 'r', 'w', 'c', or 'n'"); return NULL; } @@ -401,34 +406,53 @@ PyModuleDef_HEAD_INIT, "_dbm", NULL, - -1, + sizeof(_dbmmodulestate), dbmmodule_methods, NULL, - NULL, - NULL, - NULL + _dbmmodule_traverse, + _dbmmodule_clear, + _dbmmodule_free }; PyMODINIT_FUNC PyInit__dbm(void) { - PyObject *m, *d, *s; + PyObject *m, *o; + _dbmmodulestate * state; - if (PyType_Ready(&Dbmtype) < 0) - return NULL; m = PyModule_Create(&_dbmmodule); if (m == NULL) return NULL; - d = PyModule_GetDict(m); - if (DbmError == NULL) - DbmError = PyErr_NewException("_dbm.error", - PyExc_IOError, NULL); - s = PyUnicode_FromString(which_dbm); - if (s != NULL) { - PyDict_SetItemString(d, "library", s); - Py_DECREF(s); + + state = STATE(m); + if (state == NULL) { + Py_DECREF(m); + return NULL; } - if (DbmError != NULL) - PyDict_SetItemString(d, "error", DbmError); + + o = PyType_FromSpec(&Dbmtype_spec); + if (o == NULL) { + Py_DECREF(m); + return NULL; + } + state->Dbmtype = o; + + o = PyErr_NewException("_dbm.error", PyExc_IOError, NULL); + if (o == NULL) { + Py_DECREF(m); + return NULL; + } + state->DbmError = o; + Py_INCREF(o); + if (PyModule_AddObject(m, "error", o)) { + Py_DECREF(m); + return NULL; + } + + if (PyModule_AddStringConstant(m, "library", which_dbm)) { + Py_DECREF(m); + return NULL; + } + if (PyErr_Occurred()) { Py_DECREF(m); m = NULL;