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>
Since commit a62c732936 ("core: change the behavior of
gpiod_api_version()"), the libgpiod build expects the
GPIOD_API_VERSION_STR external define to contain the API version string.
The setup.py script in python bindings was not updated so it fails to
build the library sources into the generated wheel. Use the correct
define.
Fixes: a62c732936 ("core: change the behavior of gpiod_api_version()")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
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>
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>
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>
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>
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>
Specify useful dependencies for linting the bindings and related code.
The group can be installed with `pip install --group lint`.
As part of this group, specify version ranges for mypy and ruff to keep
linting consistent until new minimal versions of Python are targeted or
until the new annual style introduced by ruff is chosen.
Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260401001459.19159-8-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Previously, the bindings called out the SPDX license identifier in the
pyproject.toml but did not include a copy of the license in either the
source or binary distributions.
This can be problematic for tools that rely on extracting license
information, such as Buildroot or other license auditing mechanisms.
Now, when distributions are built, the license is copied out of the
libgpiod source tarball and into the target distribution.
Closes: https://github.com/brgl/libgpiod/issues/162
Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20260228184158.25003-1-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
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>
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>
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>
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>
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>
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>
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>