diff --git a/src/python/recoll/pyrclextract.cpp b/src/python/recoll/pyrclextract.cpp index bba3aa23..34dc3a71 100644 --- a/src/python/recoll/pyrclextract.cpp +++ b/src/python/recoll/pyrclextract.cpp @@ -23,7 +23,7 @@ #include #include -using namespace std; +#include #include "log.h" #include "rcldoc.h" @@ -33,6 +33,8 @@ using namespace std; #include "pyrecoll.h" +using namespace std; + // Imported from pyrecoll static PyObject *recoll_DocType; @@ -42,7 +44,7 @@ typedef struct { PyObject_HEAD /* Type-specific fields go here. */ FileInterner *xtr; - RclConfig *rclconfig; + std::shared_ptr rclconfig; recoll_DocObject *docobject; } rclx_ExtractorObject; @@ -53,6 +55,7 @@ Extractor_dealloc(rclx_ExtractorObject *self) if (self->docobject) { Py_DECREF(&self->docobject); } + self->rclconfig.reset(); delete self->xtr; Py_TYPE(self)->tp_free((PyObject*)self); } @@ -66,7 +69,6 @@ Extractor_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (self == 0) return 0; self->xtr = 0; - self->rclconfig = 0; self->docobject = 0; return (PyObject *)self; } @@ -90,7 +92,7 @@ Extractor_init(rclx_ExtractorObject *self, PyObject *args, PyObject *kwargs) Py_INCREF(dobj); self->rclconfig = dobj->rclconfig; - self->xtr = new FileInterner(*dobj->doc, self->rclconfig, + self->xtr = new FileInterner(*dobj->doc, self->rclconfig.get(), FileInterner::FIF_forPreview); return 0; } @@ -198,7 +200,7 @@ Extractor_idoctofile(rclx_ExtractorObject* self, PyObject *args, mimetype << "] doc mimetype [" << self->docobject->doc->mimetype << "\n"); if (ipath.empty() && !mimetype.compare(self->docobject->doc->mimetype)) { - status = FileInterner::idocToFile(temp, outfile, self->rclconfig, + status = FileInterner::idocToFile(temp, outfile, self->rclconfig.get(), *self->docobject->doc); } else { self->xtr->setTargetMType(mimetype); diff --git a/src/python/recoll/pyrecoll.cpp b/src/python/recoll/pyrecoll.cpp index 5bf5ec73..c5c74ff6 100644 --- a/src/python/recoll/pyrecoll.cpp +++ b/src/python/recoll/pyrecoll.cpp @@ -24,7 +24,6 @@ #include #include #include -using namespace std; #include "rclinit.h" #include "rclconfig.h" @@ -42,7 +41,7 @@ using namespace std; #include "pyrecoll.h" -static RclConfig *rclconfig; +using namespace std; #if PY_MAJOR_VERSION >=3 # define Py_TPFLAGS_HAVE_ITER 0 @@ -50,6 +49,13 @@ static RclConfig *rclconfig; #define PyLong_FromLong PyInt_FromLong #endif +// To keep old code going after we moved the static rclconfig to the +// db object (to fix multiple dbs issues), we keep a copy of the last +// created rclconfig in RCLCONFIG. This is set into the doc objec by +// doc_init, then reset to the db's by db::doc() or query::iter_next, +// the proper Doc creators. +static shared_ptr RCLCONFIG; + ////////////////////////////////////////////////////////////////////// /// SEARCHDATA SearchData code typedef struct { @@ -308,7 +314,6 @@ Doc_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (self == 0) return 0; self->doc = 0; - self->rclconfig = 0; return (PyObject *)self; } @@ -320,7 +325,7 @@ Doc_init(recoll_DocObject *self, PyObject *, PyObject *) self->doc = new Rcl::Doc; if (self->doc == 0) return -1; - self->rclconfig = rclconfig; + self->rclconfig = RCLCONFIG; return 0; } @@ -544,6 +549,11 @@ Doc_getattro(recoll_DocObject *self, PyObject *nameobj) PyErr_SetString(PyExc_AttributeError, "doc"); return 0; } + if (!self->rclconfig || !self->rclconfig->ok()) { + PyErr_SetString(PyExc_AttributeError, + "Configuration not initialized"); + return 0; + } PyObject *meth = PyObject_GenericGetAttr((PyObject*)self, nameobj); if (meth) { @@ -568,7 +578,7 @@ Doc_getattro(recoll_DocObject *self, PyObject *nameobj) Py_RETURN_NONE; } - string key = rclconfig->fieldQCanon(name); + string key = self->rclconfig->fieldQCanon(name); string value; if (idocget(self, key, value)) { LOGDEB1("Doc_getattro: [" << key << "] -> [" << value << "]\n"); @@ -586,8 +596,8 @@ Doc_setattr(recoll_DocObject *self, char *name, PyObject *value) PyErr_SetString(PyExc_AttributeError, "doc??"); return -1; } - if (!rclconfig || !rclconfig->ok()) { - PyErr_SetString(PyExc_EnvironmentError, + if (!self->rclconfig || !self->rclconfig->ok()) { + PyErr_SetString(PyExc_AttributeError, "Configuration not initialized"); return -1; } @@ -615,7 +625,7 @@ Doc_setattr(recoll_DocObject *self, char *name, PyObject *value) } string uvalue = PyBytes_AsString(putf8); Py_DECREF(putf8); - string key = rclconfig->fieldQCanon(name); + string key = self->rclconfig->fieldQCanon(name); LOGDEB0("Doc_setattr: doc " << self->doc << " [" << key << "] (" << name << ") -> [" << uvalue << "]\n"); @@ -693,6 +703,11 @@ Doc_subscript(recoll_DocObject *self, PyObject *key) PyErr_SetString(PyExc_AttributeError, "doc??"); return NULL; } + if (!self->rclconfig || !self->rclconfig->ok()) { + PyErr_SetString(PyExc_AttributeError, + "Configuration not initialized"); + return NULL; + } string name; if (PyUnicode_Check(key)) { PyObject* utf8o = PyUnicode_AsUTF8String(key); @@ -710,7 +725,7 @@ Doc_subscript(recoll_DocObject *self, PyObject *key) Py_RETURN_NONE; } - string skey = rclconfig->fieldQCanon(name); + string skey = self->rclconfig->fieldQCanon(name); string value; if (idocget(self, skey, value)) { return PyUnicode_Decode(value.c_str(), value.size(), "UTF-8","replace"); @@ -807,7 +822,13 @@ static PyTypeObject recoll_DocType = { ////////////////////////////////////////////////////// /// QUERY Query object -struct recoll_DbObject; +typedef struct recoll_DbObject { + PyObject_HEAD + /* Type-specific fields go here. */ + Rcl::Db *db; + std::shared_ptr rclconfig; +} recoll_DbObject; + typedef struct { PyObject_HEAD /* Type-specific fields go here. */ @@ -977,8 +998,8 @@ Query_execute(recoll_QueryObject* self, PyObject *args, PyObject *kwargs) // SearchData defaults to stemming in english // Use default for now but need to add way to specify language string reason; - Rcl::SearchData *sd = wasaStringToRcl(rclconfig, dostem ? stemlang : "", - utf8, reason); + Rcl::SearchData *sd = wasaStringToRcl( + self->connection->rclconfig.get(),dostem ? stemlang : "", utf8, reason); if (!sd) { PyErr_SetString(PyExc_ValueError, reason.c_str()); @@ -1034,7 +1055,7 @@ Query_executesd(recoll_QueryObject* self, PyObject *args, PyObject *kwargs) // array when enumerating keys. Also for url which is also formatted. // But not that some fields are not copied, and are only reachable if // one knows their name (e.g. xdocid). -static void movedocfields(Rcl::Doc *doc) +static void movedocfields(const RclConfig* rclconfig, Rcl::Doc *doc) { printableUrl(rclconfig->getDefCharset(), doc->url, doc->meta[Rcl::Doc::keyurl]); @@ -1065,7 +1086,7 @@ Query_iternext(PyObject *_self) PyErr_SetString(PyExc_EnvironmentError, "doc create failed"); return 0; } - + result->rclconfig = self->connection->rclconfig; // We used to check against rowcount here, but this was wrong: // xapian result count estimate are sometimes wrong, we must go on // fetching until we fail @@ -1074,7 +1095,7 @@ Query_iternext(PyObject *_self) } self->next++; - movedocfields(result->doc); + movedocfields(self->connection->rclconfig.get(), result->doc); return (PyObject *)result; } @@ -1520,11 +1541,6 @@ static PyTypeObject recoll_QueryType = { /////////////////////////////////////////////// ////// DB Db object code -typedef struct recoll_DbObject { - PyObject_HEAD - /* Type-specific fields go here. */ - Rcl::Db *db; -} recoll_DbObject; static PyObject * Db_close(recoll_DbObject *self) @@ -1534,6 +1550,7 @@ Db_close(recoll_DbObject *self) delete self->db; self->db = 0; } + self->rclconfig.reset(); Py_RETURN_NONE; } @@ -1575,26 +1592,28 @@ Db_init(recoll_DbObject *self, PyObject *args, PyObject *kwargs) // recollinit repeatedly, which *should* be ok, except that it // resets the log file. string reason; - delete rclconfig; if (confdir) { string cfd = confdir; - rclconfig = recollinit(RCLINIT_PYTHON, 0, 0, reason, &cfd); + self->rclconfig = std::shared_ptr( + recollinit(RCLINIT_PYTHON, 0, 0, reason, &cfd)); } else { - rclconfig = recollinit(RCLINIT_PYTHON, 0, 0, reason, 0); + self->rclconfig = std::shared_ptr( + recollinit(RCLINIT_PYTHON, 0, 0, reason, 0)); } + RCLCONFIG = self->rclconfig; LOGDEB("Db_init\n"); - if (rclconfig == 0) { + if (!self->rclconfig) { PyErr_SetString(PyExc_EnvironmentError, reason.c_str()); return -1; } - if (!rclconfig->ok()) { + if (!self->rclconfig->ok()) { PyErr_SetString(PyExc_EnvironmentError, "Bad config ?"); return -1; } delete self->db; - self->db = new Rcl::Db(rclconfig); + self->db = new Rcl::Db(self->rclconfig.get()); if (!self->db->open(writable ? Rcl::Db::DbUpd : Rcl::Db::DbRO)) { LOGERR("Db_init: db open error\n"); PyErr_SetString(PyExc_EnvironmentError, "Can't open index"); @@ -1657,6 +1676,25 @@ Db_query(recoll_DbObject* self) return (PyObject *)result; } +static PyObject * +Db_doc(recoll_DbObject* self) +{ + LOGDEB("Db_doc\n"); + if (self->db == 0) { + LOGERR("Db_doc: db not found " << self->db << "\n"); + PyErr_SetString(PyExc_AttributeError, "db"); + return 0; + } + recoll_DocObject *result = (recoll_DocObject *) + PyObject_CallObject((PyObject *)&recoll_DocType, 0); + if (!result) + return 0; + result->rclconfig = self->rclconfig; + Py_INCREF(self); + + return (PyObject *)result; +} + static PyObject * Db_setAbstractParams(recoll_DbObject *self, PyObject *args, PyObject *kwargs) { @@ -1667,7 +1705,7 @@ Db_setAbstractParams(recoll_DbObject *self, PyObject *args, PyObject *kwargs) &maxchars, &ctxwords)) return 0; if (self->db == 0) { - LOGERR("Db_query: db not found " << self->db << "\n"); + LOGERR("Db_setAbstractParams: db not found " << self->db << "\n"); PyErr_SetString(PyExc_AttributeError, "db id not found"); return 0; } @@ -1905,6 +1943,9 @@ static PyMethodDef Db_methods[] = { {"query", (PyCFunction)Db_query, METH_NOARGS, "query() -> Query. Return a new, blank query object for this index." }, + {"doc", (PyCFunction)Db_doc, METH_NOARGS, + "doc() -> Doc. Return a new, blank doc object for this index." + }, {"cursor", (PyCFunction)Db_query, METH_NOARGS, "cursor() -> Query. Alias for query(). Return query object." }, diff --git a/src/python/recoll/pyrecoll.h b/src/python/recoll/pyrecoll.h index 265a3535..29fcae7a 100644 --- a/src/python/recoll/pyrecoll.h +++ b/src/python/recoll/pyrecoll.h @@ -19,13 +19,16 @@ #include +#include + class RclConfig; + typedef struct { PyObject_HEAD /* Type-specific fields go here. */ Rcl::Doc *doc; /* Each doc object has a pointer to the global config, for convenience */ - RclConfig *rclconfig; + std::shared_ptr rclconfig; } recoll_DocObject; #define PYRECOLL_PACKAGE "recoll."