new file mode 100644
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
+
+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)
new file mode 100644
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
+
+/* 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;
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl> */
+
+#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__ */
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@bgdev.pl> --- 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