1
0
mirror of https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git synced 2026-10-24 00:48:53 +03:00
Commit Graph

89 Commits

Author SHA1 Message Date
Bartosz Golaszewski
3076847a6c bindings: python: gpiod v2.5.0
Changelog:
- add support for free-threaded (no-GIL) CPython
- generate wheels for CPython 3.14t (free-threaded)
- drop support for Python 3.9
- migrate the C extension and test modules to multi-phase module
  initialization
- release the GIL in chip_get_info()
- fix heap-buffer overflow bugs when setting/getting line values
- check for errors after PyIter_Next() when setting output values
- check for errors after PyIter_Next() when reading request values
- fix reference count imbalances (missing Py_None and line-name
  increments, erroneous decref on PyList_SetItem() failure)
- allow closing a Chip multiple times without raising an error
- allow releasing a LineRequest multiple times without raising an error
- remove a duplicated edge-detection setting
- add a CHANGELOG.md to track changes between releases
- copy the license file into source and binary distributions
- require setuptools v77 or newer
- remove wheel from the build dependencies
- add multi-threaded tests
- add a dedicated lint dependency group and update linter configuration
- apply linter recommendations and add type annotations across examples,
  setup and helper scripts
- modernize the C extension using upgrade_pythoncapi.py suggestions
- minor internal cleanups and performance improvements

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-17 09:20:20 +02:00
Bartosz Golaszewski
d7d6ae8dd4 bindings: python: release the GIL in chip_get_info()
Other methods that call into the C library (chip_get_line_info,
chip_watch_line_info, etc.) properly release the GIL with
Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS, but chip_get_info() was
missed. Add the missing GIL release for consistency and to avoid
blocking other Python threads during the ioctl.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-14-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
e6fb6554e9 bindings: python: check for errors after PyIter_Next() in set_output_values
PyIter_Next() returns NULL both at end-of-iteration and when an exception
is raised. The code breaks out of the loop without checking
PyErr_Occurred(), potentially swallowing Python exceptions and continuing
with a partially filled buffer.

Add the missing error check.

Fixes: 27a69b0bfd ("bindings: python: add the output_values argument to Chip.request_lines()")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-13-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Bartosz Golaszewski
3c04ff9949 bindings: python: check for errors after PyIter_Next() in request_get_values
PyIter_Next() returns NULL both at end-of-iteration and when an exception
is raised. The code breaks out of the loop without checking
PyErr_Occurred(), potentially swallowing Python exceptions and continuing
with a partially filled buffer.

Add the missing error check.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-12-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-06-08 10:01:21 +02:00
Vincent Fazio
c9378d6c63 bindings: python: support free-threaded CPython
PEP 703 [0] discusses making the GIL optional in certain builds of the
CPython interpreter.

This build option was available experiementally in Python 3.13 but has
since been stabilized in Python 3.14 per PEP 779 [1].

According to the porting guide [2], there is no strict requirement that
C extensions must be thread-safe.

Experiments have shown that no logic changes are required if callers
use sychronization mechanisms provided by the Python standard library.

The documentation has been updated to call this out specifically using
terminology from the porting guide [3].

[0]: https://peps.python.org/pep-0703/
[1]: https://peps.python.org/pep-0779/
[2]: https://py-free-threading.github.io/porting/#define-and-document-thread-safety-guarantees
[3]: https://py-free-threading.github.io/documentation-principles/#free-threading-terminology

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260527215922.18678-3-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-28 10:59:30 +02:00
Bartosz Golaszewski
a7b443f2c7 bindings: python: don't decref objects on PyList_SetItem() failure
PyList_SetItem() steals the reference to the object we want to add
regardless of whether it succeeds or fails. Don't decrease the reference
count of the object we failed to add in error path.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Closes: https://github.com/brgl/libgpiod/issues/187
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260511111538.60844-1-bartosz.golaszewski@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-12 09:29:54 +02:00
Bartosz Golaszewski
97bab4cc87 build: replace autotools with meson & ninja
Port the project from autotools to meson. All build options are
preserved with the same semantics.

