From: Bert Vermeulen Date: Sun, 15 Jan 2012 02:58:27 +0000 (+0100) Subject: srd: clean up module loading/unloading, and the decoder struct X-Git-Tag: libsigrokdecode-0.1.0~140 X-Git-Url: https://sigrok.org/gitweb/?p=libsigrokdecode.git;a=commitdiff_plain;h=451680f192b97d652fb02186f5201efa0d668a2a srd: clean up module loading/unloading, and the decoder struct PDs are now checked for a proper Decoder object, with at least the required attributes. The author, long_desc and func attributes in the decoder object are gone. --- diff --git a/controller.c b/controller.c index 07bf6f2..963345c 100644 --- a/controller.c +++ b/controller.c @@ -168,7 +168,7 @@ struct srd_decoder_instance *srd_instance_new(const char *id, } /* Create an instance of the 'Decoder' class. */ - di->py_instance = PyObject_Call(dec->py_decobj, py_args, NULL); + di->py_instance = PyObject_Call(dec->py_dec, py_args, NULL); if (!di->py_instance) { if (PyErr_Occurred()) PyErr_Print(); @@ -230,6 +230,7 @@ int srd_instance_set_probe(struct srd_decoder_instance *di, return SRD_OK; } +/* TODO: this should go into the PD stack */ struct srd_decoder_instance *srd_instance_find(char *instance_id) { GSList *l; diff --git a/decoder.c b/decoder.c index e25e4e0..6b5fc95 100644 --- a/decoder.c +++ b/decoder.c @@ -26,6 +26,9 @@ /* The list of protocol decoders. */ GSList *pd_list = NULL; +/* lives in module_sigrokdecode.c */ +extern PyObject *mod_sigrokdecode; + /** * Returns the list of supported/loaded protocol decoders. @@ -72,67 +75,89 @@ struct srd_decoder *srd_get_decoder_by_id(const char *id) */ int srd_load_decoder(const char *name, struct srd_decoder **dec) { - PyObject *py_mod, *py_res, *py_annlist, *py_ann; + PyObject *py_basedec, *py_annlist, *py_ann; struct srd_decoder *d; - int alen, r, i; + int alen, ret, i; char **ann; - /* "Import" the Python module. */ - if (!(py_mod = PyImport_ImportModule(name))) { /* NEWREF */ - PyErr_Print(); /* Returns void. */ - return SRD_ERR_PYTHON; /* TODO: More specific error? */ - } + py_basedec = NULL; + ret = SRD_ERR; + srd_dbg("loading module %s", name); - /* Get the 'Decoder' class as Python object. */ - py_res = PyObject_GetAttrString(py_mod, "Decoder"); /* NEWREF */ - if (!py_res) { - if (PyErr_Occurred()) - PyErr_Print(); /* Returns void. */ - Py_XDECREF(py_mod); - srd_err("Decoder class not found in PD module %s", name); - return SRD_ERR_PYTHON; /* TODO: More specific error? */ + if (!(d = g_try_malloc0(sizeof(struct srd_decoder)))) { + ret = SRD_ERR_MALLOC; + goto err_out; } - if (!(d = malloc(sizeof(struct srd_decoder)))) - return SRD_ERR_MALLOC; + /* Import the Python module. */ + if (!(d->py_mod = PyImport_ImportModule(name))) { + /* TODO: report exception message/traceback to err/dbg */ + srd_dbg("import failed"); + PyErr_Clear(); + ret = SRD_ERR_PYTHON; + goto err_out; + } - if ((r = h_str(py_res, "id", &(d->id))) < 0) - return r; + /* Get the 'Decoder' class as Python object. */ + if (!(d->py_dec = PyObject_GetAttrString(d->py_mod, "Decoder"))) { + /* This generated an AttributeError exception. */ + PyErr_Clear(); + srd_err("Decoder class not found in protocol decoder module %s", name); + ret = SRD_ERR_PYTHON; + goto err_out; + } - if ((r = h_str(py_res, "name", &(d->name))) < 0) - return r; + if (!(py_basedec = PyObject_GetAttrString(mod_sigrokdecode, "Decoder"))) { + srd_dbg("sigrokdecode module not loaded"); + ret = SRD_ERR_PYTHON; + goto err_out; + } - if ((r = h_str(py_res, "longname", &(d->longname))) < 0) - return r; + if (!PyObject_IsSubclass(d->py_dec, py_basedec)) { + srd_err("Decoder class in protocol decoder module %s is not " + "a subclass of sigrokdecode.Decoder", name); + ret = SRD_ERR_PYTHON; + goto err_out; + } + Py_DecRef(py_basedec); - if ((r = h_str(py_res, "desc", &(d->desc))) < 0) - return r; + if (py_attr_as_str(d->py_dec, "id", &(d->id)) != SRD_OK) { + return SRD_ERR_PYTHON; + goto err_out; + } - if ((r = h_str(py_res, "longdesc", &(d->longdesc))) < 0) - return r; + if (py_attr_as_str(d->py_dec, "name", &(d->name)) != SRD_OK) { + return SRD_ERR_PYTHON; + goto err_out; + } - if ((r = h_str(py_res, "author", &(d->author))) < 0) - return r; + if (py_attr_as_str(d->py_dec, "longname", &(d->longname)) != SRD_OK) { + return SRD_ERR_PYTHON; + goto err_out; + } - if ((r = h_str(py_res, "license", &(d->license))) < 0) - return r; + if (py_attr_as_str(d->py_dec, "desc", &(d->desc)) != SRD_OK) { + return SRD_ERR_PYTHON; + goto err_out; + } - d->py_mod = py_mod; - d->py_decobj = py_res; + if (py_attr_as_str(d->py_dec, "license", &(d->license)) != SRD_OK) { + return SRD_ERR_PYTHON; + goto err_out; + } - /* TODO: Handle func, inputformats, outputformats. */ - /* Note: They must at least be set to NULL, will segfault otherwise. */ - d->func = NULL; + /* TODO: Handle inputformats, outputformats. */ d->inputformats = NULL; d->outputformats = NULL; /* Convert class annotation attribute to GSList of **char */ d->annotations = NULL; - if (PyObject_HasAttrString(py_res, "annotations")) { - py_annlist = PyObject_GetAttrString(py_res, "annotations"); + if (PyObject_HasAttrString(d->py_dec, "annotations")) { + py_annlist = PyObject_GetAttrString(d->py_dec, "annotations"); if (!PyList_Check(py_annlist)) { srd_err("Protocol decoder module %s annotations should be a list", name); - return SRD_ERR_PYTHON; + ret = SRD_ERR_PYTHON; + goto err_out; } alen = PyList_Size(py_annlist); for (i = 0; i < alen; i++) { @@ -140,43 +165,66 @@ int srd_load_decoder(const char *name, struct srd_decoder **dec) if (!PyList_Check(py_ann) || PyList_Size(py_ann) != 2) { srd_err("Protocol decoder module %s annotation %d should be a list with two elements", name, i+1); - return SRD_ERR_PYTHON; + ret = SRD_ERR_PYTHON; + goto err_out; } - if (py_strlist_to_char(py_ann, &ann) != SRD_OK) - return SRD_ERR_PYTHON; + if (py_strlist_to_char(py_ann, &ann) != SRD_OK) { + ret = SRD_ERR_PYTHON; + goto err_out; + } d->annotations = g_slist_append(d->annotations, ann); } } *dec = d; + ret = SRD_OK; + +err_out: + if (ret != SRD_OK) { + Py_XDECREF(py_basedec); + Py_XDECREF(d->py_dec); + Py_XDECREF(d->py_mod); + g_free(d); + } - return SRD_OK; + return ret; +} + +char *srd_decoder_doc(struct srd_decoder *dec) +{ + char *doc; + + doc = NULL; + py_attr_as_str(dec->py_mod, "__doc__", &doc); + + return doc; } /** - * TODO + * Unload decoder module. + * + * @param dec The decoder struct to be unloaded. + * + * @return SRD_OK upon success, a (negative) error code otherwise. */ int srd_unload_decoder(struct srd_decoder *dec) { + g_free(dec->id); g_free(dec->name); g_free(dec->longname); g_free(dec->desc); - g_free(dec->longdesc); - g_free(dec->author); g_free(dec->license); - g_free(dec->func); /* TODO: Free everything in inputformats and outputformats. */ - if (dec->inputformats != NULL) g_slist_free(dec->inputformats); if (dec->outputformats != NULL) g_slist_free(dec->outputformats); - Py_XDECREF(dec->py_decobj); + Py_XDECREF(dec->py_dec); Py_XDECREF(dec->py_mod); /* TODO: (g_)free dec itself? */ diff --git a/module_sigrokdecode.c b/module_sigrokdecode.c index a18583b..cb43372 100644 --- a/module_sigrokdecode.c +++ b/module_sigrokdecode.c @@ -21,6 +21,12 @@ #include "sigrokdecode-internal.h" #include "config.h" + +/* When initialized, a reference to this module inside the python interpreter + * lives here. + */ +PyObject *mod_sigrokdecode = NULL; + /* lives in type_logic.c */ extern PyTypeObject srd_logic_type; @@ -236,6 +242,8 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void) PyLong_FromLong(SRD_OUTPUT_BINARY)) == -1) return NULL; + mod_sigrokdecode = mod; + return mod; } diff --git a/sigrokdecode.h b/sigrokdecode.h index 71d0ae4..863608b 100644 --- a/sigrokdecode.h +++ b/sigrokdecode.h @@ -90,18 +90,9 @@ struct srd_decoder { /** A (short, one-line) description of the decoder. */ char *desc; - /** A (long, multi-line) description of the decoder. May be NULL. */ - char *longdesc; - - /** The author of the decoder. May be NULL. */ - char *author; - /** The license of the decoder. Valid values: "gplv2+", "gplv3+". */ char *license; - /** TODO */ - char *func; - /** TODO */ GSList *inputformats; @@ -113,11 +104,11 @@ struct srd_decoder { */ GSList *annotations; - /** TODO */ + /** Python module */ PyObject *py_mod; - /** Python object that performs the decoding */ - PyObject *py_decobj; + /** sigrokdecode.Decoder class */ + PyObject *py_dec; }; struct srd_decoder_instance { @@ -191,20 +182,20 @@ int srd_register_callback(int output_type, void *cb); void *srd_find_callback(int output_type); /*--- decoder.c -------------------------------------------------------------*/ - GSList *srd_list_decoders(void); struct srd_decoder *srd_get_decoder_by_id(const char *id); int srd_load_decoder(const char *name, struct srd_decoder **dec); int srd_unload_decoder(struct srd_decoder *dec); int srd_load_all_decoders(void); int srd_unload_all_decoders(void); +char *srd_decoder_doc(struct srd_decoder *dec); /*--- util.c ----------------------------------------------------------------*/ -int h_str(PyObject *py_res, const char *key, char **outstr); +int py_attr_as_str(PyObject *py_obj, const char *attr, char **outstr); +int py_str_as_str(PyObject *py_str, char **outstr); int py_strlist_to_char(PyObject *py_strlist, char ***outstr); /*--- log.c -----------------------------------------------------------------*/ - int srd_set_loglevel(int loglevel); int srd_get_loglevel(void); diff --git a/util.c b/util.c index df433d7..51b7cd6 100644 --- a/util.c +++ b/util.c @@ -19,49 +19,81 @@ */ #include "sigrokdecode.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ +#include "sigrokdecode-internal.h" #include "config.h" /** - * Helper function to get the value of a python object's attribute, - * returned as a newly allocated char *. + * Get the value of a python object's attribute, returned as a newly + * allocated char *. * * @param py_obj The object to probe. - * @param key Name of the attribute to retrieve. + * @param attr Name of the attribute to retrieve. * @param outstr ptr to char * storage to be filled in. * * @return SRD_OK upon success, a (negative) error code otherwise. * The 'outstr' argument points to a malloc()ed string upon success. */ -int h_str(PyObject *py_obj, const char *key, char **outstr) +int py_attr_as_str(PyObject *py_obj, const char *attr, char **outstr) { - PyObject *py_str, *py_encstr; - char *str; + PyObject *py_str; int ret; - py_str = py_encstr = NULL; + if (!PyObject_HasAttrString(py_obj, attr)) { + srd_dbg("object has no attribute '%s'", attr); + return SRD_ERR_PYTHON; + } + + if (!(py_str = PyObject_GetAttrString(py_obj, attr))) { + /* TODO: report exception message/traceback to err/dbg */ + PyErr_Clear(); + return SRD_ERR_PYTHON; + } + + ret = py_str_as_str(py_str, outstr); + Py_XDECREF(py_str); + + return ret; +} + + +/** + * Get the value of a python unicode string object, returned as a newly + * allocated char *. + * + * @param py_str The unicode string object. + * @param outstr ptr to char * storage to be filled in. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * The 'outstr' argument points to a malloc()ed string upon success. + */ +int py_str_as_str(PyObject *py_str, char **outstr) +{ + PyObject *py_encstr; + int ret; + char *str; + + py_encstr = NULL; str = NULL; ret = SRD_OK; - if (!(py_str = PyObject_GetAttrString(py_obj, (char *)key))) { - /* TODO: log level 4 debug message */ + if (!PyUnicode_Check(py_str)) { + srd_dbg("not a string object"); ret = SRD_ERR_PYTHON; goto err_out; } if (!(py_encstr = PyUnicode_AsEncodedString(py_str, "utf-8", NULL))) { - /* TODO: log level 4 debug message */ ret = SRD_ERR_PYTHON; goto err_out; } if (!(str = PyBytes_AS_STRING(py_encstr))) { - /* TODO: log level 4 debug message */ ret = SRD_ERR_PYTHON; goto err_out; } if (!(*outstr = g_strdup(str))) { - /* TODO: log level 4 debug message */ + srd_dbg("malloc failed"); ret = SRD_ERR_MALLOC; goto err_out; } @@ -72,16 +104,25 @@ err_out: if (py_encstr) Py_XDECREF(py_encstr); - if (PyErr_Occurred()) - /* TODO: log level 4 debug message */ - PyErr_Print(); + if (PyErr_Occurred()) { + srd_dbg("string conversion failed"); + /* TODO: dump exception to srd_dbg */ + PyErr_Clear(); + } return ret; } + /** * Convert a python list of unicode strings to a NULL-terminated UTF8-encoded * char * array. The caller must free each string when finished. + * + * @param py_strlist The list object. + * @param outstr ptr to char ** storage to be filled in. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * The 'outstr' argument points to a malloc()ed char ** upon success. */ int py_strlist_to_char(PyObject *py_strlist, char ***outstr) { @@ -93,7 +134,8 @@ int py_strlist_to_char(PyObject *py_strlist, char ***outstr) if (!(out = g_try_malloc(sizeof(char *) * (list_len + 1)))) return SRD_ERR_MALLOC; for (i = 0; i < list_len; i++) { - if (!(py_str = PyUnicode_AsEncodedString(PyList_GetItem(py_strlist, i), "utf-8", NULL))) + if (!(py_str = PyUnicode_AsEncodedString( + PyList_GetItem(py_strlist, i), "utf-8", NULL))) return SRD_ERR_PYTHON; if (!(str = PyBytes_AS_STRING(py_str))) return SRD_ERR_PYTHON;