Skip to contentSkip to conversation

bindings: implement python bindings for libgpiod v2

PATCH Superseded Bartosz Golaszewski 5 participants 18 messages May 25, 2022 – Jun 10, 2022 GPIO

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v2 cover bindings: implement python bindings for libgpiod v2

This series adds python bindings for libgpiod v2. The series is split into several patches for easier review.

In general the python bindings follow what we did for C++ in terms of class layout except that we leverage python's flexibility to reduce the number of method variants by allowing different types of arguments.

Because python doesn't have RAII, unlike C++, we provide a module-level request_lines() helper as gpiod.Chip(path).request_lines(...) one-liner could lead to the chip left dangling even after the last reference is dropped.

Because python forces us to dynamically allocate objects all the time even for fundamental types (which are also immutable) there's no point in trying to make the EdgeEventBuffer avoid copying the events like we did in C++ for better performance. Python simply isn't designed around speed.

Bartosz Golaszewski (5):
bindings: python: remove old version
bindings: python: enum: add a piece of common code for using python's
enums from C
bindings: python: add examples for v2 API
bindings: python: add tests for v2 API
bindings: python: add the implementation for v2 API

 bindings/python/.gitignore                    |    1 +
 bindings/python/Makefile.am                   |   19 +-
 bindings/python/chip-info.c                   |  126 +
 bindings/python/chip.c                        |  552 ++++
 bindings/python/edge-event-buffer.c           |  301 ++
 bindings/python/edge-event.c                  |  191 ++
 bindings/python/enum/Makefile.am              |    9 +
 bindings/python/enum/enum.c                   |  208 ++
 bindings/python/enum/enum.h                   |   24 +
 bindings/python/examples/gpiodetect.py        |   13 +-
 bindings/python/examples/gpiofind.py          |   12 +-
 bindings/python/examples/gpioget.py           |   28 +-
 bindings/python/examples/gpioinfo.py          |   39 +-
 bindings/python/examples/gpiomon.py           |   53 +-
 bindings/python/examples/gpioset.py           |   36 +-
 bindings/python/exception.c                   |  182 ++
 bindings/python/gpiodmodule.c                 | 2662 -----------------
 bindings/python/info-event.c                  |  175 ++
 bindings/python/line-config.c                 | 1338 +++++++++
 bindings/python/line-info.c                   |  286 ++
 bindings/python/line-request.c                |  713 +++++
 bindings/python/line.c                        |  239 ++
 bindings/python/module.c                      |  281 ++
 bindings/python/module.h                      |   57 +
 bindings/python/request-config.c              |  320 ++
 bindings/python/tests/Makefile.am             |   15 +-
 bindings/python/tests/cases/__init__.py       |   12 +
 bindings/python/tests/cases/tests_chip.py     |  157 +
 .../python/tests/cases/tests_chip_info.py     |   59 +
 .../python/tests/cases/tests_edge_event.py    |  274 ++
 .../python/tests/cases/tests_info_event.py    |  135 +
 .../python/tests/cases/tests_line_config.py   |  250 ++
 .../python/tests/cases/tests_line_info.py     |   90 +
 .../python/tests/cases/tests_line_request.py  |  295 ++
 bindings/python/tests/cases/tests_misc.py     |   53 +
 .../tests/cases/tests_request_config.py       |   77 +
 bindings/python/tests/gpiod_py_test.py        |  827 +----
 bindings/python/tests/gpiomockupmodule.c      |  309 --
 bindings/python/tests/gpiosimmodule.c         |  434 +++
 configure.ac                                  |    3 +-
 40 files changed, 6973 insertions(+), 3882 deletions(-)
 create mode 100644 bindings/python/.gitignore
 create mode 100644 bindings/python/chip-info.c
 create mode 100644 bindings/python/chip.c
 create mode 100644 bindings/python/edge-event-buffer.c
 create mode 100644 bindings/python/edge-event.c
 create mode 100644 bindings/python/enum/Makefile.am
 create mode 100644 bindings/python/enum/enum.c
 create mode 100644 bindings/python/enum/enum.h
 create mode 100644 bindings/python/exception.c
 delete mode 100644 bindings/python/gpiodmodule.c
 create mode 100644 bindings/python/info-event.c
 create mode 100644 bindings/python/line-config.c
 create mode 100644 bindings/python/line-info.c
 create mode 100644 bindings/python/line-request.c
 create mode 100644 bindings/python/line.c
 create mode 100644 bindings/python/module.c
 create mode 100644 bindings/python/module.h
 create mode 100644 bindings/python/request-config.c
 create mode 100644 bindings/python/tests/cases/__init__.py
 create mode 100644 bindings/python/tests/cases/tests_chip.py
 create mode 100644 bindings/python/tests/cases/tests_chip_info.py
 create mode 100644 bindings/python/tests/cases/tests_edge_event.py
 create mode 100644 bindings/python/tests/cases/tests_info_event.py
 create mode 100644 bindings/python/tests/cases/tests_line_config.py
 create mode 100644 bindings/python/tests/cases/tests_line_info.py
 create mode 100644 bindings/python/tests/cases/tests_line_request.py
 create mode 100644 bindings/python/tests/cases/tests_misc.py
 create mode 100644 bindings/python/tests/cases/tests_request_config.py
 delete mode 100644 bindings/python/tests/gpiomockupmodule.c
 create mode 100644 bindings/python/tests/gpiosimmodule.c
Signature · 1 line
2.34.1
#1 lore raw

PATCH v2 1/5 bindings: python: remove old version

This removes v1 python bindings for easier review of v2.

---
 bindings/python/Makefile.am              |   25 -
 bindings/python/examples/Makefile.am     |   10 -
 bindings/python/examples/gpiodetect.py   |   16 -
 bindings/python/examples/gpiofind.py     |   20 -
 bindings/python/examples/gpioget.py      |   25 -
 bindings/python/examples/gpioinfo.py     |   28 -
 bindings/python/examples/gpiomon.py      |   42 -
 bindings/python/examples/gpioset.py      |   25 -
 bindings/python/gpiodmodule.c            | 2662 ----------------------
 bindings/python/tests/Makefile.am        |   13 -
 bindings/python/tests/gpiod_py_test.py   |  832 -------
 bindings/python/tests/gpiomockupmodule.c |  309 ---
 12 files changed, 4007 deletions(-)
 delete mode 100644 bindings/python/Makefile.am
 delete mode 100644 bindings/python/examples/Makefile.am
 delete mode 100755 bindings/python/examples/gpiodetect.py
 delete mode 100755 bindings/python/examples/gpiofind.py
 delete mode 100755 bindings/python/examples/gpioget.py
 delete mode 100755 bindings/python/examples/gpioinfo.py
 delete mode 100755 bindings/python/examples/gpiomon.py
 delete mode 100755 bindings/python/examples/gpioset.py
 delete mode 100644 bindings/python/gpiodmodule.c
 delete mode 100644 bindings/python/tests/Makefile.am
 delete mode 100755 bindings/python/tests/gpiod_py_test.py
 delete mode 100644 bindings/python/tests/gpiomockupmodule.c