The goal of this change is to produce the same set of files as the
current autotools approach: libraries with correct SONAME/version,
headers, pkg-config files, GObject introspection data, Python extension
modules, D-Bus interface and configuration files. This is done to enable
a one-liner test command running all available test suites.

The only thing we drop is the `make docs` target as it's rarely used and
docs can be generated easily with sphinx directly. They are also not
required for testing.

The Rust workspace is built via a custom target that invokes cargo and
uses SYSTEM_DEPS_* environment variables to point the libgpiod-sys and
gpiosim-sys crates at the locally built C libraries.

In addition to porting the build files to meson, update the docs, remove
unneded items from .gitignore files (or remove them entirely if they're
no longer needed) and update the shunit2 test scripts to no longer care
about source directories.

Tested-by: Erik Wierich <erik@riscstar.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260428-meson-build-v3-1-8da6976fb0a2@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-05-04 09:51:03 +02:00
Vincent Fazio
89ffee8349 bindings: python: migrate the gpiod._ext module to multi-phase init
Single-phase initialization has been classified as legacy within CPython
documentation [0] with multi-phase being its successor [1].

As such, switch to the new methodology.

[0]: https://docs.python.org/3/c-api/extension-modules.html#legacy-single-phase-initialization
[1]: https://docs.python.org/3/c-api/extension-modules.html#multi-phase-initialization

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260423222125.29097-7-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-24 11:01:34 +02:00
Vincent Fazio
9106b6c190 bindings: python: use PyImport_ImportModuleAttrString() when available
Commit 035ad74fdd introduced Py_gpiod_GetModuleAttrString() as a way to
get a module's attribute and was inspired by CPython's internal function
_PyImport_GetModuleAttrString().

CPython 3.14 added PyImport_ImportModuleAttrString() to the public
API [0] which allows us to use the standard function when available.

[0]: https://github.com/python/cpython/pull/128912

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260423222125.29097-6-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-24 10:49:51 +02:00
Vincent Fazio
63d4fd7ad7 bindings: python: use suggestions from upgrade_pythoncapi.py
The upgrade_pythoncapi.py script performs transforms on C extension
sources to use new syntax [0]. It's generally used with a shim header
for compatibility, but the header is not necessary for these changes.

The suggested changes are:

  * Use the `Py_IsNone` function introduced in CPython 3.10 [1] over
    direct comparison to `Py_None`

  * Use `PyObject_Free` instead of `PyObject_Del` since `PyObject_Del`
    has been aliased to `PyObject_Free` for nearly 24 years [2] and has
    been announced as deprecated [3]

  * Use the `Py_NewRef` function introduced in CPython 3.10 [4] to
    increment and return a new reference.

[0]: https://pythoncapi-compat.readthedocs.io/en/latest/index.html
[1]: 09bbebea16
[2]: 3e7b893899
[3]: https://docs.python.org/3/c-api/allocation.html#deprecated-aliases
[4]: 53a03aafd5

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260423222125.29097-5-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-24 10:47:55 +02:00
Vincent Fazio
55ce0cf681 bindings: python: simplify disallowing _ext.Request from being created
Previously, tp_new and tp_init functions were defined for Request with
the tp_init function raising a NotImplementedError to prevent the object
from being initialized (__init__, the stage after __new__).

Similar results can be achieved by removing the tp_new and tp_init
functions which raise a slightly different error:

  TypeError: cannot create 'gpiod._ext.Request' instances

As this is an internal only detail, there should be impact to users.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260423222125.29097-4-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-24 10:47:38 +02:00
Vincent Fazio
e5b6f6ecb9 bindings: python: avoid PyObject_CallMethod() during request_finalize()
The PyObject_CallMethod() function jumps through hoops to find the method
to call and to build a stack (if necessary) to call a given method.

