o->def = g_variant_new_double(dval);
} else {
srd_err("Protocol decoder %s option 'default' has "
- "value of unsupported type '%s'.", d->name,
- Py_TYPE(py_default)->tp_name);
+ "value of unsupported type.", d->name);
return SRD_ERR_PYTHON;
}
g_variant_ref_sink(o->def);
/* Import the Python module. */
if (!(d->py_mod = PyImport_ImportModule(module_name))) {
- srd_exception_catch("Import of '%s' failed.", module_name);
+ srd_exception_catch("Import of '%s' failed", module_name);
goto err_out;
}
goto err_out;
}
py_method = PyObject_GetAttrString(d->py_dec, "start");
- if (!PyFunction_Check(py_method)) {
+ if (!PyCallable_Check(py_method)) {
srd_err("Protocol decoder %s Decoder class attribute 'start' "
"is not a method.", module_name);
goto err_out;
goto err_out;
}
py_method = PyObject_GetAttrString(d->py_dec, "decode");
- if (!PyFunction_Check(py_method)) {
+ if (!PyCallable_Check(py_method)) {
srd_err("Protocol decoder %s Decoder class attribute 'decode' "
"is not a method.", module_name);
goto err_out;
return NULL;
if (!(py_str = PyObject_GetAttrString(dec->py_mod, "__doc__"))) {
- srd_exception_catch("");
+ srd_exception_catch("Failed to get docstring");
return NULL;
}
#include "libsigrokdecode.h"
#include <stdarg.h>
#include <glib.h>
-#include <frameobject.h> /* Python header not pulled in by default. */
+
+static char *py_stringify(PyObject *py_obj)
+{
+ PyObject *py_str, *py_bytes;
+ char *str = NULL;
+
+ if (!py_obj)
+ return NULL;
+
+ py_str = PyObject_Str(py_obj);
+ if (!py_str || !PyUnicode_Check(py_str))
+ goto cleanup;
+
+ py_bytes = PyUnicode_AsUTF8String(py_str);
+ if (!py_bytes)
+ goto cleanup;
+
+ str = g_strdup(PyBytes_AsString(py_bytes));
+ Py_DECREF(py_bytes);
+
+cleanup:
+ Py_XDECREF(py_str);
+ if (!str) {
+ PyErr_Clear();
+ srd_dbg("Failed to stringify object.");
+ }
+ return str;
+}
+
+static char *py_get_string_attr(PyObject *py_obj, const char *attr)
+{
+ PyObject *py_str, *py_bytes;
+ char *str = NULL;
+
+ if (!py_obj)
+ return NULL;
+
+ py_str = PyObject_GetAttrString(py_obj, attr);
+ if (!py_str || !PyUnicode_Check(py_str))
+ goto cleanup;
+
+ py_bytes = PyUnicode_AsUTF8String(py_str);
+ if (!py_bytes)
+ goto cleanup;
+
+ str = g_strdup(PyBytes_AsString(py_bytes));
+ Py_DECREF(py_bytes);
+
+cleanup:
+ Py_XDECREF(py_str);
+ if (!str) {
+ PyErr_Clear();
+ srd_dbg("Failed to get object attribute %s.", attr);
+ }
+ return str;
+}
/** @private */
SRD_PRIV void srd_exception_catch(const char *format, ...)
{
- PyObject *etype, *evalue, *etb, *py_str;
- PyTracebackObject *py_tb;
- GString *msg;
va_list args;
- char *ename, *str, *tracestr;
+ PyObject *py_etype, *py_evalue, *py_etraceback;
+ PyObject *py_modname, *py_mod, *py_func, *py_tracefmt;
+ char *msg, *etype_name, *evalue_str, *tracefmt_str;
+ const char *etype_name_fallback;
- if (!PyErr_Occurred())
- /* Nothing is wrong. */
- return;
+ py_etype = py_evalue = py_etraceback = py_mod = py_func = NULL;
- PyErr_Fetch(&etype, &evalue, &etb);
- PyErr_NormalizeException(&etype, &evalue, &etb);
+ va_start(args, format);
+ msg = g_strdup_vprintf(format, args);
+ va_end(args);
- if (!(py_str = PyObject_Str(evalue))) {
- /* Shouldn't happen. */
- srd_dbg("Failed to convert exception value to string.");
- return;
+ PyErr_Fetch(&py_etype, &py_evalue, &py_etraceback);
+ if (!py_etype) {
+ /* No current exception, so just print the message. */
+ srd_err("%s.", msg);
+ goto cleanup;
}
+ PyErr_NormalizeException(&py_etype, &py_evalue, &py_etraceback);
- /* Send the exception error message(s) to srd_err(). */
- if (evalue)
- ename = (char *)Py_TYPE(evalue)->tp_name;
+ etype_name = py_get_string_attr(py_etype, "__name__");
+ evalue_str = py_stringify(py_evalue);
+ etype_name_fallback = (etype_name) ? etype_name : "(unknown exception)";
+
+ if (evalue_str)
+ srd_err("%s: %s: %s", etype_name_fallback, msg, evalue_str);
else
- /* Can be NULL. */
- ename = "(unknown exception)";
+ srd_err("%s: %s.", etype_name_fallback, msg);
- msg = g_string_sized_new(128);
- g_string_append(msg, ename);
- g_string_append(msg, ": ");
- va_start(args, format);
- g_string_append_vprintf(msg, format, args);
- va_end(args);
- py_str_as_str(py_str, &str);
- g_string_append(msg, str);
- Py_DecRef(py_str);
- srd_err("%s", msg->str);
-
- /* Send a more precise error location to srd_dbg(), if we have it. */
- if (etb && etb != Py_None) {
- tracestr = NULL;
- py_tb = (PyTracebackObject *)etb;
- py_str = PyUnicode_FromFormat("%U:%d in %U",
- py_tb->tb_frame->f_code->co_filename,
- py_tb->tb_frame->f_lineno,
- py_tb->tb_frame->f_code->co_name);
- py_str_as_str(py_str, &tracestr);
- Py_DecRef(py_str);
- g_string_printf(msg, "%s in %s: %s", ename, tracestr, str);
- srd_dbg("%s", msg->str);
- g_free(tracestr);
+ g_free(evalue_str);
+ g_free(etype_name);
+
+ /* If there is no traceback object, we are done. */
+ if (!py_etraceback)
+ goto cleanup;
+
+ py_modname = PyUnicode_FromString("traceback");
+ if (!py_modname)
+ goto cleanup;
+
+ py_mod = PyImport_Import(py_modname);
+ Py_DECREF(py_modname);
+
+ if (!py_mod)
+ goto cleanup;
+
+ py_func = PyObject_GetAttrString(py_mod, "format_exception");
+ if (!py_func || !PyCallable_Check(py_func))
+ goto cleanup;
+
+ /* Call into Python to format the stack trace. */
+ py_tracefmt = PyObject_CallFunctionObjArgs(py_func,
+ py_etype, py_evalue, py_etraceback, NULL);
+ if (!py_tracefmt)
+ goto cleanup;
+
+ tracefmt_str = py_stringify(py_tracefmt);
+ Py_DECREF(py_tracefmt);
+
+ /* Log the detailed stack trace. */
+ if (tracefmt_str) {
+ srd_dbg("%s", tracefmt_str);
+ g_free(tracefmt_str);
}
- g_free(str);
- g_string_free(msg, TRUE);
- Py_XDECREF(etype);
- Py_XDECREF(evalue);
- Py_XDECREF(etb);
+cleanup:
+ Py_XDECREF(py_func);
+ Py_XDECREF(py_mod);
+ Py_XDECREF(py_etraceback);
+ Py_XDECREF(py_evalue);
+ Py_XDECREF(py_etype);
/* Just in case. */
PyErr_Clear();
+
+ g_free(msg);
}
extern SRD_PRIV GSList *sessions;
-/* type_logic.c */
-extern SRD_PRIV PyTypeObject srd_logic_type;
+/* module_sigrokdecode.c */
+extern SRD_PRIV PyObject *srd_logic_type;
/** @endcond */
err_out:
Py_XDECREF(py_optval);
if (PyErr_Occurred()) {
- srd_exception_catch("Stray exception in srd_inst_option_set().");
+ srd_exception_catch("Stray exception in srd_inst_option_set()");
ret = SRD_ERR_PYTHON;
}
/* Create a new instance of this decoder class. */
if (!(di->py_inst = PyObject_CallObject(dec->py_dec, NULL))) {
if (PyErr_Occurred())
- srd_exception_catch("failed to create %s instance: ",
+ srd_exception_catch("Failed to create %s instance",
decoder_id);
g_free(di->dec_channelmap);
g_free(di);
di->inst_id);
if (!(py_res = PyObject_CallMethod(di->py_inst, "start", NULL))) {
- srd_exception_catch("Protocol decoder instance %s: ",
+ srd_exception_catch("Protocol decoder instance %s",
di->inst_id);
return SRD_ERR_PYTHON;
}
* Create new srd_logic object. Each iteration around the PD's loop
* will fill one sample into this object.
*/
- logic = PyObject_New(srd_logic, &srd_logic_type);
+ logic = PyObject_New(srd_logic, (PyTypeObject *)srd_logic_type);
Py_INCREF(logic);
logic->di = (struct srd_decoder_inst *)di;
logic->start_samplenum = start_samplenum;
Py_IncRef(di->py_inst);
if (!(py_res = PyObject_CallMethod(di->py_inst, "decode",
"KKO", start_samplenum, end_samplenum, logic))) {
- srd_exception_catch("Protocol decoder instance %s: ",
+ srd_exception_catch("Protocol decoder instance %s",
di->inst_id);
return SRD_ERR_PYTHON;
}
#ifndef LIBSIGROKDECODE_LIBSIGROKDECODE_INTERNAL_H
#define LIBSIGROKDECODE_LIBSIGROKDECODE_INTERNAL_H
+/* Use the stable ABI subset as per PEP 384. */
+#define Py_LIMITED_API 0x03020000
+
#include <Python.h> /* First, so we avoid a _POSIX_C_SOURCE warning. */
#include "libsigrokdecode.h"
#define srd_warn(...) srd_log(SRD_LOG_WARN, __VA_ARGS__)
#define srd_err(...) srd_log(SRD_LOG_ERR, __VA_ARGS__)
+/* type_decoder.c */
+SRD_PRIV PyObject *srd_Decoder_type_new(void);
+
+/* type_logic.c */
+SRD_PRIV PyObject *srd_logic_type_new(void);
+
/* module_sigrokdecode.c */
PyMODINIT_FUNC PyInit_sigrokdecode(void);
/* util.c */
-SRD_PRIV int py_attr_as_str(const PyObject *py_obj, const char *attr,
- char **outstr);
-SRD_PRIV int py_dictitem_as_str(const PyObject *py_obj, const char *key,
- char **outstr);
-SRD_PRIV int py_str_as_str(const PyObject *py_str, char **outstr);
-SRD_PRIV int py_strseq_to_char(const PyObject *py_strseq, char ***outstr);
+SRD_PRIV int py_attr_as_str(PyObject *py_obj, const char *attr, char **outstr);
+SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key, char **outstr);
+SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr);
+SRD_PRIV int py_strseq_to_char(PyObject *py_strseq, char ***out_strv);
/* exception.c */
SRD_PRIV void srd_exception_catch(const char *format, ...);
/** @cond PRIVATE */
-/* type_decoder.c */
-extern SRD_PRIV PyTypeObject srd_Decoder_type;
-
-/* type_logic.c */
-extern SRD_PRIV PyTypeObject srd_logic_type;
+SRD_PRIV PyObject *srd_logic_type = NULL;
/*
* When initialized, a reference to this module inside the Python interpreter
/** @cond PRIVATE */
PyMODINIT_FUNC PyInit_sigrokdecode(void)
{
- PyObject *mod;
+ PyObject *mod, *Decoder_type, *logic_type;
- /* tp_new needs to be assigned here for compiler portability. */
- srd_Decoder_type.tp_new = PyType_GenericNew;
- if (PyType_Ready(&srd_Decoder_type) < 0)
- return NULL;
+ mod = PyModule_Create(&sigrokdecode_module);
+ if (!mod)
+ goto err_out;
- srd_logic_type.tp_new = PyType_GenericNew;
- if (PyType_Ready(&srd_logic_type) < 0)
- return NULL;
+ Decoder_type = srd_Decoder_type_new();
+ if (!Decoder_type)
+ goto err_out;
+ if (PyModule_AddObject(mod, "Decoder", Decoder_type) < 0)
+ goto err_out;
- mod = PyModule_Create(&sigrokdecode_module);
- Py_INCREF(&srd_Decoder_type);
- if (PyModule_AddObject(mod, "Decoder",
- (PyObject *)&srd_Decoder_type) == -1)
- return NULL;
- Py_INCREF(&srd_logic_type);
- if (PyModule_AddObject(mod, "srd_logic",
- (PyObject *)&srd_logic_type) == -1)
- return NULL;
+ logic_type = srd_logic_type_new();
+ if (!logic_type)
+ goto err_out;
+ if (PyModule_AddObject(mod, "srd_logic", logic_type) < 0)
+ goto err_out;
/* Expose output types as symbols in the sigrokdecode module */
- if (PyModule_AddIntConstant(mod, "OUTPUT_ANN", SRD_OUTPUT_ANN) == -1)
- return NULL;
- if (PyModule_AddIntConstant(mod, "OUTPUT_PYTHON", SRD_OUTPUT_PYTHON) == -1)
- return NULL;
- if (PyModule_AddIntConstant(mod, "OUTPUT_BINARY", SRD_OUTPUT_BINARY) == -1)
- return NULL;
- if (PyModule_AddIntConstant(mod, "OUTPUT_META", SRD_OUTPUT_META) == -1)
- return NULL;
+ if (PyModule_AddIntConstant(mod, "OUTPUT_ANN", SRD_OUTPUT_ANN) < 0)
+ goto err_out;
+ if (PyModule_AddIntConstant(mod, "OUTPUT_PYTHON", SRD_OUTPUT_PYTHON) < 0)
+ goto err_out;
+ if (PyModule_AddIntConstant(mod, "OUTPUT_BINARY", SRD_OUTPUT_BINARY) < 0)
+ goto err_out;
+ if (PyModule_AddIntConstant(mod, "OUTPUT_META", SRD_OUTPUT_META) < 0)
+ goto err_out;
/* Expose meta input symbols. */
- if (PyModule_AddIntConstant(mod, "SRD_CONF_SAMPLERATE", SRD_CONF_SAMPLERATE) == -1)
- return NULL;
+ if (PyModule_AddIntConstant(mod, "SRD_CONF_SAMPLERATE", SRD_CONF_SAMPLERATE) < 0)
+ goto err_out;
+ srd_logic_type = logic_type;
mod_sigrokdecode = mod;
return mod;
+err_out:
+ Py_XDECREF(mod);
+ srd_exception_catch("Failed to initialize module");
+
+ return NULL;
}
/** @endcond */
PyObject_HEAD
} srd_Decoder;
-/* This is only used for nicer srd_dbg() output. */
-static const char *OUTPUT_TYPES[] = {
- "OUTPUT_ANN",
- "OUTPUT_PYTHON",
- "OUTPUT_BINARY",
- "OUTPUT_META",
-};
+/* This is only used for nicer srd_dbg() output.
+ */
+static const char *output_type_name(unsigned int idx)
+{
+ static const char names[][16] = {
+ "OUTPUT_ANN",
+ "OUTPUT_PYTHON",
+ "OUTPUT_BINARY",
+ "OUTPUT_META",
+ "(invalid)"
+ };
+ return names[MIN(idx, G_N_ELEMENTS(names) - 1)];
+}
static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj,
struct srd_proto_data *pdata)
/* Should be a list of [annotation class, [string, ...]]. */
if (!PyList_Check(obj) && !PyTuple_Check(obj)) {
- srd_err("Protocol decoder %s submitted %s instead of list.",
- di->decoder->name, obj->ob_type->tp_name);
+ srd_err("Protocol decoder %s submitted an annotation that"
+ " is not a list or tuple", di->decoder->name);
return SRD_ERR_PYTHON;
}
/* Should be a tuple of (binary class, bytes). */
if (!PyTuple_Check(obj)) {
- srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with "
- "%s instead of tuple.", di->decoder->name,
- obj->ob_type->tp_name);
+ srd_err("Protocol decoder %s submitted non-tuple for SRD_OUTPUT_BINARY.",
+ di->decoder->name);
return SRD_ERR_PYTHON;
}
if (pdata->pdo->meta_type == G_VARIANT_TYPE_INT64) {
if (!PyLong_Check(obj)) {
PyErr_Format(PyExc_TypeError, "This output was registered "
- "as 'int', but '%s' was passed.", obj->ob_type->tp_name);
+ "as 'int', but something else was passed.");
return SRD_ERR_PYTHON;
}
intvalue = PyLong_AsLongLong(obj);
} else if (pdata->pdo->meta_type == G_VARIANT_TYPE_DOUBLE) {
if (!PyFloat_Check(obj)) {
PyErr_Format(PyExc_TypeError, "This output was registered "
- "as 'float', but '%s' was passed.", obj->ob_type->tp_name);
+ "as 'float', but something else was passed.");
return SRD_ERR_PYTHON;
}
dvalue = PyFloat_AsDouble(obj);
srd_spew("Instance %s put %" PRIu64 "-%" PRIu64 " %s on oid %d.",
di->inst_id, start_sample, end_sample,
- OUTPUT_TYPES[pdo->output_type], output_id);
+ output_type_name(pdo->output_type), output_id);
pdata = g_malloc0(sizeof(struct srd_proto_data));
pdata->start_sample = start_sample;
if (!(py_res = PyObject_CallMethod(
next_di->py_inst, "decode", "KKO", start_sample,
end_sample, py_data))) {
- srd_exception_catch("Calling %s decode(): ",
+ srd_exception_catch("Calling %s decode() failed",
next_di->inst_id);
}
Py_XDECREF(py_res);
else if (meta_type_py == &PyFloat_Type)
meta_type_gv = G_VARIANT_TYPE_DOUBLE;
else {
- PyErr_Format(PyExc_TypeError, "Unsupported type '%s'.",
- meta_type_py->tp_name);
+ PyErr_Format(PyExc_TypeError, "Unsupported type.");
return NULL;
}
}
{NULL, NULL, 0, NULL}
};
-/** @cond PRIVATE */
-SRD_PRIV PyTypeObject srd_Decoder_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "sigrokdecode.Decoder",
- .tp_basicsize = sizeof(srd_Decoder),
- .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
- .tp_doc = "sigrok Decoder base class",
- .tp_methods = Decoder_methods,
-};
-/** @endcond */
+/** Create the sigrokdecode.Decoder type.
+ * @return The new type object.
+ * @private
+ */
+SRD_PRIV PyObject *srd_Decoder_type_new(void)
+{
+ PyType_Spec spec;
+ PyType_Slot slots[] = {
+ { Py_tp_doc, "sigrok Decoder base class" },
+ { Py_tp_methods, Decoder_methods },
+ { Py_tp_new, (void *)&PyType_GenericNew },
+ { 0, NULL }
+ };
+ spec.name = "sigrokdecode.Decoder";
+ spec.basicsize = sizeof(srd_Decoder);
+ spec.itemsize = 0;
+ spec.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
+ spec.slots = slots;
+
+ return PyType_FromSpec(&spec);
+}
return logic->sample;
}
-/** @cond PRIVATE */
-SRD_PRIV PyTypeObject srd_logic_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "srd_logic",
- .tp_basicsize = sizeof(srd_logic),
- .tp_flags = Py_TPFLAGS_DEFAULT,
- .tp_doc = "Sigrokdecode logic sample object",
- .tp_iter = srd_logic_iter,
- .tp_iternext = srd_logic_iternext,
-};
-/** @endcond */
+/** Create the srd_logic type.
+ * @return The new type object.
+ * @private
+ */
+SRD_PRIV PyObject *srd_logic_type_new(void)
+{
+ PyType_Spec spec;
+ PyType_Slot slots[] = {
+ { Py_tp_doc, "sigrokdecode logic sample object" },
+ { Py_tp_iter, (void *)&srd_logic_iter },
+ { Py_tp_iternext, (void *)&srd_logic_iternext },
+ { Py_tp_new, (void *)&PyType_GenericNew },
+ { 0, NULL }
+ };
+ spec.name = "srd_logic";
+ spec.basicsize = sizeof(srd_logic);
+ spec.itemsize = 0;
+ spec.flags = Py_TPFLAGS_DEFAULT;
+ spec.slots = slots;
+
+ return PyType_FromSpec(&spec);
+}
#include <config.h>
#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */
-#include "libsigrokdecode.h"
/**
* Get the value of a Python object's attribute, returned as a newly
* allocated char *.
*
- * @param py_obj The object to probe.
- * @param attr Name of the attribute to retrieve.
- * @param outstr ptr to char * storage to be filled in.
+ * @param[in] py_obj The object to probe.
+ * @param[in] attr Name of the attribute to retrieve.
+ * @param[out] 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.
+ * The 'outstr' argument points to a g_malloc()ed string upon success.
*
* @private
*/
-SRD_PRIV int py_attr_as_str(const PyObject *py_obj, const char *attr,
- char **outstr)
+SRD_PRIV int py_attr_as_str(PyObject *py_obj, const char *attr, char **outstr)
{
PyObject *py_str;
int ret;
- if (!PyObject_HasAttrString((PyObject *)py_obj, attr)) {
- srd_dbg("%s object has no attribute '%s'.",
- Py_TYPE(py_obj)->tp_name, attr);
+ if (!PyObject_HasAttrString(py_obj, attr)) {
+ srd_dbg("Object has no attribute '%s'.", attr);
return SRD_ERR_PYTHON;
}
- if (!(py_str = PyObject_GetAttrString((PyObject *)py_obj, attr))) {
- srd_exception_catch("");
- return SRD_ERR_PYTHON;
- }
-
- if (!PyUnicode_Check(py_str)) {
- srd_dbg("%s attribute should be a string, but is a %s.",
- attr, Py_TYPE(py_str)->tp_name);
- Py_DecRef(py_str);
+ if (!(py_str = PyObject_GetAttrString(py_obj, attr))) {
+ srd_exception_catch("Failed to get attribute '%s'", attr);
return SRD_ERR_PYTHON;
}
ret = py_str_as_str(py_str, outstr);
- Py_DecRef(py_str);
+ Py_DECREF(py_str);
return ret;
}
* Get the value of a Python dictionary item, returned as a newly
* allocated char *.
*
- * @param py_obj The dictionary to probe.
- * @param key Key of the item to retrieve.
- * @param outstr Pointer to char * storage to be filled in.
+ * @param[in] py_obj The dictionary to probe.
+ * @param[in] key Key of the item to retrieve.
+ * @param[out] outstr Pointer 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.
+ * The 'outstr' argument points to a g_malloc()ed string upon success.
*
* @private
*/
-SRD_PRIV int py_dictitem_as_str(const PyObject *py_obj, const char *key,
+SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key,
char **outstr)
{
PyObject *py_value;
- int ret;
- if (!PyDict_Check((PyObject *)py_obj)) {
- srd_dbg("Object is a %s, not a dictionary.",
- Py_TYPE((PyObject *)py_obj)->tp_name);
+ if (!PyDict_Check(py_obj)) {
+ srd_dbg("Object is not a dictionary.");
return SRD_ERR_PYTHON;
}
- if (!(py_value = PyDict_GetItemString((PyObject *)py_obj, key))) {
+ if (!(py_value = PyDict_GetItemString(py_obj, key))) {
srd_dbg("Dictionary has no attribute '%s'.", key);
return SRD_ERR_PYTHON;
}
- if (!PyUnicode_Check(py_value)) {
- srd_dbg("Dictionary value for %s should be a string, but is "
- "a %s.", key, Py_TYPE(py_value)->tp_name);
- return SRD_ERR_PYTHON;
- }
-
- ret = py_str_as_str(py_value, outstr);
-
- return ret;
+ return py_str_as_str(py_value, outstr);
}
/**
* 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.
+ * @param[in] py_str The unicode string object.
+ * @param[out] 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.
+ * The 'outstr' argument points to a g_malloc()ed string upon success.
*
* @private
*/
-SRD_PRIV int py_str_as_str(const PyObject *py_str, char **outstr)
+SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr)
{
- PyObject *py_encstr;
- int ret;
+ PyObject *py_bytes;
char *str;
- py_encstr = NULL;
- str = NULL;
- ret = SRD_OK;
-
- if (!PyUnicode_Check((PyObject *)py_str)) {
- srd_dbg("Object is a %s, not a string object.",
- Py_TYPE((PyObject *)py_str)->tp_name);
- ret = SRD_ERR_PYTHON;
- goto err_out;
- }
-
- if (!(py_encstr = PyUnicode_AsEncodedString((PyObject *)py_str,
- "utf-8", NULL))) {
- ret = SRD_ERR_PYTHON;
- goto err_out;
- }
- if (!(str = PyBytes_AS_STRING(py_encstr))) {
- ret = SRD_ERR_PYTHON;
- goto err_out;
+ if (!PyUnicode_Check(py_str)) {
+ srd_dbg("Object is not a string object.");
+ return SRD_ERR_PYTHON;
}
- *outstr = g_strdup(str);
-
-err_out:
- if (py_encstr)
- Py_XDECREF(py_encstr);
-
- if (PyErr_Occurred()) {
- srd_exception_catch("string conversion failed");
+ py_bytes = PyUnicode_AsUTF8String(py_str);
+ if (py_bytes) {
+ str = g_strdup(PyBytes_AsString(py_bytes));
+ Py_DECREF(py_bytes);
+ if (str) {
+ *outstr = str;
+ return SRD_OK;
+ }
}
+ srd_exception_catch("Failed to extract string");
- return ret;
+ return SRD_ERR_PYTHON;
}
/**
- * Convert a Python list of unicode strings to a NULL-terminated UTF8-encoded
- * char * array. The caller must g_free() each string when finished.
+ * Convert a Python list of unicode strings to a C string vector.
+ * On success, a pointer to a newly allocated NULL-terminated array of
+ * allocated C strings is written to @a out_strv. The caller must g_free()
+ * each string and the array itself.
*
- * @param py_strlist The list object.
- * @param outstr ptr to char ** storage to be filled in.
+ * @param[in] py_strseq The sequence object.
+ * @param[out] out_strv Address of string vector to be filled in.
*
* @return SRD_OK upon success, a (negative) error code otherwise.
- * The 'outstr' argument points to a g_malloc()ed char** upon success.
*
* @private
*/
-SRD_PRIV int py_strseq_to_char(const PyObject *py_strseq, char ***outstr)
+SRD_PRIV int py_strseq_to_char(PyObject *py_strseq, char ***out_strv)
{
- PyObject *py_str;
- int list_len, i;
- char **out, *str;
+ PyObject *py_item, *py_bytes;
+ char **strv, *str;
+ ssize_t seq_len, i;
+
+ if (!PySequence_Check(py_strseq)) {
+ srd_err("Object does not provide sequence protocol.");
+ return SRD_ERR_PYTHON;
+ }
+
+ seq_len = PySequence_Size(py_strseq);
+ if (seq_len < 0) {
+ srd_exception_catch("Failed to obtain sequence size");
+ return SRD_ERR_PYTHON;
+ }
- list_len = PySequence_Size((PyObject *)py_strseq);
- if (!(out = g_try_malloc(sizeof(char *) * (list_len + 1)))) {
- srd_err("Failed to g_malloc() 'out'.");
+ strv = g_try_new0(char *, seq_len + 1);
+ if (!strv) {
+ srd_err("Failed to allocate result string vector.");
return SRD_ERR_MALLOC;
}
- for (i = 0; i < list_len; i++) {
- if (!(py_str = PyUnicode_AsEncodedString(
- PySequence_GetItem((PyObject *)py_strseq, i), "utf-8", NULL)))
- return SRD_ERR_PYTHON;
- if (!(str = PyBytes_AS_STRING(py_str)))
- return SRD_ERR_PYTHON;
- out[i] = g_strdup(str);
+
+ for (i = 0; i < seq_len; i++) {
+ py_item = PySequence_GetItem(py_strseq, i);
+ if (!py_item)
+ goto err_out;
+
+ if (!PyUnicode_Check(py_item)) {
+ Py_DECREF(py_item);
+ goto err_out;
+ }
+ py_bytes = PyUnicode_AsUTF8String(py_item);
+ Py_DECREF(py_item);
+ if (!py_bytes)
+ goto err_out;
+
+ str = g_strdup(PyBytes_AsString(py_bytes));
+ Py_DECREF(py_bytes);
+ if (!str)
+ goto err_out;
+
+ strv[i] = str;
}
- out[i] = NULL;
- *outstr = out;
+ *out_strv = strv;
return SRD_OK;
+
+err_out:
+ g_strfreev(strv);
+ srd_exception_catch("Failed to obtain string item");
+
+ return SRD_ERR_PYTHON;
}