bindings/python/Makefile.am deleted +0 -25
deleted file mode 100644
index 4405d8f..0000000
@@ -1,25 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
-
-pyexec_LTLIBRARIES = gpiod.la
-
-gpiod_la_SOURCES = gpiodmodule.c
-
-gpiod_la_CFLAGS = -I$(top_srcdir)/include/
-gpiod_la_CFLAGS += -Wall -Wextra -g -std=gnu89 $(PYTHON_CPPFLAGS)
-gpiod_la_LDFLAGS = -module -avoid-version
-gpiod_la_LIBADD = $(top_builddir)/lib/libgpiod.la $(PYTHON_LIBS)
-
-SUBDIRS = .
-
-if WITH_TESTS
-
-SUBDIRS += tests
-
-endif
-
-if WITH_EXAMPLES
-
-SUBDIRS += examples
-
-endif
bindings/python/examples/Makefile.am deleted +0 -10
deleted file mode 100644
index 4169469..0000000
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
-
-EXTRA_DIST =				\
-		gpiodetect.py		\
-		gpiofind.py		\
-		gpioget.py		\
-		gpioinfo.py		\
-		gpiomon.py		\
-		gpioset.py
bindings/python/examples/gpiodetect.py deleted +0 -16
deleted file mode 100755
index da6ee9a..0000000
@@ -1,16 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0-or-later
-# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
-
-'''Reimplementation of the gpiodetect tool in Python.'''
-
-import gpiod
-import os
-
-if __name__ == '__main__':
-    for entry in os.scandir('/dev/'):
-        if gpiod.is_gpiochip_device(entry.path):
-            with gpiod.Chip(entry.path) as chip:
-                print('{} [{}] ({} lines)'.format(chip.name(),
-                                                  chip.label(),
-                                                  chip.num_lines()))
bindings/python/examples/gpiofind.py deleted +0 -20
deleted file mode 100755
index a9ec734..0000000
@@ -1,20 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0-or-later
-# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
-
-'''Reimplementation of the gpiofind tool in Python.'''
-
-import gpiod
-import os
-import sys
-
-if __name__ == '__main__':
-    for entry in os.scandir('/dev/'):
-        if gpiod.is_gpiochip_device(entry.path):
-            with gpiod.Chip(entry.path) as chip:
-                offset = chip.find_line(sys.argv[1], unique=True)
-                if offset is not None:
-                     print('{} {}'.format(line.owner().name(), offset))
-                     sys.exit(0)
-
-    sys.exit(1)
bindings/python/examples/gpioget.py deleted +0 -25
deleted file mode 100755
index 26a2ced..0000000
@@ -1,25 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0-or-later
-# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
-
-'''Simplified reimplementation of the gpioget tool in Python.'''
-
-import gpiod
-import sys
-
-if __name__ == '__main__':
-    if len(sys.argv) < 3:
-        raise TypeError('usage: gpioget.py <gpiochip> <offset1> <offset2> ...')
-
-    with gpiod.Chip(sys.argv[1]) as chip:
-        offsets = []
-        for off in sys.argv[2:]:
-            offsets.append(int(off))
-
-        lines = chip.get_lines(offsets)
-        lines.request(consumer=sys.argv[0], type=gpiod.LINE_REQ_DIR_IN)
-        vals = lines.get_values()
-
-        for val in vals:
-            print(val, end=' ')
-        print()
bindings/python/examples/gpioinfo.py deleted +0 -28
deleted file mode 100755
index 84188f1..0000000
@@ -1,28 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0-or-later
-# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
-
-'''Simplified reimplementation of the gpioinfo tool in Python.'''
-
-import gpiod
-import os
-
-if __name__ == '__main__':
-    for entry in os.scandir('/dev/'):
-        if gpiod.is_gpiochip_device(entry.path):
-            with gpiod.Chip(entry.path) as chip:
-                print('{} - {} lines:'.format(chip.name(), chip.num_lines()))
-
-                for line in gpiod.LineIter(chip):
-                    offset = line.offset()
-                    name = line.name()
-                    consumer = line.consumer()
-                    direction = line.direction()
-                    active_low = line.is_active_low()
-
-                    print('\tline {:>3}: {:>18} {:>12} {:>8} {:>10}'.format(
-                          offset,
-                          'unnamed' if name is None else name,
-                          'unused' if consumer is None else consumer,
-                          'input' if direction == gpiod.Line.DIRECTION_INPUT else 'output',
-                          'active-low' if active_low else 'active-high'))
bindings/python/examples/gpiomon.py deleted +0 -42

42 lines not on this page · raw message

bindings/python/examples/gpioset.py deleted +0 -25
deleted file mode 100755
index 63e08dc..0000000
@@ -1,25 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0-or-later
-# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
-
-'''Simplified reimplementation of the gpioset tool in Python.'''
-
-import gpiod
-import sys
-
-if __name__ == '__main__':
-    if len(sys.argv) < 3:
-        raise TypeError('usage: gpioset.py <gpiochip> <offset1>=<value1> ...')
-
-    with gpiod.Chip(sys.argv[1]) as chip:
-        offsets = []
-        values = []
-        for arg in sys.argv[2:]:
-            arg = arg.split('=')
-            offsets.append(int(arg[0]))
-            values.append(int(arg[1]))
-
-        lines = chip.get_lines(offsets)
-        lines.request(consumer=sys.argv[0], type=gpiod.LINE_REQ_DIR_OUT)
-        lines.set_values(values)
-        input()
bindings/python/gpiodmodule.c deleted +0 -2662

2,662 lines not on this page · raw message

bindings/python/tests/Makefile.am deleted +0 -13
deleted file mode 100644
index 972b669..0000000
@@ -1,13 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
-
-dist_bin_SCRIPTS = gpiod_py_test.py
-
-pyexec_LTLIBRARIES = gpiomockup.la
-
-gpiomockup_la_SOURCES = gpiomockupmodule.c
-gpiomockup_la_CFLAGS = -I$(top_srcdir)/tests/mockup/
-gpiomockup_la_CFLAGS += -Wall -Wextra -g -std=gnu89 $(PYTHON_CPPFLAGS)
-gpiomockup_la_LDFLAGS = -module -avoid-version
-gpiomockup_la_LIBADD = $(top_builddir)/tests/mockup/libgpiomockup.la
-gpiomockup_la_LIBADD += $(PYTHON_LIBS)
bindings/python/tests/gpiod_py_test.py deleted +0 -832

134 lines not on this page · raw message

bindings/python/tests/gpiomockupmodule.c deleted +0 -309

309 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.34.1

Part of the diff is not shown here. View the raw message.

#2 lore raw

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

In reply to Bartosz Golaszewski

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

---
 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
bindings/python/enum/Makefile.am new file +9 -0
new file mode 100644
index 0000000..7dd4a12
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <[email protected]>
+
+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)
bindings/python/enum/enum.c new file +208 -0

208 lines not on this page · raw message

bindings/python/enum/enum.h new file +24 -0
new file mode 100644
index 0000000..28ddcaf
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <[email protected]> */
+
+#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__ */
Signature · 1 line
2.34.1
#3 lore raw

PATCH v2 3/5 bindings: python: add examples for v2 API

In reply to Bartosz Golaszewski

This adds the usual set of reimplementations of gpio-tools using the new python module.

---
 bindings/python/examples/Makefile.am   | 10 ++++++++
 bindings/python/examples/gpiodetect.py | 17 +++++++++++++
 bindings/python/examples/gpiofind.py   | 20 +++++++++++++++
 bindings/python/examples/gpioget.py    | 31 +++++++++++++++++++++++
 bindings/python/examples/gpioinfo.py   | 35 ++++++++++++++++++++++++++
 bindings/python/examples/gpiomon.py    | 31 +++++++++++++++++++++++
 bindings/python/examples/gpioset.py    | 35 ++++++++++++++++++++++++++
 7 files changed, 179 insertions(+)
 create mode 100644 bindings/python/examples/Makefile.am
 create mode 100755 bindings/python/examples/gpiodetect.py
 create mode 100755 bindings/python/examples/gpiofind.py
 create mode 100755 bindings/python/examples/gpioget.py
 create mode 100755 bindings/python/examples/gpioinfo.py
 create mode 100755 bindings/python/examples/gpiomon.py
 create mode 100755 bindings/python/examples/gpioset.py
bindings/python/examples/Makefile.am new file +10 -0
new file mode 100644
index 0000000..4169469
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+EXTRA_DIST =				\
+		gpiodetect.py		\
+		gpiofind.py		\
+		gpioget.py		\
+		gpioinfo.py		\
+		gpiomon.py		\
+		gpioset.py
bindings/python/examples/gpiodetect.py new file +17 -0
new file mode 100755
index 0000000..08e586b
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Reimplementation of the gpiodetect tool in Python."""
+
+import gpiod
+import os
+
+if __name__ == "__main__":
+    for entry in os.scandir("/dev/"):
+        if gpiod.is_gpiochip_device(entry.path):
+            with gpiod.Chip(entry.path) as chip:
+                info = chip.get_info()
+                print(
+                    "{} [{}] ({} lines)".format(info.name, info.label, info.num_lines)
+                )
bindings/python/examples/gpiofind.py new file +20 -0
new file mode 100755
index 0000000..e488a49
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Reimplementation of the gpiofind tool in Python."""
+
+import gpiod
+import os
+import sys
+
+if __name__ == "__main__":
+    for entry in os.scandir("/dev/"):
+        if gpiod.is_gpiochip_device(entry.path):
+            with gpiod.Chip(entry.path) as chip:
+                offset = chip.get_line_offset_from_name(sys.argv[1])
+                if offset is not None:
+                    print("{} {}".format(chip.get_info().name, offset))
+                    sys.exit(0)
+
+    sys.exit(1)
bindings/python/examples/gpioget.py new file +31 -0
new file mode 100755
index 0000000..c509f38
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpioget tool in Python."""
+
+import gpiod
+import sys
+
+Direction = gpiod.Line.Direction
+Value = gpiod.Line.Value
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise TypeError("usage: gpioget.py <gpiochip> <offset1> <offset2> ...")
+
+    path = sys.argv[1]
+    offsets = []
+    for off in sys.argv[2:]:
+        offsets.append(int(off))
+
+    with gpiod.request_lines(
+        path,
+        gpiod.RequestConfig(offsets=offsets, consumer="gpioget.py"),
+        gpiod.LineConfig(direction=Direction.INPUT),
+    ) as request:
+        vals = request.get_values()
+
+        for val in vals:
+            print("0" if val == Value.INACTIVE else "1", end=" ")
+        print()
bindings/python/examples/gpioinfo.py new file +35 -0
new file mode 100755
index 0000000..3097d10
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpioinfo tool in Python."""
+
+import gpiod
+import os
+
+if __name__ == "__main__":
+    for entry in os.scandir("/dev/"):
+        if gpiod.is_gpiochip_device(entry.path):
+            with gpiod.Chip(entry.path) as chip:
+                cinfo = chip.get_info()
+                print("{} - {} lines:".format(cinfo.name, cinfo.num_lines))
+
+                for offset in range(0, cinfo.num_lines):
+                    linfo = chip.get_line_info(offset)
+                    offset = linfo.offset
+                    name = linfo.name
+                    consumer = linfo.consumer
+                    direction = linfo.direction
+                    active_low = linfo.active_low
+
+                    print(
+                        "\tline {:>3}: {:>18} {:>12} {:>8} {:>10}".format(
+                            offset,
+                            "unnamed" if name is None else name,
+                            "unused" if consumer is None else consumer,
+                            "input"
+                            if direction == gpiod.Line.Direction.INPUT
+                            else "output",
+                            "active-low" if active_low else "active-high",
+                        )
+                    )
bindings/python/examples/gpiomon.py new file +31 -0
new file mode 100755
index 0000000..b0f4b88
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpiomon tool in Python."""
+
+import gpiod
+import sys
+
+Edge = gpiod.Line.Edge
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise TypeError("usage: gpiomon.py <gpiochip> <offset1> <offset2> ...")
+
+    path = sys.argv[1]
+    offsets = []
+    for off in sys.argv[2:]:
+        offsets.append(int(off))
+
+    buf = gpiod.EdgeEventBuffer()
+
+    with gpiod.request_lines(
+        path,
+        gpiod.RequestConfig(offsets=offsets, consumer="gpiomon.py"),
+        gpiod.LineConfig(edge_detection=Edge.BOTH),
+    ) as request:
+        while True:
+            request.read_edge_event(buf)
+            for event in buf:
+                print(event)
bindings/python/examples/gpioset.py new file +35 -0
new file mode 100755
index 0000000..3a8f8cc
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpioset tool in Python."""
+
+import gpiod
+import sys
+
+Value = gpiod.Line.Value
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise TypeError("usage: gpioset.py <gpiochip> <offset1>=<value1> ...")
+
+    path = sys.argv[1]
+    values = dict()
+    for arg in sys.argv[2:]:
+        arg = arg.split("=")
+        key = int(arg[0])
+        val = int(arg[1])
+
+        if val == 1:
+            values[key] = Value.ACTIVE
+        elif val == 0:
+            values[key] = Value.INACTIVE
+        else:
+            raise ValueError("{} is an invalid value for GPIO lines".format(val))
+
+    with gpiod.request_lines(
+        path,
+        gpiod.RequestConfig(offsets=values.keys(), consumer="gpioset.py"),
+        gpiod.LineConfig(direction=gpiod.Line.Direction.OUTPUT, output_values=values),
+    ) as request:
+        input()
Signature · 1 line
2.34.1
#4 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 108 lines · Bartosz Golaszewski