In the case of finalizing the request object, that overhead can be
avoided by simply sharing a function with `request_release()` to close the
underlying gpiod_line_request object.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260423222125.29097-3-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-24 10:47:07 +02:00
Vincent Fazio
53be72baca bindings: python: avoid PyObject_CallMethod() during chip_finalize()
The PyObject_CallMethod() function jumps through hoops to find the method
to call and to build a stack (if necessary) to call a given method.

In the case of finalizing the chip object, that overhead can be avoided
by simply sharing a function with `chip_close()` to close the underlying
gpiod_chip object.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260423222125.29097-2-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-24 10:45:30 +02:00
Bartosz Golaszewski
a2c92c9ece bindings: python: remove duplicated edge detection setting
In line_settings_init() we call gpiod_line_settings_set_edge_detection()
twice unnecessarily. Remove the second call.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Reported-by: 4fqr <4fqr@proton.me>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260407-treewide-fixes-v1-2-66c9744a56a3@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-09 09:32:37 +02:00
Bartosz Golaszewski
c6a76be56c bindings: python: fix heap-buffer overflow bugs on setting/getting values
The C extension methods request_set_values() and request_get_values()
iterate over a caller-supplied sequence and write offsets/values into
pre-allocated buffers self->offsets and self->values, without checking
the write index.

If the user tries to read values for more lines than they previously
requested, we overflow the buffers.

Add appropriate bounds checks and implement test cases for this issue.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Reported-by: 4fqr <4fqr@proton.me>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260407-treewide-fixes-v1-1-66c9744a56a3@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-09 09:32:37 +02:00
Vincent Fazio
bfffd6506c bindings: python: allow releasing a LineRequest multiple times
In Python, it is common for objects to allow their `close` method to be
executed multiple times out of convenience. An example of this is the
`IOBase` class [0] which is the base class of most file-like classes.

Update `LineRequest.release` to follow this example as it performs the
same function as `close`.

[0]: https://docs.python.org/release/3.10.0/library/io.html#io.IOBase.close

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260403154629.3944942-1-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-07 14:52:59 +02:00
Vincent Fazio
711a05b254 bindings: python: allow closing a Chip multiple times
In Python, it is common for objects to allow their `close` method to be
executed multiple times out of convenience. An example of this is the
`IOBase` class [0] which is the base class of most file-like classes.

Update `Chip.close` to follow this example.

[0]: https://docs.python.org/release/3.10.0/library/io.html#io.IOBase.close

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260403154604.3944747-1-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-04-07 14:52:19 +02:00
Bartosz Golaszewski
2e47ad76bc Revert "bindings: python: replace PyModule_AddObjectRef() with PyModule_AddObject()"
This reverts commit b436d05809.

Commit 950ff3d1b3 ("bindings: python: drop python 3.9 support")
removed support for python v3.9 and with v3.10 now being the minimum
required version, we're now free to use PyModule_AddObjectRef().

Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260330125023.109789-1-bartosz.golaszewski@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-03-31 14:58:26 +02:00
Vincent Fazio
950ff3d1b3 bindings: python: drop python 3.9 support
* Update pyproject.toml to require Python 3.10+ for runtime
* Update mypy's target version to 3.10 syntax
* Drop ruff's target version as it's inferred by the project's
  requires-python value
* Remove the linter settings that ignored UP007 & UP0045
* Update type annotations to conform to active linter rules

Closes: https://github.com/brgl/libgpiod/issues/151
Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260329182832.39824-1-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-03-30 10:57:47 +02:00
Bartosz Golaszewski
fbc0a44712 bindings: python: add missing Py_None reference increment
For lines with no name, chip_get_line_name() returns Py_None but does
not increment its reference count. Do it enough times and the following
error will occur with cpython before v3.12:

  Fatal Python error: none_dealloc: deallocating None: bug likely caused by a refcount error in a C extension

