From 14562ca53ac7adafed6a779761e25aa56ee88c8e Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 8 Sep 2026 17:28:14 +0000 Subject: [PATCH 1/4] Modernize CI --- .github/workflows/ci.yml | 2 +- .github/workflows/matchers/codespell.json | 16 ++++ .github/workflows/matchers/flake8.json | 30 ------ .github/workflows/matchers/ruff.json | 30 ++++++ .gitignore | 3 +- .pre-commit-config.yaml | 58 ++++-------- pyproject.toml | 106 +++++++++++++++------- requirements_test.txt | 5 - 8 files changed, 140 insertions(+), 110 deletions(-) create mode 100644 .github/workflows/matchers/codespell.json delete mode 100644 .github/workflows/matchers/flake8.json create mode 100644 .github/workflows/matchers/ruff.json delete mode 100644 requirements_test.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 015997ff..a6595657 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: uses: zigpy/workflows/.github/workflows/ci.yml@main with: CODE_FOLDER: zigpy_znp - CACHE_VERSION: 2 + CACHE_VERSION: 3 MINIMUM_COVERAGE_PERCENTAGE: 95 secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/matchers/codespell.json b/.github/workflows/matchers/codespell.json new file mode 100644 index 00000000..cfa66d31 --- /dev/null +++ b/.github/workflows/matchers/codespell.json @@ -0,0 +1,16 @@ +{ + "problemMatcher": [ + { + "owner": "codespell", + "severity": "warning", + "pattern": [ + { + "regexp": "^(.+):(\\d+):\\s(.+)$", + "file": 1, + "line": 2, + "message": 3 + } + ] + } + ] +} diff --git a/.github/workflows/matchers/flake8.json b/.github/workflows/matchers/flake8.json deleted file mode 100644 index e059a1cf..00000000 --- a/.github/workflows/matchers/flake8.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "problemMatcher": [ - { - "owner": "flake8-error", - "severity": "error", - "pattern": [ - { - "regexp": "^(.*):(\\d+):(\\d+):\\s([EF]\\d{3}\\s.*)$", - "file": 1, - "line": 2, - "column": 3, - "message": 4 - } - ] - }, - { - "owner": "flake8-warning", - "severity": "warning", - "pattern": [ - { - "regexp": "^(.*):(\\d+):(\\d+):\\s([CDNW]\\d{3}\\s.*)$", - "file": 1, - "line": 2, - "column": 3, - "message": 4 - } - ] - } - ] -} diff --git a/.github/workflows/matchers/ruff.json b/.github/workflows/matchers/ruff.json new file mode 100644 index 00000000..4411fc5e --- /dev/null +++ b/.github/workflows/matchers/ruff.json @@ -0,0 +1,30 @@ +{ + "problemMatcher": [ + { + "owner": "ruff-error", + "severity": "error", + "pattern": [ + { + "regexp": "^(.*):(\\d+):(\\d+):\\s([EF]\\d{3}\\s.*)$", + "file": 1, + "line": 2, + "column": 3, + "message": 4 + } + ] + }, + { + "owner": "ruff-warning", + "severity": "warning", + "pattern": [ + { + "regexp": "^(.*):(\\d+):(\\d+):\\s([CDNW]\\d{3}\\s.*)$", + "file": 1, + "line": 2, + "column": 3, + "message": 4 + } + ] + } + ] + } \ No newline at end of file diff --git a/.gitignore b/.gitignore index 444c9473..94e2584d 100644 --- a/.gitignore +++ b/.gitignore @@ -78,4 +78,5 @@ ENV/ TI Z-Stack/ -zigpy-znp-*.*.*/ \ No newline at end of file +zigpy-znp-*.*.*/ +uv.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1e7c184a..0f49222b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,47 +1,25 @@ repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.14.14 hooks: - - id: debug-statements + - id: ruff + args: ["--fix", "--exit-non-zero-on-fix", "--config", "pyproject.toml"] + - id: ruff-format + args: ["--config", "pyproject.toml"] - - repo: https://github.com/psf/black - rev: 23.10.1 + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 hooks: - - id: black + - id: codespell + additional_dependencies: [tomli] + args: ["--toml", "pyproject.toml"] - - repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 - hooks: - - id: flake8 - entry: pflake8 - additional_dependencies: - - pyproject-flake8==7.0.0 - - flake8-bugbear==24.2.6 - - flake8-comprehensions==3.14.0 - - flake8_2020==1.8.1 - - mccabe==0.7.0 - - pycodestyle==2.11.1 - - pyflakes==3.2.0 - - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort - - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.19.1 + - repo: local hooks: - id: mypy - additional_dependencies: - - zigpy>=0.91.2 - - types-setuptools - - - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 - hooks: - - id: pyupgrade - - - repo: https://github.com/fsouza/autoflake8 - rev: v0.4.1 - hooks: - - id: autoflake8 + name: mypy + entry: mypy + language: system + types_or: [python, pyi] + require_serial: true + exclude: ^tests/ diff --git a/pyproject.toml b/pyproject.toml index 73ce4d37..ac8beed8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,38 +23,26 @@ dependencies = [ [tool.setuptools.packages.find] exclude = ["tests", "tests.*"] -[project.optional-dependencies] +[dependency-groups] testing = [ + "mypy==1.19.1", + "pre-commit", + "coverage[toml]", "pytest>=7.3.1", "pytest-asyncio>=0.21.0", - "pytest-timeout>=2.1.0", - "pytest-mock>=3.10.0", "pytest-cov>=4.1.0", + "pytest-mock>=3.10.0", + "pytest-timeout>=2.1.0", +] +ci = [ + {include-group = "testing"}, + "pytest-github-actions-annotate-failures", + "pytest-xdist", ] [tool.setuptools-git-versioning] enabled = true - -[tool.black] -safe = true -quiet = true - -[tool.isort] -multi_line_output = 3 -length_sort = true -balanced_wrapping = true -include_trailing_comma = true -force_grid_wrap = 0 -use_parentheses = true -line_length = 88 -indent = " " -sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] -default_section = "THIRDPARTY" -known_first_party = ["zigpy_znp", "tests"] -forced_separate = "tests" -combine_as_imports = true - [tool.pytest.ini_options] addopts = "--showlocals --verbose" testpaths = ["tests"] @@ -64,19 +52,65 @@ log_date_format = "%Y-%m-%d %H:%M:%S" asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" -[tool.flake8] -exclude = ".venv,.git,.tox,docs,venv,bin,lib,deps,build" -# To work with Black -max-line-length = 88 -# W503: Line break occurred before a binary operator -# E203: Whitespace before ':' -# D202 No blank lines allowed after function docstring -ignore = "W503,E203,D202" +[tool.ruff] +required-version = ">=0.5.0" +target-version = "py311" +line-length = 88 + +exclude = [ + ".venv", + ".git", + ".tox", + "docs", + "venv", + "bin", + "lib", + "deps", + "build", +] + +[tool.ruff.lint] +select = [ + "B", # flake8-bugbear + "C", # complexity + "E", # pycodestyle + "F", # pyflakes/autoflake + "I", # isort + "SIM", # flake8-simplify + "UP", # pyupgrade + "W", # pycodestyle +] + +ignore = [ + "B008", # Do not perform function call in argument defaults + "C901", # Function is too complex + "E501", # Line too long + "SIM102", # Use a single if statement instead of nested if statements + "SIM108", # Use ternary operator instead of if-else-block + "SIM117", # Merge with-statements that use the same scope +] + +[tool.ruff.lint.isort] +force-sort-within-sections = true +known-first-party = [ + "zigpy_znp", + "tests", +] +forced-separate = ["tests"] +combine-as-imports = true +split-on-trailing-comma = false + +[tool.codespell] +ignore-words-list = ["ser", "nd", "hass", "checkin", "SUBSTRUCT", "substruct"] +skip = ["./.*", "tests/*", "pyproject.toml"] +quiet-level = 2 [tool.mypy] check_untyped_defs = true show_error_codes = true show_error_context = true +error_summary = true +exclude = ["^tests/"] disable_error_code = [ "attr-defined", "assignment", @@ -95,5 +129,11 @@ disable_error_code = [ [tool.coverage.run] source = ["zigpy_znp"] -[tool.pyupgrade] -py311plus = true +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "if typing.TYPE_CHECKING:", + "raise NotImplementedError", + "raise NotImplementedError()", +] diff --git a/requirements_test.txt b/requirements_test.txt deleted file mode 100644 index 43866720..00000000 --- a/requirements_test.txt +++ /dev/null @@ -1,5 +0,0 @@ -pytest>=7.3.1 -pytest-asyncio>=0.21.0 -pytest-timeout>=2.1.0 -pytest-mock>=3.10.0 -pytest-cov>=4.1.0 From 0112ae4c11f2a8143eaf160d027032a70aaadb69 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 8 Sep 2026 17:28:50 +0000 Subject: [PATCH 2/4] Run `pre-commit` --- tests/api/test_connect.py | 4 +- tests/api/test_listeners.py | 4 +- tests/api/test_network_state.py | 10 ++-- tests/api/test_nvram.py | 6 +- tests/api/test_nvram_migration.py | 2 +- tests/api/test_request.py | 10 ++-- tests/api/test_response.py | 2 +- tests/application/test_joining.py | 14 +++-- tests/application/test_requests.py | 18 +++--- tests/application/test_startup.py | 10 ++-- tests/application/test_zdo_requests.py | 2 +- tests/application/test_zigpy_callbacks.py | 6 +- tests/conftest.py | 64 +++++++++++----------- tests/test_commands.py | 10 ++-- tests/test_frames.py | 4 +- tests/test_trace_logging.py | 2 +- tests/test_types_basic.py | 2 +- tests/test_types_cstruct.py | 18 +++--- tests/test_types_named.py | 12 ++-- tests/test_uart.py | 12 ++-- tests/test_utils.py | 4 +- tests/tools/test_energy_scan.py | 4 +- tests/tools/test_flash.py | 6 +- tests/tools/test_logging.py | 2 +- tests/tools/test_network_backup_restore.py | 22 ++++---- tests/tools/test_network_scan.py | 6 +- tests/tools/test_nvram.py | 16 +++--- zigpy_znp/api.py | 50 ++++++++--------- zigpy_znp/commands/__init__.py | 8 +-- zigpy_znp/commands/ubl.py | 3 +- zigpy_znp/commands/zdo.py | 3 +- zigpy_znp/config.py | 20 +++---- zigpy_znp/frames.py | 4 +- zigpy_znp/nvram.py | 6 +- zigpy_znp/tools/common.py | 8 +-- zigpy_znp/tools/energy_scan.py | 10 ++-- zigpy_znp/tools/flash_read.py | 6 +- zigpy_znp/tools/flash_write.py | 8 +-- zigpy_znp/tools/network_backup.py | 8 +-- zigpy_znp/tools/network_restore.py | 8 +-- zigpy_znp/tools/network_scan.py | 12 ++-- zigpy_znp/tools/nvram_read.py | 10 ++-- zigpy_znp/tools/nvram_reset.py | 6 +- zigpy_znp/tools/nvram_write.py | 6 +- zigpy_znp/types/__init__.py | 4 +- zigpy_znp/types/basic.py | 4 +- zigpy_znp/types/commands.py | 8 +-- zigpy_znp/types/cstruct.py | 6 +- zigpy_znp/types/named.py | 6 +- zigpy_znp/types/zigpy_types.py | 10 ++-- zigpy_znp/uart.py | 11 +--- zigpy_znp/utils.py | 6 +- zigpy_znp/zigbee/application.py | 33 ++++++----- zigpy_znp/zigbee/device.py | 4 +- zigpy_znp/znp/security.py | 10 ++-- 55 files changed, 270 insertions(+), 280 deletions(-) diff --git a/tests/api/test_connect.py b/tests/api/test_connect.py index 6436add8..f51fe785 100644 --- a/tests/api/test_connect.py +++ b/tests/api/test_connect.py @@ -3,9 +3,9 @@ import pytest -import zigpy_znp.types as t -import zigpy_znp.commands as c from zigpy_znp.api import ZNP +import zigpy_znp.commands as c +import zigpy_znp.types as t from ..conftest import BaseServerZNP, CoroutineMock, config_for_port_path diff --git a/tests/api/test_listeners.py b/tests/api/test_listeners.py index 323c6762..482031da 100644 --- a/tests/api/test_listeners.py +++ b/tests/api/test_listeners.py @@ -3,9 +3,9 @@ import pytest -import zigpy_znp.types as t +from zigpy_znp.api import CallbackResponseListener, OneShotResponseListener import zigpy_znp.commands as c -from zigpy_znp.api import OneShotResponseListener, CallbackResponseListener +import zigpy_znp.types as t async def test_resolve(mocker): diff --git a/tests/api/test_network_state.py b/tests/api/test_network_state.py index f27a257e..da6fa3df 100644 --- a/tests/api/test_network_state.py +++ b/tests/api/test_network_state.py @@ -4,16 +4,16 @@ import pytest from zigpy.exceptions import FormationFailure -import zigpy_znp.types as t import zigpy_znp.commands as c +import zigpy_znp.types as t from zigpy_znp.types.nvids import ExNvIds, OsalNvIds from ..conftest import ( ALL_DEVICES, FORMED_DEVICES, BaseZStack1CC2531, - FormedZStack3CC2531, FormedLaunchpadCC26X2R1, + FormedZStack3CC2531, ) @@ -64,9 +64,9 @@ async def test_broken_cc2531_load_state(device, make_connected_znp, caplog): znp, znp_server = await make_connected_znp(server_cls=device) # "Bad" TCLK seed is a TCLK from Z-Stack 1 with the first 16 bytes overwritten - znp_server._nvram[ExNvIds.LEGACY][ - OsalNvIds.TCLK_SEED - ] += b"liance092\x00\x00\x00\x00\x00\x00\x00" + znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.TCLK_SEED] += ( + b"liance092\x00\x00\x00\x00\x00\x00\x00" + ) caplog.set_level(logging.ERROR) await znp.load_network_info() diff --git a/tests/api/test_nvram.py b/tests/api/test_nvram.py index 38b1adfe..ee1ebd45 100644 --- a/tests/api/test_nvram.py +++ b/tests/api/test_nvram.py @@ -1,9 +1,9 @@ import pytest -import zigpy_znp.types as t import zigpy_znp.commands as c -from zigpy_znp.types import nvids from zigpy_znp.exceptions import SecurityError +import zigpy_znp.types as t +from zigpy_znp.types import nvids async def test_osal_writes_invalid(connected_znp): @@ -271,7 +271,7 @@ async def test_osal_read_security_bypass(connected_znp, nvid, value): @pytest.mark.parametrize("nvid", [nvids.OsalNvIds.POLL_RATE_OLD16]) -@pytest.mark.parametrize("value", [b"\xAB\xCD"]) +@pytest.mark.parametrize("value", [b"\xab\xcd"]) async def test_osal_read_proxied(connected_znp, nvid, value): znp, znp_server = connected_znp diff --git a/tests/api/test_nvram_migration.py b/tests/api/test_nvram_migration.py index 78456803..dbe25458 100644 --- a/tests/api/test_nvram_migration.py +++ b/tests/api/test_nvram_migration.py @@ -1,8 +1,8 @@ import pytest +import zigpy_znp.commands as c import zigpy_znp.const as const import zigpy_znp.types as t -import zigpy_znp.commands as c from zigpy_znp.types.nvids import ExNvIds, OsalNvIds from ..conftest import FORMED_DEVICES, FormedZStack3CC2531 diff --git a/tests/api/test_request.py b/tests/api/test_request.py index 70496658..88c20834 100644 --- a/tests/api/test_request.py +++ b/tests/api/test_request.py @@ -3,11 +3,11 @@ import pytest -import zigpy_znp.types as t -import zigpy_znp.config as conf import zigpy_znp.commands as c -from zigpy_znp.frames import GeneralFrame +import zigpy_znp.config as conf from zigpy_znp.exceptions import CommandNotRecognized, InvalidCommandResponse +from zigpy_znp.frames import GeneralFrame +import zigpy_znp.types as t async def test_callback_rsp(connected_znp): @@ -293,7 +293,7 @@ async def test_handling_known_bad_command_parsing(connected_znp, caplog): header=t.CommandHeader( id=0x9F, subsystem=t.Subsystem.ZDO, type=t.CommandType.AREQ ), - data=b"\x13\xDB\x84\x01\x21", + data=b"\x13\xdb\x84\x01\x21", ) caplog.set_level(logging.WARNING) @@ -312,7 +312,7 @@ async def test_handling_unknown_bad_command_parsing(connected_znp): header=t.CommandHeader( id=0xCB, subsystem=t.Subsystem.ZDO, type=t.CommandType.AREQ ), - data=b"\x13\xDB\x84\x01\x21", + data=b"\x13\xdb\x84\x01\x21", ) with pytest.raises(ValueError): diff --git a/tests/api/test_response.py b/tests/api/test_response.py index ed89c924..49e497db 100644 --- a/tests/api/test_response.py +++ b/tests/api/test_response.py @@ -2,8 +2,8 @@ import pytest -import zigpy_znp.types as t import zigpy_znp.commands as c +import zigpy_znp.types as t from zigpy_znp.utils import deduplicate_commands diff --git a/tests/application/test_joining.py b/tests/application/test_joining.py index f86e2382..886a92d3 100644 --- a/tests/application/test_joining.py +++ b/tests/application/test_joining.py @@ -3,20 +3,20 @@ from unittest import mock import pytest -import zigpy.util import zigpy.types +import zigpy.util import zigpy.zdo.types as zdo_t -import zigpy_znp.types as t import zigpy_znp.commands as c +import zigpy_znp.types as t from ..conftest import ( FORMED_DEVICES, FORMED_ZSTACK3_DEVICES, CoroutineMock, FormedLaunchpadCC26X2R1, - zdo_request_matcher, serialize_zdo_command, + zdo_request_matcher, ) @@ -132,7 +132,7 @@ async def test_join_device(device, make_application): @pytest.mark.parametrize("device", FORMED_ZSTACK3_DEVICES) -@pytest.mark.parametrize("permit_result", [None, asyncio.TimeoutError()]) +@pytest.mark.parametrize("permit_result", [None, TimeoutError()]) async def test_permit_join_with_key(device, permit_result, make_application, mocker): app, znp_server = make_application(server_cls=device) @@ -168,8 +168,10 @@ async def test_permit_join_with_key(device, permit_result, make_application, moc mocker.patch.object(app, "permit", new=CoroutineMock(side_effect=permit_result)) - with contextlib.nullcontext() if permit_result is None else pytest.raises( - asyncio.TimeoutError + with ( + contextlib.nullcontext() + if permit_result is None + else pytest.raises(asyncio.TimeoutError) ): await app.permit_with_link_key(node=ieee, link_key=link_key, time_s=1) diff --git a/tests/application/test_requests.py b/tests/application/test_requests.py index 649f9391..f073cf68 100644 --- a/tests/application/test_requests.py +++ b/tests/application/test_requests.py @@ -1,22 +1,22 @@ import asyncio import pytest -import zigpy.types as zigpy_t import zigpy.endpoint +from zigpy.exceptions import DeliveryError import zigpy.profiles +import zigpy.types as zigpy_t import zigpy.zdo.types as zdo_t -from zigpy.exceptions import DeliveryError -import zigpy_znp.types as t -import zigpy_znp.config as conf import zigpy_znp.commands as c +import zigpy_znp.config as conf +import zigpy_znp.types as t from ..conftest import ( FORMED_DEVICES, CoroutineMock, FormedLaunchpadCC26X2R1, - zdo_request_matcher, serialize_zdo_command, + zdo_request_matcher, ) @@ -382,7 +382,7 @@ async def test_nonstandard_profile(device, make_application): SecurityUse=t.Bool(False), TimeStamp=12345678, TSN=0, - Data=b"\x08" + bytes([req.TSN]) + b"\x0B\x00\x00", + Data=b"\x08" + bytes([req.TSN]) + b"\x0b\x00\x00", MacSrcAddr=device.nwk, MsgResultRadius=29, ), @@ -608,7 +608,7 @@ async def test_request_recovery_route_rediscovery_then_assoc_failure( device = app.add_initialized_device(ieee=t.EUI64(range(8)), nwk=0xABCD) - assoc_device, _ = c.util.Device.deserialize(b"\xFF" * 100) + assoc_device, _ = c.util.Device.deserialize(b"\xff" * 100) assoc_device.shortAddr = device.nwk assoc_device.nodeRelation = c.util.NodeRelation.CHILD_FFD_RX_IDLE @@ -697,7 +697,7 @@ async def test_request_recovery_assoc_remove( device = app.add_initialized_device(ieee=t.EUI64(range(8)), nwk=0xABCD) - assoc_device, _ = c.util.Device.deserialize(b"\xFF" * 100) + assoc_device, _ = c.util.Device.deserialize(b"\xff" * 100) assoc_device.shortAddr = device.nwk assoc_device.nodeRelation = c.util.NodeRelation.CHILD_FFD_RX_IDLE @@ -722,7 +722,7 @@ def assoc_get_with_addr(req): nonlocal assoc_device if assoc_device is None: - dev, _ = c.util.Device.deserialize(b"\xFF" * 100) + dev, _ = c.util.Device.deserialize(b"\xff" * 100) return c.UTIL.AssocGetWithAddress.Rsp(Device=dev) return c.UTIL.AssocGetWithAddress.Rsp(Device=assoc_device) diff --git a/tests/application/test_startup.py b/tests/application/test_startup.py index 91ee6df5..8394a863 100644 --- a/tests/application/test_startup.py +++ b/tests/application/test_startup.py @@ -2,22 +2,22 @@ import voluptuous as vol from zigpy.exceptions import NetworkNotFormed -import zigpy_znp.types as t -import zigpy_znp.config as conf -import zigpy_znp.commands as c from zigpy_znp.api import ZNP +import zigpy_znp.commands as c +import zigpy_znp.config as conf from zigpy_znp.exceptions import InvalidCommandResponse +import zigpy_znp.types as t from zigpy_znp.types.nvids import ExNvIds, OsalNvIds from ..conftest import ( ALL_DEVICES, EMPTY_DEVICES, FORMED_DEVICES, - CoroutineMock, BaseZStack3Device, + CoroutineMock, + FormedLaunchpadCC26X2R1, FormedZStack1CC2531, FormedZStack3CC2531, - FormedLaunchpadCC26X2R1, ) DEV_NETWORK_SETTINGS = { diff --git a/tests/application/test_zdo_requests.py b/tests/application/test_zdo_requests.py index 55d098c1..dbd2feae 100644 --- a/tests/application/test_zdo_requests.py +++ b/tests/application/test_zdo_requests.py @@ -3,8 +3,8 @@ import pytest import zigpy.zdo.types as zdo_t -import zigpy_znp.types as t import zigpy_znp.commands as c +import zigpy_znp.types as t from tests.conftest import FormedLaunchpadCC26X2R1 diff --git a/tests/application/test_zigpy_callbacks.py b/tests/application/test_zigpy_callbacks.py index 815c4229..2ad6c2af 100644 --- a/tests/application/test_zigpy_callbacks.py +++ b/tests/application/test_zigpy_callbacks.py @@ -5,8 +5,8 @@ import zigpy.types as zigpy_t import zigpy.zdo.types as zdo_t -import zigpy_znp.types as t import zigpy_znp.commands as c +import zigpy_znp.types as t from ..conftest import FORMED_DEVICES, serialize_zdo_command @@ -258,7 +258,7 @@ async def test_receive_af_broadcast(device, make_application, mocker): SecurityUse=t.Bool.false, TimeStamp=4442962, TSN=0, - Data=b"\x11\xA6\x00\x74\xB5\x7C\x00\x02\x5F", + Data=b"\x11\xa6\x00\x74\xb5\x7c\x00\x02\x5f", MacSrcAddr=0x0000, MsgResultRadius=0, ) @@ -303,7 +303,7 @@ async def test_receive_af_group(device, make_application, mocker): SecurityUse=t.Bool.false, TimeStamp=4442962, TSN=0, - Data=b"\x11\xA6\x00\x74\xB5\x7C\x00\x02\x5F", + Data=b"\x11\xa6\x00\x74\xb5\x7c\x00\x02\x5f", MacSrcAddr=0x0000, MsgResultRadius=0, ) diff --git a/tests/conftest.py b/tests/conftest.py index 7c4ee2d3..565e3a5c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,14 +1,14 @@ -import json import asyncio import inspect +import json import logging import pathlib from unittest.mock import Mock, patch import pytest -import zigpy.types import zigpy.config import zigpy.device +import zigpy.types try: # Python 3.8 already has this @@ -19,14 +19,14 @@ import zigpy.endpoint import zigpy.zdo.types as zdo_t -import zigpy_znp.const as const -import zigpy_znp.types as t -import zigpy_znp.config as conf -import zigpy_znp.commands as c from zigpy_znp.api import ZNP -from zigpy_znp.uart import ZnpMtProtocol +import zigpy_znp.commands as c +import zigpy_znp.config as conf +import zigpy_znp.const as const from zigpy_znp.nvram import NVRAMHelper +import zigpy_znp.types as t from zigpy_znp.types.nvids import ExNvIds, NvSysIds, OsalNvIds, is_secure_nvid +from zigpy_znp.uart import ZnpMtProtocol from zigpy_znp.zigbee.application import ControllerApplication LOGGER = logging.getLogger(__name__) @@ -515,12 +515,12 @@ def create_nib(self, _=None): FrameCounter=2500, ) - self._nvram[ExNvIds.LEGACY][ - OsalNvIds.NWK_ACTIVE_KEY_INFO - ] = self.nvram_serialize(key_info.Active) - self._nvram[ExNvIds.LEGACY][ - OsalNvIds.NWK_ALTERN_KEY_INFO - ] = self.nvram_serialize(key_info.Active) + self._nvram[ExNvIds.LEGACY][OsalNvIds.NWK_ACTIVE_KEY_INFO] = ( + self.nvram_serialize(key_info.Active) + ) + self._nvram[ExNvIds.LEGACY][OsalNvIds.NWK_ALTERN_KEY_INFO] = ( + self.nvram_serialize(key_info.Active) + ) self._nvram[ExNvIds.LEGACY][OsalNvIds.NWKKEY] = self.nvram_serialize(key_info) def _default_nib(self): @@ -805,7 +805,7 @@ def osal_nvram_init(self, req): if len(req.Value) > req.ItemLen: return c.SYS.OSALNVItemInit.Rsp(Status=t.Status.INVALID_PARAMETER) - self._nvram[ExNvIds.LEGACY][req.Id] = req.Value.ljust(req.ItemLen, b"\xFF") + self._nvram[ExNvIds.LEGACY][req.Id] = req.Value.ljust(req.ItemLen, b"\xff") return c.SYS.OSALNVItemInit.Rsp(Status=t.Status.NV_ITEM_UNINIT) @@ -858,9 +858,9 @@ def reset_req(self, request, *, _handle_startup_reset=True): if startup & t.StartupOptions.ClearState: self._create_network_nvram() - self._nvram[ExNvIds.LEGACY][ - OsalNvIds.STARTUP_OPTION - ] = t.StartupOptions.NONE.serialize() + self._nvram[ExNvIds.LEGACY][OsalNvIds.STARTUP_OPTION] = ( + t.StartupOptions.NONE.serialize() + ) # Resetting recreates the EXTADDR NVRAM item if ( @@ -914,7 +914,7 @@ def register_zdo_callback(self, request): @reply_to(c.UTIL.AssocFindDevice.Req(Index=0)) def assoc_find_dev_responder(self, req): - return req.Rsp(Device=t.Bytes(b"\xFF" * (36 if self.align_structs else 28))) + return req.Rsp(Device=t.Bytes(b"\xff" * (36 if self.align_structs else 28))) class BaseZStack1CC2531(BaseZStackDevice): @@ -1170,7 +1170,7 @@ class BaseLaunchpadCC26X2R1(BaseZStack3Device): def _create_network_nvram(self): super()._create_network_nvram() - self._nvram[ExNvIds.LEGACY][OsalNvIds.APS_LINK_KEY_TABLE] = b"\xFF" * 20 + self._nvram[ExNvIds.LEGACY][OsalNvIds.APS_LINK_KEY_TABLE] = b"\xff" * 20 self._nvram[ExNvIds.ADDRMGR] = { addr: self.nvram_serialize(const.EMPTY_ADDR_MGR_ENTRY_ZSTACK3) for addr in range(0x0000, 0x0100 + 1) @@ -1309,7 +1309,7 @@ class BaseZStack3CC2531(BaseZStack3Device): def _create_network_nvram(self): super()._create_network_nvram() - self._nvram[ExNvIds.LEGACY][OsalNvIds.APS_LINK_KEY_TABLE] = b"\xFF" * 17 + self._nvram[ExNvIds.LEGACY][OsalNvIds.APS_LINK_KEY_TABLE] = b"\xff" * 17 self._nvram[ExNvIds.LEGACY][OsalNvIds.ADDRMGR] = 124 * self.nvram_serialize( const.EMPTY_ADDR_MGR_ENTRY_ZSTACK1 ) @@ -1317,21 +1317,21 @@ def _create_network_nvram(self): def create_nib(self, _=None): super().create_nib() - self._nvram[ExNvIds.LEGACY][ - OsalNvIds.LEGACY_NWK_SEC_MATERIAL_TABLE_START - ] = self.nvram_serialize( - t.NwkSecMaterialDesc( - FrameCounter=2500, - ExtendedPanID=self.nib.extendedPANID, + self._nvram[ExNvIds.LEGACY][OsalNvIds.LEGACY_NWK_SEC_MATERIAL_TABLE_START] = ( + self.nvram_serialize( + t.NwkSecMaterialDesc( + FrameCounter=2500, + ExtendedPanID=self.nib.extendedPANID, + ) ) ) - self._nvram[ExNvIds.LEGACY][ - OsalNvIds.LEGACY_NWK_SEC_MATERIAL_TABLE_END - ] = self.nvram_serialize( - t.NwkSecMaterialDesc( - FrameCounter=0xFFFFFFFF, - ExtendedPanID=t.EUI64.convert("FF:FF:FF:FF:FF:FF:FF:FF"), + self._nvram[ExNvIds.LEGACY][OsalNvIds.LEGACY_NWK_SEC_MATERIAL_TABLE_END] = ( + self.nvram_serialize( + t.NwkSecMaterialDesc( + FrameCounter=0xFFFFFFFF, + ExtendedPanID=t.EUI64.convert("FF:FF:FF:FF:FF:FF:FF:FF"), + ) ) ) diff --git a/tests/test_commands.py b/tests/test_commands.py index 705e09b2..20e517e2 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1,14 +1,14 @@ -import keyword -import dataclasses from collections import defaultdict +import dataclasses +import keyword import pytest import zigpy.types as zigpy_t import zigpy.zdo.types -import zigpy_znp.frames as frames -import zigpy_znp.commands as c from zigpy_znp import types as t +import zigpy_znp.commands as c +import zigpy_znp.frames as frames def test_command_header(): @@ -552,7 +552,7 @@ def test_neighbors_missing_payload(): subsystem=t.Subsystem.ZDO, type=t.CommandType.AREQ, ), - data=b"\x1F\x82\x84", + data=b"\x1f\x82\x84", ) assert c.ZDO.MgmtLqiRsp.Callback.from_frame(frame) == c.ZDO.MgmtLqiRsp.Callback( diff --git a/tests/test_frames.py b/tests/test_frames.py index 70be1c8e..79c2c7ac 100644 --- a/tests/test_frames.py +++ b/tests/test_frames.py @@ -1,8 +1,8 @@ import pytest -import zigpy_znp.types as t -import zigpy_znp.frames as frames from zigpy_znp.exceptions import InvalidFrame +import zigpy_znp.frames as frames +import zigpy_znp.types as t def test_general_frame(): diff --git a/tests/test_trace_logging.py b/tests/test_trace_logging.py index 2e86e7a6..34a20f07 100644 --- a/tests/test_trace_logging.py +++ b/tests/test_trace_logging.py @@ -20,7 +20,7 @@ def test_existing_trace_level(trace_level, monkeypatch): monkeypatch_addLevelName(monkeypatch, trace_level, "TRACE") monkeypatch.setattr(logging, "TRACE", trace_level, raising=False) - assert logging.TRACE == trace_level + assert trace_level == logging.TRACE # If a TRACE level already exists and TRACE < DEBUG, we use it assert _find_trace_level() == trace_level diff --git a/tests/test_types_basic.py b/tests/test_types_basic.py index 2ca55388..e65725df 100644 --- a/tests/test_types_basic.py +++ b/tests/test_types_basic.py @@ -11,7 +11,7 @@ class TestList(t.LVList, item_type=t.uint8_t, length_type=t.uint8_t): assert ( t.serialize_list([t.uint8_t(0xF0), t.Bytes(b"asd"), TestList([0xAB, 0xCD])]) - == b"\xF0asd\x02\xAB\xCD" + == b"\xf0asd\x02\xab\xcd" ) assert t.serialize_list([]) == b"" diff --git a/tests/test_types_cstruct.py b/tests/test_types_cstruct.py index 07d56199..0359a265 100644 --- a/tests/test_types_cstruct.py +++ b/tests/test_types_cstruct.py @@ -118,12 +118,12 @@ class TestStruct(t.CStruct): expected = b"" expected += t.uint8_t(1).serialize() - expected += b"\xFF" + t.uint16_t(2).serialize() + expected += b"\xff" + t.uint16_t(2).serialize() expected += t.uint32_t(3).serialize() expected += t.uint8_t(4).serialize() - expected += b"\xFF\xFF\xFF" + t.uint32_t(5).serialize() + expected += b"\xff\xff\xff" + t.uint32_t(5).serialize() expected += t.uint8_t(6).serialize() - expected += b"\xFF\xFF\xFF" + expected += b"\xff\xff\xff" struct = TestStruct(a=1, b=2, c=3, d=4, e=5, f=6) assert struct.serialize(align=True) == expected @@ -138,14 +138,14 @@ class TestStruct(t.CStruct): def test_struct_aligned_nested_serialization_deserialization(): class Inner(t.CStruct): - _padding_byte = b"\xCD" + _padding_byte = b"\xcd" c: t.uint8_t d: t.uint32_t e: t.uint8_t class TestStruct(t.CStruct): - _padding_byte = b"\xAB" + _padding_byte = b"\xab" a: t.uint8_t b: Inner @@ -155,13 +155,13 @@ class TestStruct(t.CStruct): expected += t.uint8_t(1).serialize() # Inner struct - expected += b"\xAB\xAB\xAB" + t.uint8_t(2).serialize() - expected += b"\xCD\xCD\xCD" + t.uint32_t(3).serialize() + expected += b"\xab\xab\xab" + t.uint8_t(2).serialize() + expected += b"\xcd\xcd\xcd" + t.uint32_t(3).serialize() expected += t.uint8_t(4).serialize() - expected += b"\xCD\xCD\xCD" # Aligned to 4 bytes + expected += b"\xcd\xcd\xcd" # Aligned to 4 bytes expected += t.uint16_t(5).serialize() - expected += b"\xAB\xAB" # Also aligned to 4 bytes due to inner struct + expected += b"\xab\xab" # Also aligned to 4 bytes due to inner struct struct = TestStruct(a=1, b=Inner(c=2, d=3, e=4), f=5) assert struct.serialize(align=True) == expected diff --git a/tests/test_types_named.py b/tests/test_types_named.py index 277c3cf1..66d0b187 100644 --- a/tests/test_types_named.py +++ b/tests/test_types_named.py @@ -1,7 +1,7 @@ import pytest -import zigpy_znp.types as t import zigpy_znp.commands as c +import zigpy_znp.types as t def test_status(): @@ -72,7 +72,7 @@ def test_addr_mode_address(): with pytest.raises(ValueError): # 0xab is not a valid mode - data = b"\xAB\xaa\x55\x02\x03\x04\x05\x06\x07" + data = b"\xab\xaa\x55\x02\x03\x04\x05\x06\x07" t.AddrModeAddress.deserialize(data) with pytest.raises(ValueError): @@ -81,8 +81,8 @@ def test_addr_mode_address(): t.AddrModeAddress.deserialize(data) # Bytes at the end for NWK address mode are ignored - data1 = b"\x02\x0E\xAD" + b"\xC0\x8C\x97\x83\xB0\x20\x33" - data2 = b"\x02\x0E\xAD" + b"\x3F\xB9\x5B\x64\x20\x86\xD6" + data1 = b"\x02\x0e\xad" + b"\xc0\x8c\x97\x83\xb0\x20\x33" + data2 = b"\x02\x0e\xad" + b"\x3f\xb9\x5b\x64\x20\x86\xd6" r1, _ = t.AddrModeAddress.deserialize(data1) r2, _ = t.AddrModeAddress.deserialize(data2) @@ -90,8 +90,8 @@ def test_addr_mode_address(): assert r1 == r2 # All of the bytes are used for IEEE address mode - data1 = b"\x02\x0E\xAD\xC0\x8C\x97\x83\xB0\x20\x33" - data2 = b"\x02\x0E\xAD\x3F\xB9\x5B\x64\x20\x86\xD6" + data1 = b"\x02\x0e\xad\xc0\x8c\x97\x83\xb0\x20\x33" + data2 = b"\x02\x0e\xad\x3f\xb9\x5b\x64\x20\x86\xd6" r3, _ = t.AddrModeAddress.deserialize(b"\x03" + data1[1:]) r4, _ = t.AddrModeAddress.deserialize(b"\x03" + data2[1:]) diff --git a/tests/test_uart.py b/tests/test_uart.py index c2868f77..f6ea91fb 100644 --- a/tests/test_uart.py +++ b/tests/test_uart.py @@ -2,11 +2,11 @@ import pytest -import zigpy_znp.types as t -import zigpy_znp.config as conf -import zigpy_znp.commands as c from zigpy_znp import uart as znp_uart +import zigpy_znp.commands as c +import zigpy_znp.config as conf from zigpy_znp.frames import TransportFrame +import zigpy_znp.types as t @pytest.fixture @@ -187,9 +187,9 @@ def test_uart_rx_sof_stress(connected_uart): test_frame_bytes = TransportFrame(test_frame).serialize() # We include an almost-valid frame and many stray SoF markers - uart.data_received(b"\xFE" + b"\xFE" + b"\xFE" + test_frame_bytes[:-1] + b"\x00") - uart.data_received(b"\xFE\xFE\x00\xFE\x01") - uart.data_received(b"\xFE" + b"\xFE" + b"\xFE" + test_frame_bytes + b"\x00\x00") + uart.data_received(b"\xfe" + b"\xfe" + b"\xfe" + test_frame_bytes[:-1] + b"\x00") + uart.data_received(b"\xfe\xfe\x00\xfe\x01") + uart.data_received(b"\xfe" + b"\xfe" + b"\xfe" + test_frame_bytes + b"\x00\x00") # We should see the valid frame exactly once znp.frame_received.assert_called_once_with(test_frame) diff --git a/tests/test_utils.py b/tests/test_utils.py index aa23370e..84c01466 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -2,9 +2,9 @@ import pytest -import zigpy_znp.types as t import zigpy_znp.commands as c -from zigpy_znp.utils import deduplicate_commands, combine_concurrent_calls +import zigpy_znp.types as t +from zigpy_znp.utils import combine_concurrent_calls, deduplicate_commands def test_command_deduplication_simple(): diff --git a/tests/tools/test_energy_scan.py b/tests/tools/test_energy_scan.py index 4a284593..97a08322 100644 --- a/tests/tools/test_energy_scan.py +++ b/tests/tools/test_energy_scan.py @@ -3,15 +3,15 @@ import pytest import zigpy.zdo.types as zdo_t -import zigpy_znp.types as t import zigpy_znp.commands as c from zigpy_znp.tools.energy_scan import main as energy_scan +import zigpy_znp.types as t from ..conftest import ( EMPTY_DEVICES, FORMED_DEVICES, - serialize_zdo_command, deserialize_zdo_command, + serialize_zdo_command, ) diff --git a/tests/tools/test_flash.py b/tests/tools/test_flash.py index 0ca8bc28..3ed1d096 100644 --- a/tests/tools/test_flash.py +++ b/tests/tools/test_flash.py @@ -2,10 +2,10 @@ import pytest -import zigpy_znp.types as t import zigpy_znp.commands as c from zigpy_znp.tools.flash_read import main as flash_read -from zigpy_znp.tools.flash_write import main as flash_write, get_firmware_crcs +from zigpy_znp.tools.flash_write import get_firmware_crcs, main as flash_write +import zigpy_znp.types as t from ..conftest import BaseServerZNP, CoroutineMock @@ -138,7 +138,7 @@ async def test_flash_write_bad_size(make_znp_server, tmp_path, mocker): # Add an extra byte BAD_FIRMWARE = bytearray(len(FAKE_FLASH)) - BAD_FIRMWARE += b"\xFF" + BAD_FIRMWARE += b"\xff" # No communication will happen because the CRC will be invalid firmware_file = tmp_path / "bad-firmware.bin" diff --git a/tests/tools/test_logging.py b/tests/tools/test_logging.py index 951046b7..4891d5dd 100644 --- a/tests/tools/test_logging.py +++ b/tests/tools/test_logging.py @@ -4,7 +4,7 @@ import pytest from zigpy_znp.logger import _TRACE -from zigpy_znp.tools.common import UnclosableFile, ClosableFileType, setup_parser +from zigpy_znp.tools.common import ClosableFileType, UnclosableFile, setup_parser @pytest.mark.parametrize( diff --git a/tests/tools/test_network_backup_restore.py b/tests/tools/test_network_backup_restore.py index 5f25fa13..954c489d 100644 --- a/tests/tools/test_network_backup_restore.py +++ b/tests/tools/test_network_backup_restore.py @@ -1,30 +1,30 @@ -import json import asyncio import dataclasses +import json -import pytest -import zigpy.state from jsonschema import ValidationError +import pytest from zigpy.exceptions import NetworkNotFormed +import zigpy.state -import zigpy_znp.types as t -from zigpy_znp.znp import security -from zigpy_znp.types.nvids import ExNvIds, OsalNvIds from zigpy_znp.tools.common import validate_backup_json from zigpy_znp.tools.network_backup import main as network_backup from zigpy_znp.tools.network_restore import main as network_restore +import zigpy_znp.types as t +from zigpy_znp.types.nvids import ExNvIds, OsalNvIds +from zigpy_znp.znp import security +from ..application.test_startup import DEV_NETWORK_SETTINGS from ..conftest import ( ALL_DEVICES, EMPTY_DEVICES, FORMED_DEVICES, + BaseLaunchpadCC26X2R1, BaseZStack1CC2531, BaseZStack3CC2531, FormedZStack1CC2531, - BaseLaunchpadCC26X2R1, ResetLaunchpadCC26X2R1, ) -from ..application.test_startup import DEV_NETWORK_SETTINGS BARE_NETWORK_INFO = zigpy.state.NetworkInfo( extended_pan_id=t.EUI64.convert("ab:de:fa:bc:de:fa:bc:de"), @@ -257,7 +257,7 @@ async def test_network_restore_pick_optimal_tclk( async def test_nwk_frame_counter_zstack1(make_connected_znp): znp, znp_server = await make_connected_znp(BaseZStack1CC2531) znp_server._nvram[ExNvIds.LEGACY] = { - OsalNvIds.NWKKEY: b"\x01" + b"\xAB" * 16 + b"\x78\x56\x34\x12" + OsalNvIds.NWKKEY: b"\x01" + b"\xab" * 16 + b"\x78\x56\x34\x12" } assert (await security.read_nwk_frame_counter(znp)) == 0x12345678 @@ -272,7 +272,7 @@ async def test_nwk_frame_counter_zstack30(make_connected_znp): znp.node_info = BARE_NODE_INFO znp_server._nvram[ExNvIds.LEGACY] = { # This value is ignored - OsalNvIds.NWKKEY: b"\x01" + b"\xAB" * 16 + b"\x78\x56\x34\x12", + OsalNvIds.NWKKEY: b"\x01" + b"\xab" * 16 + b"\x78\x56\x34\x12", # Wrong EPID, ignored OsalNvIds.LEGACY_NWK_SEC_MATERIAL_TABLE_START: bytes.fromhex( "0f000000058eea0f004b1200" @@ -282,7 +282,7 @@ async def test_nwk_frame_counter_zstack30(make_connected_znp): + BARE_NETWORK_INFO.extended_pan_id.serialize(), # Generic EPID but ignored since EPID matches (OsalNvIds.LEGACY_NWK_SEC_MATERIAL_TABLE_START + 2): bytes.fromhex("02000000") - + b"\xFF" * 8, + + b"\xff" * 8, } assert (await security.read_nwk_frame_counter(znp)) == 0x00000001 diff --git a/tests/tools/test_network_scan.py b/tests/tools/test_network_scan.py index cda1cb66..621c5b21 100644 --- a/tests/tools/test_network_scan.py +++ b/tests/tools/test_network_scan.py @@ -1,12 +1,12 @@ import pytest -import zigpy_znp.types as t import zigpy_znp.commands as c from zigpy_znp.exceptions import InvalidCommandResponse -from zigpy_znp.types.nvids import ExNvIds, OsalNvIds from zigpy_znp.tools.network_scan import main as network_scan +import zigpy_znp.types as t +from zigpy_znp.types.nvids import ExNvIds, OsalNvIds -from ..conftest import FormedZStack1CC2531, FormedLaunchpadCC26X2R1 +from ..conftest import FormedLaunchpadCC26X2R1, FormedZStack1CC2531 @pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1]) diff --git a/tests/tools/test_nvram.py b/tests/tools/test_nvram.py index 251245b8..13f60074 100644 --- a/tests/tools/test_nvram.py +++ b/tests/tools/test_nvram.py @@ -3,12 +3,12 @@ import pytest -import zigpy_znp.types as t import zigpy_znp.commands as c -from zigpy_znp.types.nvids import NWK_NVID_TABLES, ExNvIds, OsalNvIds from zigpy_znp.tools.nvram_read import main as nvram_read from zigpy_znp.tools.nvram_reset import main as nvram_reset from zigpy_znp.tools.nvram_write import main as nvram_write +import zigpy_znp.types as t +from zigpy_znp.types.nvids import NWK_NVID_TABLES, ExNvIds, OsalNvIds from ..conftest import ALL_DEVICES, BaseZStack1CC2531, FormedLaunchpadCC26X2R1 @@ -43,15 +43,15 @@ async def test_nvram_read(device, make_znp_server, tmp_path, mocker): znp_server = make_znp_server(server_cls=device) # Make one reaaally long, requiring multiple writes to read it - znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.HAS_CONFIGURED_ZSTACK3] = b"\xFF" * 300 + znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.HAS_CONFIGURED_ZSTACK3] = b"\xff" * 300 # Make a few secure but unreadable if issubclass(device, BaseZStack1CC2531): # Normal NVID - znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.TCLK_SEED] = b"\xFF" * 32 + znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.TCLK_SEED] = b"\xff" * 32 # Part of a table - znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.LEGACY_TCLK_TABLE_START] = b"\xFF" + znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.LEGACY_TCLK_TABLE_START] = b"\xff" # XXX: this is not a great way to do it but deepcopy won't work here old_nvram_repr = repr(znp_server._nvram) @@ -110,10 +110,10 @@ async def test_nvram_write(device, make_znp_server, tmp_path, mocker): znp_server._nvram = {ExNvIds.LEGACY: {}} # This has a differing length - znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.HAS_CONFIGURED_ZSTACK1] = b"\xEE\xEE" + znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.HAS_CONFIGURED_ZSTACK1] = b"\xee\xee" # This already exists - znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.HAS_CONFIGURED_ZSTACK3] = b"\xBB" + znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.HAS_CONFIGURED_ZSTACK3] = b"\xbb" await nvram_write([znp_server._port_path, "-i", str(backup_file)]) @@ -131,7 +131,7 @@ async def test_nvram_write(device, make_znp_server, tmp_path, mocker): @pytest.mark.parametrize("device", ALL_DEVICES) async def test_nvram_reset(device, make_znp_server): znp_server = make_znp_server(server_cls=device) - znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.STARTUP_OPTION] = b"\xFF" + znp_server._nvram[ExNvIds.LEGACY][OsalNvIds.STARTUP_OPTION] = b"\xff" await nvram_reset([znp_server._port_path]) diff --git a/zigpy_znp/api.py b/zigpy_znp/api.py index dbdf9cf9..bc1aa826 100644 --- a/zigpy_znp/api.py +++ b/zigpy_znp/api.py @@ -1,38 +1,38 @@ from __future__ import annotations -import os -import time -import typing import asyncio -import logging -import itertools +from collections import Counter, defaultdict import contextlib import dataclasses import importlib.metadata -from collections import Counter, defaultdict +import itertools +import logging +import os +import time +import typing -import zigpy.state -import zigpy.zdo.types as zdo_t +from zigpy.datastructures import PriorityLock import zigpy.exceptions from zigpy.exceptions import NetworkNotFormed -from zigpy.datastructures import PriorityLock +import zigpy.state +import zigpy.zdo.types as zdo_t -import zigpy_znp.const as const -import zigpy_znp.types as t +from zigpy_znp import uart +import zigpy_znp.commands as c import zigpy_znp.config as conf +import zigpy_znp.const as const +from zigpy_znp.exceptions import CommandNotRecognized, InvalidCommandResponse +from zigpy_znp.frames import GeneralFrame import zigpy_znp.logger as log -import zigpy_znp.commands as c -from zigpy_znp import uart from zigpy_znp.nvram import NVRAMHelper +import zigpy_znp.types as t +from zigpy_znp.types.nvids import ExNvIds, OsalNvIds from zigpy_znp.utils import ( - CatchAllResponse, BaseResponseListener, - OneShotResponseListener, CallbackResponseListener, + CatchAllResponse, + OneShotResponseListener, ) -from zigpy_znp.frames import GeneralFrame -from zigpy_znp.exceptions import CommandNotRecognized, InvalidCommandResponse -from zigpy_znp.types.nvids import ExNvIds, OsalNvIds if typing.TYPE_CHECKING: import typing_extensions @@ -297,7 +297,7 @@ async def start_network(self): # Both versions still end with this callback async with asyncio.timeout(STARTUP_TIMEOUT): await started_as_coordinator - except asyncio.TimeoutError as e: + except TimeoutError as e: raise zigpy.exceptions.FormationFailure( "Network formation refused: there is too much RF interference." " Make sure your coordinator is on a USB 2.0 extension cable and" @@ -667,7 +667,7 @@ async def ping_task(): try: async with asyncio.timeout(CONNECT_PING_TIMEOUT): return await self.request(c.SYS.Ping.Req()) - except asyncio.TimeoutError: + except TimeoutError: pass # If that doesn't work, send the bootloader skip bytes and try again. @@ -683,7 +683,7 @@ async def ping_task(): try: async with asyncio.timeout(2 * CONNECT_PING_TIMEOUT): return await self.request(c.SYS.Ping.Req()) - except asyncio.TimeoutError: + except TimeoutError: pass async with self.capture_responses([CatchAllResponse()]) as responses: @@ -707,7 +707,7 @@ async def ping_task(): try: async with asyncio.timeout(CONNECT_PING_TIMEOUT): result = await ping_task # type:ignore[misc] - except asyncio.TimeoutError: + except TimeoutError: ping_task.cancel() if isinstance(result, c.SYS.Ping.Rsp): @@ -939,14 +939,12 @@ def callback_for_response( @typing.overload def wait_for_responses( self, responses, *, context: typing_extensions.Literal[False] = ... - ) -> asyncio.Future: - ... + ) -> asyncio.Future: ... @typing.overload def wait_for_responses( self, responses, *, context: typing_extensions.Literal[True] - ) -> tuple[asyncio.Future, OneShotResponseListener]: - ... + ) -> tuple[asyncio.Future, OneShotResponseListener]: ... def wait_for_responses( self, responses, *, context: bool = False diff --git a/zigpy_znp/commands/__init__.py b/zigpy_znp/commands/__init__.py index b20c22fa..ac09ec7f 100644 --- a/zigpy_znp/commands/__init__.py +++ b/zigpy_znp/commands/__init__.py @@ -1,15 +1,15 @@ from .af import AF from .app import APP +from .app_config import AppConfig from .mac import MAC +from .rpc_error import RPCError +from .sapi import SAPI from .sys import SYS from .ubl import UBL +from .util import UTIL from .zdo import ZDO from .zgp import ZGP from .znp import ZNP -from .sapi import SAPI -from .util import UTIL -from .rpc_error import RPCError -from .app_config import AppConfig ALL_COMMANDS = [ RPCError, diff --git a/zigpy_znp/commands/ubl.py b/zigpy_znp/commands/ubl.py index 77da1074..027e636f 100644 --- a/zigpy_znp/commands/ubl.py +++ b/zigpy_znp/commands/ubl.py @@ -1,5 +1,4 @@ -"""Serial bootloader interface. Implemented in sb_exec_v2.c -""" +"""Serial bootloader interface. Implemented in sb_exec_v2.c""" import zigpy_znp.types as t diff --git a/zigpy_znp/commands/zdo.py b/zigpy_znp/commands/zdo.py index 982ee5f8..fdf0c7e0 100644 --- a/zigpy_znp/commands/zdo.py +++ b/zigpy_znp/commands/zdo.py @@ -516,8 +516,7 @@ class ZDO(t.CommandsBase, subsystem=t.Subsystem.ZDO): t.Param( "RemoveChildren_Rejoin", LeaveOptions, - "Specifies actions to be performed by " - "device when leaving the network.", + "Specifies actions to be performed by device when leaving the network.", ), ), rsp_schema=t.STATUS_SCHEMA, diff --git a/zigpy_znp/config.py b/zigpy_znp/config.py index 09183ac4..ceed7318 100644 --- a/zigpy_znp/config.py +++ b/zigpy_znp/config.py @@ -1,28 +1,28 @@ -import typing import numbers +import typing import voluptuous as vol from zigpy.config import ( # noqa: F401 - CONF_NWK, CONF_DEVICE, - CONF_NWK_KEY, - CONFIG_SCHEMA, - SCHEMA_DEVICE, - CONF_NWK_PAN_ID, CONF_DEVICE_PATH, + CONF_MAX_CONCURRENT_REQUESTS, + CONF_NWK, CONF_NWK_CHANNEL, CONF_NWK_CHANNELS, - CONF_NWK_UPDATE_ID, + CONF_NWK_EXTENDED_PAN_ID, + CONF_NWK_KEY, + CONF_NWK_PAN_ID, CONF_NWK_TC_ADDRESS, CONF_NWK_TC_LINK_KEY, - CONF_NWK_EXTENDED_PAN_ID, - CONF_MAX_CONCURRENT_REQUESTS, + CONF_NWK_UPDATE_ID, + CONFIG_SCHEMA, + SCHEMA_DEVICE, cv_boolean, ) from zigpy_znp.commands.util import LEDMode -ConfigType = typing.Dict[str, typing.Any] +ConfigType = dict[str, typing.Any] VolPositiveNumber = vol.All(numbers.Real, vol.Range(min=0)) diff --git a/zigpy_znp/frames.py b/zigpy_znp/frames.py index a21dafda..639d3afc 100644 --- a/zigpy_znp/frames.py +++ b/zigpy_znp/frames.py @@ -1,10 +1,10 @@ from __future__ import annotations -import functools import dataclasses +import functools -import zigpy_znp.types as t from zigpy_znp.exceptions import InvalidFrame +import zigpy_znp.types as t @dataclasses.dataclass(frozen=True) diff --git a/zigpy_znp/nvram.py b/zigpy_znp/nvram.py index 311e98fb..74570fe1 100644 --- a/zigpy_znp/nvram.py +++ b/zigpy_znp/nvram.py @@ -1,10 +1,10 @@ -import logging import itertools +import logging -import zigpy_znp.types as t import zigpy_znp.commands as c +from zigpy_znp.exceptions import InvalidCommandResponse, SecurityError +import zigpy_znp.types as t from zigpy_znp.types import nvids -from zigpy_znp.exceptions import SecurityError, InvalidCommandResponse LOGGER = logging.getLogger(__name__) diff --git a/zigpy_znp/tools/common.py b/zigpy_znp/tools/common.py index 33682548..6c9bebdd 100644 --- a/zigpy_znp/tools/common.py +++ b/zigpy_znp/tools/common.py @@ -1,15 +1,15 @@ from __future__ import annotations +import argparse +import logging import sys import typing -import logging -import argparse -import jsonschema import coloredlogs +import jsonschema -import zigpy_znp.types as t import zigpy_znp.logger as log +import zigpy_znp.types as t LOG_LEVELS = [logging.INFO, logging.DEBUG, log._TRACE] OPEN_COORDINATOR_BACKUP_SCHEMA = { diff --git a/zigpy_znp/tools/energy_scan.py b/zigpy_znp/tools/energy_scan.py index 64385e21..b8504904 100644 --- a/zigpy_znp/tools/energy_scan.py +++ b/zigpy_znp/tools/energy_scan.py @@ -1,14 +1,14 @@ -import sys import asyncio -import logging +from collections import defaultdict, deque import itertools -from collections import deque, defaultdict +import logging +import sys -import zigpy.zdo.types as zdo_t from zigpy.exceptions import NetworkNotFormed +import zigpy.zdo.types as zdo_t -import zigpy_znp.types as t from zigpy_znp.tools.common import setup_parser +import zigpy_znp.types as t from zigpy_znp.zigbee.application import ControllerApplication LOGGER = logging.getLogger(__name__) diff --git a/zigpy_znp/tools/flash_read.py b/zigpy_znp/tools/flash_read.py index ee893624..ccbd4507 100644 --- a/zigpy_znp/tools/flash_read.py +++ b/zigpy_znp/tools/flash_read.py @@ -1,9 +1,9 @@ -import sys import asyncio import logging +import sys -import zigpy_znp.commands as c from zigpy_znp.api import ZNP +import zigpy_znp.commands as c from zigpy_znp.config import CONFIG_SCHEMA from zigpy_znp.tools.common import ClosableFileType, setup_parser @@ -17,7 +17,7 @@ async def read_firmware(znp: ZNP) -> bytearray: request=c.UBL.HandshakeReq.Req(), callback=c.UBL.HandshakeRsp.Callback(partial=True), ) - except asyncio.TimeoutError: + except TimeoutError: raise RuntimeError( "Did not receive a bootloader handshake response!" " Make sure your adapter has just been plugged in and" diff --git a/zigpy_znp/tools/flash_write.py b/zigpy_znp/tools/flash_write.py index 56735471..7ca857d2 100644 --- a/zigpy_znp/tools/flash_write.py +++ b/zigpy_znp/tools/flash_write.py @@ -1,15 +1,15 @@ from __future__ import annotations -import sys import asyncio import logging +import sys -import zigpy_znp.types as t -import zigpy_znp.commands as c from zigpy_znp.api import ZNP +import zigpy_znp.commands as c from zigpy_znp.config import CONFIG_SCHEMA from zigpy_znp.tools.common import ClosableFileType, setup_parser from zigpy_znp.tools.nvram_reset import nvram_reset +import zigpy_znp.types as t LOGGER = logging.getLogger(__name__) @@ -73,7 +73,7 @@ async def write_firmware(znp: ZNP, firmware: bytes, reset_nvram: bool): request=c.UBL.HandshakeReq.Req(), callback=c.UBL.HandshakeRsp.Callback(partial=True), ) - except asyncio.TimeoutError: + except TimeoutError: raise RuntimeError( "Did not receive a bootloader handshake response!" " Make sure your adapter has just been plugged in and" diff --git a/zigpy_znp/tools/network_backup.py b/zigpy_znp/tools/network_backup.py index 1deade0e..06ec06dd 100644 --- a/zigpy_znp/tools/network_backup.py +++ b/zigpy_znp/tools/network_backup.py @@ -1,17 +1,17 @@ from __future__ import annotations -import sys -import json import asyncio -import logging import datetime import importlib.metadata +import json +import logging +import sys import zigpy.state -import zigpy_znp.types as t from zigpy_znp.api import ZNP from zigpy_znp.tools.common import ClosableFileType, setup_parser, validate_backup_json +import zigpy_znp.types as t from zigpy_znp.zigbee.application import ControllerApplication LIB_VERSION = importlib.metadata.version("zigpy-znp") diff --git a/zigpy_znp/tools/network_restore.py b/zigpy_znp/tools/network_restore.py index 543615d5..c703d211 100644 --- a/zigpy_znp/tools/network_restore.py +++ b/zigpy_znp/tools/network_restore.py @@ -1,16 +1,16 @@ from __future__ import annotations -import sys -import json import asyncio +import json +import sys import zigpy.state import zigpy.zdo.types as zdo_t -import zigpy_znp.const as const -import zigpy_znp.types as t from zigpy_znp.api import ZNP +import zigpy_znp.const as const from zigpy_znp.tools.common import ClosableFileType, setup_parser, validate_backup_json +import zigpy_znp.types as t from zigpy_znp.zigbee.application import ControllerApplication diff --git a/zigpy_znp/tools/network_scan.py b/zigpy_znp/tools/network_scan.py index c48a79b3..7f52b462 100644 --- a/zigpy_znp/tools/network_scan.py +++ b/zigpy_znp/tools/network_scan.py @@ -1,15 +1,15 @@ -import sys -import time import asyncio -import logging import itertools +import logging +import sys +import time -import zigpy_znp.types as t -import zigpy_znp.commands as c from zigpy_znp.api import ZNP +import zigpy_znp.commands as c from zigpy_znp.config import CONFIG_SCHEMA -from zigpy_znp.types.nvids import OsalNvIds from zigpy_znp.tools.common import setup_parser +import zigpy_znp.types as t +from zigpy_znp.types.nvids import OsalNvIds LOGGER = logging.getLogger(__name__) diff --git a/zigpy_znp/tools/nvram_read.py b/zigpy_znp/tools/nvram_read.py index 29ed5a0d..080bc8ff 100644 --- a/zigpy_znp/tools/nvram_read.py +++ b/zigpy_znp/tools/nvram_read.py @@ -1,14 +1,14 @@ -import sys -import json import asyncio +import json import logging +import sys -import zigpy_znp.types as t from zigpy_znp.api import ZNP from zigpy_znp.config import CONFIG_SCHEMA -from zigpy_znp.exceptions import SecurityError, CommandNotRecognized -from zigpy_znp.types.nvids import NWK_NVID_TABLES, ExNvIds, OsalNvIds +from zigpy_znp.exceptions import CommandNotRecognized, SecurityError from zigpy_znp.tools.common import ClosableFileType, setup_parser +import zigpy_znp.types as t +from zigpy_znp.types.nvids import NWK_NVID_TABLES, ExNvIds, OsalNvIds LOGGER = logging.getLogger(__name__) diff --git a/zigpy_znp/tools/nvram_reset.py b/zigpy_znp/tools/nvram_reset.py index 31caa78f..f14b45fc 100644 --- a/zigpy_znp/tools/nvram_reset.py +++ b/zigpy_znp/tools/nvram_reset.py @@ -1,16 +1,16 @@ -import sys import asyncio import logging +import sys from zigpy_znp.api import ZNP from zigpy_znp.config import CONFIG_SCHEMA +from zigpy_znp.tools.common import setup_parser from zigpy_znp.types.nvids import ( - NWK_NVID_TABLES, NWK_NVID_TABLE_KEYS, + NWK_NVID_TABLES, ExNvIds, OsalNvIds, ) -from zigpy_znp.tools.common import setup_parser LOGGER = logging.getLogger(__name__) diff --git a/zigpy_znp/tools/nvram_write.py b/zigpy_znp/tools/nvram_write.py index f672f0a9..8b72a0cc 100644 --- a/zigpy_znp/tools/nvram_write.py +++ b/zigpy_znp/tools/nvram_write.py @@ -1,12 +1,12 @@ -import sys -import json import asyncio +import json import logging +import sys from zigpy_znp.api import ZNP from zigpy_znp.config import CONFIG_SCHEMA -from zigpy_znp.types.nvids import ExNvIds, OsalNvIds from zigpy_znp.tools.common import ClosableFileType, setup_parser +from zigpy_znp.types.nvids import ExNvIds, OsalNvIds LOGGER = logging.getLogger(__name__) diff --git a/zigpy_znp/types/__init__.py b/zigpy_znp/types/__init__.py index 77573f39..02648c2a 100644 --- a/zigpy_znp/types/__init__.py +++ b/zigpy_znp/types/__init__.py @@ -1,7 +1,7 @@ from .zigpy_types import * # noqa: F401, F403 # isort:skip from .basic import * # noqa: F401, F403 -from .named import * # noqa: F401, F403 +from .commands import * # noqa: F401, F403 from .cstruct import * # noqa: F401, F403 +from .named import * # noqa: F401, F403 from .structs import * # noqa: F401, F403 -from .commands import * # noqa: F401, F403 diff --git a/zigpy_znp/types/basic.py b/zigpy_znp/types/basic.py index bdefd4fd..53e8de89 100644 --- a/zigpy_znp/types/basic.py +++ b/zigpy_znp/types/basic.py @@ -32,13 +32,13 @@ class bitmap16(enum.IntFlag): else: from zigpy.types import ( # noqa: F401 + bitmap8, + bitmap16, enum8, enum16, enum24, enum40, enum64, - bitmap8, - bitmap16, uint16_t, uint24_t, uint32_t, diff --git a/zigpy_znp/types/commands.py b/zigpy_znp/types/commands.py index 83acf954..03933f2c 100644 --- a/zigpy_znp/types/commands.py +++ b/zigpy_znp/types/commands.py @@ -1,9 +1,9 @@ from __future__ import annotations +import dataclasses import enum -import typing import logging -import dataclasses +import typing import zigpy.zdo.types @@ -307,7 +307,7 @@ def __init__(self, *, partial=False, **params): all_params = [p.name for p in self.schema] optional_params = [p.name for p in self.schema if p.optional] given_params = set(params.keys()) - given_optional = [p for p in params.keys() if p in optional_params] + given_optional = [p for p in params if p in optional_params] unknown_params = given_params - set(all_params) missing_params = (set(all_params) - set(optional_params)) - given_params @@ -503,7 +503,7 @@ def __delattr__(self, key): def __repr__(self): params = [f"{p.name}={v!r}" for p, v in self._bound_params.values()] - return f'{self.__class__.__qualname__}({", ".join(params)})' + return f"{self.__class__.__qualname__}({', '.join(params)})" __str__ = __repr__ diff --git a/zigpy_znp/types/cstruct.py b/zigpy_znp/types/cstruct.py index 7ef6f7b8..810edf08 100644 --- a/zigpy_znp/types/cstruct.py +++ b/zigpy_znp/types/cstruct.py @@ -1,8 +1,8 @@ from __future__ import annotations -import typing -import inspect import dataclasses +import inspect +import typing import zigpy.types as zigpy_t @@ -40,7 +40,7 @@ def get_size_and_alignment(self, align=False) -> tuple[int, int]: class CStruct: - _padding_byte = b"\xFF" + _padding_byte = b"\xff" def __init_subclass__(cls): super().__init_subclass__() diff --git a/zigpy_znp/types/named.py b/zigpy_znp/types/named.py index e85af7e3..88888f83 100644 --- a/zigpy_znp/types/named.py +++ b/zigpy_znp/types/named.py @@ -1,9 +1,9 @@ from __future__ import annotations +import dataclasses import enum -import typing import logging -import dataclasses +import typing import zigpy.types from zigpy.zdo.types import Status as ZDOStatus # noqa: F401 @@ -12,7 +12,7 @@ LOGGER = logging.getLogger(__name__) -JSONType = typing.Dict[str, typing.Any] +JSONType = dict[str, typing.Any] class AddrMode(basic.enum8): diff --git a/zigpy_znp/types/zigpy_types.py b/zigpy_znp/types/zigpy_types.py index 91cfb100..6d7b8dbd 100644 --- a/zigpy_znp/types/zigpy_types.py +++ b/zigpy_znp/types/zigpy_types.py @@ -1,13 +1,13 @@ from zigpy.types import ( # noqa: F401 - NWK, EUI64, + NWK, Bool, - PanId, - Struct, - KeyData, Channels, + CharacterString, ClusterId, ExtendedPanId, - CharacterString, + KeyData, + PanId, SerializableBytes, + Struct, ) diff --git a/zigpy_znp/uart.py b/zigpy_znp/uart.py index cc1e0217..24c9c11e 100644 --- a/zigpy_znp/uart.py +++ b/zigpy_znp/uart.py @@ -1,17 +1,17 @@ from __future__ import annotations -import typing import asyncio import logging +import typing import zigpy.config from zigpy.serial import SerialProtocol, create_serial_connection import zigpy_znp.config as conf +from zigpy_znp.exceptions import InvalidFrame import zigpy_znp.frames as frames import zigpy_znp.logger as log from zigpy_znp.types import Bytes -from zigpy_znp.exceptions import InvalidFrame LOGGER = logging.getLogger(__name__) @@ -128,12 +128,7 @@ def _extract_frame(self) -> frames.TransportFrame: return frame def __repr__(self) -> str: - return ( - f"<" - f"{type(self).__name__} connected to {self.url!r}" - f" (api: {self._api})" - f">" - ) + return f"<{type(self).__name__} connected to {self.url!r} (api: {self._api})>" async def connect(config: conf.ConfigType, api) -> ZnpMtProtocol: diff --git a/zigpy_znp/utils.py b/zigpy_znp/utils.py index 04d05ef1..b9dd259b 100644 --- a/zigpy_znp/utils.py +++ b/zigpy_znp/utils.py @@ -1,11 +1,11 @@ from __future__ import annotations -import typing import asyncio +import dataclasses +import functools import inspect import logging -import functools -import dataclasses +import typing import zigpy_znp.types as t diff --git a/zigpy_znp/zigbee/application.py b/zigpy_znp/zigbee/application.py index 8bf8b3cf..b9e3b345 100644 --- a/zigpy_znp/zigbee/application.py +++ b/zigpy_znp/zigbee/application.py @@ -1,30 +1,30 @@ from __future__ import annotations -import os import asyncio import logging +import os -import zigpy.zcl -import zigpy.zdo -import zigpy.util -import zigpy.state -import zigpy.types +import zigpy.application import zigpy.config +import zigpy.config.defaults import zigpy.device +from zigpy.exceptions import DeliveryError import zigpy.profiles +import zigpy.state +import zigpy.types +import zigpy.util +import zigpy.zcl +import zigpy.zdo import zigpy.zdo.types as zdo_t -import zigpy.application -import zigpy.config.defaults -from zigpy.exceptions import DeliveryError -import zigpy_znp.const as const -import zigpy_znp.types as t -import zigpy_znp.config as conf -import zigpy_znp.commands as c from zigpy_znp.api import ZNP -from zigpy_znp.utils import combine_concurrent_calls +import zigpy_znp.commands as c +import zigpy_znp.config as conf +import zigpy_znp.const as const from zigpy_znp.exceptions import CommandNotRecognized, InvalidCommandResponse +import zigpy_znp.types as t from zigpy_znp.types.nvids import OsalNvIds +from zigpy_znp.utils import combine_concurrent_calls from zigpy_znp.zigbee.device import ZNPCoordinator ZDO_ENDPOINT = 0 @@ -607,7 +607,7 @@ async def _set_led_mode(self, *, led: t.uint8_t, mode: c.util.LEDMode) -> None: c.UTIL.LEDControl.Req(LED=led, Mode=mode), RspStatus=t.Status.SUCCESS, ) - except (asyncio.TimeoutError, CommandNotRecognized): + except (TimeoutError, CommandNotRecognized): LOGGER.info("This build of Z-Stack does not appear to support LED control") async def _write_stack_settings(self) -> bool: @@ -779,8 +779,7 @@ async def _send_request_raw( ) ) or ( # Or a direct unicast request - dst_addr.mode == t.AddrMode.NWK - and dst_addr.address == self._device.nwk + dst_addr.mode == t.AddrMode.NWK and dst_addr.address == self._device.nwk ): self.packet_received( zigpy.types.ZigbeePacket( diff --git a/zigpy_znp/zigbee/device.py b/zigpy_znp/zigbee/device.py index 3e414076..3f9d0bc6 100644 --- a/zigpy_znp/zigbee/device.py +++ b/zigpy_znp/zigbee/device.py @@ -2,9 +2,9 @@ import logging -import zigpy.zdo -import zigpy.device import zigpy.application +import zigpy.device +import zigpy.zdo LOGGER = logging.getLogger(__name__) diff --git a/zigpy_znp/znp/security.py b/zigpy_znp/znp/security.py index bc909fd0..9aba120b 100644 --- a/zigpy_znp/znp/security.py +++ b/zigpy_znp/znp/security.py @@ -1,15 +1,15 @@ from __future__ import annotations -import typing -import logging import dataclasses +import logging +import typing import zigpy.state import zigpy.zdo.types as zdo_t +from zigpy_znp.api import ZNP import zigpy_znp.const as const import zigpy_znp.types as t -from zigpy_znp.api import ZNP from zigpy_znp.types.nvids import ExNvIds, OsalNvIds LOGGER = logging.getLogger(__name__) @@ -264,9 +264,7 @@ async def read_devices( if entry.extAddr in ( t.EUI64.convert("00:00:00:00:00:00:00:00"), t.EUI64.convert("FF:FF:FF:FF:FF:FF:FF:FF"), - ): - continue - elif entry.type in ( + ) or entry.type in ( t.AddrMgrUserType.Default, t.AddrMgrUserType.Binding, ): From 927fc8da58eb65def2208c7967ab4af845cdccd7 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 8 Sep 2026 17:33:48 +0000 Subject: [PATCH 3/4] Fix issues --- pyproject.toml | 11 +++-------- tests/conftest.py | 2 +- tests/test_commands.py | 2 +- zigpy_znp/api.py | 13 +++++++------ zigpy_znp/commands/sys.py | 2 +- zigpy_znp/nvram.py | 2 +- zigpy_znp/tools/energy_scan.py | 2 +- zigpy_znp/tools/flash_read.py | 4 ++-- zigpy_znp/tools/flash_write.py | 4 ++-- zigpy_znp/types/__init__.py | 10 ++++++---- zigpy_znp/types/commands.py | 12 +++++++----- zigpy_znp/types/nvids.py | 9 +++------ zigpy_znp/types/structs.py | 2 +- zigpy_znp/zigbee/application.py | 4 +++- zigpy_znp/znp/security.py | 8 ++++++-- 15 files changed, 45 insertions(+), 42 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ac8beed8..9f314683 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,14 +81,8 @@ select = [ "W", # pycodestyle ] -ignore = [ - "B008", # Do not perform function call in argument defaults - "C901", # Function is too complex - "E501", # Line too long - "SIM102", # Use a single if statement instead of nested if statements - "SIM108", # Use ternary operator instead of if-else-block - "SIM117", # Merge with-statements that use the same scope -] +[tool.ruff.lint.mccabe] +max-complexity = 25 [tool.ruff.lint.isort] force-sort-within-sections = true @@ -106,6 +100,7 @@ skip = ["./.*", "tests/*", "pyproject.toml"] quiet-level = 2 [tool.mypy] +ignore_missing_imports = true check_untyped_defs = true show_error_codes = true show_error_context = true diff --git a/tests/conftest.py b/tests/conftest.py index 565e3a5c..58b26904 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -417,7 +417,7 @@ def deserialize_zdo_command(command_id, data): field_names, field_types = zdo_t.CLUSTERS[command_id] args, data = zigpy.types.deserialize(data, field_types) - return dict(zip(field_names, args)) + return dict(zip(field_names, args, strict=True)) class BaseZStackDevice(BaseServerZNP): diff --git a/tests/test_commands.py b/tests/test_commands.py index 20e517e2..d3376713 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -214,7 +214,7 @@ def test_command_param_binding(): # Invalid ones cannot with pytest.raises(AttributeError): - ping_rsp.Oops + _ = ping_rsp.Oops # bytes are converted into t.ShortBytes cmd = c.SYS.NVWrite.Req( diff --git a/zigpy_znp/api.py b/zigpy_znp/api.py index bc1aa826..e55d5a7f 100644 --- a/zigpy_znp/api.py +++ b/zigpy_znp/api.py @@ -366,7 +366,7 @@ async def write_network_info( raise zigpy.exceptions.FormationFailure( "Network formation failed: NVRAM is corrupted, re-flash your adapter's" " firmware." - ) + ) from rsp # Form a network with completely random settings to get NVRAM to a known state for item, value in { @@ -375,7 +375,7 @@ async def write_network_info( OsalNvIds.PRECFGKEY: os.urandom(16), # XXX: Z2M requires this item to be False OsalNvIds.PRECFGKEYS_ENABLE: t.Bool(False), - # Z-Stack will scan all of thse channels during formation + # Z-Stack will scan all of these channels during formation OsalNvIds.CHANLIST: const.STARTUP_CHANNELS, }.items(): await self.nvram.osal_write(item, value, create=True) @@ -608,7 +608,9 @@ async def migrate_nvram(self) -> bool: if entries != fixed_entries: LOGGER.warning( "Repairing %d invalid empty address manager entries (total %d)", - sum(i != j for i, j in zip(entries, fixed_entries)), + sum( + i != j for i, j in zip(entries, fixed_entries, strict=True) + ), len(entries), ) await security.write_addr_manager_entries(self, fixed_entries) @@ -659,6 +661,7 @@ async def ping_task(): for dtr, rts in zip( self._znp_config[conf.CONF_CONNECT_DTR_STATES], self._znp_config[conf.CONF_CONNECT_RTS_STATES], + strict=True, ): await self._uart.set_dtr_rts(dtr=dtr, rts=rts) await asyncio.sleep(BOOTLOADER_PIN_TOGGLE_DELAY) @@ -812,10 +815,8 @@ def remove_listener(self, listener: BaseResponseListener) -> None: LOGGER.log(log.TRACE, "Removing listener %s", listener) for header in listener.matching_headers(): - try: + with contextlib.suppress(ValueError): self._listeners[header].remove(listener) - except ValueError: - pass if not self._listeners[header]: LOGGER.log( diff --git a/zigpy_znp/commands/sys.py b/zigpy_znp/commands/sys.py index 3a8968d5..8dcf08e7 100644 --- a/zigpy_znp/commands/sys.py +++ b/zigpy_znp/commands/sys.py @@ -114,7 +114,7 @@ class SYS(t.CommandsBase, subsystem=t.Subsystem.SYS): t.Param( "Capabilities", t.MTCapabilities, - "Represents the intefaces this device can handle", + "Represents the interfaces this device can handle", ), ), ) diff --git a/zigpy_znp/nvram.py b/zigpy_znp/nvram.py index 74570fe1..7c3afcc8 100644 --- a/zigpy_znp/nvram.py +++ b/zigpy_znp/nvram.py @@ -190,7 +190,7 @@ async def osal_read(self, nv_id: t.uint16_t, *, item_type): if not self.znp.capabilities & t.MTCapabilities.SAPI or nv_id > 0xFF: raise SecurityError( f"NV item cannot be read due to security constraints: {nv_id!r}" - ) + ) from e read_rsp = await self.znp.request( c.SAPI.ZBReadConfiguration.Req(ConfigId=nv_id), diff --git a/zigpy_znp/tools/energy_scan.py b/zigpy_znp/tools/energy_scan.py index b8504904..c0ed4a7e 100644 --- a/zigpy_znp/tools/energy_scan.py +++ b/zigpy_znp/tools/energy_scan.py @@ -47,7 +47,7 @@ async def perform_energy_scan(radio_path, num_scans=None): _, scanned_channels, _, _, energy_values = rsp - for channel, energy in zip(scanned_channels, energy_values): + for channel, energy in zip(scanned_channels, energy_values, strict=False): energies = channel_energies[channel] energies.append(energy) diff --git a/zigpy_znp/tools/flash_read.py b/zigpy_znp/tools/flash_read.py index ccbd4507..51577101 100644 --- a/zigpy_znp/tools/flash_read.py +++ b/zigpy_znp/tools/flash_read.py @@ -17,13 +17,13 @@ async def read_firmware(znp: ZNP) -> bytearray: request=c.UBL.HandshakeReq.Req(), callback=c.UBL.HandshakeRsp.Callback(partial=True), ) - except TimeoutError: + except TimeoutError as e: raise RuntimeError( "Did not receive a bootloader handshake response!" " Make sure your adapter has just been plugged in and" " nothing else has had a chance to communicate with it. Alternatively," " press the button furthest from the USB port. The LED should turn red." - ) + ) from e if handshake_rsp.Status != c.ubl.BootloaderStatus.SUCCESS: raise RuntimeError(f"Bad bootloader handshake response: {handshake_rsp}") diff --git a/zigpy_znp/tools/flash_write.py b/zigpy_znp/tools/flash_write.py index 7ca857d2..9606e884 100644 --- a/zigpy_znp/tools/flash_write.py +++ b/zigpy_znp/tools/flash_write.py @@ -73,13 +73,13 @@ async def write_firmware(znp: ZNP, firmware: bytes, reset_nvram: bool): request=c.UBL.HandshakeReq.Req(), callback=c.UBL.HandshakeRsp.Callback(partial=True), ) - except TimeoutError: + except TimeoutError as e: raise RuntimeError( "Did not receive a bootloader handshake response!" " Make sure your adapter has just been plugged in and" " nothing else has had a chance to communicate with it. Alternatively, " " press the button furthest from the USB port. The LED should turn red." - ) + ) from e if handshake_rsp.Status != c.ubl.BootloaderStatus.SUCCESS: raise RuntimeError(f"Bad bootloader handshake response: {handshake_rsp}") diff --git a/zigpy_znp/types/__init__.py b/zigpy_znp/types/__init__.py index 02648c2a..b14590f5 100644 --- a/zigpy_znp/types/__init__.py +++ b/zigpy_znp/types/__init__.py @@ -1,7 +1,9 @@ -from .zigpy_types import * # noqa: F401, F403 # isort:skip - +# isort: off +# These star imports must stay in dependency order: each module uses names re-exported +# by the ones above it. +from .zigpy_types import * # noqa: F401, F403 from .basic import * # noqa: F401, F403 -from .commands import * # noqa: F401, F403 -from .cstruct import * # noqa: F401, F403 from .named import * # noqa: F401, F403 +from .cstruct import * # noqa: F401, F403 from .structs import * # noqa: F401, F403 +from .commands import * # noqa: F401, F403 diff --git a/zigpy_znp/types/commands.py b/zigpy_znp/types/commands.py index 03933f2c..002826c7 100644 --- a/zigpy_znp/types/commands.py +++ b/zigpy_znp/types/commands.py @@ -415,7 +415,7 @@ def from_frame(cls, frame, *, align=False) -> CommandBase: params[param.name], data = param.type.deserialize(data, align=align) else: params[param.name], data = param.type.deserialize(data) - except ValueError: + except ValueError as e: if not data and param.optional: # If we're out of data and the parameter is optional, we're done break @@ -424,7 +424,7 @@ def from_frame(cls, frame, *, align=False) -> CommandBase: raise ValueError( f"Frame data is truncated (parsed {params})," f" required parameter remains: {param}" - ) + ) from e else: # Otherwise, let the exception happen raise @@ -442,7 +442,9 @@ def matches(self, other: CommandBase) -> bool: assert self.header == other.header - param_pairs = zip(self._bound_params.values(), other._bound_params.values()) + param_pairs = zip( + self._bound_params.values(), other._bound_params.values(), strict=True + ) for ( (expected_param, expected_value), @@ -539,9 +541,9 @@ class DeviceState(t.enum8): BackoffBeforeRejoin = 0x0C # ReJoining a PAN in secure mode scanning in all channels, only for end devices RejoinSecureScanningAllChannels = 0x0D - # ReJoining a PAN in unsecure mode scanning in current channel, only for end devices + # ReJoining a PAN in insecure mode scanning in current channel, only for end devices RejoinInsecureScanningCurrentChannel = 0x0E - # ReJoining a PAN in unsecure mode scanning in all channels, only for end devices + # ReJoining a PAN in insecure mode scanning in all channels, only for end devices RejoinInsecureScanningAllChannels = 0x0F diff --git a/zigpy_znp/types/nvids.py b/zigpy_znp/types/nvids.py index b12a31b6..92672e17 100644 --- a/zigpy_znp/types/nvids.py +++ b/zigpy_znp/types/nvids.py @@ -200,7 +200,7 @@ class OsalNvIds(BaseNvIds): TCLK_JOIN_DEV = ( 0x0102 # Nv Id where Joining device store their APS key. Key is in plain text. ) - TCLK_DEFAULT = 0x0103 # Not accually a Nv Item but Id used by SecMgr + TCLK_DEFAULT = 0x0103 # Not actually a Nv Item but Id used by SecMgr LEGACY_TCLK_IC_TABLE_START = 0x0104 # Deprecated. Refer to EX_TCLK_IC_TABLE LEGACY_TCLK_IC_TABLE_END = 0x0110 # IC keys, referred with shift byte @@ -275,11 +275,8 @@ def is_secure_nvid(nvid: OsalNvIds) -> bool: if OsalNvIds.LEGACY_TCLK_TABLE_START <= nvid <= OsalNvIds.LEGACY_TCLK_TABLE_END: return True - if ( + return ( OsalNvIds.LEGACY_APS_LINK_KEY_DATA_START <= nvid <= OsalNvIds.LEGACY_APS_LINK_KEY_DATA_END - ): - return True - - return False + ) diff --git a/zigpy_znp/types/structs.py b/zigpy_znp/types/structs.py index 706a3367..8f03c868 100644 --- a/zigpy_znp/types/structs.py +++ b/zigpy_znp/types/structs.py @@ -222,7 +222,7 @@ class APSLinkKeyTable( class LinkInfo(cstruct.CStruct): # Counter of transmission success/failures txCounter: basic.uint8_t - # Average of sending rssi values if link staus is enabled + # Average of sending rssi values if link status is enabled # i.e. NWK_LINK_STATUS_PERIOD is defined as non zero txCost: basic.uint8_t # average of received rssi values. diff --git a/zigpy_znp/zigbee/application.py b/zigpy_znp/zigbee/application.py index b9e3b345..be95a9ae 100644 --- a/zigpy_znp/zigbee/application.py +++ b/zigpy_znp/zigbee/application.py @@ -996,7 +996,9 @@ async def send_packet(self, packet: zigpy.types.ZigbeePacket) -> None: raise last_error except InvalidCommandResponse as e: status = e.response.Status - raise DeliveryError(f"Failed to send request: {status!r}", status=status) + raise DeliveryError( + f"Failed to send request: {status!r}", status=status + ) from e finally: # We *must* re-add the device association if we previously removed it but # the request still failed. Otherwise, it may be a direct child and we will diff --git a/zigpy_znp/znp/security.py b/zigpy_znp/znp/security.py index 9aba120b..9779c604 100644 --- a/zigpy_znp/znp/security.py +++ b/zigpy_znp/znp/security.py @@ -31,11 +31,15 @@ def rotate(lst: list, n: int) -> list: def compute_key(ieee: t.EUI64, tclk_seed: t.KeyData, shift: int) -> t.KeyData: rotated_tclk_seed = rotate(tclk_seed, n=shift) - return t.KeyData([a ^ b for a, b in zip(rotated_tclk_seed, 2 * ieee.serialize())]) + return t.KeyData( + [a ^ b for a, b in zip(rotated_tclk_seed, 2 * ieee.serialize(), strict=True)] + ) def compute_tclk_seed(ieee: t.EUI64, key: t.KeyData, shift: int) -> t.KeyData: - rotated_tclk_seed = bytes(a ^ b for a, b in zip(key, 2 * ieee.serialize())) + rotated_tclk_seed = bytes( + a ^ b for a, b in zip(key, 2 * ieee.serialize(), strict=True) + ) return t.KeyData(rotate(rotated_tclk_seed, n=-shift)) From 2ff442740b9115cb053b3b91e6d58bda31ce1261 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Tue, 8 Sep 2026 17:37:22 +0000 Subject: [PATCH 4/4] Switch to mypy mirror for pre-commit --- .pre-commit-config.yaml | 11 +++++------ pyproject.toml | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0f49222b..755e6f56 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,12 +14,11 @@ repos: additional_dependencies: [tomli] args: ["--toml", "pyproject.toml"] - - repo: local + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v2.3.1 hooks: - id: mypy - name: mypy - entry: mypy - language: system - types_or: [python, pyi] - require_serial: true + additional_dependencies: + - zigpy>=1.5.0 + - types-setuptools exclude: ^tests/ diff --git a/pyproject.toml b/pyproject.toml index 9f314683..d3153d28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ exclude = ["tests", "tests.*"] [dependency-groups] testing = [ - "mypy==1.19.1", + "mypy==2.3.1", "pre-commit", "coverage[toml]", "pytest>=7.3.1", @@ -95,7 +95,6 @@ combine-as-imports = true split-on-trailing-comma = false [tool.codespell] -ignore-words-list = ["ser", "nd", "hass", "checkin", "SUBSTRUCT", "substruct"] skip = ["./.*", "tests/*", "pyproject.toml"] quiet-level = 2