Target the Python that Raspberry Pi OS ships
Requiring 3.13 meant the device needed an interpreter the distribution does not
have, which is what dragged uv in, and uv then had to be matched to the Pi's
32-bit userland by hand and to build Pillow from source because no armv7 wheel
exists for a 3.13 ABI. Dropping to 3.11 removes all of that: apt provides the
interpreter and piwheels has prebuilt armhf wheels for the native dependencies.
The 3.13-only syntax was shallow - PEP 695 throughout, which converts back
mechanically:
type X = Y -> X: TypeAlias = Y
type Handler[E: Event] -> E = TypeVar("E", bound=Event) plus a plain alias,
which is generic anyway because it carries a TypeVar
def f[T: Bound](...) -> a module-level TypeVar
Also drop the one @override (3.12, and static-only), and stop the lirc test
harness calling Server.close_clients(), which is 3.13: the scripted handler now
releases its connection when asked, which is what that call was there to force.
Verified on 3.11.14 - 485 passed, mypy strict clean - and still 496 passed on
the 3.14 dev venv, which additionally has the analysis extra.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -70,6 +70,7 @@ class ScriptedLircd:
|
||||
def __init__(self) -> None:
|
||||
self._writer: asyncio.StreamWriter | None = None
|
||||
self._connected = asyncio.Event()
|
||||
self._closing = asyncio.Event()
|
||||
self.connection_count = 0
|
||||
|
||||
async def _handle(self, _reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
|
||||
@@ -77,7 +78,14 @@ class ScriptedLircd:
|
||||
self.connection_count += 1
|
||||
self._connected.set()
|
||||
with contextlib.suppress(asyncio.CancelledError):
|
||||
await asyncio.Event().wait() # held open until the test drops it
|
||||
await self._closing.wait() # held open until the test drops it
|
||||
writer.close()
|
||||
with contextlib.suppress(OSError, asyncio.CancelledError):
|
||||
await writer.wait_closed()
|
||||
|
||||
def shutdown(self) -> None:
|
||||
"""Let go of every connection still being held open, so the server can close."""
|
||||
self._closing.set()
|
||||
|
||||
async def wait_connected(self) -> None:
|
||||
await self._connected.wait()
|
||||
@@ -108,8 +116,10 @@ async def lircd() -> AsyncIterator[tuple[ScriptedLircd, asyncio.base_events.Serv
|
||||
finally:
|
||||
server.close()
|
||||
# The scripted connection handler holds its connection open forever (it does
|
||||
# not know the test is done), so `wait_closed()` alone would hang here.
|
||||
server.close_clients()
|
||||
# not know the test is done), so `wait_closed()` alone would hang here. 3.13
|
||||
# has Server.close_clients() for exactly this; ask the handlers to let go
|
||||
# instead, because the device runs the Pi's Python 3.11.
|
||||
station.shutdown()
|
||||
await server.wait_closed()
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import AsyncIterator
|
||||
from typing import TypeVar
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -58,7 +59,10 @@ def seen(bus: EventBus) -> list[Event]:
|
||||
return events
|
||||
|
||||
|
||||
def only[T: Event](events: list[Event], event_type: type[T]) -> list[T]:
|
||||
T = TypeVar("T", bound=Event)
|
||||
|
||||
|
||||
def only(events: list[Event], event_type: type[T]) -> list[T]:
|
||||
return [e for e in events if isinstance(e, event_type)]
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from pathlib import Path
|
||||
from typing import TypeVar
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -55,7 +56,10 @@ def seen(bus: EventBus) -> list[Event]:
|
||||
return events
|
||||
|
||||
|
||||
def only[T: Event](events: list[Event], event_type: type[T]) -> list[T]:
|
||||
T = TypeVar("T", bound=Event)
|
||||
|
||||
|
||||
def only(events: list[Event], event_type: type[T]) -> list[T]:
|
||||
return [e for e in events if isinstance(e, event_type)]
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from pathlib import Path
|
||||
from typing import TypeVar
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -49,7 +50,10 @@ def seen(sim: Simulation) -> list[Event]:
|
||||
return events
|
||||
|
||||
|
||||
def only[T: Event](events: list[Event], event_type: type[T]) -> list[T]:
|
||||
T = TypeVar("T", bound=Event)
|
||||
|
||||
|
||||
def only(events: list[Event], event_type: type[T]) -> list[T]:
|
||||
return [e for e in events if isinstance(e, event_type)]
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import contextlib
|
||||
import json
|
||||
from collections.abc import AsyncIterator
|
||||
from pathlib import Path
|
||||
from typing import TypeAlias
|
||||
|
||||
import httpx2
|
||||
import pytest
|
||||
@@ -27,7 +28,7 @@ from tests.websocket_harness import WebSocketSession, websocket_connect
|
||||
TRACK_SECONDS = 10.0
|
||||
|
||||
#: Shorthand: every test takes the same client type.
|
||||
type Client = httpx2.AsyncClient
|
||||
Client: TypeAlias = httpx2.AsyncClient
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
Reference in New Issue
Block a user