bindings: python: Would type aliases make things easier to read?
We can alias types in python, so instead of littering the codebase with complex types like:
dict[Union[Iterable[Union[int, str]], int, str], Optional[LineSettings]]
Or when we move to only support 3.10+:
dict[Iterable[int | str] | int | str, LineSettings | None]
We can instead do something like:
RequestKey = Union[int, str]
IterableRequestKeys = Iterable[RequestKey]
IterableRequestKeys_or_RequestKey = Union[IterableRequestKeys, RequestKey]
config: dict[IterableRequestKeys_or_RequestKey, Optional[LineSettings]],
The resolved type is equivalent to the previous syntax, but it's maybe a little easier to digest.
https://docs.python.org/3.9/library/typing.html#type-aliases
Adding a diff just to give a sense of what this may look like
diff --git a/bindings/python/gpiod/__init__.py b/bindings/python/gpiod/__init__.py
index 854e41f..e95cf21 100644
--- a/bindings/python/gpiod/__init__.py
+++ b/bindings/python/gpiod/__init__.py
@@ -7,8 +7,10 @@ Python bindings for libgpiod.
This module wraps the native C API of libgpiod in a set of python classes.
"""
-from collections.abc import Iterable
-from typing import Optional, Union
+from typing import Optional, TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from .line_request import RequestKey, IterableRequestKeys_or_RequestKey
from . import (
_ext,
@@ -87,10 +89,10 @@ def is_gpiochip_device(path: str) -> bool:
def request_lines(
(venv) vfazio@vfazio2:~/development/libgpiod/bindings/python$ PAGER= git diff
diff --git a/bindings/python/gpiod/__init__.py b/bindings/python/gpiod/__init__.py
index 854e41f..e95cf21 100644
--- a/bindings/python/gpiod/__init__.py
+++ b/bindings/python/gpiod/__init__.py
@@ -7,8 +7,10 @@ Python bindings for libgpiod.
This module wraps the native C API of libgpiod in a set of python classes.
"""
-from collections.abc import Iterable
-from typing import Optional, Union
+from typing import Optional, TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from .line_request import RequestKey, IterableRequestKeys_or_RequestKey
from . import (
_ext,
@@ -87,10 +89,10 @@ def is_gpiochip_device(path: str) -> bool:
def request_lines(
path: str,
- config: dict[Union[Iterable[Union[int, str]], int, str], Optional[LineSettings]],
+ config: dict[IterableRequestKeys_or_RequestKey, Optional[LineSettings]],
consumer: Optional[str] = None,
event_buffer_size: Optional[int] = None,
- output_values: Optional[dict[Union[int, str], line.Value]] = None,
+ output_values: Optional[dict[RequestKey, line.Value]] = None,
) -> LineRequest:
"""
Open a GPIO chip pointed to by 'path', request lines according to the
diff --git a/bindings/python/gpiod/chip.py b/bindings/python/gpiod/chip.py
index cccfb03..3bdc6f1 100644
--- a/bindings/python/gpiod/chip.py
+++ b/bindings/python/gpiod/chip.py
@@ -22,6 +22,11 @@ if TYPE_CHECKING:
from .chip_info import ChipInfo
from .info_event import InfoEvent
from .line_info import LineInfo
+ from .line_request import (
+ RequestKey,
+ IterableRequestKeys,
+ IterableRequestKeys_or_RequestKey,
+ )
__all__ = ["Chip"]
@@ -118,7 +123,7 @@ class Chip:
return self._info
- def line_offset_from_id(self, id: Union[str, int]) -> int:
+ def line_offset_from_id(self, id: RequestKey) -> int:
"""
Map a line's identifier to its offset within the chip.
@@ -156,13 +161,13 @@ class Chip:
return offset
- def _get_line_info(self, line: Union[int, str], watch: bool) -> LineInfo:
+ def _get_line_info(self, line: RequestKey, watch: bool) -> LineInfo:
self._check_closed()
return cast(_ext.Chip, self._chip).get_line_info(
self.line_offset_from_id(line), watch
)
- def get_line_info(self, line: Union[int, str]) -> LineInfo:
+ def get_line_info(self, line: RequestKey) -> LineInfo:
"""
Get the snapshot of information about the line at given offset.
@@ -175,7 +180,7 @@ class Chip:
"""
return self._get_line_info(line, watch=False)
- def watch_line_info(self, line: Union[int, str]) -> LineInfo:
+ def watch_line_info(self, line: RequestKey) -> LineInfo:
"""
Get the snapshot of information about the line at given offset and
start watching it for future changes.
@@ -189,7 +194,7 @@ class Chip:
"""
return self._get_line_info(line, watch=True)
- def unwatch_line_info(self, line: Union[int, str]) -> None:
+ def unwatch_line_info(self, line: RequestKey) -> None:
"""
Stop watching a line for status changes.
@@ -238,7 +243,7 @@ class Chip:
def _resolve_config_keys_to_offsets(
self,
- config_keys: Iterable[Union[Iterable[Union[int, str]], int, str]],
+ config_keys: Iterable[IterableRequestKeys_or_RequestKey],
) -> list[int]:
offsets: list[int] = list()
for key in config_keys:
@@ -252,12 +257,10 @@ class Chip:
def request_lines(
self,
- config: dict[
- Union[Iterable[Union[int, str]], int, str], Optional[LineSettings]
- ],
+ config: dict[IterableRequestKeys_or_RequestKey, Optional[LineSettings]],
consumer: Optional[str] = None,
event_buffer_size: Optional[int] = None,
- output_values: Optional[dict[Union[int, str], Value]] = None,
+ output_values: Optional[dict[RequestKey, Value]] = None,
) -> LineRequest:
"""
Request a set of lines for exclusive usage.
diff --git a/bindings/python/gpiod/line_request.py b/bindings/python/gpiod/line_request.py
index 0220ba3..1ed2502 100644
--- a/bindings/python/gpiod/line_request.py
+++ b/bindings/python/gpiod/line_request.py
@@ -22,6 +22,11 @@ if TYPE_CHECKING:
__all__ = ["LineRequest"]
+RequestKey = Union[int, str]
+IterableRequestKeys = Iterable[RequestKey]
+IterableRequestKeys_or_RequestKey = Union[IterableRequestKeys, RequestKey]
+
+
class LineRequest:
"""
Stores the context of a set of requested GPIO lines.
@@ -79,7 +84,7 @@ class LineRequest:
cast(_ext.Request, self._req).release()
self._req = None
- def get_value(self, line: Union[int, str]) -> Value:
+ def get_value(self, line: RequestKey) -> Value:
"""
Get a single GPIO line value.
@@ -92,7 +97,7 @@ class LineRequest:
"""
return self.get_values([line])[0]
- def _line_to_offset(self, line: Union[int, str]) -> int:
+ def _line_to_offset(self, line: RequestKey) -> int:
if isinstance(line, int):
return line
else:
@@ -102,9 +107,7 @@ class LineRequest:
else:
return _line
- def get_values(
- self, lines: Optional[Iterable[Union[int, str]]] = None
- ) -> list[Value]:
+ def get_values(self, lines: Optional[IterableRequestKeys] = None) -> list[Value]:
"""
Get values of a set of GPIO lines.
@@ -127,7 +130,7 @@ class LineRequest:
cast(_ext.Request, self._req).get_values(offsets, buf)
return buf
- def set_value(self, line: Union[int, str], value: Value) -> None:
+ def set_value(self, line: RequestKey, value: Value) -> None:
"""
Set the value of a single GPIO line.
@@ -139,7 +142,7 @@ class LineRequest:
"""
self.set_values({line: value})
- def set_values(self, values: dict[Union[int, str], Value]) -> None:
+ def set_values(self, values: dict[RequestKey, Value]) -> None:
"""
Set the values of a subset of GPIO lines.
@@ -155,9 +158,7 @@ class LineRequest:
def reconfigure_lines(
self,
- config: dict[
- Union[Iterable[Union[int, str]], int, str], Optional[LineSettings]
- ],
+ config: dict[IterableRequestKeys_or_RequestKey, Optional[LineSettings]],
) -> None:
"""
Reconfigure requested lines.
We can alias types in python, so instead of littering the codebase with complex types like:
dict[Union[Iterable[Union[int, str]], int, str], Optional[LineSettings]]
Or when we move to only support 3.10+:
I guess this answers my question from under the other ticket about the minimum required version.
dict[Iterable[int | str] | int | str, LineSettings | None]
We can instead do something like:
RequestKey = Union[int, str] IterableRequestKeys = Iterable[RequestKey] IterableRequestKeys_or_RequestKey = Union[IterableRequestKeys, RequestKey]
config: dict[IterableRequestKeys_or_RequestKey, Optional[LineSettings]],
The resolved type is equivalent to the previous syntax, but it's maybe a little easier to digest.
https://docs.python.org/3.9/library/typing.html#type-aliases
I like it and am willing to accept it as long as you'll be the one driving it. :)