Add the missing Py_INCREF(). It's safe to do even on python >= v3.12 as
Py_INCREF() has no effect on immortal objects in more recent versions.

Fixes: 0c0993569c ("bindings: python: ext: add ability to query line name")
Reported-by: Pierre Gramme <pgramme@gmail.com>
Closes: https://github.com/brgl/libgpiod/issues/166
Link: https://lore.kernel.org/r/20260113092852.42573-1-bartosz.golaszewski@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-01-13 16:11:52 +01:00
Bartosz Golaszewski
e1d5096c6d bindings: python: gpiod v2.4.0
Changelog:
- make request and reconfigure behavior for lines requested by names
  consistent with that of lines requested by offsets
- fix parsing non-tuple keys when requesting a line
- disable UP045 linter check
- use quoted types when casting
- generate wheels for CPython 3.14
- issue a warning when reconfiguring a line not in the original request
- update dependencies for wheel generation
- update licensing information
- coding style fixes
- minor performance improvements

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-10-24 16:34:39 +02:00
Vincent Fazio
962a1924e1 bindings: python: line_request: warn on unknown lines when reconfiguring
Previously, attempting to reconfigure a line that was not included in
the original request provided no feedback to the caller that there was
invalid data in the request.

Now, emit a warning when an unknown offset or line name is encountered.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20251009130516.3729433-10-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-10-13 17:24:07 +02:00
Vincent Fazio
6a7d3306e4 bindings: python: chip: map names for lines requested by offset
Previously, lines requested by name could also be referred to by offset
when calling LineRequest.reconfigure_lines. However, lines requested by
offset could not be reconfigured by name if they had one.

Now, the name map is populated if the line has a name regardless of if
it's requested by name or offset.

To make behavior as consistent with earlier versions as possible, a line
requested by name always returns the first line on the chip with that
name. If there are duplicate line names and lines are requested by
offset, the first line in the request is mapped to the name. A line
requested by name always overrides a name mapped via an offset request.

If lines 0-3 all have the same name and a config object looks like:
  {("foo", 1, 2): None}
or
  {(1, "foo", 2): None}

Then lines 0-2 are requested and line 0 can be reconfigured by name.

If the config object looks like:
  {(1, 2): None}

Then lines 1 and 2 are requested and line 1 can be reconfigured by name.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20251009130516.3729433-9-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-10-13 17:24:07 +02:00
Vincent Fazio
0c0993569c bindings: python: ext: add ability to query line name
Investigation has shown that the construction of the Enum values to
create the LineInfo object is expensive [0].

Add a method to retrieve the line name from a given offset without the
overhead of generating a full LineInfo object.

[0]: https://github.com/brgl/libgpiod/issues/149#issuecomment-3319448712

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20251009130516.3729433-8-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-10-13 17:24:07 +02:00
Vincent Fazio
e2d7b6abaf bindings: python: line_request: ignore invalid line names in reconfigure_lines
Previously, calling `reconfigure_lines` with line identifiers that were
not included in the original request produced different behaviors
depending on the identifier type.

If the identifier was an integer, it would be silently ignored.

If the identifier was a string, it would raise a `ValueError`.

To be consistent, invalid line names are now silently ignored.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20251009130516.3729433-7-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-10-13 17:24:07 +02:00
Vincent Fazio
0110c11626 bindings: python: chip: check mapped_output_values membership once
Simplify looking up mapped output values by providing a default value to
dict.get instead of checking for membership in the dict and then
fetching the value subsequently.

While hashing and lookup is fast, it's unnecessary overhead.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20251009130516.3729433-6-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-10-13 17:24:07 +02:00
Vincent Fazio
ea21b447d7 bindings: python: chip: simplify duplicate checking
Previously, a check was performed to prevent requests that included
duplicative lines.

While this provided quick feedback on erroneous requests, it penalized
callers that had a good config due to the over head of iterating the
config multiple times.

