diff --git a/.devfile/Containerfile b/.devfile/Containerfile index dac8869aa..0c7d9f90f 100644 --- a/.devfile/Containerfile +++ b/.devfile/Containerfile @@ -13,7 +13,7 @@ COPY --from=ghcr.io/astral-sh/uv:latest /uvx /bin/uvx USER root -RUN dnf -y install make git python3.12 libusbx python3-pyusb golang podman && dnf clean all +RUN dnf -y install make git python3.12 python3.12-devel libusbx python3-pyusb golang podman gcc && dnf clean all USER 10001 diff --git a/.devfile/Containerfile.client b/.devfile/Containerfile.client index 6dbba09e2..73260f1ec 100644 --- a/.devfile/Containerfile.client +++ b/.devfile/Containerfile.client @@ -24,7 +24,7 @@ USER root # switch to python 3.12 as the default RUN rm -rf /usr/bin/python && ln -s /usr/bin/python3.12 /usr/bin/python -RUN dnf -y install make git python3.12 python3.12 libusbx python3-pyusb python3.12-pip golang && dnf clean all +RUN dnf -y install make git python3.12 python3.12-devel libusbx python3-pyusb python3.12-pip golang gcc && dnf clean all USER 10001 diff --git a/Dockerfile b/Dockerfile index edc7b3949..26a6e256d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ RUN dnf install -y make git && \ COPY --from=uv /uv /uvx /bin/ FROM fedora:40 AS product -RUN dnf install -y python3 ustreamer libusb1 && \ +RUN dnf install -y python3 python3-devel ustreamer libusb1 gcc && \ dnf clean all && \ rm -rf /var/cache/dnf COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ diff --git a/docs/source/api-reference/drivers/index.md b/docs/source/api-reference/drivers/index.md index 6d71480bf..fc7e35397 100644 --- a/docs/source/api-reference/drivers/index.md +++ b/docs/source/api-reference/drivers/index.md @@ -11,4 +11,5 @@ can.md pyserial.md sdwire.md ustreamer.md +raspberrypi.md ``` diff --git a/docs/source/api-reference/drivers/raspberrypi.md b/docs/source/api-reference/drivers/raspberrypi.md new file mode 100644 index 000000000..4060a7b67 --- /dev/null +++ b/docs/source/api-reference/drivers/raspberrypi.md @@ -0,0 +1,37 @@ +# Raspberry Pi drivers + +Raspberry Pi drivers are a set of drivers for the various peripherals on Pi and similar single board computers. + +## Driver configuration +```yaml +export: + my_serial: + type: "jumpstarter_driver_raspberrypi.driver.DigitalIO" + config: + pin: "D3" +``` + +### Config parameters + +| Parameter | Description | Type | Required | Default | +|-----------|-------------|------|----------|---------| +| pin | Name of the GPIO pin to connect to, in [Adafruit Blinka format](https://docs.circuitpython.org/projects/blinka/en/latest/index.html#usage-example) | str | yes | | + +## DigitalIOClient API +```{eval-rst} +.. autoclass:: jumpstarter_driver_raspberrypi.client.DigitalIOClient + :members: +``` + +## Examples +Switch pin to push pull output and set output to high +```{testcode} +digitalioclient.switch_to_output(value=False, drive_mode=digitalio.DriveMode.PUSH_PULL) # default to low +digitalioclient.value = True +``` + +Switch pin to input with pull up and read value +```{testcode} +digitalioclient.switch_to_input(pull=digitalio.Pull.UP) +print(digitalioclient.value) +``` diff --git a/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py b/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py index 3242c78c9..138cdc186 100644 --- a/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py +++ b/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/client.py @@ -1,21 +1,55 @@ from dataclasses import dataclass +from digitalio import DriveMode, Pull + from jumpstarter.client import DriverClient @dataclass(kw_only=True) -class DigitalOutputClient(DriverClient): - def off(self): - self.call("off") +class DigitalIOClient(DriverClient): + """DigitalIO (Digital GPIO) client class - def on(self): - self.call("on") + Client methods for the DigitalIO driver. + """ + def switch_to_output(self, value: bool = False, drive_mode: DriveMode = DriveMode.PUSH_PULL) -> None: + """ + Switch pin to output mode with given default value and drive mode + """ -@dataclass(kw_only=True) -class DigitalInputClient(DriverClient): - def wait_for_active(self, timeout: float | None = None): - self.call("wait_for_active", timeout) + match drive_mode: + case DriveMode.PUSH_PULL: + drive_mode = 0 + case DriveMode.OPEN_DRAIN: + drive_mode = 1 + case _: + raise ValueError("unrecognized drive_mode") + self.call("switch_to_output", value, drive_mode) + + def switch_to_input(self, pull: Pull | None = None) -> None: + """ + Switch pin to input mode with given pull up/down mode + """ + + match pull: + case None: + pull = 0 + case Pull.UP: + pull = 1 + case Pull.DOWN: + pull = 2 + case _: + raise ValueError("unrecognized pull") + self.call("switch_to_input", pull) + + @property + def value(self) -> bool: + """ + Current value of the pin + """ + + return self.call("get_value") - def wait_for_inactive(self, timeout: float | None = None): - self.call("wait_for_inactive", timeout) + @value.setter + def value(self, value: bool) -> None: + self.call("set_value", value) diff --git a/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/driver.py b/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/driver.py index f159d2345..aded63cbe 100644 --- a/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/driver.py +++ b/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/driver.py @@ -1,63 +1,128 @@ +from collections.abc import AsyncGenerator from dataclasses import dataclass, field +from time import sleep -from gpiozero import DigitalInputDevice, DigitalOutputDevice, InputDevice +import board +from digitalio import DigitalInOut, DriveMode, Pull +from jumpstarter_driver_power.driver import PowerInterface, PowerReading from jumpstarter.driver import Driver, export @dataclass(kw_only=True) -class DigitalOutput(Driver): - pin: int | str - device: InputDevice = field(init=False) # Start as input +class DigitalIO(Driver): + pin: str + device: DigitalInOut = field(init=False) @classmethod def client(cls) -> str: - return "jumpstarter_driver_raspberrypi.client.DigitalOutputClient" + return "jumpstarter_driver_raspberrypi.client.DigitalIOClient" def __post_init__(self): if hasattr(super(), "__post_init__"): super().__post_init__() - # Initialize as InputDevice first - self.device = InputDevice(pin=self.pin) + # Defaults to input with no pull + try: + self.device = DigitalInOut(pin=getattr(board, self.pin)) + except AttributeError as err: + raise ValueError(f"Invalid pin name: {self.pin}") from err def close(self): if hasattr(self, "device"): - self.device.close() - super().close() + self.device.deinit() @export - def off(self): - if not isinstance(self.device, DigitalOutputDevice): - self.device.close() - self.device = DigitalOutputDevice(pin=self.pin, initial_value=None) - self.device.off() + def switch_to_output(self, value: bool = False, drive_mode: int = 0) -> None: + match drive_mode: + case 0: + drive_mode = DriveMode.PUSH_PULL + case 1: + drive_mode = DriveMode.OPEN_DRAIN + case _: + raise ValueError("unrecognized drive_mode") + + self.device.switch_to_output(value, drive_mode) @export - def on(self): - if not isinstance(self.device, DigitalOutputDevice): - self.device.close() - self.device = DigitalOutputDevice(pin=self.pin, initial_value=None) - self.device.on() + def switch_to_input(self, pull: int = 0) -> None: + match pull: + case 0: + pull = None + case 1: + pull = Pull.UP + case 2: + pull = Pull.DOWN + case _: + raise ValueError("unrecognized pull") + + self.device.switch_to_input(pull) + + @export + def set_value(self, value: bool) -> None: + self.device.value = value + + @export + def get_value(self) -> bool: + return self.device.value @dataclass(kw_only=True) -class DigitalInput(Driver): - pin: int | str - device: DigitalInputDevice = field(init=False) +class DigitalPowerSwitch(PowerInterface, DigitalIO): + value: bool = False + drive_mode: str = "PUSH_PULL" - @classmethod - def client(cls) -> str: - return "jumpstarter_driver_raspberrypi.client.DigitalInputClient" + def __post_init__(self): + if hasattr(super(), "__post_init__"): + super().__post_init__() + + try: + self.device.switch_to_output(value=self.value, drive_mode=getattr(DriveMode, self.drive_mode)) + except AttributeError as err: + raise ValueError(f"Invalid drive mode: {self.drive_mode}") from err + + @export + def on(self) -> None: + self.device.value = True + + @export + def off(self) -> None: + self.device.value = False + + @export + def read(self) -> AsyncGenerator[PowerReading, None]: + raise NotImplementedError + + +@dataclass(kw_only=True) +class DigitalPowerButton(PowerInterface, DigitalIO): + value: bool = False + drive_mode: str = "OPEN_DRAIN" + on_press_seconds: int = 1 + off_press_seconds: int = 5 def __post_init__(self): if hasattr(super(), "__post_init__"): super().__post_init__() - self.device = DigitalInputDevice(pin=self.pin) + + try: + self.device.switch_to_output(value=self.value, drive_mode=getattr(DriveMode, self.drive_mode)) + except AttributeError as err: + raise ValueError(f"Invalid drive mode: {self.drive_mode}") from err + + def press(self, seconds: int) -> None: + self.device.value = self.value + self.device.value = not self.value + sleep(seconds) + self.device.value = self.value + + @export + def on(self) -> None: + self.press(self.on_press_seconds) @export - def wait_for_active(self, timeout: float | None = None): - self.device.wait_for_active(timeout) + def off(self) -> None: + self.press(self.off_press_seconds) @export - def wait_for_inactive(self, timeout: float | None = None): - self.device.wait_for_inactive(timeout) + def read(self) -> AsyncGenerator[PowerReading, None]: + raise NotImplementedError diff --git a/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/driver_test.py b/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/driver_test.py index 19cd1d3e6..24044f985 100644 --- a/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/driver_test.py +++ b/packages/jumpstarter-driver-raspberrypi/jumpstarter_driver_raspberrypi/driver_test.py @@ -1,46 +1,47 @@ -from concurrent.futures import ThreadPoolExecutor -from gpiozero import Device -from gpiozero.pins.mock import MockFactory - -from jumpstarter_driver_raspberrypi.driver import DigitalInput, DigitalOutput from jumpstarter.common.utils import serve -Device.pin_factory = MockFactory() +def test_drivers_gpio_digital_input(monkeypatch): + monkeypatch.setenv("BLINKA_OS_AGNOSTIC", "1") -def test_drivers_gpio_digital_output(): - pin_factory = MockFactory() - Device.pin_factory = pin_factory - pin_number = 1 - mock_pin = pin_factory.pin(pin_number) + from digitalio import Pull - instance = DigitalOutput(pin=pin_number) + from jumpstarter_driver_raspberrypi.driver import DigitalIO - assert not mock_pin.state + with serve(DigitalIO(pin="Dx_INPUT_TOGGLE")) as client: + client.switch_to_input(pull=Pull.UP) + assert client.value + assert not client.value + assert client.value - with serve(instance) as client: - client.off() - assert not mock_pin.state - client.on() - assert mock_pin.state +def test_drivers_gpio_digital_output(monkeypatch): + monkeypatch.setenv("BLINKA_OS_AGNOSTIC", "1") - client.off() - assert not mock_pin.state + from digitalio import DriveMode + + from jumpstarter_driver_raspberrypi.driver import DigitalIO - mock_pin.assert_states([False, True, False]) + with serve(DigitalIO(pin="Dx_OUTPUT")) as client: + client.switch_to_output(value=True, drive_mode=DriveMode.PUSH_PULL) + client.value = True + assert client.value + client.value = False + # Dx_OUTPUT is always True + assert client.value -def test_drivers_gpio_digital_input(): - instance = DigitalInput(pin=4) +def test_drivers_gpio_power(monkeypatch): + monkeypatch.setenv("BLINKA_OS_AGNOSTIC", "1") - with serve(instance) as client: - with ThreadPoolExecutor() as pool: - pool.submit(client.wait_for_active) - instance.device.pin.drive_high() + from jumpstarter_driver_raspberrypi.driver import DigitalPowerButton, DigitalPowerSwitch - with ThreadPoolExecutor() as pool: - pool.submit(client.wait_for_inactive) - instance.device.pin.drive_low() + with serve(DigitalPowerSwitch(pin="Dx_OUTPUT", drive_mode="PUSH_PULL")) as client: + client.off() + client.on() + + with serve(DigitalPowerButton(pin="Dx_OUTPUT", drive_mode="PUSH_PULL", off_press_seconds=1)) as client: + client.off() + client.on() diff --git a/packages/jumpstarter-driver-raspberrypi/pyproject.toml b/packages/jumpstarter-driver-raspberrypi/pyproject.toml index 2d2665fbb..84aff7091 100644 --- a/packages/jumpstarter-driver-raspberrypi/pyproject.toml +++ b/packages/jumpstarter-driver-raspberrypi/pyproject.toml @@ -12,7 +12,8 @@ license = { text = "Apache-2.0" } requires-python = ">=3.11" dependencies = [ "jumpstarter", - "gpiozero>=2.0.1", + "jumpstarter-driver-power", + "adafruit-blinka>=8.51.0", ] [dependency-groups] diff --git a/uv.lock b/uv.lock index 36c6fb3b1..11687601f 100644 --- a/uv.lock +++ b/uv.lock @@ -47,6 +47,94 @@ docs = [ { name = "sphinxcontrib-mermaid", specifier = ">=0.9.2" }, ] +[[package]] +name = "adafruit-blinka" +version = "8.53.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "adafruit-circuitpython-typing" }, + { name = "adafruit-platformdetect" }, + { name = "adafruit-pureio" }, + { name = "binho-host-adapter" }, + { name = "pyftdi" }, + { name = "sysv-ipc", marker = "platform_machine != 'mips' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/61/fbf8d45ad548eebc3c260414a7a1695ed3e8b69175701b6ce4dc584d4d80/adafruit_blinka-8.53.0.tar.gz", hash = "sha256:3c1a267f7045abf6ca16e31240711875e3ecabddc1f990ab58a28db1e0eb326b", size = 250664 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/83/75031ecb9645697bb6ebddda8b60a0512bdf6c0db43b5365053cd2c8654a/Adafruit_Blinka-8.53.0-py3-none-any.whl", hash = "sha256:42129af3e5cd1c6d3404c3d281d7c59b9e0a674d9fc9d98d7b79f93905e157cc", size = 364720 }, +] + +[[package]] +name = "adafruit-circuitpython-busdevice" +version = "5.2.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "adafruit-blinka" }, + { name = "adafruit-circuitpython-typing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/c0/f6347ab32f077413c20f55bc4b0f1592f35affd4d26753394c5ed6c36c4c/adafruit_circuitpython_busdevice-5.2.11.tar.gz", hash = "sha256:a9a1310bee7021703ccc247bb3ff04d0873573948a6c7bee9016361cd6707a71", size = 27627 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/c7/9f0e2b2674cb5b1fb35d067a7585a2a76596a36044264eb390980d428ccf/adafruit_circuitpython_busdevice-5.2.11-py3-none-any.whl", hash = "sha256:d4379c9ae86a15f7044dea815a94525ca9eda6a7c0b2fa0e75cf9e700c9384b8", size = 7539 }, +] + +[[package]] +name = "adafruit-circuitpython-connectionmanager" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "adafruit-blinka" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/8b/8316002905f97a7f7e9c3a53dd9bb5a17889033ec55c403a6e55077f6298/adafruit_circuitpython_connectionmanager-3.1.3.tar.gz", hash = "sha256:0f133bdedf454ede0c0a866ed605fe166cc85f75cfcea74758e3622ae403e5f9", size = 37381 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/7d/896b31bd31eff89e5cab5d3acec9d3d34f5a0654ceab25e01865e628d9f9/adafruit_circuitpython_connectionmanager-3.1.3-py3-none-any.whl", hash = "sha256:9df3a4c617dae27bad1ac8607f1a084312c8498d831ebe1c6a2c8d5cb309daea", size = 7811 }, +] + +[[package]] +name = "adafruit-circuitpython-requests" +version = "4.1.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "adafruit-blinka" }, + { name = "adafruit-circuitpython-connectionmanager" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/45/070129e6b77f801514cd974524c6b9fd502aa04dd5c2e45ab03e85c96cac/adafruit_circuitpython_requests-4.1.9.tar.gz", hash = "sha256:b9eeb252b43946f1a90c34ca8844e07bb1e01cd210c927f561d0e10b97c5ff9d", size = 66232 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/40/ff356fd61ef3ea044b7944687d62419c304217381ee20d9fa444aeb98339/adafruit_circuitpython_requests-4.1.9-py3-none-any.whl", hash = "sha256:d0f0a899c6ef143eab9a50a9625be43f5f8da7b9688c1496891999fa20107c93", size = 10721 }, +] + +[[package]] +name = "adafruit-circuitpython-typing" +version = "1.11.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "adafruit-blinka" }, + { name = "adafruit-circuitpython-busdevice" }, + { name = "adafruit-circuitpython-requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/24/80/8c280fa7d42a23dce40b2fe64f708d18fa32b384adbf6934955d2c2ebecf/adafruit_circuitpython_typing-1.11.2.tar.gz", hash = "sha256:c7ac8532a9ad7e4a65d5588764b7483c0b6967d305c37faebcc0c5356d677e33", size = 29277 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/d5/76a6bca9cf08907b48dfc8ccbccbd190155353f521876e02d6b7bb244003/adafruit_circuitpython_typing-1.11.2-py3-none-any.whl", hash = "sha256:e1401a09bbfdf67e43875cc6755b3af0eda8381b12c9c8f759bd7676b7425e1c", size = 11101 }, +] + +[[package]] +name = "adafruit-platformdetect" +version = "3.77.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/4e/2b2ca031227de47e2aab6cf092b78934c9c0033a685075ddab3e0c0b55fe/adafruit_platformdetect-3.77.0.tar.gz", hash = "sha256:adce6386059637e92b4cb5d3430d016119cd3eb19f9276920c54515f3d798949", size = 48024 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/18/b18e9ff2aee42f03082675c5d18d4eb02411477e07c86d74833d3396792e/Adafruit_PlatformDetect-3.77.0-py3-none-any.whl", hash = "sha256:93f599c21e7db2d92bc32ac69ba5063876404c4af87d11358863e62f407409be", size = 25542 }, +] + +[[package]] +name = "adafruit-pureio" +version = "1.1.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/b7/f1672435116822079bbdab42163f9e6424769b7db778873d95d18c085230/Adafruit_PureIO-1.1.11.tar.gz", hash = "sha256:c4cfbb365731942d1f1092a116f47dfdae0aef18c5b27f1072b5824ad5ea8c7c", size = 35511 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/9d/28e9d12f36e13c5f2acba3098187b0e931290ecd1d8df924391b5ad2db19/Adafruit_PureIO-1.1.11-py3-none-any.whl", hash = "sha256:281ab2099372cc0decc26326918996cbf21b8eed694ec4764d51eefa029d324e", size = 10678 }, +] + [[package]] name = "aiofiles" version = "24.1.0" @@ -181,11 +269,11 @@ wheels = [ [[package]] name = "babel" -version = "2.16.0" +version = "2.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, ] [[package]] @@ -229,14 +317,27 @@ wheels = [ [[package]] name = "beautifulsoup4" -version = "4.12.3" +version = "4.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 } +sdist = { url = "https://files.pythonhosted.org/packages/58/f3/d90227cc52f7b8fcd0f2af804f56e55edf8dd07036b681a2809e3245318b/beautifulsoup4-4.13.1.tar.gz", hash = "sha256:741c8b6903a1e4ae8ba32b9c9ae7510dab7a197fdbadcf9fcdeb0891ef5ec66a", size = 618295 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 }, + { url = "https://files.pythonhosted.org/packages/18/75/899bf9b6270b2ce5e8f01b8da121b29e4b88256feb2cf6c6418d4cc42130/beautifulsoup4-4.13.1-py3-none-any.whl", hash = "sha256:72465267014897bb10ca749bb632bde6c2d20f3254afd5458544bd74e6c2e6d8", size = 185056 }, +] + +[[package]] +name = "binho-host-adapter" +version = "0.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyserial" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/36/29b7b896e83e195fac6d64ccff95c0f24a18ee86e7437a22e60e0331d90a/binho-host-adapter-0.1.6.tar.gz", hash = "sha256:1e6da7a84e208c13b5f489066f05774bff1d593d0f5bf1ca149c2b8e83eae856", size = 10068 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/6b/0f13486003aea3eb349c2946b7ec9753e7558b78e35d22c938062a96959c/binho_host_adapter-0.1.6-py3-none-any.whl", hash = "sha256:f71ca176c1e2fc1a5dce128beb286da217555c6c7c805f2ed282a6f3507ec277", size = 10540 }, ] [[package]] @@ -271,11 +372,11 @@ wheels = [ [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.1.31" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, ] [[package]] @@ -376,18 +477,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] -[[package]] -name = "colorzero" -version = "2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/688824a06e8c4d04c7d2fd2af2d8da27bed51af20ee5f094154e1d680334/colorzero-2.0.tar.gz", hash = "sha256:e7d5a5c26cd0dc37b164ebefc609f388de24f8593b659191e12d85f8f9d5eb58", size = 25382 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/a6/ddd0f130e44a7593ac6c55aa93f6e256d2270fd88e9d1b64ab7f22ab8fde/colorzero-2.0-py2.py3-none-any.whl", hash = "sha256:0e60d743a6b8071498a56465f7719c96a5e92928f858bab1be2a0d606c9aa0f8", size = 26573 }, -] - [[package]] name = "coverage" version = "7.6.10" @@ -612,18 +701,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9d/47/603554949a37bca5b7f894d51896a9c534b9eab808e2520a748e081669d0/google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a", size = 210770 }, ] -[[package]] -name = "gpiozero" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorzero" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e4/47/334b8db8a981eca9a0fb1e7e48e1997a5eaa8f40bb31c504299dcca0e6ff/gpiozero-2.0.1.tar.gz", hash = "sha256:d4ea1952689ec7e331f9d4ebc9adb15f1d01c2c9dcfabb72e752c9869ab7e97e", size = 136176 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/eb/6518a1b00488d48995034226846653c382d676cf5f04be62b3c3fae2c6a1/gpiozero-2.0.1-py3-none-any.whl", hash = "sha256:8f621de357171d574c0b7ea0e358cb66e560818a47b0eeedf41ce1cdbd20c70b", size = 150818 }, -] - [[package]] name = "grpcio" version = "1.70.0" @@ -679,7 +756,7 @@ wheels = [ [[package]] name = "imagehash" -version = "4.3.1" +version = "4.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, @@ -687,9 +764,9 @@ dependencies = [ { name = "pywavelets" }, { name = "scipy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6c/f4/9821fe373a4788bca43f00491b008f930de0b12a60ff631852d1f984b966/ImageHash-4.3.1.tar.gz", hash = "sha256:7038d1b7f9e0585beb3dd8c0a956f02b95a346c0b5f24a9e8cc03ebadaf0aa70", size = 296989 } +sdist = { url = "https://files.pythonhosted.org/packages/cd/de/5c0189b0582e21583c2a213081c35a2501c0f9e51f21f6a52f55fbb9a4ff/ImageHash-4.3.2.tar.gz", hash = "sha256:e54a79805afb82a34acde4746a16540503a9636fd1ffb31d8e099b29bbbf8156", size = 303190 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/b4/19a746a986c6e38595fa5947c028b1b8e287773dcad766e648897ad2a4cf/ImageHash-4.3.1-py2.py3-none-any.whl", hash = "sha256:5ad9a5cde14fe255745a8245677293ac0d67f09c330986a351f34b614ba62fb5", size = 296543 }, + { url = "https://files.pythonhosted.org/packages/31/2c/5f0903a53a62029875aaa3884c38070cc388248a2c1b9aa935632669e5a7/ImageHash-4.3.2-py2.py3-none-any.whl", hash = "sha256:02b0f965f8c77cd813f61d7d39031ea27d4780e7ebcad56c6cd6a709acc06e5f", size = 296657 }, ] [[package]] @@ -733,6 +810,7 @@ wheels = [ [[package]] name = "jumpstarter" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter" } dependencies = [ { name = "aiohttp" }, @@ -779,6 +857,7 @@ dev = [ [[package]] name = "jumpstarter-cli" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-cli" } dependencies = [ { name = "jumpstarter-cli-admin" }, @@ -811,6 +890,7 @@ dev = [ [[package]] name = "jumpstarter-cli-admin" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-cli-admin" } dependencies = [ { name = "jumpstarter-cli-common" }, @@ -841,6 +921,7 @@ dev = [ [[package]] name = "jumpstarter-cli-client" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-cli-client" } dependencies = [ { name = "jumpstarter-cli-common" }, @@ -867,6 +948,7 @@ dev = [ [[package]] name = "jumpstarter-cli-common" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-cli-common" } dependencies = [ { name = "asyncclick" }, @@ -897,6 +979,7 @@ dev = [ [[package]] name = "jumpstarter-cli-exporter" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-cli-exporter" } dependencies = [ { name = "asyncclick" }, @@ -927,6 +1010,7 @@ dev = [ [[package]] name = "jumpstarter-driver-can" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-driver-can" } dependencies = [ { name = "can-isotp" }, @@ -955,6 +1039,7 @@ dev = [ [[package]] name = "jumpstarter-driver-composite" +version = "0.5.1.dev168+gded89ae.d20250129" source = { editable = "packages/jumpstarter-driver-composite" } dependencies = [ { name = "asyncclick" }, @@ -983,6 +1068,7 @@ dev = [ [[package]] name = "jumpstarter-driver-dutlink" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-driver-dutlink" } dependencies = [ { name = "asyncclick" }, @@ -1055,6 +1141,7 @@ dev = [ [[package]] name = "jumpstarter-driver-network" +version = "0.5.1.dev168+gded89ae.d20250129" source = { editable = "packages/jumpstarter-driver-network" } dependencies = [ { name = "fabric" }, @@ -1087,6 +1174,7 @@ dev = [ [[package]] name = "jumpstarter-driver-opendal" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-driver-opendal" } dependencies = [ { name = "asyncclick" }, @@ -1115,6 +1203,7 @@ dev = [ [[package]] name = "jumpstarter-driver-power" +version = "0.5.1.dev168+gded89ae.d20250129" source = { editable = "packages/jumpstarter-driver-power" } dependencies = [ { name = "asyncclick" }, @@ -1145,6 +1234,7 @@ dev = [ [[package]] name = "jumpstarter-driver-pyserial" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-driver-pyserial" } dependencies = [ { name = "asyncclick" }, @@ -1175,10 +1265,12 @@ dev = [ [[package]] name = "jumpstarter-driver-raspberrypi" +version = "0.5.1.dev183+g1382071.d20250203" source = { editable = "packages/jumpstarter-driver-raspberrypi" } dependencies = [ - { name = "gpiozero" }, + { name = "adafruit-blinka" }, { name = "jumpstarter" }, + { name = "jumpstarter-driver-power" }, ] [package.dev-dependencies] @@ -1189,8 +1281,9 @@ dev = [ [package.metadata] requires-dist = [ - { name = "gpiozero", specifier = ">=2.0.1" }, + { name = "adafruit-blinka", specifier = ">=8.51.0" }, { name = "jumpstarter", editable = "packages/jumpstarter" }, + { name = "jumpstarter-driver-power", editable = "packages/jumpstarter-driver-power" }, ] [package.metadata.requires-dev] @@ -1201,6 +1294,7 @@ dev = [ [[package]] name = "jumpstarter-driver-sdwire" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-driver-sdwire" } dependencies = [ { name = "jumpstarter" }, @@ -1268,6 +1362,7 @@ dev = [ [[package]] name = "jumpstarter-driver-ustreamer" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-driver-ustreamer" } dependencies = [ { name = "jumpstarter" }, @@ -1328,6 +1423,7 @@ requires-dist = [ [[package]] name = "jumpstarter-imagehash" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-imagehash" } dependencies = [ { name = "imagehash" }, @@ -1354,6 +1450,7 @@ dev = [ [[package]] name = "jumpstarter-kubernetes" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-kubernetes" } dependencies = [ { name = "jumpstarter" }, @@ -1386,6 +1483,7 @@ dev = [ [[package]] name = "jumpstarter-protocol" +version = "0.5.1.dev168+gded89ae" source = { editable = "packages/jumpstarter-protocol" } dependencies = [ { name = "grpcio" }, @@ -1416,6 +1514,7 @@ dev = [ [[package]] name = "jumpstarter-testing" +version = "0.5.1.dev168+gded89ae.d20250129" source = { editable = "packages/jumpstarter-testing" } dependencies = [ { name = "jumpstarter" }, @@ -1986,6 +2085,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, ] +[[package]] +name = "pyftdi" +version = "0.56.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyserial" }, + { name = "pyusb" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/96/a8de7b7e5556d4b00d1ca1969fc34c89a1b6d177876c7a31d42631b090fc/pyftdi-0.56.0-py3-none-any.whl", hash = "sha256:3ef0baadbf9031dde9d623ae66fac2d16ded36ce1b66c17765ca1944cb38b8b0", size = 145718 }, +] + [[package]] name = "pygls" version = "1.3.1" @@ -2076,14 +2187,14 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "0.25.2" +version = "0.25.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/df/adcc0d60f1053d74717d21d58c0048479e9cab51464ce0d2965b086bd0e2/pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f", size = 53950 } +sdist = { url = "https://files.pythonhosted.org/packages/f2/a8/ecbc8ede70921dd2f544ab1cadd3ff3bf842af27f87bbdea774c7baa1d38/pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a", size = 54239 } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/d8/defa05ae50dcd6019a95527200d3b3980043df5aa445d40cb0ef9f7f98ab/pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075", size = 19400 }, + { url = "https://files.pythonhosted.org/packages/67/17/3493c5624e48fd97156ebaec380dcaafee9506d7e2c46218ceebbb57d7de/pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3", size = 19467 }, ] [[package]] @@ -2244,27 +2355,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.9.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/7f/60fda2eec81f23f8aa7cbbfdf6ec2ca11eb11c273827933fb2541c2ce9d8/ruff-0.9.3.tar.gz", hash = "sha256:8293f89985a090ebc3ed1064df31f3b4b56320cdfcec8b60d3295bddb955c22a", size = 3586740 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/77/4fb790596d5d52c87fd55b7160c557c400e90f6116a56d82d76e95d9374a/ruff-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624", size = 11656815 }, - { url = "https://files.pythonhosted.org/packages/a2/a8/3338ecb97573eafe74505f28431df3842c1933c5f8eae615427c1de32858/ruff-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c", size = 11594821 }, - { url = "https://files.pythonhosted.org/packages/8e/89/320223c3421962762531a6b2dd58579b858ca9916fb2674874df5e97d628/ruff-0.9.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c59ab92f8e92d6725b7ded9d4a31be3ef42688a115c6d3da9457a5bda140e2b4", size = 11040475 }, - { url = "https://files.pythonhosted.org/packages/b2/bd/1d775eac5e51409535804a3a888a9623e87a8f4b53e2491580858a083692/ruff-0.9.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc153c25e715be41bb228bc651c1e9b1a88d5c6e5ed0194fa0dfea02b026439", size = 11856207 }, - { url = "https://files.pythonhosted.org/packages/7f/c6/3e14e09be29587393d188454064a4aa85174910d16644051a80444e4fd88/ruff-0.9.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:646909a1e25e0dc28fbc529eab8eb7bb583079628e8cbe738192853dbbe43af5", size = 11420460 }, - { url = "https://files.pythonhosted.org/packages/ef/42/b7ca38ffd568ae9b128a2fa76353e9a9a3c80ef19746408d4ce99217ecc1/ruff-0.9.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a5a46e09355695fbdbb30ed9889d6cf1c61b77b700a9fafc21b41f097bfbba4", size = 12605472 }, - { url = "https://files.pythonhosted.org/packages/a6/a1/3167023f23e3530fde899497ccfe239e4523854cb874458ac082992d206c/ruff-0.9.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c4bb09d2bbb394e3730d0918c00276e79b2de70ec2a5231cd4ebb51a57df9ba1", size = 13243123 }, - { url = "https://files.pythonhosted.org/packages/d0/b4/3c600758e320f5bf7de16858502e849f4216cb0151f819fa0d1154874802/ruff-0.9.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96a87ec31dc1044d8c2da2ebbed1c456d9b561e7d087734336518181b26b3aa5", size = 12744650 }, - { url = "https://files.pythonhosted.org/packages/be/38/266fbcbb3d0088862c9bafa8b1b99486691d2945a90b9a7316336a0d9a1b/ruff-0.9.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb7554aca6f842645022fe2d301c264e6925baa708b392867b7a62645304df4", size = 14458585 }, - { url = "https://files.pythonhosted.org/packages/63/a6/47fd0e96990ee9b7a4abda62de26d291bd3f7647218d05b7d6d38af47c30/ruff-0.9.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabc332b7075a914ecea912cd1f3d4370489c8018f2c945a30bcc934e3bc06a6", size = 12419624 }, - { url = "https://files.pythonhosted.org/packages/84/5d/de0b7652e09f7dda49e1a3825a164a65f4998175b6486603c7601279baad/ruff-0.9.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:33866c3cc2a575cbd546f2cd02bdd466fed65118e4365ee538a3deffd6fcb730", size = 11843238 }, - { url = "https://files.pythonhosted.org/packages/9e/be/3f341ceb1c62b565ec1fb6fd2139cc40b60ae6eff4b6fb8f94b1bb37c7a9/ruff-0.9.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:006e5de2621304c8810bcd2ee101587712fa93b4f955ed0985907a36c427e0c2", size = 11484012 }, - { url = "https://files.pythonhosted.org/packages/a3/c8/ff8acbd33addc7e797e702cf00bfde352ab469723720c5607b964491d5cf/ruff-0.9.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ba6eea4459dbd6b1be4e6bfc766079fb9b8dd2e5a35aff6baee4d9b1514ea519", size = 12038494 }, - { url = "https://files.pythonhosted.org/packages/73/b1/8d9a2c0efbbabe848b55f877bc10c5001a37ab10aca13c711431673414e5/ruff-0.9.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90230a6b8055ad47d3325e9ee8f8a9ae7e273078a66401ac66df68943ced029b", size = 12473639 }, - { url = "https://files.pythonhosted.org/packages/cb/44/a673647105b1ba6da9824a928634fe23186ab19f9d526d7bdf278cd27bc3/ruff-0.9.3-py3-none-win32.whl", hash = "sha256:eabe5eb2c19a42f4808c03b82bd313fc84d4e395133fb3fc1b1516170a31213c", size = 9834353 }, - { url = "https://files.pythonhosted.org/packages/c3/01/65cadb59bf8d4fbe33d1a750103e6883d9ef302f60c28b73b773092fbde5/ruff-0.9.3-py3-none-win_amd64.whl", hash = "sha256:040ceb7f20791dfa0e78b4230ee9dce23da3b64dd5848e40e3bf3ab76468dcf4", size = 10821444 }, - { url = "https://files.pythonhosted.org/packages/69/cb/b3fe58a136a27d981911cba2f18e4b29f15010623b79f0f2510fd0d31fd3/ruff-0.9.3-py3-none-win_arm64.whl", hash = "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b", size = 10038168 }, +version = "0.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/63/77ecca9d21177600f551d1c58ab0e5a0b260940ea7312195bd2a4798f8a8/ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0", size = 3553799 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/b9/0e168e4e7fb3af851f739e8f07889b91d1a33a30fca8c29fa3149d6b03ec/ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347", size = 11652408 }, + { url = "https://files.pythonhosted.org/packages/2c/22/08ede5db17cf701372a461d1cb8fdde037da1d4fa622b69ac21960e6237e/ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00", size = 11587553 }, + { url = "https://files.pythonhosted.org/packages/42/05/dedfc70f0bf010230229e33dec6e7b2235b2a1b8cbb2a991c710743e343f/ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4", size = 11020755 }, + { url = "https://files.pythonhosted.org/packages/df/9b/65d87ad9b2e3def67342830bd1af98803af731243da1255537ddb8f22209/ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d", size = 11826502 }, + { url = "https://files.pythonhosted.org/packages/93/02/f2239f56786479e1a89c3da9bc9391120057fc6f4a8266a5b091314e72ce/ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c", size = 11390562 }, + { url = "https://files.pythonhosted.org/packages/c9/37/d3a854dba9931f8cb1b2a19509bfe59e00875f48ade632e95aefcb7a0aee/ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f", size = 12548968 }, + { url = "https://files.pythonhosted.org/packages/fa/c3/c7b812bb256c7a1d5553433e95980934ffa85396d332401f6b391d3c4569/ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684", size = 13187155 }, + { url = "https://files.pythonhosted.org/packages/bd/5a/3c7f9696a7875522b66aa9bba9e326e4e5894b4366bd1dc32aa6791cb1ff/ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d", size = 12704674 }, + { url = "https://files.pythonhosted.org/packages/be/d6/d908762257a96ce5912187ae9ae86792e677ca4f3dc973b71e7508ff6282/ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df", size = 14529328 }, + { url = "https://files.pythonhosted.org/packages/2d/c2/049f1e6755d12d9cd8823242fa105968f34ee4c669d04cac8cea51a50407/ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247", size = 12385955 }, + { url = "https://files.pythonhosted.org/packages/91/5a/a9bdb50e39810bd9627074e42743b00e6dc4009d42ae9f9351bc3dbc28e7/ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e", size = 11810149 }, + { url = "https://files.pythonhosted.org/packages/e5/fd/57df1a0543182f79a1236e82a79c68ce210efb00e97c30657d5bdb12b478/ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe", size = 11479141 }, + { url = "https://files.pythonhosted.org/packages/dc/16/bc3fd1d38974f6775fc152a0554f8c210ff80f2764b43777163c3c45d61b/ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb", size = 12014073 }, + { url = "https://files.pythonhosted.org/packages/47/6b/e4ca048a8f2047eb652e1e8c755f384d1b7944f69ed69066a37acd4118b0/ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a", size = 12435758 }, + { url = "https://files.pythonhosted.org/packages/c2/40/4d3d6c979c67ba24cf183d29f706051a53c36d78358036a9cd21421582ab/ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145", size = 9796916 }, + { url = "https://files.pythonhosted.org/packages/c3/ef/7f548752bdb6867e6939489c87fe4da489ab36191525fadc5cede2a6e8e2/ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5", size = 10773080 }, + { url = "https://files.pythonhosted.org/packages/0e/4e/33df635528292bd2d18404e4daabcd74ca8a9853b2e1df85ed3d32d24362/ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6", size = 10001738 }, ] [[package]] @@ -2301,15 +2412,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e4/1f/5d46a8d94e9f6d2c913cbb109e57e7eed914de38ea99e2c4d69a9fc93140/scipy-1.15.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bc7136626261ac1ed988dca56cfc4ab5180f75e0ee52e58f1e6aa74b5f3eacd5", size = 43181730 }, ] -[[package]] -name = "setuptools" -version = "75.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 }, -] - [[package]] name = "six" version = "1.17.0" @@ -2518,6 +2620,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 }, ] +[[package]] +name = "sysv-ipc" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/d7/5d2f861155e9749f981e6c58f2a482d3ab458bf8c35ae24d4b4d5899ebf9/sysv_ipc-1.1.0.tar.gz", hash = "sha256:0f063cbd36ec232032e425769ebc871f195a7d183b9af32f9901589ea7129ac3", size = 99448 } + [[package]] name = "tqdm" version = "4.67.1" @@ -2558,19 +2666,19 @@ wheels = [ [[package]] name = "typos" -version = "1.29.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/17/bc/58050dbc50c2effc0a0a00f49eaeb0779c595b415b4d1544dafcac99394c/typos-1.29.4.tar.gz", hash = "sha256:590d776448d634e2437a326ff7f2218d53439f2d4e478337bb17ff627caaa2cd", size = 1484737 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4e/5fcc4777326b2b12217233f51339e42d5f71bfd739a3c357fd2bc938181e/typos-1.29.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3825b4b6f6deec99b78c998ae5d1681334920a0519fe6ae4a3fdc6c041834728", size = 3057434 }, - { url = "https://files.pythonhosted.org/packages/d7/a5/5bf0f007476b13ffc2a99a81edec6baee6720381927765224a25449a4dec/typos-1.29.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f7a318f65bbea371f24ea3f5ab1ba1953d5f5ad7c1115582b4f6992ed79dcfcb", size = 2931341 }, - { url = "https://files.pythonhosted.org/packages/11/35/6bd1009dca9297063d96b7c1e29d66bea5f8c1e5aad64a33c7dd67f208c3/typos-1.29.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5e3aedf4d99055b27b70f472abb694cc420d03b003e71ab8877acf2a24a945e", size = 4271641 }, - { url = "https://files.pythonhosted.org/packages/77/2b/6458d18b2a9882d0ed0229ba7cc2197476a996a05589d151ad600a91cccb/typos-1.29.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59b134bad893e0611a0c0212750c0e49503552b600033b8c2c5cc2b8222e75de", size = 3417769 }, - { url = "https://files.pythonhosted.org/packages/bb/2e/8fbcde73fb4c6c826bd5d2c39d16d883915240cbdc693f5d99191574e11b/typos-1.29.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7dac616eacae037e71e6c1089b82dc296c1a5b4621b132eba5c79b7044b1a74", size = 4124138 }, - { url = "https://files.pythonhosted.org/packages/29/c6/6d66f554b4425f1bdd2220dbe5626e80db814ac4ac2a635e5c72ba75ec61/typos-1.29.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fdf9d2550796d36120e464f6dac4e525d397204728dc87be0c749f4a3746ce37", size = 3356751 }, - { url = "https://files.pythonhosted.org/packages/b2/0c/ff2dac645ec184518aeb78585b5e97d096f28278c78db762f78c81cba95a/typos-1.29.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:564a39c4803d2285c9756069ca0a4490e4ccecedeea568b085421cd9e035a865", size = 4180122 }, - { url = "https://files.pythonhosted.org/packages/2d/df/46177b00c22183bd4b0f9e10adbf2cb74c025ddac55f574b2f2045e09d7b/typos-1.29.4-py3-none-win32.whl", hash = "sha256:f66f292fe88c2ffe537373654c5e157a930f4ba302400916e0b9d802ad6e5932", size = 2735883 }, - { url = "https://files.pythonhosted.org/packages/90/54/063bf78200373b0ad7ab291fbb79a845e17df1a4cac9a58c8bd2dfdc4491/typos-1.29.4-py3-none-win_amd64.whl", hash = "sha256:937cd4ef6576f1d5a5f5473ea9ac789b332fb62bee7f6e4f6cfb1cfdd2e9e618", size = 2883074 }, +version = "1.29.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/8d/d36d0ff090606b41b3472049734b2c15d8a1a95f9a7e997df26b54e14444/typos-1.29.5.tar.gz", hash = "sha256:313bb0636159f976ba9039901be7fe8e763b02aba3d10e26c2e59a3f02da36b2", size = 1484309 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/bf/3b41bae5b096365ecc4d8fbc0eaf09df2f81965678ca1aadf878c46293c7/typos-1.29.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:489613574767922ce5db771887d58711890e30d13707e6be1419acbc71a526d0", size = 3112847 }, + { url = "https://files.pythonhosted.org/packages/d9/d0/c274a853d40ff3f81d58da2e382fe0ec06e52be117cec9c6d2982c83cd2b/typos-1.29.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5a958af074cc618f123fd3248c57d228823ca0d7bdb247bd5498c357b60ac194", size = 2989343 }, + { url = "https://files.pythonhosted.org/packages/cb/f3/1ff4cf284d276a89fbe6797bd72f8411cfe4c713dee0bcaf3377aac3cc51/typos-1.29.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:728cf9d051eed593e22ee52692c369011bb2c594f0ceb79ad317043c0b06fdde", size = 4285699 }, + { url = "https://files.pythonhosted.org/packages/fa/c5/4b4b7ad7ae59ed177898d2db31012098261dbb9acf50f6a68a6351a61e45/typos-1.29.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b672bdb041d8d1eb9a512446768d057bfac17ab498500903248d971d4e9a2db9", size = 3426871 }, + { url = "https://files.pythonhosted.org/packages/20/d6/c7afa17b1bd7bb9ebd707d903860eee49721a5dd0daacd02344dc6517ee2/typos-1.29.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77182dfece380f095458ac867d0f35c2aab038c55d773d434f35d049a29401b5", size = 4143657 }, + { url = "https://files.pythonhosted.org/packages/73/79/a19cdc08e1686dc8d576e217296c8df16e2c30b25393d25fc5cd5a06aed8/typos-1.29.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4181b651b7659d386bfa3bfdb0f2d865f27d2f3805b21b7db92141cdd72e030e", size = 3353559 }, + { url = "https://files.pythonhosted.org/packages/6b/35/fef92b59dc68120c870ba0eb71fe9fd4215b841dd7349b8a47f69659ca9d/typos-1.29.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2f7ed324feb5694d64defdbb532bd66731e45fbb8553c81c0d5dd8e9eb8543be", size = 4177205 }, + { url = "https://files.pythonhosted.org/packages/92/55/66a881a449268d562b57e8ab8617f3a3824549c1cbd2723aab8fb3ff5893/typos-1.29.5-py3-none-win32.whl", hash = "sha256:dc382d6afb9b01f25df63e845df96fee232b958b14083de7a5ee721e1ebf1a1c", size = 2740654 }, + { url = "https://files.pythonhosted.org/packages/53/e0/7c33a98dc2cceebbb51224ef2408c6be653c96bced5858a699c211324055/typos-1.29.5-py3-none-win_amd64.whl", hash = "sha256:ee7081dca0d0e046121b67a7a5113e2d7cad069a13e489682730cd07fc1020a8", size = 2890452 }, ] [[package]]