Issue #15651: PEP 3121 refactoring for _elementtree

Patch by Antoine Pitrou (based on Robin Schreiber's original patch)
This commit is contained in:
Eli Bendersky 2013-08-10 08:00:39 -07:00
parent c7c953adfe
commit 532d03e547
1 changed files with 89 additions and 30 deletions

View File

@ -66,10 +66,51 @@ static PyTypeObject TreeBuilder_Type;
static PyTypeObject XMLParser_Type; static PyTypeObject XMLParser_Type;
/* glue functions (see the init function for details) */ /* Per-module state; PEP 3121 */
static PyObject* elementtree_parseerror_obj; typedef struct {
static PyObject* elementtree_deepcopy_obj; PyObject *parseerror_obj;
static PyObject* elementpath_obj; PyObject *deepcopy_obj;
PyObject *elementpath_obj;
} elementtreestate;
static struct PyModuleDef elementtreemodule;
/* Given a module object (assumed to be _elementtree), get its per-module
* state.
*/
#define ET_STATE(mod) ((elementtreestate *) PyModule_GetState(mod))
/* Find the module instance imported in the currently running sub-interpreter
* and get its state.
*/
#define ET_STATE_GLOBAL \
((elementtreestate *) PyModule_GetState(PyState_FindModule(&elementtreemodule)))
static int
elementtree_clear(PyObject *m)
{
elementtreestate *st = ET_STATE(m);
Py_CLEAR(st->parseerror_obj);
Py_CLEAR(st->deepcopy_obj);
Py_CLEAR(st->elementpath_obj);
return 0;
}
static int
elementtree_traverse(PyObject *m, visitproc visit, void *arg)
{
elementtreestate *st = ET_STATE(m);
Py_VISIT(st->parseerror_obj);
Py_VISIT(st->deepcopy_obj);
Py_VISIT(st->elementpath_obj);
return 0;
}
static void
elementtree_free(void *m)
{
elementtree_clear((PyObject *)m);
}
/* helpers */ /* helpers */
@ -77,11 +118,11 @@ LOCAL(PyObject*)
deepcopy(PyObject* object, PyObject* memo) deepcopy(PyObject* object, PyObject* memo)
{ {
/* do a deep copy of the given object */ /* do a deep copy of the given object */
PyObject* args; PyObject* args;
PyObject* result; PyObject* result;
elementtreestate *st = ET_STATE_GLOBAL;
if (!elementtree_deepcopy_obj) { if (!st->deepcopy_obj) {
PyErr_SetString( PyErr_SetString(
PyExc_RuntimeError, PyExc_RuntimeError,
"deepcopy helper not found" "deepcopy helper not found"
@ -92,7 +133,7 @@ deepcopy(PyObject* object, PyObject* memo)
args = PyTuple_Pack(2, object, memo); args = PyTuple_Pack(2, object, memo);
if (!args) if (!args)
return NULL; return NULL;
result = PyObject_CallObject(elementtree_deepcopy_obj, args); result = PyObject_CallObject(st->deepcopy_obj, args);
Py_DECREF(args); Py_DECREF(args);
return result; return result;
} }
@ -1047,6 +1088,7 @@ element_find(ElementObject *self, PyObject *args, PyObject *kwds)
PyObject* tag; PyObject* tag;
PyObject* namespaces = Py_None; PyObject* namespaces = Py_None;
static char *kwlist[] = {"path", "namespaces", 0}; static char *kwlist[] = {"path", "namespaces", 0};
elementtreestate *st = ET_STATE_GLOBAL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:find", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:find", kwlist,
&tag, &namespaces)) &tag, &namespaces))
@ -1055,7 +1097,7 @@ element_find(ElementObject *self, PyObject *args, PyObject *kwds)
if (checkpath(tag) || namespaces != Py_None) { if (checkpath(tag) || namespaces != Py_None) {
_Py_IDENTIFIER(find); _Py_IDENTIFIER(find);
return _PyObject_CallMethodId( return _PyObject_CallMethodId(
elementpath_obj, &PyId_find, "OOO", self, tag, namespaces st->elementpath_obj, &PyId_find, "OOO", self, tag, namespaces
); );
} }
@ -1083,6 +1125,7 @@ element_findtext(ElementObject *self, PyObject *args, PyObject *kwds)
PyObject* namespaces = Py_None; PyObject* namespaces = Py_None;
_Py_IDENTIFIER(findtext); _Py_IDENTIFIER(findtext);
static char *kwlist[] = {"path", "default", "namespaces", 0}; static char *kwlist[] = {"path", "default", "namespaces", 0};
elementtreestate *st = ET_STATE_GLOBAL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO:findtext", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO:findtext", kwlist,
&tag, &default_value, &namespaces)) &tag, &default_value, &namespaces))
@ -1090,7 +1133,7 @@ element_findtext(ElementObject *self, PyObject *args, PyObject *kwds)
if (checkpath(tag) || namespaces != Py_None) if (checkpath(tag) || namespaces != Py_None)
return _PyObject_CallMethodId( return _PyObject_CallMethodId(
elementpath_obj, &PyId_findtext, "OOOO", self, tag, default_value, namespaces st->elementpath_obj, &PyId_findtext, "OOOO", self, tag, default_value, namespaces
); );
if (!self->extra) { if (!self->extra) {
@ -1122,6 +1165,7 @@ element_findall(ElementObject *self, PyObject *args, PyObject *kwds)
PyObject* tag; PyObject* tag;
PyObject* namespaces = Py_None; PyObject* namespaces = Py_None;
static char *kwlist[] = {"path", "namespaces", 0}; static char *kwlist[] = {"path", "namespaces", 0};
elementtreestate *st = ET_STATE_GLOBAL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:findall", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:findall", kwlist,
&tag, &namespaces)) &tag, &namespaces))
@ -1130,7 +1174,7 @@ element_findall(ElementObject *self, PyObject *args, PyObject *kwds)
if (checkpath(tag) || namespaces != Py_None) { if (checkpath(tag) || namespaces != Py_None) {
_Py_IDENTIFIER(findall); _Py_IDENTIFIER(findall);
return _PyObject_CallMethodId( return _PyObject_CallMethodId(
elementpath_obj, &PyId_findall, "OOO", self, tag, namespaces st->elementpath_obj, &PyId_findall, "OOO", self, tag, namespaces
); );
} }
@ -1162,13 +1206,14 @@ element_iterfind(ElementObject *self, PyObject *args, PyObject *kwds)
PyObject* namespaces = Py_None; PyObject* namespaces = Py_None;
_Py_IDENTIFIER(iterfind); _Py_IDENTIFIER(iterfind);
static char *kwlist[] = {"path", "namespaces", 0}; static char *kwlist[] = {"path", "namespaces", 0};
elementtreestate *st = ET_STATE_GLOBAL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:iterfind", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:iterfind", kwlist,
&tag, &namespaces)) &tag, &namespaces))
return NULL; return NULL;
return _PyObject_CallMethodId( return _PyObject_CallMethodId(
elementpath_obj, &PyId_iterfind, "OOO", self, tag, namespaces st->elementpath_obj, &PyId_iterfind, "OOO", self, tag, namespaces
); );
} }
@ -2351,6 +2396,7 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag,
{ {
PyObject* node; PyObject* node;
PyObject* this; PyObject* this;
elementtreestate *st = ET_STATE_GLOBAL;
if (self->data) { if (self->data) {
if (self->this == self->last) { if (self->this == self->last) {
@ -2381,7 +2427,7 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag,
} else { } else {
if (self->root) { if (self->root) {
PyErr_SetString( PyErr_SetString(
elementtree_parseerror_obj, st->parseerror_obj,
"multiple elements on top level" "multiple elements on top level"
); );
goto error; goto error;
@ -2670,6 +2716,10 @@ static PyTypeObject TreeBuilder_Type = {
#include "expat.h" #include "expat.h"
#include "pyexpat.h" #include "pyexpat.h"
/* The PyExpat_CAPI structure is an immutable dispatch table, so it can be
* cached globally without being in per-module state.
*/
static struct PyExpat_CAPI *expat_capi; static struct PyExpat_CAPI *expat_capi;
#define EXPAT(func) (expat_capi->func) #define EXPAT(func) (expat_capi->func)
@ -2779,6 +2829,7 @@ static void
expat_set_error(enum XML_Error error_code, int line, int column, char *message) expat_set_error(enum XML_Error error_code, int line, int column, char *message)
{ {
PyObject *errmsg, *error, *position, *code; PyObject *errmsg, *error, *position, *code;
elementtreestate *st = ET_STATE_GLOBAL;
errmsg = PyUnicode_FromFormat("%s: line %d, column %d", errmsg = PyUnicode_FromFormat("%s: line %d, column %d",
message ? message : EXPAT(ErrorString)(error_code), message ? message : EXPAT(ErrorString)(error_code),
@ -2786,7 +2837,7 @@ expat_set_error(enum XML_Error error_code, int line, int column, char *message)
if (errmsg == NULL) if (errmsg == NULL)
return; return;
error = PyObject_CallFunction(elementtree_parseerror_obj, "O", errmsg); error = PyObject_CallFunction(st->parseerror_obj, "O", errmsg);
Py_DECREF(errmsg); Py_DECREF(errmsg);
if (!error) if (!error)
return; return;
@ -2816,7 +2867,7 @@ expat_set_error(enum XML_Error error_code, int line, int column, char *message)
} }
Py_DECREF(position); Py_DECREF(position);
PyErr_SetObject(elementtree_parseerror_obj, error); PyErr_SetObject(st->parseerror_obj, error);
Py_DECREF(error); Py_DECREF(error);
} }
@ -3639,22 +3690,29 @@ static PyMethodDef _functions[] = {
}; };
static struct PyModuleDef _elementtreemodule = { static struct PyModuleDef elementtreemodule = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT,
"_elementtree", "_elementtree",
NULL, NULL,
-1, sizeof(elementtreestate),
_functions, _functions,
NULL, NULL,
NULL, elementtree_traverse,
NULL, elementtree_clear,
NULL elementtree_free
}; };
PyMODINIT_FUNC PyMODINIT_FUNC
PyInit__elementtree(void) PyInit__elementtree(void)
{ {
PyObject *m, *temp; PyObject *m, *temp;
elementtreestate *st;
m = PyState_FindModule(&elementtreemodule);
if (m) {
Py_INCREF(m);
return m;
}
/* Initialize object types */ /* Initialize object types */
if (PyType_Ready(&ElementIter_Type) < 0) if (PyType_Ready(&ElementIter_Type) < 0)
@ -3666,16 +3724,17 @@ PyInit__elementtree(void)
if (PyType_Ready(&XMLParser_Type) < 0) if (PyType_Ready(&XMLParser_Type) < 0)
return NULL; return NULL;
m = PyModule_Create(&_elementtreemodule); m = PyModule_Create(&elementtreemodule);
if (!m) if (!m)
return NULL; return NULL;
st = ET_STATE(m);
if (!(temp = PyImport_ImportModule("copy"))) if (!(temp = PyImport_ImportModule("copy")))
return NULL; return NULL;
elementtree_deepcopy_obj = PyObject_GetAttrString(temp, "deepcopy"); st->deepcopy_obj = PyObject_GetAttrString(temp, "deepcopy");
Py_XDECREF(temp); Py_XDECREF(temp);
if (!(elementpath_obj = PyImport_ImportModule("xml.etree.ElementPath"))) if (!(st->elementpath_obj = PyImport_ImportModule("xml.etree.ElementPath")))
return NULL; return NULL;
/* link against pyexpat */ /* link against pyexpat */
@ -3695,11 +3754,11 @@ PyInit__elementtree(void)
return NULL; return NULL;
} }
elementtree_parseerror_obj = PyErr_NewException( st->parseerror_obj = PyErr_NewException(
"xml.etree.ElementTree.ParseError", PyExc_SyntaxError, NULL "xml.etree.ElementTree.ParseError", PyExc_SyntaxError, NULL
); );
Py_INCREF(elementtree_parseerror_obj); Py_INCREF(st->parseerror_obj);
PyModule_AddObject(m, "ParseError", elementtree_parseerror_obj); PyModule_AddObject(m, "ParseError", st->parseerror_obj);
Py_INCREF((PyObject *)&Element_Type); Py_INCREF((PyObject *)&Element_Type);
PyModule_AddObject(m, "Element", (PyObject *)&Element_Type); PyModule_AddObject(m, "Element", (PyObject *)&Element_Type);