bindings: python: tests: provide and use system.check_kernel_version()
We claim that libgpiod python bindings have no dependencies other than
the python standard library but this is not true - the tests do depend
on deprecated distutils for kernel version parsing.
As distutils is deprecated, the recommended improvement is to use the
Version class from packaging.version but this would too entail pulling
in an external module.
Let's instead implement the kernel version check in C using uname() and
put it into the tests.system extension. This allows us to entirely drop
the distutils import.
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.
Vincent Fazio [Thu, 14 Nov 2024 14:51:16 +0000 (08:51 -0600)]
bindings: python: configure and document dev dependencies
Mypy [0] is a popular static type checker that validates attribute and
variable use and ensures function arguments adhere to type annotations.
Ruff [1] is a popular Rust-based Python linter and code formatter. It
has support for a large set of linting rules [2] and largely complies
with the Black format [3].
Add documentation to README.md for how to run the tools.
Vincent Fazio [Thu, 14 Nov 2024 14:51:15 +0000 (08:51 -0600)]
bindings: python: tests: 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]
Vincent Fazio [Thu, 14 Nov 2024 14:51:13 +0000 (08:51 -0600)]
bindings: python: tests: add type hints to internal members
Add type hints for internal members of the test cases so type checkers
can ensure the code properly constrains to these types and accounts for
scenarios where the values are `None`.
In most cases, the type checker is allowed to assume the inferred type
upon assignment.
An example is any test case that creates a `Chip` as part of its `setUp`
method. Many of these tests subsequently set the reference to `None` in
`tearDown` to free up resources to be collected by the GC.
If the member was "properly" typed to be `Optional`, all references in
the unit tests would need to be guarded by an assert or a cast or
ignored by the type checker. This is noisy and doesn't add value since
for the life of the test it should always be a valid reference.
Instead, allow `tearDown` to violate the inferred type and inform the
type checker to ignore the assignment via directive.
If the tests are ever changed to set the member to something other than
the inferred type outside of `tearDown`, explicit type hints and proper
checks should be added.
Vincent Fazio [Thu, 14 Nov 2024 14:51:07 +0000 (08:51 -0600)]
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]
Vincent Fazio [Thu, 14 Nov 2024 14:51:06 +0000 (08:51 -0600)]
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.
Vincent Fazio [Thu, 14 Nov 2024 14:51:04 +0000 (08:51 -0600)]
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.
Vincent Fazio [Thu, 14 Nov 2024 14:51:03 +0000 (08:51 -0600)]
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`.
Vincent Fazio [Thu, 14 Nov 2024 14:51:02 +0000 (08:51 -0600)]
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.
Vincent Fazio [Thu, 14 Nov 2024 14:51:01 +0000 (08:51 -0600)]
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`.
Vincent Fazio [Thu, 14 Nov 2024 14:50:56 +0000 (08:50 -0600)]
bindings: python: loosen type requirements in public API
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.
Vincent Fazio [Tue, 8 Oct 2024 17:51:39 +0000 (12:51 -0500)]
bindings: python: import gpiod attributes in external module
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.
dbus: display D-Bus API versions implemented by the manager and gpiocli
When passing -v/--version to gpiocli or gpio-manager, it's useful to
also know the version of the GPIO D-Bus API the program implements.
Define the version in the gpiodbus.h header and print it together with
the library version.
There's a reference overflow when adding chips to the global list and an
underflow when fetching existing chips from that list in connect_line().
Fix both issues.
Some distros split glib-mkenums and other GLib build utilities into a
separate package but the glib-2.0 pkgconfig file pointing to it is
typically shipped as part of the -dev package. This can lead to a
situation where configure thinks it knows where glib-mkenums is but make
then fails because it's not really installed on the system. Check the
existence of the actual executable in addition to querying pkgconfig.
AC_HEADER_STDC is deprecated and should be replaced with
AC_CHECK_INCLUDES_DEFAULT. We haven't done this until now because the
latter is only available since autoconf v2.71. We kept supporting v2.69
for the sake of Debian stable but it has since upgraded to autoconf v2.71
so we update configure.ac as well and fix an autoconf warning.
bindings: python: add misc build files to EXTRA_DIST
Several files are not being included in distro tarballs so the
functionality of the python build is limited compared to the repo. Add
them to EXTRA_DIST.
Fixes: d588a6a5928a ("bindings: python: standalone build tooling for tests") Fixes: 72d2fa01a2c1 ("bindings: python: add script to generate sdist and wheels") Fixes: 85089d0e40ac ("bindings: python: add pyproject.toml, pep 518") Fixes: 389a966d4e61 ("bindings: python: move long_description into README.md") Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
On 32-bit architectures the size of a gulong will typically be 4 bytes
while we try to package an 8-byte integer when creating the edge event
variant. The client also expects wrong types and crashes. Use strict
type sizes for sending the events.
Currently, for the output of LineSettings.__repr__() to be eval()able,
the user must have pulled all the relevant definitions from gpiod.line
within the scope where it is used. Modify the output so that all the user
needs is `import gpiod`.
Kent Gibson [Tue, 17 Sep 2024 12:54:55 +0000 (20:54 +0800)]
bindings: python: examples: add graceful exit to async_watch_line_value
The purpose of the example is demonstrate using a request with poll().
It provides a hint as to how the poll can be combined with other fds but,
as Python comes with batteries included, the Python version of the example
can be readily extended to actually demonstrate this, as well as how it
can be used in multi-threaded environments.
Extend the example to use an eventfd to allow the poll() to be run in
a background thread and be gracefully terminated by the main thread.
Kent Gibson [Wed, 4 Sep 2024 12:50:14 +0000 (20:50 +0800)]
dbus: update glib dependency to 2.80
The gpio-manager makes use of g_log_writer_default_set_debug_domains()
which was added in glib 2.80, but the dependency in configure.ac is only
glib 2.54. This results in compile errors when built with glib between 2.54
and 2.80.
Update the glib dependency, and related dependencies, to 2.80.
Kent Gibson [Sat, 31 Aug 2024 07:52:12 +0000 (15:52 +0800)]
build: fix HAS_GI_DOCGEN never defined error
When building without --enable-bindings-glib configure reports this error:
...
checking for help2man... true
checking that generated files are newer than configure... done
configure: error: conditional "HAS_GI_DOCGEN" was never defined.
Usually this means the macro was only invoked conditionally.
make: *** [Makefile:440: config.status] Error 1
Move the initialization of HAS_GI_DOCGEN outside the conditional
with_bindings_glib section so it is always initialized.
tests: don't use g_value_set_static_string() for non-static strings
As pointed out by Philip Withnall, g_value_set_static_string() must only
be used with actual static strings and not with ones whose life-time is
tied to that of their owner. Use g_value_set_string() to get the gpiosim
properties and rework the existing getter functions returning const
gchar * to return the address provided by libgpiosim directly instead of
passing through the GObject property path.
dbus: add the D-Bus daemon, command-line client and tests
Add the D-Bus API definition and its implementation in the form of a GPIO
manager daemon and a companion command-line client as well as some
additional configuration and data files (systemd service, example udev
configuration, etc.) and test suites.
tests: split out the common test code for bash scripts
In order to allow the upcoming DBus command-line client tests to reuse the
existing bash test harness, let's put the common code into an importable
file and rename run_tool to run_prog to reflect that it now can run any
program.
tests: split out reusable test code into a local static library
In order to allow the upcoming GLib and DBus bindings to reuse the test
code, let's put all common elements into reusable libtool objects and
export the relevant symbols in internal headers.
bindings: python: tests: check that event clock is property set in request
We currently only have a test-case that checks if the event clock
property is correctly set in the LineSettings object but not whether it
is actually passed to the line request. Extend the existing test-case for
line requests to account for event clocks as well.
strncpy() truncates the destination buffer if it isn't large enough to
hold the copy. Thus, let's increase the size of the destination strings
to add the NULL character at the end.
strncpy() truncates the destination buffer if it isn't large enough to
hold the copy. Thus, let's increase the size of the destination strings
to add the NULL character at the end.
Add a test-case for line request by name with multiple entries.
Signed-off-by: Chuang Zhu <git@chuang.cz>
[Bartosz:
- tweak the commit message
- improve the test class name
- extend the test assertion to test the 'baz' line too] Co-developed-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> Reviewed-by: Kent Gibson <warthog618@gmail.com> Link: https://lore.kernel.org/r/20240710125719.33655-3-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
bindings: python: fix line request by name with multiple entries
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.
bindings: rust: tests: set direction when reconfiguring lines
Linux kernel commit b44039638741 ("gpiolib: cdev: Ignore reconfiguration
without direction") made the direction setting mandatory for line config
passed to the kernel when reconfiguring requested lines. Fix the Rust
test cases which don't do it and now fail due to the rest of the
settings being ignored.
bindings: cxx: tests: set direction when reconfiguring lines
Linux kernel commit b44039638741 ("gpiolib: cdev: Ignore reconfiguration
without direction") made the direction setting mandatory for line config
passed to the kernel when reconfiguring requested lines. Fix the C++ test
case which doesn't do it and now fails due to the rest of the settings
being ignored.
Kent Gibson [Wed, 26 Jun 2024 05:38:08 +0000 (13:38 +0800)]
bindings: python: tests: add coverage of kernel reconfigure as-is behaviour
The kernel's handling of reconfigure with default values, as is the
case for providing a None value as the settings to the Python bindings'
reconfigure_lines(), resets any flags set to non-default values when the
line is requested to their default values. While the flags are cleared,
the kernel makes no corresponding change to the electrical settings -
though subsequent calls to get and set values will apply the updated
flags.
The tests for missing or None settings are extended to demonstrate the
issue for active_low and drive flags, though the issue applies to all
flags.
The tests fail unless the kernel is patched to ignore reconfiguration
of lines without direction set.
Kent Gibson [Wed, 26 Jun 2024 05:38:07 +0000 (13:38 +0800)]
bindings: python: more flexible reconfigure_lines()
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.
A number of the corner cases for reconfiguration are untested, including
- using None for default settings
- missing settings for some lines
- jumbled line ordering relative to the request
- extra settings for non-requested lines
README: list the development packages required to build the library
The error messages emitted by configure when either libtool, pkg-config
or autoconf-archive packages are missing on the host are not very clear
and seem to cause confusion among users building the project from
sources. List the required packages in the README.
Kent Gibson [Fri, 5 Jul 2024 02:17:50 +0000 (10:17 +0800)]
doc: fix sphinx config for rtd
Generating the latest documentation on readthedocs is broken as the
index.html generated by Doxygen is now being overwritten by one
generated by Sphinx.
Make Sphinx generate a differently named root page that does not
conflict with the index.html generated by Doxygen.
Changelog:
- add a script for generating sdist and wheels
- fix make build
- support casting of line.Value to bool
- test fixes
- fix __repr__() implementations
Extend the "Contributing" section of the README to include mentions of
shell scripts having to pass the `shellcheck` test and the entire tree
having to conform to `reuse lint` requirements.
Vincent Fazio [Tue, 11 Jun 2024 20:50:41 +0000 (15:50 -0500)]
bindings: python: add script to generate sdist and wheels
Introduce a shell script that generates an sdist tarball and PEP 600/656
conformant wheels.
The wheels are generated via cibuildwheel, a tool provided by the Python
Packaging Authority (PyPA) [0]. The tool leverages toolchains within
containers maintained by PyPA [1] to generate wheels that are runnable
on hosts that meet the platform compatibility tag [2] requirements.
By default, the script creates X86_64 and AArch64 CPython 3.9-3.12
wheels for glibc and musl libc based systems.
These defaults can be overridden via CIBW_* environment variables [3].
$@ does not break up quoted arguments which is what we want in all cases
in the bash test-suite. Use it instead of $*. While at it: prevent
globbing with double quotes but allow variable expansion.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Reviewed-by: Kent Gibson <warthog618@gmail.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20240528-fix-bash-tests-v3-2-e9b5be2ba8bf@linaro.org Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>