This adds the usual set of reimplementations of gpio-tools using the new python module.

---
 bindings/python/examples/Makefile.am   | 10 ++++++++
 bindings/python/examples/gpiodetect.py | 17 +++++++++++++
 bindings/python/examples/gpiofind.py   | 20 +++++++++++++++
 bindings/python/examples/gpioget.py    | 31 +++++++++++++++++++++++
 bindings/python/examples/gpioinfo.py   | 35 ++++++++++++++++++++++++++
 bindings/python/examples/gpiomon.py    | 31 +++++++++++++++++++++++
 bindings/python/examples/gpioset.py    | 35 ++++++++++++++++++++++++++
 7 files changed, 179 insertions(+)
 create mode 100644 bindings/python/examples/Makefile.am
 create mode 100755 bindings/python/examples/gpiodetect.py
 create mode 100755 bindings/python/examples/gpiofind.py
 create mode 100755 bindings/python/examples/gpioget.py
 create mode 100755 bindings/python/examples/gpioinfo.py
 create mode 100755 bindings/python/examples/gpiomon.py
 create mode 100755 bindings/python/examples/gpioset.py
bindings/python/examples/Makefile.am new file +10 -0
new file mode 100644
index 0000000..4169469
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+EXTRA_DIST =				\
+		gpiodetect.py		\
+		gpiofind.py		\
+		gpioget.py		\
+		gpioinfo.py		\
+		gpiomon.py		\
+		gpioset.py
bindings/python/examples/gpiodetect.py new file +17 -0
new file mode 100755
index 0000000..08e586b
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Reimplementation of the gpiodetect tool in Python."""
+
+import gpiod
+import os
+
+if __name__ == "__main__":
+    for entry in os.scandir("/dev/"):
+        if gpiod.is_gpiochip_device(entry.path):
+            with gpiod.Chip(entry.path) as chip:
+                info = chip.get_info()
+                print(
+                    "{} [{}] ({} lines)".format(info.name, info.label, info.num_lines)
+                )
bindings/python/examples/gpiofind.py new file +20 -0
new file mode 100755
index 0000000..e488a49
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Reimplementation of the gpiofind tool in Python."""
+
+import gpiod
+import os
+import sys
+
+if __name__ == "__main__":
+    for entry in os.scandir("/dev/"):
+        if gpiod.is_gpiochip_device(entry.path):
+            with gpiod.Chip(entry.path) as chip:
+                offset = chip.get_line_offset_from_name(sys.argv[1])
+                if offset is not None:
+                    print("{} {}".format(chip.get_info().name, offset))
+                    sys.exit(0)
+
+    sys.exit(1)
bindings/python/examples/gpioget.py new file +16 -0
new file mode 100755
index 0000000..c509f38
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpioget tool in Python."""
+
+import gpiod
+import sys
+
+Direction = gpiod.Line.Direction
+Value = gpiod.Line.Value
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise TypeError("usage: gpioget.py <gpiochip> <offset1> <offset2> ...")
+

bindings/python/examples/gpioget.py line 26

Quoted 10 lines
bindings/python/examples/gpioget.py new file +10 -0
+    path = sys.argv[1]
+    offsets = []
+    for off in sys.argv[2:]:
+        offsets.append(int(off))
+
+    with gpiod.request_lines(
+        path,
+        gpiod.RequestConfig(offsets=offsets, consumer="gpioget.py"),
+        gpiod.LineConfig(direction=Direction.INPUT),
+    ) as request:

With request_lines() being the primary entry point to the gpiod module, consider extending it to allow the RequestConfig and LineConfig kwargs to be passed directly to request_lines(), and for those transient objects to be constructed within request_lines().
That way the average user does not need to concern themselves with those objects and the code is easier to read.
i.e.
with gpiod.request_lines(
path,
offsets=offsets,
consumer="gpioget.py",
direction=Direction.INPUT,
) as request:

You can still support passing in complete RequestConfig and LineConfig as kwargs for cases where the user requires complex configuration. i.e.
with gpiod.request_lines(
path,
req_cfg=gpiod.RequestConfig(offsets=offsets, consumer="gpioget.py"), line_cfg=gpiod.LineConfig(direction=Direction.INPUT),
) as request:

Or for those use cases the user could use the Chip.request_lines() (which wouldn't have the kwarg sugar) instead.

Would be good to have some examples with complex configuration as well, not just the tool reimplementations.

Quoted 73 lines
bindings/python/examples/gpioget.py new file +5 -0
+        vals = request.get_values()
+
+        for val in vals:
+            print("0" if val == Value.INACTIVE else "1", end=" ")
+        print()
bindings/python/examples/gpioinfo.py new file +35 -0
new file mode 100755
index 0000000..3097d10
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpioinfo tool in Python."""
+
+import gpiod
+import os
+
+if __name__ == "__main__":
+    for entry in os.scandir("/dev/"):
+        if gpiod.is_gpiochip_device(entry.path):
+            with gpiod.Chip(entry.path) as chip:
+                cinfo = chip.get_info()
+                print("{} - {} lines:".format(cinfo.name, cinfo.num_lines))
+
+                for offset in range(0, cinfo.num_lines):
+                    linfo = chip.get_line_info(offset)
+                    offset = linfo.offset
+                    name = linfo.name
+                    consumer = linfo.consumer
+                    direction = linfo.direction
+                    active_low = linfo.active_low
+
+                    print(
+                        "\tline {:>3}: {:>18} {:>12} {:>8} {:>10}".format(
+                            offset,
+                            "unnamed" if name is None else name,
+                            "unused" if consumer is None else consumer,
+                            "input"
+                            if direction == gpiod.Line.Direction.INPUT
+                            else "output",
+                            "active-low" if active_low else "active-high",
+                        )
+                    )
bindings/python/examples/gpiomon.py new file +21 -0
new file mode 100755
index 0000000..b0f4b88
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpiomon tool in Python."""
+
+import gpiod
+import sys
+
+Edge = gpiod.Line.Edge
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise TypeError("usage: gpiomon.py <gpiochip> <offset1> <offset2> ...")
+
+    path = sys.argv[1]
+    offsets = []
+    for off in sys.argv[2:]:
+        offsets.append(int(off))
+
+    buf = gpiod.EdgeEventBuffer()

bindings/python/examples/gpiomon.py line 31

Quoted 10 lines
bindings/python/examples/gpiomon.py new file +10 -0
+
+    with gpiod.request_lines(
+        path,
+        gpiod.RequestConfig(offsets=offsets, consumer="gpiomon.py"),
+        gpiod.LineConfig(edge_detection=Edge.BOTH),
+    ) as request:
+        while True:
+            request.read_edge_event(buf)
+            for event in buf:
+                print(event)

Can you hide the buffer here to simplify the common case? How about having the request manage the buffer, and add a generator method that returns the next event, say edge_events()?

For the uncommon case, add kwargs to allow the user to provide the buffer, or to specify the buffer size. If neither provided then the request constructs a default sized buffer.

Then the code becomes:

with gpiod.request_lines(
path,
offsets=offsets,
consumer="gpiomon.py",
edge_detection=Edge.BOTH,
) as request:
for event in request.edge_events():
print(event)

Quoted 44 lines
bindings/python/examples/gpioset.py new file +35 -0
new file mode 100755
index 0000000..3a8f8cc
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpioset tool in Python."""
+
+import gpiod
+import sys
+
+Value = gpiod.Line.Value
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise TypeError("usage: gpioset.py <gpiochip> <offset1>=<value1> ...")
+
+    path = sys.argv[1]
+    values = dict()
+    for arg in sys.argv[2:]:
+        arg = arg.split("=")
+        key = int(arg[0])
+        val = int(arg[1])
+
+        if val == 1:
+            values[key] = Value.ACTIVE
+        elif val == 0:
+            values[key] = Value.INACTIVE
+        else:
+            raise ValueError("{} is an invalid value for GPIO lines".format(val))
+
+    with gpiod.request_lines(
+        path,
+        gpiod.RequestConfig(offsets=values.keys(), consumer="gpioset.py"),
+        gpiod.LineConfig(direction=gpiod.Line.Direction.OUTPUT, output_values=values),
+    ) as request:
+        input()
Signature · 1 line
2.34.1