Now, duplicate check is performed as the config object is iterated.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20251009130516.3729433-5-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-10-13 17:24:07 +02:00
Vincent Fazio
728a2f9b22 bindings: python: chip: track requested lines when enumerating
Previously, an offset map was maintained to lookup the name used to
request a line so that a list could be generated that reflected the
identifier used to request a given line.

For example, if a config looked like {0: None, ("foo", "bar"): None}, the
LineRequest.lines property would contain [0, "foo", "bar"].

Now, the line identifier is tracked in request order as the config
object is iterated to avoid maintaining an offset map.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20251009130516.3729433-4-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-10-13 17:24:07 +02:00
Vincent Fazio
11aa4254b2 bindings: python: remove unused attribute from LineRequest
The _offset_map attribute was a map of offset -> line name. Values were
only inserted when a line was requested via name. As this map is never
utilized, remove it.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20251009130516.3729433-3-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-10-13 17:24:07 +02:00
Vincent Fazio
8c9d5b4fdf bindings: python: make config iteration consistent
Previously, Chip.request_lines and LineRequest.reconfigure_lines
iterated over a config object but did so in subtly different ways.

Create a generator that iterates over a config object and returns line +
settings pairs. Update the aforementioned functions to use the generator
to consolidate and condense code.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20251009130516.3729433-2-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-10-13 17:24:07 +02:00
Vincent Fazio
36f1e51b37 bindings: python: use quoted types when casting
Linter check T006 [0] recommends using quotes around types (making them
strings) when casting values to a type to avoid the overhead of runtime
type evaluation.

This provides a small performance benefit at runtime [1].

  >>> timeit(lambda: cast(gpiod._ext.Request, "a"), number=100000000)
  6.4817840601317585
  >>> timeit(lambda: cast("gpiod._ext.Request", "a"), number=100000000)
  4.369633618975058

This also allows moving some imports behind type checking guards as they
are not required at runtime.

[0]: https://docs.astral.sh/ruff/rules/runtime-cast-value/
[1]: https://github.com/snok/flake8-type-checking/issues/119#issuecomment-2511022212

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20250909125300.3507377-1-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-09-09 15:04:54 +02:00
Vincent Fazio
1f2ba0deab bindings: python: parse non-tuple Iterable keys
When `chip.request_lines` was modified to allow `Iterable`s instead of
only `tuple`s, the code that checked for duplicate line entries in the
`config` argument was not updated to account for the expanded types.

If the `config` argument had a key that was not a `tuple`, `str`, or
`int`, a `TypeError` would be raised when resolving the line offset.

Refactor the code the resolves IDs to offsets into a separate function
to make the logic a bit clearer and to account for the widened types.

Fixes: 8f62e6c453 ("bindings: python: loosen type requirements in public API")
Closes: https://github.com/brgl/libgpiod/issues/148
Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://lore.kernel.org/r/20250902123905.6491-1-vfazio@gmail.com
[Bartosz: don't pass the only argument by name in the call to
_resolve_config_keys_to_offsets()]
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-09-08 09:29:13 +02:00
Bartosz Golaszewski
bf0d733f4d bindings: python: gpiod v2.3.0
Minor gpiod package release.

Changelog:
- add fileno() for Chip and LineRequest
- relax the verbosity adjustment in build_tests.sh
- fix potential PyDict_Next() crash
- fix tests failing after a kernel fix to gpio-sim
- various documentation fixes
- rework the code treewide to make it conformant with mypy and ruff
  linter recommendations
- fix requesting lines by name with multiple entries
- fix a use-after-free bug in python bindings
- fix passing the event clock property to line requests in python
  bindings
