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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Previously, `Chip.request_lines` and `LineRequest.reconfigure_lines`
were typed to accept a config argument that was either an int, a str,
or a tuple[int | str].
This had two downsides, namely:
* The tuple was typed as having only a single element and not a variable
number of elements. The examples and test suite relied on a variable
length tuple.
* The tuple type itself was overly restictive. The function
implementations had no requirement that the value be a tuple, only
that it was iterable if it was not a str or an int.
Now, these functions accept an Iterable[int | str] instead of tuples to
offer greater flexibility to callers.
This change does not break compatibility for existing users.
Closes: https://github.com/brgl/libgpiod/issues/102
Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241114145116.2123714-4-vfazio@xes-inc.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
We no longer use the select module directly in chip.py so drop the
unneeded import.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Previously, the external module relied on gpiod attributes being present
within `globals()` to construct return values back to the caller.
This assumption required callers of the external module to have imported
the attributes to populate `globals()` for the interface to work.
Having this implicit contract is opaque and prone to causing issues if
imports within gpiod modules ever get reworked.
Now, the external module explicitly imports attributes from gpiod in
order to return values back to the caller.
There should be no concern about circular imports as the external module
should be completely imported by the time callers call into it.
Since Py_gpiod_GetGlobalType is no longer used, it has been replaced
with Py_gpiod_GetModuleAttrString which returns a new PyObject*
reference for the named module and attribute that must be decremented
when no longer in use.
Closes: https://github.com/brgl/libgpiod/issues/101
Signed-off-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://lore.kernel.org/r/20241008175139.1198980-1-vfazio@xes-inc.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
When multiple entries are requested using line names in
Chip.request_lines(), only the the last entry is added to
LineRequest._name_map, causing a ValueError when trying to use functions
like LineRequest.set_value() on any former entries.
Move the required variables to the correct scope.
Signed-off-by: Chuang Zhu <git@chuang.cz>
[Bartosz: tweak the commit message]
Co-developed-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: Kent Gibson <warthog618@gmail.com>
Link: https://lore.kernel.org/r/20240710125719.33655-2-brgl@bgdev.pl
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
The C API requires the configuration passed to reconfigure_lines()
to contain the lines in the same order as they were requested. This is
problematic for the Python bindings which accepts the configuration in
the form of a dict. For versions prior to Python 3.6, dicts do not
maintain insertion order, so iterating over the dict emits lines in
unreliable order.
Even with later Python versions, the ordering requirement makes
reconfigure_lines() awkward to use as subsequent configurations may
group line settings quite differently to the request, yet the user must
go out of their way to reproduce the original ordering.
This is a task better performed by the bindings.
Further, while the documentation for reconfigure_lines() states that
None settings values are treated as default values, the current
implementation raises an error when it tries to dereference the None,
thinking it is an actual object.
Similarly, providing default values for lines for which no settings
are provided would allow support for reconfiguring a subset of
requested lines.
Rework reconfigure_lines() to remove the ordering requirement and
construct the configuration provided to the C API in request order.
Populate missing or None line settings with default values to satisfy
the requirements of the C API that all requested lines must be
reconfigured.
Closes: https://github.com/brgl/libgpiod/issues/54
Signed-off-by: Kent Gibson <warthog618@gmail.com>
Link: https://lore.kernel.org/r/20240626053808.179457-3-warthog618@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>