On Fri, Oct 4, 2024 at 7:15 PM Vincent Fazio <vfazio@xxxxxxxxxxx> wrote: > > Previously, the external module relied on gpiod attributes being present > within `globals()` to construct return values back to the caller. > > This assumption required callers of the external module to have imported > the attributes to populate `globals()` for the interface to work. > > Having this implicit contract is opaque and prone to causing issues if > imports within gpiod modules ever get reworked. > > Now, the external module explicitly imports attributes from gpiod in > order to return values back to the caller. > > There should be no concern about circular imports as the external module > should be completely imported by the time callers call into it. > > Since Py_gpiod_GetGlobalType is no longer used, it has been replaced > with Py_gpiod_GetModuleAttrString which returns a new PyObject* > reference for the named module and attribute that must be decremented > when no longer in use. > > Closes: https://github.com/brgl/libgpiod/issues/101 > Signed-off-by: Vincent Fazio <vfazio@xxxxxxxxxxx> > --- > bindings/python/gpiod/ext/chip.c | 39 +++++++++++++++++----------- > bindings/python/gpiod/ext/common.c | 14 ++++++---- > bindings/python/gpiod/ext/internal.h | 3 ++- > bindings/python/gpiod/ext/request.c | 26 ++++++++++++------- > 4 files changed, 52 insertions(+), 30 deletions(-) > > diff --git a/bindings/python/gpiod/ext/chip.c b/bindings/python/gpiod/ext/chip.c > index e8eaad8..fcfb960 100644 > --- a/bindings/python/gpiod/ext/chip.c > +++ b/bindings/python/gpiod/ext/chip.c > @@ -75,31 +75,34 @@ static PyObject *chip_get_info(chip_object *self, PyObject *Py_UNUSED(ignored)) > struct gpiod_chip_info *info; > PyObject *type, *ret; > > - type = Py_gpiod_GetGlobalType("ChipInfo"); > + type = Py_gpiod_GetModuleAttrString("gpiod.chip_info", "ChipInfo"); > if (!type) > return NULL; > > info = gpiod_chip_get_info(self->chip); > - if (!info) > + if (!info) { > + Py_DECREF(type); > return PyErr_SetFromErrno(PyExc_OSError); > + } > > - ret = PyObject_CallFunction(type, "ssI", > - gpiod_chip_info_get_name(info), > - gpiod_chip_info_get_label(info), > - gpiod_chip_info_get_num_lines(info)); > - gpiod_chip_info_free(info); > - return ret; > + ret = PyObject_CallFunction(type, "ssI", > + gpiod_chip_info_get_name(info), > + gpiod_chip_info_get_label(info), > + gpiod_chip_info_get_num_lines(info)); > + gpiod_chip_info_free(info); > + Py_DECREF(type); > + return ret; > } > > static PyObject *make_line_info(struct gpiod_line_info *info) > { > - PyObject *type; > + PyObject *type, *ret; > > - type = Py_gpiod_GetGlobalType("LineInfo"); > + type = Py_gpiod_GetModuleAttrString("gpiod.line_info", "LineInfo"); > if (!type) > return NULL; > > - return PyObject_CallFunction(type, "IsOsiOiiiiOk", > + ret = PyObject_CallFunction(type, "IsOsiOiiiiOk", > gpiod_line_info_get_offset(info), > gpiod_line_info_get_name(info), > gpiod_line_info_is_used(info) ? > @@ -115,6 +118,8 @@ static PyObject *make_line_info(struct gpiod_line_info *info) > gpiod_line_info_is_debounced(info) ? > Py_True : Py_False, > gpiod_line_info_get_debounce_period_us(info)); > + Py_DECREF(type); > + return ret; > } > > static PyObject *chip_get_line_info(chip_object *self, PyObject *args) > @@ -168,10 +173,6 @@ chip_read_info_event(chip_object *self, PyObject *Py_UNUSED(ignored)) > struct gpiod_info_event *event; > struct gpiod_line_info *info; > > - type = Py_gpiod_GetGlobalType("InfoEvent"); > - if (!type) > - return NULL; > - > Py_BEGIN_ALLOW_THREADS; > event = gpiod_chip_read_info_event(self->chip); > Py_END_ALLOW_THREADS; > @@ -186,12 +187,20 @@ chip_read_info_event(chip_object *self, PyObject *Py_UNUSED(ignored)) > return NULL; > } > > + type = Py_gpiod_GetModuleAttrString("gpiod.info_event", "InfoEvent"); > + if (!type) { > + Py_DECREF(info_obj); > + gpiod_info_event_free(event); > + return NULL; > + } > + > event_obj = PyObject_CallFunction(type, "iKO", > gpiod_info_event_get_event_type(event), > gpiod_info_event_get_timestamp_ns(event), > info_obj); > Py_DECREF(info_obj); > gpiod_info_event_free(event); > + Py_DECREF(type); > return event_obj; > } > > diff --git a/bindings/python/gpiod/ext/common.c b/bindings/python/gpiod/ext/common.c > index 07fca8c..62201b6 100644 > --- a/bindings/python/gpiod/ext/common.c > +++ b/bindings/python/gpiod/ext/common.c > @@ -64,15 +64,19 @@ PyObject *Py_gpiod_SetErrFromErrno(void) > return PyErr_SetFromErrno(exc); > } > > -PyObject *Py_gpiod_GetGlobalType(const char *type_name) > +PyObject *Py_gpiod_GetModuleAttrString(const char *modname, > + const char *attrname) > { > - PyObject *globals; > + PyObject *module, *attribute; > > - globals = PyEval_GetGlobals(); > - if (!globals) > + module = PyImport_ImportModule(modname); > + if (!module) > return NULL; > > - return PyDict_GetItemString(globals, type_name); > + attribute = PyObject_GetAttrString(module, attrname); > + Py_DECREF(module); > + > + return attribute; > } > > unsigned int Py_gpiod_PyLongAsUnsignedInt(PyObject *pylong) > diff --git a/bindings/python/gpiod/ext/internal.h b/bindings/python/gpiod/ext/internal.h > index 7d223c0..15aedfb 100644 > --- a/bindings/python/gpiod/ext/internal.h > +++ b/bindings/python/gpiod/ext/internal.h > @@ -8,7 +8,8 @@ > #include <Python.h> > > PyObject *Py_gpiod_SetErrFromErrno(void); > -PyObject *Py_gpiod_GetGlobalType(const char *type_name); > +PyObject *Py_gpiod_GetModuleAttrString(const char *modname, > + const char *attrname); > unsigned int Py_gpiod_PyLongAsUnsignedInt(PyObject *pylong); > void Py_gpiod_dealloc(PyObject *self); > PyObject *Py_gpiod_MakeRequestObject(struct gpiod_line_request *request, > diff --git a/bindings/python/gpiod/ext/request.c b/bindings/python/gpiod/ext/request.c > index 5db69fe..4a035f4 100644 > --- a/bindings/python/gpiod/ext/request.c > +++ b/bindings/python/gpiod/ext/request.c > @@ -149,10 +149,6 @@ static PyObject *request_get_values(request_object *self, PyObject *args) > if (num_offsets < 0) > return NULL; > > - type = Py_gpiod_GetGlobalType("Value"); > - if (!type) > - return NULL; > - > iter = PyObject_GetIter(offsets); > if (!iter) > return NULL; > @@ -183,14 +179,21 @@ static PyObject *request_get_values(request_object *self, PyObject *args) > if (ret) > return Py_gpiod_SetErrFromErrno(); > > + type = Py_gpiod_GetModuleAttrString("gpiod.line", "Value"); > + if (!type) > + return NULL; > + > for (pos = 0; pos < num_offsets; pos++) { > val = PyObject_CallFunction(type, "i", self->values[pos]); > - if (!val) > + if (!val) { > + Py_DECREF(type); > return NULL; > + } > > ret = PyList_SetItem(values, pos, val); > if (ret) { > Py_DECREF(val); > + Py_DECREF(type); > return NULL; > } Don't you need to Py_DECREF(type) here before returning? > } > @@ -279,10 +282,6 @@ static PyObject *request_read_edge_events(request_object *self, PyObject *args) > max_events = 64; > } > > - type = Py_gpiod_GetGlobalType("EdgeEvent"); > - if (!type) > - return NULL; > - > Py_BEGIN_ALLOW_THREADS; > ret = gpiod_line_request_read_edge_events(self->request, > self->buffer, max_events); > @@ -296,10 +295,17 @@ static PyObject *request_read_edge_events(request_object *self, PyObject *args) > if (!events) > return NULL; > > + type = Py_gpiod_GetModuleAttrString("gpiod.edge_event", "EdgeEvent"); > + if (!type) { > + Py_DECREF(events); > + return NULL; > + } > + > for (i = 0; i < num_events; i++) { > event = gpiod_edge_event_buffer_get_event(self->buffer, i); > if (!event) { > Py_DECREF(events); > + Py_DECREF(type); > return NULL; > } > > @@ -311,6 +317,7 @@ static PyObject *request_read_edge_events(request_object *self, PyObject *args) > gpiod_edge_event_get_line_seqno(event)); > if (!event_obj) { > Py_DECREF(events); > + Py_DECREF(type); > return NULL; > } > > @@ -318,6 +325,7 @@ static PyObject *request_read_edge_events(request_object *self, PyObject *args) > if (ret) { > Py_DECREF(event_obj); > Py_DECREF(events); > + Py_DECREF(type); > return NULL; > } And here? > } > -- > 2.34.1 > > Bart