diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index 8a2853497..a3f69cecf 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -5,7 +5,7 @@ name: Latest release env: CACHE_VERSION: 21 - DEFAULT_PYTHON: "3.12" + DEFAULT_PYTHON: "3.13" # Only run on merges on: diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 7a0c85406..d7a847135 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -5,7 +5,7 @@ name: Latest commit env: CACHE_VERSION: 7 - DEFAULT_PYTHON: "3.12" + DEFAULT_PYTHON: "3.13" PRE_COMMIT_HOME: ~/.cache/pre-commit on: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bbec947dc..40975393e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ ci: default_language_version: # force all unspecified python hooks to run python3 - python: python3.12 + python: python3.13 repos: # Run manually in CI skipping the branch checks @@ -28,18 +28,18 @@ repos: args: - --branch=main - repo: https://github.com/asottile/pyupgrade - rev: v3.19.0 + rev: v3.19.1 hooks: - id: pyupgrade args: [--py39-plus] # Moved codespell configuration to setup.cfg as per 'all-files' issues not reading args - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.1 hooks: - id: codespell exclude_types: [csv, json] - repo: https://github.com/PyCQA/bandit - rev: 1.8.0 + rev: 1.8.2 hooks: - id: bandit args: @@ -52,7 +52,7 @@ repos: hooks: - id: yamllint - repo: https://github.com/biomejs/pre-commit - rev: v0.5.0 + rev: v0.6.1 hooks: - id: biome-lint additional_dependencies: ["@biomejs/biome@1.8.3"] @@ -102,6 +102,6 @@ repos: language: script pass_filenames: false - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.43.0 + rev: v0.44.0 hooks: - id: markdownlint diff --git a/plugwise_usb/connection/queue.py b/plugwise_usb/connection/queue.py index 29d1f74d1..4aa075a9f 100644 --- a/plugwise_usb/connection/queue.py +++ b/plugwise_usb/connection/queue.py @@ -137,9 +137,10 @@ async def _send_queue_worker(self) -> None: self._submit_queue.task_done() return - while self._stick.queue_depth > 3: - _LOGGER.info("Awaiting plugwise responses %d", self._stick.queue_depth) + if self._stick.queue_depth > 3: await sleep(0.125) + if self._stick.queue_depth > 3: + _LOGGER.warning("Awaiting plugwise responses %d", self._stick.queue_depth) await self._stick.write_to_stick(request) self._submit_queue.task_done() diff --git a/plugwise_usb/connection/receiver.py b/plugwise_usb/connection/receiver.py index 950bc70f3..7ea263038 100644 --- a/plugwise_usb/connection/receiver.py +++ b/plugwise_usb/connection/receiver.py @@ -166,10 +166,9 @@ def connection_made(self, transport: SerialTransport) -> None: async def close(self) -> None: """Close connection.""" - if self._transport is None: - return - self._transport.close() await self._stop_running_tasks() + if self._transport: + self._transport.close() async def _stop_running_tasks(self) -> None: """Cancel and stop any running task.""" @@ -183,10 +182,12 @@ async def _stop_running_tasks(self) -> None: cancel_response.priority = Priority.CANCEL await self._message_queue.put(cancel_response) await self._message_worker_task - self._message_worker_task = None + self._message_worker_task = None + if self._data_worker_task is not None and not self._data_worker_task.done(): await self._data_queue.put(b"FFFFFFFF") await self._data_worker_task + self._data_worker_task = None # region Process incoming data diff --git a/pyproject.toml b/pyproject.toml index 5aeeb4bd3..9f437c73d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "v0.40.0a27" +version = "v0.40.0a29" license = {file = "LICENSE"} description = "Plugwise USB (Stick) module for Python 3." readme = "README.md" @@ -219,7 +219,7 @@ omit= [ ] [tool.ruff] -target-version = "py312" +target-version = "py313" lint.select = [ "B002", # Python does not support the unary prefix increment @@ -306,6 +306,7 @@ lint.ignore = [ "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target "UP006", # keep type annotation style as is "UP007", # keep type annotation style as is + "UP031" # Ignored due to performance: https://github.com/charliermarsh/ruff/issues/2923 #"UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)` ] diff --git a/scripts/python-venv.sh b/scripts/python-venv.sh index 75b374fb8..655803ccc 100755 --- a/scripts/python-venv.sh +++ b/scripts/python-venv.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -eu -pyversions=(3.12 3.11) +pyversions=( 3.13 ) my_path=$(git rev-parse --show-toplevel) my_venv=${my_path}/venv diff --git a/tests/bandit.yaml b/tests/bandit.yaml index 46566cc98..4a8cda726 100644 --- a/tests/bandit.yaml +++ b/tests/bandit.yaml @@ -12,7 +12,6 @@ tests: - B317 - B318 - B319 - - B320 - B601 - B602 - B604 diff --git a/tests/test_usb.py b/tests/test_usb.py index 97731aec6..17002b9d1 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -77,7 +77,7 @@ class DummyTransport: def __init__( self, loop: asyncio.AbstractEventLoop, - test_data: dict[bytes, tuple[str, bytes, bytes]] | None = None, + test_data: dict[bytes, tuple[str, bytes, bytes | None]] | None = None, ) -> None: """Initialize dummy transport class.""" self._loop = loop @@ -169,7 +169,7 @@ class MockSerial: """Mock serial connection.""" def __init__( - self, custom_response: dict[bytes, tuple[str, bytes, bytes]] | None + self, custom_response: dict[bytes, tuple[str, bytes, bytes | None]] | None ) -> None: """Init mocked serial connection.""" self.custom_response = custom_response @@ -421,7 +421,7 @@ async def test_stick_connect_timeout(self, monkeypatch: pytest.MonkeyPatch) -> N b"\x05\x05\x03\x03000AB43C\r\n": ( "STICK INIT timeout", b"000000E1", # Timeout ack - b"", + None, ), } ).mock_connection, @@ -526,6 +526,7 @@ async def node_loaded(self, event: pw_api.NodeEvent, mac: str) -> None: # type: f"Invalid {event} event, expected " + f"{pw_api.NodeEvent.LOADED}" ) ) + async def node_motion_state( self, feature: pw_api.NodeFeature, # type: ignore[name-defined] @@ -1497,7 +1498,7 @@ async def test_stick_network_down(self, monkeypatch: pytest.MonkeyPatch) -> None def fake_env(self, env: str) -> str | None: """Fake environment.""" if env == "APPDATA": - return "c:\\user\\tst\\appdata" + return "appdata_folder" if env == "~": return "/home/usr" return None @@ -2125,7 +2126,6 @@ def fake_cache(dummy: object, setting: str) -> str | None: construct_message(b"0100555555555555555500BF", b"0000") ) - async def load_callback(event: pw_api.NodeEvent, mac: str) -> None: # type: ignore[name-defined] """Load callback for event.""" @@ -2418,12 +2418,22 @@ async def test_node_discovery_and_load( # Get state get_state_timestamp = dt.now(UTC).replace(minute=0, second=0, microsecond=0) state = await stick.nodes["0098765432101234"].get_state( - (pw_api.NodeFeature.AVAILABLE, pw_api.NodeFeature.PING, pw_api.NodeFeature.INFO, pw_api.NodeFeature.RELAY) + ( + pw_api.NodeFeature.AVAILABLE, + pw_api.NodeFeature.PING, + pw_api.NodeFeature.INFO, + pw_api.NodeFeature.RELAY, + ) ) # Check Available assert state[pw_api.NodeFeature.AVAILABLE].state - assert state[pw_api.NodeFeature.AVAILABLE].last_seen.replace(minute=0, second=0, microsecond=0) == get_state_timestamp + assert ( + state[pw_api.NodeFeature.AVAILABLE].last_seen.replace( + minute=0, second=0, microsecond=0 + ) + == get_state_timestamp + ) # Check Ping assert state[pw_api.NodeFeature.PING].rssi_in == 69