- improve the output of LineSettings.__repr__()
- import gpiod attributes in external module
- specify a tar extract filter for python >= 3.12

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-02-19 13:36:05 +01:00
Bartosz Golaszewski
7ac2650893 bindings: gpiod: reword the docstring for LineRequest.__init__()
Change the docstring for LineRequest.__init__() so that when it appears
in sphinx docs as part of the class description, it won't sound as if
the class was not to be used at all.

Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20250211-improve-docs-v4-6-dc56702c2ca8@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-02-17 09:17:56 +01:00
Bartosz Golaszewski
d46e8f67aa bindings: python: doc: describe undocumented members
There are several public members in python bindings that are missing
docstrings. Document them for completeness.

Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20250211-improve-docs-v4-5-dc56702c2ca8@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-02-17 09:17:53 +01:00
Bartosz Golaszewski
b1bc6664f6 bindings: python: doc: make code examples appear as such in sphinx
In order for code examples inside docstrings to be interpreted as such
by sphinx, we need to use a double colon ("Example::").

Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20250211-improve-docs-v4-4-dc56702c2ca8@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-02-17 09:17:50 +01:00
Bartosz Golaszewski
8aa4d84b6f bindings: python: doc: update the docstring for gpiod.request_lines()
The global request_lines() function was updated to take explicitly typed
arguments instead of just passing *args and **kwargs down to
Chip.request_lines(). However, the doc remained unchanged. Update it now
to reflect the actual function parameters.

Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20250211-improve-docs-v4-3-dc56702c2ca8@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-02-17 09:17:48 +01:00
Bartosz Golaszewski
38adbae195 bindings: python: provide fileno() for Chip and LineRequest
Certain polling APIs in the standard library - most notably: the
select() function and the poll class - allow to poll any object that
implements the fileno() method returning the underlying file descriptor
number.

Implement fileno() for Chip and LineRequest which allows users to do:

  rd, _, _ = select([chip/request], [], [], 1)

where rd will contain the actual object passed to select which makes for
easier reading of events afterwards.

Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241211-python-fileno-v2-1-84c642572485@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-12-12 15:35:04 +01:00
Bartosz Golaszewski
6d9133a259 bindings: python: fix license tags
Several files under bindings/python/ have wrong license tags.

In general:
- Makefiles should be covered by GPL
- all python sources under bindings/python/gpiod/ and
  bindings/python/tests/ that are part of importable modules should be
  licensed under LGPL
- executable examples should be licensed under GPL

Reported-by: Vincent Fazio <vfazio@xes-inc.com>
Closes: https://github.com/brgl/libgpiod/issues/110
Acked-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241127150529.126896-1-brgl@bgdev.pl
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-12-05 13:56:15 +01:00
George Harker
7f12d44b31 bindings: python: fix potential PyDict_Next() crash
PyDict_Next() does not guarantee pos is contiguous, and pypy increments
it past the end of the dict size. Fix reliance on pos for constructing
args for libgpiod call.

Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Signed-off-by: George Harker <george@george-graphics.co.uk>
Link: https://lore.kernel.org/r/20241202171302.51437-1-george@george-graphics.co.uk
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-12-02 20:45:17 +01:00
Bartosz Golaszewski
93613618d2 bindings: python: add .pyi files to EXTRA_DIST
While python sdist and wheels are built separately these days, we still
want to package all the extra files in the regular libgpiod release
tarballs. Add new .pyi files to automake's EXTRA_DIST.

Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241120-drop-distutils-v1-1-7498e8b3babe@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-11-25 10:03:45 +01:00
Bartosz Golaszewski
2ede434e67 bindings: python: build: update makefile after renaming of internal.py
The module is now called _internal.py so update the Makefile. Without
this, we're no longer able to generate the distro tarball.

Fixes: b306ce694a ("bindings: python: make internal a private submodule")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-11-20 14:56:34 +01:00
Vincent Fazio
42308c2df1 bindings: python: selectively use f-strings
Since their inclusion in Python 3.6, f-strings have become the preferred
way to format strings with variable values as they are generally more
readable as the value substitution is in place and doesn't have to be
parsed from the list or arguments to `.format()`.

Where it does not impact readability (when the line is <120 characters),
swap usage of `.format()` to an f-string.

For lines that are not converted, inform the linter to ignore attempts
to upgrade those instances to f-strings [0]

