1
0
mirror of https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git synced 2026-10-24 00:48:53 +03:00
Files
libgpiod/bindings/python/gpiod/line_request.py
Vincent Fazio cba46d470a bindings: python: line_request: simplify __bool__ expression
The `True if <> else False` ternary is unnecessary and the return value
can be simplified to the bool result of checking if the request object
is not `None`.

The underlying `Request` object does not have an explicit `__bool__`
implementation so the check only proved that the object existed which
is preserved.

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Link: https://patch.msgid.link/20261020003728.13669-7-vfazio@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-10-20 15:08:42 +02:00

285 lines
8.1 KiB
Python

# SPDX-License-Identifier: LGPL-2.1-or-later
# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
from __future__ import annotations
import warnings
from typing import TYPE_CHECKING, cast
from . import _ext
from ._internal import config_iter, poll_fd
from .exception import RequestReleasedError
from .line_settings import LineSettings, _line_settings_to_ext
if TYPE_CHECKING:
from collections.abc import Iterable
from datetime import timedelta
from types import TracebackType
from .edge_event import EdgeEvent
from .line import Value
__all__ = ["LineRequest"]
class LineRequest:
"""
Stores the context of a set of requested GPIO lines.
Synchronization: objects of this class require external synchronization.
Protect calls with a lock when sharing an instance across threads.
"""
def __init__(self, req: _ext.Request):
"""
Note: LineRequest objects can only be instantiated by a Chip parent.
LineRequest.__init__() is not part of stable API.
"""
self._req: _ext.Request | None = req
self._chip_name: str
self._offsets: list[int]
self._name_map: dict[str, int]
self._lines: list[int | str]
def __bool__(self) -> bool:
"""
Boolean conversion for GPIO line requests.
Returns:
True if the request is live and False if it's been released.
"""
return self._req is not None
def __enter__(self) -> LineRequest:
"""
Controlled execution enter callback.
"""
self._check_released()
return self
def __exit__(
self,
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
) -> None:
"""
Controlled execution exit callback.
"""
self.release()
def _check_released(self) -> None:
if not self._req:
raise RequestReleasedError()
def release(self) -> None:
"""
Release this request and free all associated resources. The object must
not be used after a call to this method.
"""
if self._req is not None:
self._req.release()
self._req = None
def get_value(self, line: int | str) -> Value:
"""
Get a single GPIO line value.
Args:
line:
Offset or name of the line to get value for.
Returns:
Logical value of the line.
"""
return self.get_values([line])[0]
def _line_to_offset(self, line: int | str) -> int:
if isinstance(line, int):
return line
if (_line := self._name_map.get(line)) is None:
raise ValueError(f"unknown line name: {line}")
return _line
def get_values(self, lines: Iterable[int | str] | None = None) -> list[Value]:
"""
Get values of a set of GPIO lines.
Args:
lines:
List of names or offsets of GPIO lines to get values for. Can be
None in which case all requested lines will be read.
Returns:
List of logical line values.
"""
self._check_released()
lines = lines or self._lines
offsets = [self._line_to_offset(line) for line in lines]
buf = cast("list[Value]", [None] * len(offsets))
cast("_ext.Request", self._req).get_values(offsets, buf)
return buf
def set_value(self, line: int | str, value: Value) -> None:
"""
Set the value of a single GPIO line.
Args:
line:
Offset or name of the line to set.
value:
New value.
"""
self.set_values({line: value})
def set_values(self, values: dict[int | str, Value]) -> None:
"""
Set the values of a subset of GPIO lines.
Args:
values:
Dictionary mapping line offsets or names to desired values.
"""
self._check_released()
mapped = {self._line_to_offset(line): value for line, value in values.items()}
cast("_ext.Request", self._req).set_values(mapped)
def reconfigure_lines(
self,
config: dict[Iterable[int | str] | int | str, LineSettings | None],
) -> None:
"""
Reconfigure requested lines.
Args:
config
Dictionary mapping offsets or names (or tuples thereof) to
LineSettings. If no entry exists, or a None is passed as the
settings, then the configuration for that line is not changed.
Any settings for non-requested lines are ignored.
"""
self._check_released()
line_cfg = _ext.LineConfig()
line_settings = {}
for line, settings in config_iter(config):
try:
offset = self._line_to_offset(line)
if offset in self.offsets:
line_settings[offset] = settings
else:
warnings.warn(
f"Line offset '{offset}' was not included in original request.",
stacklevel=2,
)
except ValueError:
# _line_to_offset will raise a ValueError when it encounters
# an unrecognized line name. Ignore these like we do offsets
# that were not in the original request.
warnings.warn(
f"Line name '{line}' was not included in original request.",
stacklevel=2,
)
for offset in self.offsets:
settings = line_settings.get(offset) or LineSettings()
line_cfg.add_line_settings([offset], _line_settings_to_ext(settings))
cast("_ext.Request", self._req).reconfigure_lines(line_cfg)
def wait_edge_events(self, timeout: timedelta | float | None = None) -> bool:
"""
Wait for edge events on any of the requested lines.
Args:
timeout:
Wait time limit expressed as either a datetime.timedelta object
or the number of seconds stored in a float. If set to 0, the
method returns immediately, if set to None it blocks indefinitely.
Returns:
True if events are ready to be read. False on timeout.
"""
self._check_released()
return poll_fd(self.fd, timeout)
def read_edge_events(self, max_events: int | None = None) -> list[EdgeEvent]:
"""
Read a number of edge events from a line request.
Args:
max_events:
Maximum number of events to read.
Returns:
List of read EdgeEvent objects.
"""
self._check_released()
return cast("_ext.Request", self._req).read_edge_events(max_events)
def fileno(self) -> int:
"""
Return the underlying file descriptor.
"""
return self.fd
def __str__(self) -> str:
"""
Return a user-friendly, human-readable description of this request.
"""
if not self._req:
return "<LineRequest RELEASED>"
return f'<LineRequest chip="{self.chip_name}" num_lines={self.num_lines} offsets={self.offsets} fd={self.fd}>'
@property
def chip_name(self) -> str:
"""
Name of the chip this request was made on.
"""
self._check_released()
return self._chip_name
@property
def num_lines(self) -> int:
"""
Number of requested lines.
"""
self._check_released()
return len(self._offsets)
@property
def offsets(self) -> list[int]:
"""
List of requested offsets. Lines requested by name are mapped to their
offsets.
"""
self._check_released()
return self._offsets
@property
def lines(self) -> list[int | str]:
"""
List of requested lines. Lines requested by name are listed as such.
"""
self._check_released()
return self._lines
@property
def fd(self) -> int:
"""
File descriptor associated with this request.
"""
self._check_released()
return cast("_ext.Request", self._req).fd