This adds a small library of code that will be used both by the test module as well as the main gpiod module for creating enum types in C Signed-off-by: Bartosz Golaszewski <brgl@xxxxxxxx> --- bindings/python/enum/Makefile.am | 9 ++ bindings/python/enum/enum.c | 208 +++++++++++++++++++++++++++++++ bindings/python/enum/enum.h | 24 ++++ 3 files changed, 241 insertions(+) create mode 100644 bindings/python/enum/Makefile.am create mode 100644 bindings/python/enum/enum.c create mode 100644 bindings/python/enum/enum.h diff --git a/bindings/python/enum/Makefile.am b/bindings/python/enum/Makefile.am new file mode 100644 index 0000000..7dd4a12 --- /dev/null +++ b/bindings/python/enum/Makefile.am @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@xxxxxxxx> + +noinst_LTLIBRARIES = libpycenum.la +libpycenum_la_SOURCES = enum.c enum.h + +libpycenum_la_CFLAGS = -Wall -Wextra -g -std=gnu89 $(PYTHON_CPPFLAGS) +libpycenum_la_LDFLAGS = -module -avoid-version +libpycenum_la_LIBADD = $(PYTHON_LIBS) diff --git a/bindings/python/enum/enum.c b/bindings/python/enum/enum.c new file mode 100644 index 0000000..22a384a --- /dev/null +++ b/bindings/python/enum/enum.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@xxxxxxxx> + +/* Code allowing to inherit from enum.Enum in a C extension. */ + +#include "enum.h" + +static PyObject *make_enum_args(const PyCEnum_EnumDef *enum_def) +{ + PyObject *dict, *args, *key, *val, *name; + const PyCEnum_EnumVal *item; + int ret; + + dict = PyDict_New(); + if (!dict) + return NULL; + + for (item = enum_def->values; item->name; item++) { + key = PyUnicode_FromString(item->name); + if (!key) { + Py_DECREF(dict); + return NULL; + } + + val = PyLong_FromLong(item->value); + if (!val) { + Py_DECREF(key); + Py_DECREF(dict); + return NULL; + } + + ret = PyDict_SetItem(dict, key, val); + Py_DECREF(key); + Py_DECREF(val); + if (ret) { + Py_DECREF(dict); + return NULL; + } + } + + name = PyUnicode_FromString(enum_def->name); + if (!name) { + Py_DECREF(dict); + return NULL; + } + + args = PyTuple_Pack(2, name, dict); + Py_DECREF(name); + Py_DECREF(dict); + return args; +} + +static PyObject *make_enum_type(const PyCEnum_EnumDef *enum_def) +{ + PyObject *new_type, *args, *enum_mod, *enum_type; + + args = make_enum_args(enum_def); + if (!args) + return NULL; + + enum_mod = PyImport_ImportModule("enum"); + if (!enum_mod) { + Py_DECREF(args); + return NULL; + } + + enum_type = PyObject_GetAttrString(enum_mod, "Enum"); + if (!enum_type) { + Py_DECREF(enum_mod); + Py_DECREF(args); + return NULL; + } + + new_type = PyObject_Call(enum_type, args, NULL); + Py_DECREF(enum_type); + Py_DECREF(enum_mod); + Py_DECREF(args); + return new_type; +} + +int PyCEnum_AddEnumsToType(const PyCEnum_EnumDef *defs, PyTypeObject *type) +{ + const PyCEnum_EnumDef *enum_def; + PyObject *enum_type; + int ret; + + for (enum_def = defs; enum_def->name; enum_def++) { + enum_type = make_enum_type(enum_def); + if (!enum_type) + return -1; + + ret = PyDict_SetItemString(type->tp_dict, + enum_def->name, enum_type); + if (ret) { + Py_DECREF(enum_type); + return -1; + } + } + + PyType_Modified(type); + return 0; +} + +static PyObject *map_c_to_python(PyObject *iter, int value) +{ + PyObject *next, *val; + long num; + + for (;;) { + next = PyIter_Next(iter); + if (!next) + break; + + val = PyObject_GetAttrString(next, "value"); + if (!val) { + Py_DECREF(next); + return NULL; + } + + num = PyLong_AsLong(val); + Py_DECREF(val); + + if (value == num) + return next; + + Py_DECREF(next); + } + + PyErr_SetString(PyExc_NotImplementedError, + "enum value does not exist"); + return NULL; +} + +PyObject *PyCEnum_MapCToPy(PyObject *parent, const char *enum_name, int value) +{ + PyObject *enum_type, *iter, *ret; + + enum_type = PyObject_GetAttrString(parent, enum_name); + if (!enum_type) + return NULL; + + iter = PyObject_GetIter(enum_type); + if (!iter) { + Py_DECREF(enum_type); + return NULL; + } + + ret = map_c_to_python(iter, value); + Py_DECREF(iter); + Py_DECREF(enum_type); + Py_INCREF(ret); + return ret; +} + +static int map_python_to_c(PyObject *iter, int value) +{ + PyObject *next, *val; + long num; + + for (;;) { + next = PyIter_Next(iter); + if (!next) + break; + + val = PyObject_GetAttrString(next, "value"); + if (!val) { + Py_DECREF(next); + return -1; + } + + num = PyLong_AsLong(val); + Py_DECREF(val); + + if (value == num) + return value; + + Py_DECREF(next); + } + + PyErr_SetString(PyExc_NotImplementedError, + "enum value does not exist"); + return -1; +} + +int PyCEnum_MapPyToC(PyObject *parent, const char *enum_name, PyObject *value) +{ + PyObject *enum_type, *iter, *val; + int ret; + + enum_type = PyObject_GetAttrString(parent, enum_name); + if (!enum_type) + return -1; + + iter = PyObject_GetIter(enum_type); + if (!iter) { + Py_DECREF(enum_type); + return -1; + } + + val = PyObject_GetAttrString(value, "value"); + if (!val) + return -1; + + ret = map_python_to_c(iter, PyLong_AsLong(val)); + Py_DECREF(iter); + Py_DECREF(enum_type); + return ret; +} diff --git a/bindings/python/enum/enum.h b/bindings/python/enum/enum.h new file mode 100644 index 0000000..28ddcaf --- /dev/null +++ b/bindings/python/enum/enum.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@xxxxxxxx> */ + +#ifndef __LIBGPIOD_PYTHON_ENUM_H__ +#define __LIBGPIOD_PYTHON_ENUM_H__ + +#include <Python.h> + +typedef struct { + const char *name; + long value; +} PyCEnum_EnumVal; + +typedef struct { + const char *name; + const PyCEnum_EnumVal *values; +} PyCEnum_EnumDef; + +int PyCEnum_AddEnumsToType(const PyCEnum_EnumDef *defs, PyTypeObject *type); + +PyObject *PyCEnum_MapCToPy(PyObject *parent, const char *enum_name, int value); +int PyCEnum_MapPyToC(PyObject *parent, const char *enum_name, PyObject *value); + +#endif /* __LIBGPIOD_PYTHON_ENUM_H__ */ -- 2.34.1