The focus of my comments above is to simplify the API for the most common case, and to make it a little more Pythonic rather than mirroring the C API, in both cases by hiding implementation details that the casual user doesn't need to know about.

I'm pretty sure other minor things that I'm not 100% comfortable with are the same as with the C++ bindings, and the Python is in keeping with that, so I wont recover the same ground.

I'm ok with the series overall. As per my C++ comments, it would be great to get some feedback from Python developers.

Cheers,
Kent.

#5 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 293 lines · Kent Gibson
Quoted 108 lines · Bartosz Golaszewski

This adds the usual set of reimplementations of gpio-tools using the new python module.

---
 bindings/python/examples/Makefile.am   | 10 ++++++++
 bindings/python/examples/gpiodetect.py | 17 +++++++++++++
 bindings/python/examples/gpiofind.py   | 20 +++++++++++++++
 bindings/python/examples/gpioget.py    | 31 +++++++++++++++++++++++
 bindings/python/examples/gpioinfo.py   | 35 ++++++++++++++++++++++++++
 bindings/python/examples/gpiomon.py    | 31 +++++++++++++++++++++++
 bindings/python/examples/gpioset.py    | 35 ++++++++++++++++++++++++++
 7 files changed, 179 insertions(+)
 create mode 100644 bindings/python/examples/Makefile.am
 create mode 100755 bindings/python/examples/gpiodetect.py
 create mode 100755 bindings/python/examples/gpiofind.py
 create mode 100755 bindings/python/examples/gpioget.py
 create mode 100755 bindings/python/examples/gpioinfo.py
 create mode 100755 bindings/python/examples/gpiomon.py
 create mode 100755 bindings/python/examples/gpioset.py