[0]: https://docs.astral.sh/ruff/rules/f-string/

Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241114145116.2123714-15-vfazio@xes-inc.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-11-19 15:21:01 +01:00
Vincent Fazio
f3444341f4 bindings: python: cast return value of LineRequest.get_values
The `values` argument of `_ext.Request.get_values` uses a preallocated
`list[None]` as a buffer that is populated with `Value`s by the external
module that are then returned from the function.

Use `cast` to inform the type checker it's a `list[Value]` despite how
it's allocated.

Also, as `lines` is typed as an `Iterable`, there is no guarantee it has
a `__len__` method. Instead, use the size of the `offsets` array to
allocate the buffer.

Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241114145116.2123714-14-vfazio@xes-inc.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-11-19 15:21:01 +01:00
Vincent Fazio
fcddb71b45 bindings: python: convert lines to offsets in LineRequest
Remove `_check_line_name` in favor of a new function, `_line_to_offset`,
that converts a line reference to an offset.

This new function helps narrow types and simplifies the iteration logic
that is used to build objects to interface with `_ext.Request`.

Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241114145116.2123714-13-vfazio@xes-inc.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-11-19 15:21:01 +01:00
Vincent Fazio
fcb520f9fe bindings: python: fix LineRequest union-attr type errors
Since `LineRequest._req` is typed to be `Optional` and because type
checkers may not be able to infer that an object is not `None` from an
earlier call (such as `_check_released`) it is necessary to inform type
checkers of the state of the object to silence union-attr [0] errors.

Instead of littering the code with "# type: ignore" comments, use casts
to inform type checkers that objects are not `None`.

Using `assert` is another option, however this duplicates the logic in
`_check_released` so is redundant at best and, at worst, is not a safe
replacement as `assert` can be elided in optimized Python environments
and these checks need to be runtime enforced.

[0]: https://mypy.readthedocs.io/en/stable/error_code_list.html#check-that-attribute-exists-in-each-union-item-union-attr

Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241114145116.2123714-12-vfazio@xes-inc.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-11-19 15:21:01 +01:00
Vincent Fazio
01efd10160 bindings: python: add type hints for LineRequest's internal members
Add type hints for LineRequest's internal members so type checkers can
ensure the code properly constrains to these types and accounts for
scenarios where the values are `None`.

Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241114145116.2123714-11-vfazio@xes-inc.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-11-19 15:21:01 +01:00
Vincent Fazio
72e51c8a94 bindings: python: fix Chip union-attr type errors
Since `Chip._chip` is typed to be `Optional` and because type checkers
may not be able to infer that an object is not `None` from an earlier
call (such as `_check_closed`) it is necessary to inform type checkers
of the state of the object to silence union-attr [0] errors.

Instead of littering the code with "# type: ignore" comments, use casts
to inform type checkers that objects are not `None`.

Using `assert` is another option, however this duplicates the logic in
`_check_closed` so is redundant at best and, at worst, is not a safe
replacement as `assert` can be elided in optimized Python environments
and these checks need to be runtime enforced.

[0]: https://mypy.readthedocs.io/en/stable/error_code_list.html#check-that-attribute-exists-in-each-union-item-union-attr

Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241114145116.2123714-10-vfazio@xes-inc.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-11-19 15:21:01 +01:00
Vincent Fazio
1cc8bedab6 bindings: python: add type hints for Chip's internal members
Add type hints for Chip's internal members so type checkers can ensure
the code properly constrains to these types and accounts for scenarios
where the values are `None`.

Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241114145116.2123714-9-vfazio@xes-inc.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-11-19 15:21:01 +01:00
Vincent Fazio
2a2904082f bindings: python: add type hint for the sec variable in poll_fd
The sec variable can be either a float or None, so type it accordingly
to avoid complaints from type checkers.

Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241114145116.2123714-8-vfazio@xes-inc.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2024-11-19 15:21:01 +01:00