diff mbox series

[libgpiod,v2,2/5] bindings: python: enum: add a piece of common code for using python's enums from C

Message ID 20220525140704.94983-3-brgl@bgdev.pl
State New
Headers show
Series bindings: implement python bindings for libgpiod v2 | expand

Commit Message

Bartosz Golaszewski May 25, 2022, 2:07 p.m. UTC
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
diff mbox series

Patch

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@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)
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@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;
+}
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@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__ */