bindings/python/examples/Makefile.am new file +10 -0
new file mode 100644
index 0000000..4169469
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+EXTRA_DIST =				\
+		gpiodetect.py		\
+		gpiofind.py		\
+		gpioget.py		\
+		gpioinfo.py		\
+		gpiomon.py		\
+		gpioset.py
bindings/python/examples/gpiodetect.py new file +17 -0
new file mode 100755
index 0000000..08e586b
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Reimplementation of the gpiodetect tool in Python."""
+
+import gpiod
+import os
+
+if __name__ == "__main__":
+    for entry in os.scandir("/dev/"):
+        if gpiod.is_gpiochip_device(entry.path):
+            with gpiod.Chip(entry.path) as chip:
+                info = chip.get_info()
+                print(
+                    "{} [{}] ({} lines)".format(info.name, info.label, info.num_lines)
+                )
bindings/python/examples/gpiofind.py new file +20 -0
new file mode 100755
index 0000000..e488a49
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Reimplementation of the gpiofind tool in Python."""
+
+import gpiod
+import os
+import sys
+
+if __name__ == "__main__":
+    for entry in os.scandir("/dev/"):
+        if gpiod.is_gpiochip_device(entry.path):
+            with gpiod.Chip(entry.path) as chip:
+                offset = chip.get_line_offset_from_name(sys.argv[1])
+                if offset is not None:
+                    print("{} {}".format(chip.get_info().name, offset))
+                    sys.exit(0)
+
+    sys.exit(1)
bindings/python/examples/gpioget.py new file +16 -0
new file mode 100755
index 0000000..c509f38
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpioget tool in Python."""
+
+import gpiod
+import sys
+
+Direction = gpiod.Line.Direction
+Value = gpiod.Line.Value
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise TypeError("usage: gpioget.py <gpiochip> <offset1> <offset2> ...")
+

bindings/python/examples/gpioget.py line 26

Quoted 10 lines
bindings/python/examples/gpioget.py new file +10 -0
+    path = sys.argv[1]
+    offsets = []
+    for off in sys.argv[2:]:
+        offsets.append(int(off))
+
+    with gpiod.request_lines(
+        path,
+        gpiod.RequestConfig(offsets=offsets, consumer="gpioget.py"),
+        gpiod.LineConfig(direction=Direction.INPUT),
+    ) as request:

With request_lines() being the primary entry point to the gpiod module, consider extending it to allow the RequestConfig and LineConfig kwargs to be passed directly to request_lines(), and for those transient objects to be constructed within request_lines().
That way the average user does not need to concern themselves with those objects and the code is easier to read.
i.e.
with gpiod.request_lines(
path,
offsets=offsets,
consumer="gpioget.py",
direction=Direction.INPUT,
) as request:

You can still support passing in complete RequestConfig and LineConfig as kwargs for cases where the user requires complex configuration. i.e.
with gpiod.request_lines(
path,
req_cfg=gpiod.RequestConfig(offsets=offsets, consumer="gpioget.py"), line_cfg=gpiod.LineConfig(direction=Direction.INPUT),
) as request:

Or for those use cases the user could use the Chip.request_lines() (which wouldn't have the kwarg sugar) instead.

Would be good to have some examples with complex configuration as well, not just the tool reimplementations.

Quoted 73 lines
bindings/python/examples/gpioget.py new file +5 -0
+        vals = request.get_values()
+
+        for val in vals:
+            print("0" if val == Value.INACTIVE else "1", end=" ")
+        print()
bindings/python/examples/gpioinfo.py new file +35 -0
new file mode 100755
index 0000000..3097d10
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpioinfo tool in Python."""
+
+import gpiod
+import os
+
+if __name__ == "__main__":
+    for entry in os.scandir("/dev/"):
+        if gpiod.is_gpiochip_device(entry.path):
+            with gpiod.Chip(entry.path) as chip:
+                cinfo = chip.get_info()
+                print("{} - {} lines:".format(cinfo.name, cinfo.num_lines))
+
+                for offset in range(0, cinfo.num_lines):
+                    linfo = chip.get_line_info(offset)
+                    offset = linfo.offset
+                    name = linfo.name
+                    consumer = linfo.consumer
+                    direction = linfo.direction
+                    active_low = linfo.active_low
+
+                    print(
+                        "\tline {:>3}: {:>18} {:>12} {:>8} {:>10}".format(
+                            offset,
+                            "unnamed" if name is None else name,
+                            "unused" if consumer is None else consumer,
+                            "input"
+                            if direction == gpiod.Line.Direction.INPUT
+                            else "output",
+                            "active-low" if active_low else "active-high",
+                        )
+                    )
bindings/python/examples/gpiomon.py new file +21 -0
new file mode 100755
index 0000000..b0f4b88
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpiomon tool in Python."""
+
+import gpiod
+import sys
+
+Edge = gpiod.Line.Edge
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise TypeError("usage: gpiomon.py <gpiochip> <offset1> <offset2> ...")
+
+    path = sys.argv[1]
+    offsets = []
+    for off in sys.argv[2:]:
+        offsets.append(int(off))
+
+    buf = gpiod.EdgeEventBuffer()

bindings/python/examples/gpiomon.py line 31

Quoted 10 lines
bindings/python/examples/gpiomon.py new file +10 -0
+
+    with gpiod.request_lines(
+        path,
+        gpiod.RequestConfig(offsets=offsets, consumer="gpiomon.py"),
+        gpiod.LineConfig(edge_detection=Edge.BOTH),
+    ) as request:
+        while True:
+            request.read_edge_event(buf)
+            for event in buf:
+                print(event)

Can you hide the buffer here to simplify the common case? How about having the request manage the buffer, and add a generator method that returns the next event, say edge_events()?

For the uncommon case, add kwargs to allow the user to provide the buffer, or to specify the buffer size. If neither provided then the request constructs a default sized buffer.

Then the code becomes:

with gpiod.request_lines(
path,
offsets=offsets,
consumer="gpiomon.py",
edge_detection=Edge.BOTH,
) as request:
for event in request.edge_events():
print(event)

Quoted 41 lines
bindings/python/examples/gpioset.py new file +35 -0
new file mode 100755
index 0000000..3a8f8cc
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+"""Simplified reimplementation of the gpioset tool in Python."""
+
+import gpiod
+import sys
+
+Value = gpiod.Line.Value
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise TypeError("usage: gpioset.py <gpiochip> <offset1>=<value1> ...")
+
+    path = sys.argv[1]
+    values = dict()
+    for arg in sys.argv[2:]:
+        arg = arg.split("=")
+        key = int(arg[0])
+        val = int(arg[1])
+
+        if val == 1:
+            values[key] = Value.ACTIVE
+        elif val == 0:
+            values[key] = Value.INACTIVE
+        else:
+            raise ValueError("{} is an invalid value for GPIO lines".format(val))
+
+    with gpiod.request_lines(
+        path,
+        gpiod.RequestConfig(offsets=values.keys(), consumer="gpioset.py"),
+        gpiod.LineConfig(direction=gpiod.Line.Direction.OUTPUT, output_values=values),
+    ) as request:
+        input()
Quoted 9 lines
Quoted 3 lines
Signature · 1 line
2.34.1

The focus of my comments above is to simplify the API for the most common case, and to make it a little more Pythonic rather than mirroring the C API, in both cases by hiding implementation details that the casual user doesn't need to know about.

Further to this, and recalling our discussions on tool changes, it would be great if the Python API supported identification of line by name, not just (chip,offset).

e.g.
with gpiod.request_lines(
lines=("GPIO17", "GPIO18"),
edge_detection=Edge.BOTH,
) as request:
for event in request.edge_events():
print(event)

with the returned event extended to contain the line name if the line was identified by name in request_lines().

The lines kwarg replaces offsets, and could contain names (strings) or offsets (integers), or a combination. If any offsets are present then the chip path kwarg must also be provided. If the chip isn't provided, request_lines() would find the corresponding chip based on the line name.

Cheers,
Kent.

#6 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 2 lines · Kent Gibson
Quoted 1 line · Kent Gibson

On Wed, May 25, 2022 at 04:07:02PM +0200, Bartosz Golaszewski wrote:

...

Quoted 18 lines
Quoted 5 lines

The focus of my comments above is to simplify the API for the most common case, and to make it a little more Pythonic rather than mirroring the C API, in both cases by hiding implementation details that the casual user doesn't need to know about.

Further to this, and recalling our discussions on tool changes, it would be great if the Python API supported identification of line by name, not just (chip,offset).

e.g.
with gpiod.request_lines(
lines=("GPIO17", "GPIO18"),
edge_detection=Edge.BOTH,
) as request:
for event in request.edge_events():
print(event)

Quoted 7 lines

with the returned event extended to contain the line name if the line was identified by name in request_lines().

The lines kwarg replaces offsets, and could contain names (strings) or offsets (integers), or a combination. If any offsets are present then the chip path kwarg must also be provided. If the chip isn't provided, request_lines() would find the corresponding chip based on the line name.

From Python programmer perspective it's a good idea, but from GPIO (ABI) perspective, it may be confusing. Line name is not unique (globally) and basically not a part of ABI.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#7 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 27 lines · Andy Shevchenko
Quoted 2 lines · Kent Gibson
Quoted 1 line · Kent Gibson

On Wed, May 25, 2022 at 04:07:02PM +0200, Bartosz Golaszewski wrote:

...

Quoted 21 lines
Quoted 5 lines

The focus of my comments above is to simplify the API for the most common case, and to make it a little more Pythonic rather than mirroring the C API, in both cases by hiding implementation details that the casual user doesn't need to know about.

Further to this, and recalling our discussions on tool changes, it would be great if the Python API supported identification of line by name, not just (chip,offset).

e.g.
with gpiod.request_lines(
lines=("GPIO17", "GPIO18"),
edge_detection=Edge.BOTH,
) as request:
for event in request.edge_events():
print(event)

with the returned event extended to contain the line name if the line was identified by name in request_lines().

Quoted 9 lines
Quoted 4 lines

The lines kwarg replaces offsets, and could contain names (strings) or offsets (integers), or a combination. If any offsets are present then the chip path kwarg must also be provided. If the chip isn't provided, request_lines() would find the corresponding chip based on the line name.

From Python programmer perspective it's a good idea, but from GPIO (ABI) perspective, it may be confusing. Line name is not unique (globally) and basically not a part of ABI.

"basically not a part of the ABI"???
Damn - we should've removed it from the line info for uAPI v2 ;-).

A common request from users is to be able to request lines by name. Of the libgpiod bindings, Python is the best suited to allow that possibility directly as part of its core API.
It also happens to be the one most likely to be used by said users.

While identifying line by name can't be guaranteed to work universally, that doesn't mean that we should automatically exclude the possibility. It is possible with the current ABI - it is awkward, but possible. In libgpiod v1, gpiod_ctxless_find_line(), gpiod_chip_find_line() et al., and in v2 gpiod_chip_get_line_offset_from_name(), do just that - I'm merely suggesting that similar functionality be incorporated into request_lines().

Line names should be unique in well configured systems, even if the kernel itself does not guarantee it.
The binding would perform an exhaustive search to ensure the requested line name is unique, and throw if not (unlike the libgpiod v1 functions that return the first match - yikes).
(We could always extend the GPIO uAPI to make the mapping process less painful, e.g. an ioctl to perform the name to offset mapping, including uniqueness check, for a chip.)
For applications targetting systems that don't guarantee uniqueness, the (chip,offset) approach remains available.
And if the line names are thought to be unique within a chip, the middle ground of (chip,name) is also available.

Wrt confusion, the alternative would be to provide a separate name based API wrapper, or insist that the user jump through the name mapping hoops themselves prior to calling the offset based API.
Are either of those less confusing?

But if the purpose of the Python binding is purely to minimally wrap the C ABI, warts and all, then my suggestion should most certainly be ignored.

Cheers,
Kent.

#8 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 2 lines · Kent Gibson
Quoted 1 line · Andy Shevchenko

On Sat, Jun 04, 2022 at 10:41:31AM +0800, Kent Gibson wrote:

...

Quoted 6 lines
Quoted 3 lines

From Python programmer perspective it's a good idea, but from GPIO (ABI) perspective, it may be confusing. Line name is not unique (globally) and basically not a part of ABI.

"basically not a part of the ABI"???
Damn - we should've removed it from the line info for uAPI v2 ;-).

Yep, names are just aliases for the line numbers, but _not_ unique in the system. You may have many lines with the same name alias, but number. I remember that we made it stricter in the kernel, but as far as I understand there is nothing prevents some drivers to behave badly.

https://elixir.bootlin.com/linux/v5.19-rc1/source/drivers/gpio/gpiolib.c#L331

Also note, we don't validate names given by properties. Any DT may contain same names for several lines on the same chip.

...

Quoted 2 lines

Line names should be unique in well configured systems, even if the kernel itself does not guarantee it.

It's not about kernel, but like you said "well configured systems". If we push the strict rules, we might be punished by users who want actually to have ambiguous names.

...

Quoted 2 lines

But if the purpose of the Python binding is purely to minimally wrap the C ABI, warts and all, then my suggestion should most certainly be ignored.

As i said, it's very good suggestion, but pity we don't strict line naming in the kernel.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#9 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

In reply to Kent Gibson
Quoted 68 lines · Kent Gibson
Quoted 27 lines · Andy Shevchenko
Quoted 2 lines · Kent Gibson
Quoted 1 line · Kent Gibson

On Wed, May 25, 2022 at 04:07:02PM +0200, Bartosz Golaszewski wrote:

...

Quoted 21 lines
Quoted 5 lines

The focus of my comments above is to simplify the API for the most common case, and to make it a little more Pythonic rather than mirroring the C API, in both cases by hiding implementation details that the casual user doesn't need to know about.

Further to this, and recalling our discussions on tool changes, it would be great if the Python API supported identification of line by name, not just (chip,offset).

e.g.
with gpiod.request_lines(
lines=("GPIO17", "GPIO18"),
edge_detection=Edge.BOTH,
) as request:
for event in request.edge_events():
print(event)

with the returned event extended to contain the line name if the line was identified by name in request_lines().

Quoted 9 lines
Quoted 4 lines

The lines kwarg replaces offsets, and could contain names (strings) or offsets (integers), or a combination. If any offsets are present then the chip path kwarg must also be provided. If the chip isn't provided, request_lines() would find the corresponding chip based on the line name.

From Python programmer perspective it's a good idea, but from GPIO (ABI) perspective, it may be confusing. Line name is not unique (globally) and basically not a part of ABI.

"basically not a part of the ABI"???
Damn - we should've removed it from the line info for uAPI v2 ;-).

A common request from users is to be able to request lines by name. Of the libgpiod bindings, Python is the best suited to allow that possibility directly as part of its core API.
It also happens to be the one most likely to be used by said users.

While identifying line by name can't be guaranteed to work universally, that doesn't mean that we should automatically exclude the possibility. It is possible with the current ABI - it is awkward, but possible. In libgpiod v1, gpiod_ctxless_find_line(), gpiod_chip_find_line() et al., and in v2 gpiod_chip_get_line_offset_from_name(), do just that - I'm merely suggesting that similar functionality be incorporated into request_lines().

Line names should be unique in well configured systems, even if the kernel itself does not guarantee it.
The binding would perform an exhaustive search to ensure the requested line name is unique, and throw if not (unlike the libgpiod v1 functions that return the first match - yikes).
(We could always extend the GPIO uAPI to make the mapping process less painful, e.g. an ioctl to perform the name to offset mapping, including uniqueness check, for a chip.)
For applications targetting systems that don't guarantee uniqueness, the (chip,offset) approach remains available.
And if the line names are thought to be unique within a chip, the middle ground of (chip,name) is also available.

Quoted 8 lines

Wrt confusion, the alternative would be to provide a separate name based API wrapper, or insist that the user jump through the name mapping hoops themselves prior to calling the offset based API.
Are either of those less confusing?

But if the purpose of the Python binding is purely to minimally wrap the C ABI, warts and all, then my suggestion should most certainly be ignored.

I actually have a third alternative. I would like the gpiod module to only expose the C API functionality but how about a gpiod_extended or something similar with all kinds of python helpers? Python users are indeed used to modules making the work easier and I'm not against it but writing it in C would be a PITA so I'm thinking about a secondary pure python module with those kinds of extensions.

Bart

#10 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 78 lines · Bartosz Golaszewski
Quoted 68 lines · Kent Gibson
Quoted 27 lines · Andy Shevchenko
Quoted 2 lines · Kent Gibson
Quoted 1 line · Kent Gibson

On Wed, May 25, 2022 at 04:07:02PM +0200, Bartosz Golaszewski wrote:

...

Quoted 21 lines
Quoted 5 lines

The focus of my comments above is to simplify the API for the most common case, and to make it a little more Pythonic rather than mirroring the C API, in both cases by hiding implementation details that the casual user doesn't need to know about.

Further to this, and recalling our discussions on tool changes, it would be great if the Python API supported identification of line by name, not just (chip,offset).

e.g.
with gpiod.request_lines(
lines=("GPIO17", "GPIO18"),
edge_detection=Edge.BOTH,
) as request:
for event in request.edge_events():
print(event)

with the returned event extended to contain the line name if the line was identified by name in request_lines().

Quoted 9 lines
Quoted 4 lines

The lines kwarg replaces offsets, and could contain names (strings) or offsets (integers), or a combination. If any offsets are present then the chip path kwarg must also be provided. If the chip isn't provided, request_lines() would find the corresponding chip based on the line name.

From Python programmer perspective it's a good idea, but from GPIO (ABI) perspective, it may be confusing. Line name is not unique (globally) and basically not a part of ABI.

"basically not a part of the ABI"???
Damn - we should've removed it from the line info for uAPI v2 ;-).

A common request from users is to be able to request lines by name. Of the libgpiod bindings, Python is the best suited to allow that possibility directly as part of its core API.
It also happens to be the one most likely to be used by said users.

While identifying line by name can't be guaranteed to work universally, that doesn't mean that we should automatically exclude the possibility. It is possible with the current ABI - it is awkward, but possible. In libgpiod v1, gpiod_ctxless_find_line(), gpiod_chip_find_line() et al., and in v2 gpiod_chip_get_line_offset_from_name(), do just that - I'm merely suggesting that similar functionality be incorporated into request_lines().

Line names should be unique in well configured systems, even if the kernel itself does not guarantee it.
The binding would perform an exhaustive search to ensure the requested line name is unique, and throw if not (unlike the libgpiod v1 functions that return the first match - yikes).
(We could always extend the GPIO uAPI to make the mapping process less painful, e.g. an ioctl to perform the name to offset mapping, including uniqueness check, for a chip.)
For applications targetting systems that don't guarantee uniqueness, the (chip,offset) approach remains available.
And if the line names are thought to be unique within a chip, the middle ground of (chip,name) is also available.

Quoted 8 lines

Wrt confusion, the alternative would be to provide a separate name based API wrapper, or insist that the user jump through the name mapping hoops themselves prior to calling the offset based API.
Are either of those less confusing?

But if the purpose of the Python binding is purely to minimally wrap the C ABI, warts and all, then my suggestion should most certainly be ignored.

Quoted 7 lines

I actually have a third alternative. I would like the gpiod module to only expose the C API functionality but how about a gpiod_extended or something similar with all kinds of python helpers? Python users are indeed used to modules making the work easier and I'm not against it but writing it in C would be a PITA so I'm thinking about a secondary pure python module with those kinds of extensions.

Agree that it would be easier to write a pythonic wrapper around the C API in Python, so no problem with that.
However, the pythonic wrapper should the one named gpiod, as it is intended to be the primary interface for Python. Rename your existing to gpiod_c or gpiod_core or something.

Btw, I've only mentioned a small part of the API so far, but the same applies to the remainder. e.g. the RequestConfig and LineConfig could use the lines kwarg treatment as well. Though I suspect implementing that will be a bit of a bear, in either language.

Cheers,
Kent.

#11 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 88 lines · Kent Gibson
Quoted 78 lines · Bartosz Golaszewski
Quoted 68 lines · Kent Gibson
Quoted 27 lines · Andy Shevchenko
Quoted 2 lines · Kent Gibson
Quoted 1 line · Kent Gibson

On Wed, May 25, 2022 at 04:07:02PM +0200, Bartosz Golaszewski wrote:

...

Quoted 21 lines
Quoted 5 lines

The focus of my comments above is to simplify the API for the most common case, and to make it a little more Pythonic rather than mirroring the C API, in both cases by hiding implementation details that the casual user doesn't need to know about.

Further to this, and recalling our discussions on tool changes, it would be great if the Python API supported identification of line by name, not just (chip,offset).

e.g.
with gpiod.request_lines(
lines=("GPIO17", "GPIO18"),
edge_detection=Edge.BOTH,
) as request:
for event in request.edge_events():
print(event)

with the returned event extended to contain the line name if the line was identified by name in request_lines().

Quoted 9 lines
Quoted 4 lines

The lines kwarg replaces offsets, and could contain names (strings) or offsets (integers), or a combination. If any offsets are present then the chip path kwarg must also be provided. If the chip isn't provided, request_lines() would find the corresponding chip based on the line name.

From Python programmer perspective it's a good idea, but from GPIO (ABI) perspective, it may be confusing. Line name is not unique (globally) and basically not a part of ABI.

"basically not a part of the ABI"???
Damn - we should've removed it from the line info for uAPI v2 ;-).

A common request from users is to be able to request lines by name. Of the libgpiod bindings, Python is the best suited to allow that possibility directly as part of its core API.
It also happens to be the one most likely to be used by said users.

While identifying line by name can't be guaranteed to work universally, that doesn't mean that we should automatically exclude the possibility. It is possible with the current ABI - it is awkward, but possible. In libgpiod v1, gpiod_ctxless_find_line(), gpiod_chip_find_line() et al., and in v2 gpiod_chip_get_line_offset_from_name(), do just that - I'm merely suggesting that similar functionality be incorporated into request_lines().

Line names should be unique in well configured systems, even if the kernel itself does not guarantee it.
The binding would perform an exhaustive search to ensure the requested line name is unique, and throw if not (unlike the libgpiod v1 functions that return the first match - yikes).
(We could always extend the GPIO uAPI to make the mapping process less painful, e.g. an ioctl to perform the name to offset mapping, including uniqueness check, for a chip.)
For applications targetting systems that don't guarantee uniqueness, the (chip,offset) approach remains available.
And if the line names are thought to be unique within a chip, the middle ground of (chip,name) is also available.

Quoted 8 lines

Wrt confusion, the alternative would be to provide a separate name based API wrapper, or insist that the user jump through the name mapping hoops themselves prior to calling the offset based API.
Are either of those less confusing?

But if the purpose of the Python binding is purely to minimally wrap the C ABI, warts and all, then my suggestion should most certainly be ignored.

Quoted 7 lines

I actually have a third alternative. I would like the gpiod module to only expose the C API functionality but how about a gpiod_extended or something similar with all kinds of python helpers? Python users are indeed used to modules making the work easier and I'm not against it but writing it in C would be a PITA so I'm thinking about a secondary pure python module with those kinds of extensions.

Quoted 6 lines

Agree that it would be easier to write a pythonic wrapper around the C API in Python, so no problem with that.
However, the pythonic wrapper should the one named gpiod, as it is intended to be the primary interface for Python. Rename your existing to gpiod_c or gpiod_core or something.

I don't agree. The module that wraps the C library should still be called gpiod and be the primary interface. The pythonic module would just offer helpers that would still use the gpiod data types for most part.

Bart

Quoted 5 lines

Btw, I've only mentioned a small part of the API so far, but the same applies to the remainder. e.g. the RequestConfig and LineConfig could use the lines kwarg treatment as well. Though I suspect implementing that will be a bit of a bear, in either language.

#12 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 11 lines · Bartosz Golaszewski
Quoted 5 lines · Kent Gibson

Agree that it would be easier to write a pythonic wrapper around the C API in Python, so no problem with that.
However, the pythonic wrapper should the one named gpiod, as it is intended to be the primary interface for Python. Rename your existing to gpiod_c or gpiod_core or something.

I don't agree. The module that wraps the C library should still be called gpiod and be the primary interface. The pythonic module would just offer helpers that would still use the gpiod data types for most part.

As a Python user, I'd much rather see the high level API being the primary interface and being named 'gpiod'. The easier to use and more Pythonic, the better. The low level library bindings and low level data types are just an implementation detail for me when coding in Python. If I wanted low level, I'd code everything directly in C.

Just my two cents. Thanks for the good work in either case.

Jiri

#13 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 14 lines · Jiri Benc
Quoted 11 lines · Bartosz Golaszewski
Quoted 5 lines · Kent Gibson

Agree that it would be easier to write a pythonic wrapper around the C API in Python, so no problem with that.
However, the pythonic wrapper should the one named gpiod, as it is intended to be the primary interface for Python. Rename your existing to gpiod_c or gpiod_core or something.

I don't agree. The module that wraps the C library should still be called gpiod and be the primary interface. The pythonic module would just offer helpers that would still use the gpiod data types for most part.

Quoted 6 lines

As a Python user, I'd much rather see the high level API being the primary interface and being named 'gpiod'. The easier to use and more Pythonic, the better. The low level library bindings and low level data types are just an implementation detail for me when coding in Python. If I wanted low level, I'd code everything directly in C.

But Kent is not talking about a whole new "pythonic" layer on top of the code that is the subject of this series. The bindings are already quite pythonic in that you can get most stuff done with a "logical" one-liner. The gpiod module doesn't map C API 1:1, it already simplifies a bunch of interfaces. Kent's idea IIUC is about providing a set of helpers that would produce the gpiod objects in shorter code by hiding the details of intermediate objects being created.

Re the event buffer: yeah, I think in python (unlike C++ or future Rust bindings) it makes sense to hide it within the request as we can't profit from implicitly not copying the event objects.

If anyone wants to create an even simpler, complete interface for gpiod, then it's a task for a whole new project. Think: pydbus built on top of GLib dbus bindings in python, built on top of glib's dbus implementation.

Bart

#14 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 22 lines · Bartosz Golaszewski
Quoted 14 lines · Jiri Benc
Quoted 11 lines · Bartosz Golaszewski
Quoted 5 lines · Kent Gibson

Agree that it would be easier to write a pythonic wrapper around the C API in Python, so no problem with that.
However, the pythonic wrapper should the one named gpiod, as it is intended to be the primary interface for Python. Rename your existing to gpiod_c or gpiod_core or something.

I don't agree. The module that wraps the C library should still be called gpiod and be the primary interface. The pythonic module would just offer helpers that would still use the gpiod data types for most part.

Quoted 6 lines

As a Python user, I'd much rather see the high level API being the primary interface and being named 'gpiod'. The easier to use and more Pythonic, the better. The low level library bindings and low level data types are just an implementation detail for me when coding in Python. If I wanted low level, I'd code everything directly in C.

Quoted 8 lines

But Kent is not talking about a whole new "pythonic" layer on top of the code that is the subject of this series. The bindings are already quite pythonic in that you can get most stuff done with a "logical" one-liner. The gpiod module doesn't map C API 1:1, it already simplifies a bunch of interfaces. Kent's idea IIUC is about providing a set of helpers that would produce the gpiod objects in shorter code by hiding the details of intermediate objects being created.

Yeah, no, I'm saying that there should be one primary Python interface to gpiod, and it should be the most pythonic. And complete. The casual user should be able to get by with a few simple commands, but the complete functionality should still be accessible, via that same API, for more complicated cases.

Quoted 4 lines

Re the event buffer: yeah, I think in python (unlike C++ or future Rust bindings) it makes sense to hide it within the request as we can't profit from implicitly not copying the event objects.

That is a consequence of building on top of the gpiod C API, as you have to deal with two object models, and the related type conversions or lifecycle management.
A native binding built on top of the ioctls can use native objects throughout can take better advantage of the language.
e.g. here the equivalent of my Python suggestion using my Rust gpiocdev library (sadly without named lines support as I hadn't considered that at the time):

    // request the lines
    let req = Request::builder()
        .on_chip("/dev/gpiochip0")
        .with_lines(&[17,18])
        .with_edge_detection(EdgeDetection::BothEdges)
        .request()?;

    // wait for line edge events
    for event in req.edge_events()? {
        println!("{:?}", event?);
    }

That has a hidden event buffer within the request. The default size is 1, but add .with_user_event_buffer_size(N) and you get an N event buffer. There is no copying involved, just borrowing, as all the objects are visible to the borrow checker.

Quoted 5 lines

If anyone wants to create an even simpler, complete interface for gpiod, then it's a task for a whole new project. Think: pydbus built on top of GLib dbus bindings in python, built on top of glib's dbus implementation.

Don't tempt me - though I would target the GPIO uAPI ioctls, not gpiod, for the reasons above.

Cheers,
Kent.

#15 lore raw

Re: [libgpiod v2][PATCH 3/5] bindings: python: add examples for v2 API

Quoted 53 lines · Kent Gibson
Quoted 22 lines · Bartosz Golaszewski
Quoted 14 lines · Jiri Benc
Quoted 11 lines · Bartosz Golaszewski
Quoted 5 lines · Kent Gibson

Agree that it would be easier to write a pythonic wrapper around the C API in Python, so no problem with that.
However, the pythonic wrapper should the one named gpiod, as it is intended to be the primary interface for Python. Rename your existing to gpiod_c or gpiod_core or something.

I don't agree. The module that wraps the C library should still be called gpiod and be the primary interface. The pythonic module would just offer helpers that would still use the gpiod data types for most part.

Quoted 6 lines

As a Python user, I'd much rather see the high level API being the primary interface and being named 'gpiod'. The easier to use and more Pythonic, the better. The low level library bindings and low level data types are just an implementation detail for me when coding in Python. If I wanted low level, I'd code everything directly in C.

Quoted 8 lines

But Kent is not talking about a whole new "pythonic" layer on top of the code that is the subject of this series. The bindings are already quite pythonic in that you can get most stuff done with a "logical" one-liner. The gpiod module doesn't map C API 1:1, it already simplifies a bunch of interfaces. Kent's idea IIUC is about providing a set of helpers that would produce the gpiod objects in shorter code by hiding the details of intermediate objects being created.

Yeah, no, I'm saying that there should be one primary Python interface to gpiod, and it should be the most pythonic. And complete. The casual user should be able to get by with a few simple commands, but the complete functionality should still be accessible, via that same API, for more complicated cases.

Quoted 4 lines

Re the event buffer: yeah, I think in python (unlike C++ or future Rust bindings) it makes sense to hide it within the request as we can't profit from implicitly not copying the event objects.

That is a consequence of building on top of the gpiod C API, as you have to deal with two object models, and the related type conversions or lifecycle management.
A native binding built on top of the ioctls can use native objects throughout can take better advantage of the language.
e.g. here the equivalent of my Python suggestion using my Rust gpiocdev library (sadly without named lines support as I hadn't considered that at the time):

Quoted 7 lines
    // request the lines
    let req = Request::builder()
        .on_chip("/dev/gpiochip0")
        .with_lines(&[17,18])
        .with_edge_detection(EdgeDetection::BothEdges)
        .request()?;

So in Python you'd like to see an equivalent in the form of:

req = gpiod.request_lines(chip="/dev/gpiochip0", lines=["GPIO17", "RESET_0"], edge_detection=Edge.BOTH)

Quoted 4 lines
    // wait for line edge events
    for event in req.edge_events()? {
        println!("{:?}", event?);
    }

and:

for event in req.read_edge_events():
print(event)

Note that for the event reading: implementing yield in C API is not currently possible. I think the best way to implement the above example is to simply return a buffer filled with events that is already iterable.

Ok I can work with that. It also makes sense to take keyword arguments by default in all methods.

Thanks
Bart

Quoted 17 lines

That has a hidden event buffer within the request. The default size is 1, but add .with_user_event_buffer_size(N) and you get an N event buffer. There is no copying involved, just borrowing, as all the objects are visible to the borrow checker.

Quoted 5 lines

If anyone wants to create an even simpler, complete interface for gpiod, then it's a task for a whole new project. Think: pydbus built on top of GLib dbus bindings in python, built on top of glib's dbus implementation.

Don't tempt me - though I would target the GPIO uAPI ioctls, not gpiod, for the reasons above.

Cheers,
Kent.

#16 lore raw

PATCH v2 4/5 bindings: python: add tests for v2 API

In reply to Bartosz Golaszewski

This adds a python wrapper around libgpiosim and a set of test cases for the v2 API using python's standard unittest module.

---
 bindings/python/tests/Makefile.am             |  14 +
 bindings/python/tests/cases/__init__.py       |  12 +
 bindings/python/tests/cases/tests_chip.py     | 157 +++++++
 .../python/tests/cases/tests_chip_info.py     |  59 +++
 .../python/tests/cases/tests_edge_event.py    | 274 +++++++++++
 .../python/tests/cases/tests_info_event.py    | 135 ++++++
 .../python/tests/cases/tests_line_config.py   | 250 ++++++++++
 .../python/tests/cases/tests_line_info.py     |  90 ++++
 .../python/tests/cases/tests_line_request.py  | 295 ++++++++++++
 bindings/python/tests/cases/tests_misc.py     |  53 +++
 .../tests/cases/tests_request_config.py       |  77 ++++
 bindings/python/tests/gpiod_py_test.py        |  25 +
 bindings/python/tests/gpiosimmodule.c         | 434 ++++++++++++++++++
 13 files changed, 1875 insertions(+)
 create mode 100644 bindings/python/tests/Makefile.am
 create mode 100644 bindings/python/tests/cases/__init__.py
 create mode 100644 bindings/python/tests/cases/tests_chip.py
 create mode 100644 bindings/python/tests/cases/tests_chip_info.py
 create mode 100644 bindings/python/tests/cases/tests_edge_event.py
 create mode 100644 bindings/python/tests/cases/tests_info_event.py
 create mode 100644 bindings/python/tests/cases/tests_line_config.py
 create mode 100644 bindings/python/tests/cases/tests_line_info.py
 create mode 100644 bindings/python/tests/cases/tests_line_request.py
 create mode 100644 bindings/python/tests/cases/tests_misc.py
 create mode 100644 bindings/python/tests/cases/tests_request_config.py
 create mode 100755 bindings/python/tests/gpiod_py_test.py
 create mode 100644 bindings/python/tests/gpiosimmodule.c
bindings/python/tests/Makefile.am new file +14 -0
new file mode 100644
index 0000000..099574f
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+dist_bin_SCRIPTS = gpiod_py_test.py
+
+pyexec_LTLIBRARIES = gpiosim.la
+
+gpiosim_la_SOURCES = gpiosimmodule.c
+gpiosim_la_CFLAGS = -I$(top_srcdir)/tests/gpiosim/
+gpiosim_la_CFLAGS += -Wall -Wextra -g -std=gnu89 $(PYTHON_CPPFLAGS)
+gpiosim_la_LDFLAGS = -module -avoid-version
+gpiosim_la_LIBADD = $(top_builddir)/tests/gpiosim/libgpiosim.la
+gpiosim_la_LIBADD += $(top_builddir)/bindings/python/enum/libpycenum.la
+gpiosim_la_LIBADD += $(PYTHON_LIBS)
bindings/python/tests/cases/__init__.py new file +12 -0
new file mode 100644
index 0000000..6503663
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <[email protected]>
+
+from .tests_chip import *
+from .tests_chip_info import *
+from .tests_edge_event import *
+from .tests_info_event import *
+from .tests_line_config import *
+from .tests_line_info import *
+from .tests_line_request import *
+from .tests_misc import *
+from .tests_request_config import *
bindings/python/tests/cases/tests_chip.py new file +157 -0

157 lines not on this page · raw message

bindings/python/tests/cases/tests_chip_info.py new file +59 -0

59 lines not on this page · raw message

bindings/python/tests/cases/tests_edge_event.py new file +274 -0

274 lines not on this page · raw message

bindings/python/tests/cases/tests_info_event.py new file +135 -0

135 lines not on this page · raw message

bindings/python/tests/cases/tests_line_config.py new file +250 -0

250 lines not on this page · raw message

bindings/python/tests/cases/tests_line_info.py new file +90 -0

90 lines not on this page · raw message

bindings/python/tests/cases/tests_line_request.py new file +295 -0

295 lines not on this page · raw message

bindings/python/tests/cases/tests_misc.py new file +53 -0

53 lines not on this page · raw message

bindings/python/tests/cases/tests_request_config.py new file +77 -0

77 lines not on this page · raw message

bindings/python/tests/gpiod_py_test.py new file +25 -0
new file mode 100755
index 0000000..6a49461
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <[email protected]>
+
+import os
+import unittest
+
+from cases import *
+from packaging import version
+
+
+def check_kernel(major, minor, release):
+    current = os.uname().release.split("-")[0]
+    required = "{}.{}.{}".format(major, minor, release)
+    if version.parse(current) < version.parse(required):
+        raise NotImplementedError(
+            "linux kernel version must be at least {} - got {}".format(
+                required, current
+            )
+        )
+
+
+if __name__ == "__main__":
+    check_kernel(5, 17, 4)
+    unittest.main()
bindings/python/tests/gpiosimmodule.c new file +434 -0

434 lines not on this page · raw message

Signature · 1 line
2.34.1
#17 lore raw

PATCH v2 5/5 bindings: python: add the implementation for v2 API

In reply to Bartosz Golaszewski

This is the implementation of the new python API for libgpiod v2.

---
 bindings/python/.gitignore          |    1 +
 bindings/python/Makefile.am         |   40 +
 bindings/python/chip-info.c         |  126 +++
 bindings/python/chip.c              |  552 +++++++++++
 bindings/python/edge-event-buffer.c |  301 ++++++
 bindings/python/edge-event.c        |  191 ++++
 bindings/python/exception.c         |  182 ++++
 bindings/python/info-event.c        |  175 ++++
 bindings/python/line-config.c       | 1338 +++++++++++++++++++++++++++
 bindings/python/line-info.c         |  286 ++++++
 bindings/python/line-request.c      |  713 ++++++++++++++
 bindings/python/line.c              |  239 +++++
 bindings/python/module.c            |  281 ++++++
 bindings/python/module.h            |   57 ++
 bindings/python/request-config.c    |  320 +++++++
 configure.ac                        |    3 +-
 16 files changed, 4804 insertions(+), 1 deletion(-)
 create mode 100644 bindings/python/.gitignore
 create mode 100644 bindings/python/Makefile.am
 create mode 100644 bindings/python/chip-info.c
 create mode 100644 bindings/python/chip.c
 create mode 100644 bindings/python/edge-event-buffer.c
 create mode 100644 bindings/python/edge-event.c
 create mode 100644 bindings/python/exception.c
 create mode 100644 bindings/python/info-event.c
 create mode 100644 bindings/python/line-config.c
 create mode 100644 bindings/python/line-info.c
 create mode 100644 bindings/python/line-request.c
 create mode 100644 bindings/python/line.c
 create mode 100644 bindings/python/module.c
 create mode 100644 bindings/python/module.h
 create mode 100644 bindings/python/request-config.c
bindings/python/.gitignore new file +1 -0
new file mode 100644
index 0000000..bee8a64
@@ -0,0 +1 @@
+__pycache__
bindings/python/Makefile.am new file +40 -0
new file mode 100644
index 0000000..3f7ee5f
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <[email protected]>
+
+pyexec_LTLIBRARIES = gpiod.la
+
+gpiod_la_SOURCES = \
+	chip.c \
+	chip-info.c \
+	edge-event.c \
+	edge-event-buffer.c \
+	exception.c \
+	info-event.c \
+	line.c \
+	line-config.c \
+	line-info.c \
+	line-request.c \
+	module.c \
+	module.h \
+	request-config.c
+
+gpiod_la_CFLAGS = -I$(top_srcdir)/include/
+gpiod_la_CFLAGS += -Wall -Wextra -g -std=gnu89 $(PYTHON_CPPFLAGS)
+gpiod_la_CFLAGS += -include $(top_builddir)/config.h
+gpiod_la_LDFLAGS = -module -avoid-version
+gpiod_la_LIBADD = $(top_builddir)/lib/libgpiod.la $(PYTHON_LIBS)
+gpiod_la_LIBADD += $(top_builddir)/bindings/python/enum/libpycenum.la
+
+SUBDIRS = enum .
+
+if WITH_TESTS
+
+SUBDIRS += tests
+
+endif
+
+if WITH_EXAMPLES
+
+SUBDIRS += examples
+
+endif
bindings/python/chip-info.c new file +126 -0

126 lines not on this page · raw message

bindings/python/chip.c new file +552 -0

552 lines not on this page · raw message

bindings/python/edge-event-buffer.c new file +301 -0

301 lines not on this page · raw message

bindings/python/edge-event.c new file +191 -0

191 lines not on this page · raw message

bindings/python/exception.c new file +182 -0

182 lines not on this page · raw message

bindings/python/info-event.c new file +175 -0

175 lines not on this page · raw message

bindings/python/line-config.c new file +1338 -0

1,338 lines not on this page · raw message

bindings/python/line-info.c new file +286 -0

94 lines not on this page · raw message

bindings/python/line-request.c new file +713 -0

713 lines not on this page · raw message

bindings/python/line.c new file +239 -0

239 lines not on this page · raw message

bindings/python/module.c new file +281 -0

281 lines not on this page · raw message

bindings/python/module.h new file +57 -0

57 lines not on this page · raw message

bindings/python/request-config.c new file +320 -0

320 lines not on this page · raw message

configure.ac +2 -1
index ab03673..7a794e2 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.34.1

Part of the diff is not shown here. View the raw message.