Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Errors: shake up how error digests are triggered, catch ints supplied as pin labels. #3

Merged
merged 1 commit into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions gpiodevice/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
CHIP_GLOB = "/dev/gpiochip*"


# Deprecated
friendly_errors: bool = False


@errors.collect
def check_pins_available(chip: gpiod.Chip, pins) -> bool:
def check_pins_available(chip: gpiod.Chip, pins, fatal: bool = True) -> bool:
"""Check if a list of pins are in use on a given gpiochip device.

Raise a RuntimeError with a friendly list of in-use pins and their consumer if
any are in used.
If any pins are used: raises a helpful list of pins and their consumer if fatal == True, otherwise returns False.

"""
if pins is None:
Expand All @@ -42,18 +42,20 @@ def check_pins_available(chip: gpiod.Chip, pins) -> bool:
used += 1
yield errors.GPIOError(f"{label}: (line {pin}, {line_info.name}) currently claimed by {line_info.consumer}")

if used and friendly_errors:
if used and fatal:
raise errors.ErrorDigest("some pins we need are in use!")

return used == 0


@errors.collect
def find_chip_by_label(labels: (list[str], tuple[str], str), pins: dict[str, (int, str)] = None):
def find_chip_by_label(labels: (list[str], tuple[str], str), pins: dict[str, (int, str)] = None, fatal: bool = True):
"""Try to find a gpiochip device matching one of a set of labels.

Raise a RuntimeError with a friendly error digest if one is not found.

If no suitable gpiochip is found: raises a helpful digest of errors if fatal == True, otherwise returns None.

"""
if isinstance(labels, str):
labels = (labels,)
Expand All @@ -73,21 +75,26 @@ def find_chip_by_label(labels: (list[str], tuple[str], str), pins: dict[str, (in
else:
yield errors.GPIONotFound(f"{path}: this is not the GPIO we're looking for! ({label})")

if friendly_errors:
if fatal:
raise errors.ErrorDigest("suitable gpiochip device not found!")

return None


@errors.collect
def find_chip_by_pins(pins: (list[str], tuple[str], str), ignore_claimed: bool = False):
def find_chip_by_pins(pins: (list[str], tuple[str], str), ignore_claimed: bool = False, fatal: bool = True):
"""Try to find a gpiochip device that includes all of the named pins.

Does not care whether pins are in use or not.

"pins" can be a single string, a list/tuple or a comma-separated string of names.

If no suitable gpiochip is found: raises a helpful digest of errors if fatal == True, otherwise returns None.

"""
if isinstance(pins, int):
pins = (pins,)

if isinstance(pins, str):
if "," in pins:
pins = [pin.strip() for pin in pins.split(",")]
Expand All @@ -106,6 +113,11 @@ def find_chip_by_pins(pins: (list[str], tuple[str], str), ignore_claimed: bool =
failed = False

for pin_id in pins:
if isinstance(pin_id, int):
failed = True
yield errors.GPIOError(f'{path}: {pin_id} is an int and has been skipped, did you mean "PIN{pin_id}" or "GPIO{pin_id}"?')
continue

try:
offset = chip.line_offset_from_id(pin_id)
yield errors.GPIOFound(f"{pin_id}: (line {offset}) found - {path} ({label})!")
Expand All @@ -123,7 +135,7 @@ def find_chip_by_pins(pins: (list[str], tuple[str], str), ignore_claimed: bool =
if not failed:
return chip

if friendly_errors:
if fatal:
raise errors.ErrorDigest("suitable gpiochip not found!")

return None
Expand Down
2 changes: 1 addition & 1 deletion gpiodevice/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(self, message: str, icon: str = "✅"):
GPIOBaseError.__init__(self, message, icon)


def collect(fn):
def collect(fn, fatal=False):
def wrapper(*args, **kwargs):
errors = []

Expand Down
9 changes: 9 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@ def gpiod():
sys.modules["gpiod"] = gpiopd
yield gpiod
del sys.modules["gpiod"]


@pytest.fixture(scope="function", autouse=True)
def cleanup():
yield
try:
del sys.modules["gpiodevice"]
except KeyError:
pass
22 changes: 22 additions & 0 deletions tests/test_features.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pytest


def test_find_chip_by_pins_int(gpiod):
import gpiodevice

with pytest.raises(SystemExit):
gpiodevice.find_chip_by_pins(1)


def test_find_chip_by_pins_quiet(gpiod):
import gpiodevice

assert gpiodevice.find_chip_by_pins(1, fatal=False) is None
assert gpiodevice.find_chip_by_pins("GPIO1", fatal=False) is None


def test_find_chip_by_pins_str(gpiod):
import gpiodevice

with pytest.raises(SystemExit):
gpiodevice.find_chip_by_pins("GPIO1")
4 changes: 0 additions & 4 deletions tests/test_setup.py

This file was deleted.

Loading