diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index df00524b9..ccc20f597 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,10 @@ repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.292 + hooks: + - id: ruff + files: ^(scripts|test|zwave_js_server)/.+\.py$ + args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/psf/black-pre-commit-mirror rev: 23.9.1 hooks: @@ -17,11 +23,6 @@ repos: hooks: - id: pydocstyle files: ^(zwave_js_server|test)/.+\.py$ - - repo: https://github.com/pycqa/flake8 - rev: 6.1.0 - hooks: - - id: flake8 - files: ^((zwave_js_server|test)/.+)?[^/]+\.py$ - repo: local hooks: - id: mypy @@ -33,7 +34,7 @@ repos: files: ^(zwave_js_server)/.+\.py$ - id: pylint name: pylint - entry: scripts/run-in-env.sh pylint -j 0 --ignore-missing-annotations=y + entry: scripts/run-in-env.sh pylint -j 0 language: script types: [python] files: ^zwave_js_server/.+\.py$ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..1b56881dd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,50 @@ +[tool.ruff] +select = [ + "D", + "E", + "F", + "G", + "I", + "PLC", + "PLE", + "PLR", + "PLW", + "UP", + "W", +] +ignore = [ + "D202", + "D212", + "D203", + "D213", + "E501", + "PLR0911", # Too many return statements ({returns} > {max_returns}) + "PLR0912", # Too many branches ({branches} > {max_branches}) + "PLR0913", # Too many arguments to function call ({c_args} > {max_args}) + "PLR0915", # Too many statements ({statements} > {max_statements}) + "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable + "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 +] +exclude = [ + ".venv", + ".git", + ".tox", + "docs", + "venv", + "bin", + "lib", + "deps", + "build", +] +line-length = 88 + +[tool.ruff.isort] +force-sort-within-sections = true +known-first-party = [ + "zwave_js_server", +] +combine-as-imports = true +split-on-trailing-comma = false +case-sensitive = true diff --git a/requirements_lint.txt b/requirements_lint.txt index b3d874e95..43cc07008 100644 --- a/requirements_lint.txt +++ b/requirements_lint.txt @@ -1,7 +1,6 @@ black==23.9.1 -flake8==6.1.0 mypy==1.5.1 -pydocstyle==6.3.0 pylint==3.0.1 pylint-strict-informational==0.1 pre-commit==3.5.0 +ruff==0.0.292 \ No newline at end of file diff --git a/scripts/generate_multilevel_sensor_constants.py b/scripts/generate_multilevel_sensor_constants.py index 56bf025b3..4fabb9fa9 100755 --- a/scripts/generate_multilevel_sensor_constants.py +++ b/scripts/generate_multilevel_sensor_constants.py @@ -2,12 +2,13 @@ """Script to generate Multilevel Sensor CC constants.""" from __future__ import annotations +from collections import defaultdict +from collections.abc import Callable, Mapping import json import pathlib import re import subprocess -from collections import defaultdict -from collections.abc import Callable, Mapping +import sys import requests from slugify import slugify @@ -271,7 +272,7 @@ def generate_int_enum_base_class(class_name: str, docstring: str) -> list[str]: is not None ): print("Repo is dirty and needs to be committed!") - exit(1) + sys.exit(1) else: print( "Could not run `git diff --stat` on repo, please run it to determine whether " diff --git a/scripts/generate_notification_constants.py b/scripts/generate_notification_constants.py index 9e40fc604..6d058283a 100755 --- a/scripts/generate_notification_constants.py +++ b/scripts/generate_notification_constants.py @@ -2,11 +2,12 @@ """Script to generate Notification CC constants.""" from __future__ import annotations +from collections.abc import Callable, Mapping import json import pathlib import re import subprocess -from collections.abc import Callable, Mapping +import sys import requests from slugify import slugify @@ -308,7 +309,7 @@ def generate_int_enum_base_class(class_name: str, docstring: str) -> list[str]: is not None ): print("Repo is dirty and needs to be committed!") - exit(1) + sys.exit(1) else: print( "Could not run `git diff --stat` on repo, please run it to determine whether " diff --git a/scripts/run_mock_server.py b/scripts/run_mock_server.py index 2b12facb8..50ab1ea7d 100644 --- a/scripts/run_mock_server.py +++ b/scripts/run_mock_server.py @@ -3,10 +3,10 @@ import argparse import asyncio -import json -import logging from collections import defaultdict from collections.abc import Hashable +import json +import logging from typing import Any from aiohttp import WSMsgType, web, web_request @@ -28,12 +28,15 @@ class HashableDict(dict): """Dictionary that can be used as a key in a dictionary.""" def __key(self) -> tuple: + """Return key representation of HashableDict.""" return tuple((k, self[k]) for k in sorted(self)) def __hash__(self) -> int: # type: ignore + """Return hash representation of HashableDict.""" return hash(self.__key()) def __eq__(self, other: Any) -> bool: + """Return whether HashableDict is equal to other.""" # pylint: disable=protected-access return isinstance(other, HashableDict) and self.__key() == other.__key() @@ -292,14 +295,14 @@ def main() -> None: """Run main entrypoint.""" args = get_args() - with open(args.network_state_path, "r", encoding="utf8") as fp: + with open(args.network_state_path, encoding="utf8") as fp: network_state_dump: list[dict] = json.load(fp) events_to_replay = [] command_results: defaultdict[HashableDict, list] = defaultdict(list) if args.combined_replay_dump_path: - with open(args.combined_replay_dump_path, "r", encoding="utf8") as fp: + with open(args.combined_replay_dump_path, encoding="utf8") as fp: records: list[dict] = json.load(fp) for record in records: @@ -313,7 +316,7 @@ def main() -> None: add_command_result(command_results, record) if args.events_to_replay_path: - with open(args.events_to_replay_path, "r", encoding="utf8") as fp: + with open(args.events_to_replay_path, encoding="utf8") as fp: records = json.load(fp) if ( bad_record := next( @@ -331,7 +334,7 @@ def main() -> None: events_to_replay.extend([record["event_msg"] for record in records]) if args.command_results_path: - with open(args.command_results_path, "r", encoding="utf8") as fp: + with open(args.command_results_path, encoding="utf8") as fp: records = json.load(fp) if ( bad_record := next( diff --git a/setup.cfg b/setup.cfg index 07ded6bd0..91c1cf195 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,26 +1,3 @@ -[flake8] -exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build -# To work with Black -max-line-length = 88 -# E501: line too long -# W503: Line break occurred before a binary operator -# E203: Whitespace before ':' -# D202 No blank lines allowed after function docstring -# W504 line break after binary operator -ignore = - E501, - W503, - E203, - D202, - W504 - -[isort] -multi_line_output = 3 -include_trailing_comma = True -force_grid_wrap = 0 -use_parentheses = True -line_length = 88 - [mypy] plugins = pydantic.mypy follow_imports = skip @@ -39,8 +16,5 @@ warn_unused_configs = true [mypy-test.*,] ignore_errors = true -[pydocstyle] -add-ignore = D202 - [tool:pytest] asyncio_mode = auto diff --git a/test/conftest.py b/test/conftest.py index b00a986a3..3ca94a506 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,13 +1,13 @@ """Provide common pytest fixtures.""" import asyncio -import json from collections import deque from copy import deepcopy +import json from unittest.mock import AsyncMock, Mock, patch -import pytest from aiohttp import ClientSession, ClientWebSocketResponse from aiohttp.http_websocket import WSMessage, WSMsgType +import pytest from zwave_js_server.client import Client from zwave_js_server.model.controller import Controller diff --git a/test/model/test_controller.py b/test/model/test_controller.py index d64af9b79..4935ac96d 100644 --- a/test/model/test_controller.py +++ b/test/model/test_controller.py @@ -1,6 +1,6 @@ """Test the controller model.""" -import json from copy import deepcopy +import json from unittest.mock import patch import pytest @@ -21,8 +21,10 @@ ) from zwave_js_server.event import Event from zwave_js_server.exceptions import RepeaterRssiErrorReceived, RssiErrorReceived -from zwave_js_server.model import association as association_pkg -from zwave_js_server.model import controller as controller_pkg +from zwave_js_server.model import ( + association as association_pkg, + controller as controller_pkg, +) from zwave_js_server.model.controller.firmware import ControllerFirmwareUpdateStatus from zwave_js_server.model.controller.rebuild_routes import ( RebuildRoutesOptions, @@ -183,7 +185,7 @@ def test_controller_mods(): def test_controller_status(): - """ "Test controller status functionality.""" + """Test controller status functionality.""" state = json.loads(load_fixture("basic_dump.txt").split("\n")[0])["result"]["state"] state["controller"]["status"] = 0 @@ -425,7 +427,8 @@ async def test_begin_inclusion_errors(controller, uuid4, mock_command): InclusionStrategy.SECURITY_S0, dsk="test" ) - # Test that Security S2 Inclusion Strategy doesn't support providing `force_security` + # Test that Security S2 Inclusion Strategy doesn't support providing + # `force_security` with pytest.raises(ValueError): await controller.async_begin_inclusion( InclusionStrategy.SECURITY_S2, force_security=True @@ -1845,7 +1848,8 @@ async def test_get_available_firmware_updates(multisensor_6, uuid4, mock_command {"command": "controller.get_available_firmware_updates"}, {"updates": [FIRMWARE_UPDATE_INFO]}, ) - updates = await multisensor_6.client.driver.controller.async_get_available_firmware_updates( + controller = multisensor_6.client.driver.controller + updates = await controller.async_get_available_firmware_updates( multisensor_6, "test" ) assert len(updates) == 1 diff --git a/test/model/test_driver.py b/test/model/test_driver.py index 1020675b7..537c9d756 100644 --- a/test/model/test_driver.py +++ b/test/model/test_driver.py @@ -5,9 +5,11 @@ from zwave_js_server.const import LogLevel from zwave_js_server.event import Event -from zwave_js_server.model import driver as driver_pkg -from zwave_js_server.model import log_config as log_config_pkg -from zwave_js_server.model import log_message as log_message_pkg +from zwave_js_server.model import ( + driver as driver_pkg, + log_config as log_config_pkg, + log_message as log_message_pkg, +) from .. import load_fixture diff --git a/test/model/test_node.py b/test/model/test_node.py index 8647f6707..58400d693 100644 --- a/test/model/test_node.py +++ b/test/model/test_node.py @@ -1,8 +1,8 @@ """Test the node model.""" import asyncio -import json from copy import deepcopy from datetime import datetime, timezone +import json from typing import Any from unittest.mock import AsyncMock, patch @@ -34,8 +34,7 @@ RssiErrorReceived, UnwriteableValue, ) -from zwave_js_server.model import endpoint as endpoint_pkg -from zwave_js_server.model import node as node_pkg +from zwave_js_server.model import endpoint as endpoint_pkg, node as node_pkg from zwave_js_server.model.node.firmware import ( NodeFirmwareUpdateInfo, NodeFirmwareUpdateStatus, @@ -183,7 +182,7 @@ def test_from_state(client): async def test_highest_security_value(lock_schlage_be469, ring_keypad): - """Test the highest_security_class property""" + """Test the highest_security_class property.""" assert lock_schlage_be469.highest_security_class == SecurityClass.S0_LEGACY assert ring_keypad.highest_security_class is None @@ -238,7 +237,8 @@ async def test_device_config( "controller being inoperable or otherwise unavailable.)" ) assert device_config.metadata.manual == ( - "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=MarketCertificationFiles/2479/ZP3111-5_R2_20170316.pdf" + "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=" + "MarketCertificationFiles/2479/ZP3111-5_R2_20170316.pdf" ) assert device_config.metadata.wakeup is None assert device_config.metadata.comments == [{"level": "info", "text": "test"}] diff --git a/test/test_client.py b/test/test_client.py index e6d8fd6ff..782d8d69b 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -1,13 +1,13 @@ """Test the client.""" import asyncio -import logging from datetime import datetime +import logging from unittest.mock import Mock, patch -import pytest from aiohttp.client_exceptions import ClientError, WSServerHandshakeError from aiohttp.client_reqrep import ClientResponse, RequestInfo from aiohttp.http_websocket import WSMsgType +import pytest from zwave_js_server.client import LOGGER, Client from zwave_js_server.const import MAX_SERVER_SCHEMA_VERSION, LogLevel, __version__ diff --git a/test/test_version.py b/test/test_version.py index 3a5f95f10..b57c1759c 100644 --- a/test/test_version.py +++ b/test/test_version.py @@ -23,7 +23,7 @@ async def test_get_server_version(client_session, ws_client, url, version_data): async def test_missing_server_schema_version( client_session, ws_client, url, version_data ): - """test missing schema version processed as schema version 0.""" + """Test missing schema version processed as schema version 0.""" del version_data["minSchemaVersion"] del version_data["maxSchemaVersion"] ws_client.receive_json.return_value = version_data diff --git a/tox.ini b/tox.ini index ac6ac43d0..344d99741 100644 --- a/tox.ini +++ b/tox.ini @@ -18,9 +18,8 @@ basepython = python3 ignore_errors = True commands = black --check ./ - flake8 zwave_js_server test + ruff check zwave_js_server scripts test pylint zwave_js_server - pydocstyle zwave_js_server test deps = -rrequirements.txt -rrequirements_lint.txt diff --git a/zwave_js_server/client.py b/zwave_js_server/client.py index b5acf76c7..5b63c4d2c 100644 --- a/zwave_js_server/client.py +++ b/zwave_js_server/client.py @@ -2,16 +2,16 @@ from __future__ import annotations import asyncio -import logging -import pprint -import uuid from collections import defaultdict from collections.abc import Callable from copy import deepcopy from datetime import datetime +import logging from operator import itemgetter +import pprint from types import TracebackType from typing import Any, cast +import uuid from aiohttp import ClientSession, ClientWebSocketResponse, WSMsgType, client_exceptions @@ -94,7 +94,10 @@ def __init__( def __repr__(self) -> str: """Return the representation.""" prefix = "" if self.connected else "not " - return f"{type(self).__name__}(ws_server_url={self.ws_server_url!r}, {prefix}connected)" + return ( + f"{type(self).__name__}(ws_server_url={self.ws_server_url!r}, " + f"{prefix}connected)" + ) @property def connected(self) -> bool: @@ -115,10 +118,11 @@ async def async_send_command( raise InvalidServerVersion( self.version, require_schema, - "Command not available due to incompatible server version. Update the Z-Wave " - f"JS Server to a version that supports at least api schema {require_schema}.", + "Command not available due to incompatible server version. Update " + "the Z-Wave JS Server to a version that supports at least api schema " + f"{require_schema}.", ) - future: "asyncio.Future[dict]" = self._loop.create_future() + future: asyncio.Future[dict] = self._loop.create_future() message_id = message["messageId"] = uuid.uuid4().hex self._result_futures[message_id] = future await self._send_json_message(message) @@ -136,8 +140,9 @@ async def async_send_command_no_wait( raise InvalidServerVersion( self.version, require_schema, - "Command not available due to incompatible server version. Update the Z-Wave " - f"JS Server to a version that supports at least api schema {require_schema}.", + "Command not available due to incompatible server version. Update " + "the Z-Wave JS Server to a version that supports at least api schema " + f"{require_schema}.", ) message["messageId"] = uuid.uuid4().hex await self._send_json_message(message) @@ -180,8 +185,8 @@ async def connect(self) -> None: f"at least api schema {MIN_SERVER_SCHEMA_VERSION}", ) # store the (highest possible) schema version we're going to use/request - # this is a bit future proof as we might decide to use a pinned version at some point - # for now we just negotiate the highest available schema version and + # this is a bit future proof as we might decide to use a pinned version at some + # point for now we just negotiate the highest available schema version and # guard incompatibility with the MIN_SERVER_SCHEMA_VERSION if version.max_schema_version < MAX_SERVER_SCHEMA_VERSION: self.schema_version = version.max_schema_version @@ -554,7 +559,7 @@ async def _send_json_message(self, message: dict[str, Any]) -> None: await self._client.send_json(message) - async def __aenter__(self) -> "Client": + async def __aenter__(self) -> Client: """Connect to the websocket.""" await self.connect() return self diff --git a/zwave_js_server/const/__init__.py b/zwave_js_server/const/__init__.py index 846c7cd8a..c156c862a 100644 --- a/zwave_js_server/const/__init__.py +++ b/zwave_js_server/const/__init__.py @@ -1,10 +1,10 @@ """Constants for the Z-Wave JS python library.""" from __future__ import annotations -import logging from dataclasses import dataclass, field from enum import Enum, IntEnum from importlib import metadata +import logging from typing import TypedDict PACKAGE_NAME = "zwave-js-server-python" @@ -391,7 +391,8 @@ class SetValueStatus(IntEnum): FAIL = 2 # The endpoint specified in the value ID does not exist ENDPOINT_NOT_FOUND = 3 - # The given CC or its API is not implemented (yet) or it has no `setValue` implementation + # The given CC or its API is not implemented (yet) or it has no `setValue` + # implementation NOT_IMPLEMENTED = 4 # The value to set (or a related value) is invalid INVALID_VALUE = 5 diff --git a/zwave_js_server/event.py b/zwave_js_server/event.py index 741565465..d6e34b78d 100644 --- a/zwave_js_server/event.py +++ b/zwave_js_server/event.py @@ -1,8 +1,8 @@ """Provide Event base classes for Z-Wave JS.""" from __future__ import annotations -import logging from dataclasses import dataclass, field +import logging from typing import Callable, Literal try: diff --git a/zwave_js_server/exceptions.py b/zwave_js_server/exceptions.py index da07efacb..6f41af9e1 100644 --- a/zwave_js_server/exceptions.py +++ b/zwave_js_server/exceptions.py @@ -68,7 +68,7 @@ class InvalidServerVersion(BaseZwaveJSServerError): def __init__( self, - version_info: "VersionInfo", + version_info: VersionInfo, required_schema_version: int, message: str, ) -> None: @@ -138,14 +138,14 @@ class BulkSetConfigParameterFailed(BaseZwaveJSServerError): """ Exception raised when bulk setting a config parameter fails. - Derived from another exception + Derived from another exception. """ class InvalidCommandClass(BaseZwaveJSServerError): """Exception raised when Zwave Value has an invalid command class.""" - def __init__(self, value: "Value", command_class: "CommandClass") -> None: + def __init__(self, value: Value, command_class: CommandClass) -> None: """Initialize an invalid Command Class error.""" self.value = value self.command_class = command_class @@ -162,7 +162,7 @@ class UnknownValueData(BaseZwaveJSServerError): library. """ - def __init__(self, value: "Value", path: str) -> None: + def __init__(self, value: Value, path: str) -> None: """Initialize an unknown data error.""" self.value = value self.path = path @@ -178,7 +178,7 @@ def __init__(self, value: "Value", path: str) -> None: class RssiErrorReceived(BaseZwaveJSServerError): """Exception raised when an RSSI error is received.""" - def __init__(self, error: "RssiError") -> None: + def __init__(self, error: RssiError) -> None: """Initialize an RSSI error.""" self.error = error super().__init__() diff --git a/zwave_js_server/model/controller/__init__.py b/zwave_js_server/model/controller/__init__.py index e14e3b764..e18140ebf 100644 --- a/zwave_js_server/model/controller/__init__.py +++ b/zwave_js_server/model/controller/__init__.py @@ -14,8 +14,8 @@ InclusionStrategy, NodeType, QRCodeVersion, - RemoveNodeReason, RFRegion, + RemoveNodeReason, ZwaveFeature, ) from ...event import Event, EventBase @@ -70,7 +70,7 @@ class NVMProgress: class Controller(EventBase): """Represent a Z-Wave JS controller.""" - def __init__(self, client: "Client", state: dict) -> None: + def __init__(self, client: Client, state: dict) -> None: """Initialize controller.""" super().__init__() self.client = client @@ -261,7 +261,8 @@ async def async_begin_inclusion( if force_security is not None: if inclusion_strategy != InclusionStrategy.DEFAULT: raise ValueError( - "`forceSecurity` option is only supported with inclusion_strategy=DEFAULT" + "`forceSecurity` option is only supported with inclusion_strategy=" + "DEFAULT" ) options["forceSecurity"] = force_security @@ -270,7 +271,8 @@ async def async_begin_inclusion( if provisioning is not None: if inclusion_strategy != InclusionStrategy.SECURITY_S2: raise ValueError( - "`provisioning` option is only supported with inclusion_strategy=SECURITY_S2" + "`provisioning` option is only supported with inclusion_strategy=" + "SECURITY_S2" ) if dsk is not None: @@ -283,8 +285,8 @@ async def async_begin_inclusion( provisioning ) < MINIMUM_QR_STRING_LENGTH or not provisioning.startswith("90"): raise ValueError( - f"QR code string must be at least {MINIMUM_QR_STRING_LENGTH} characters " - "long and start with `90`" + f"QR code string must be at least {MINIMUM_QR_STRING_LENGTH} " + "characters long and start with `90`" ) options["provisioning"] = provisioning # If we get a Smart Start QR code, we provision the node and return because @@ -294,8 +296,8 @@ async def async_begin_inclusion( and provisioning.version == QRCodeVersion.SMART_START ): raise ValueError( - "Smart Start QR codes can't use the normal inclusion process. Use the " - "provision_smart_start_node command to provision this device." + "Smart Start QR codes can't use the normal inclusion process. Use " + "the provision_smart_start_node command to provision this device." ) # Otherwise we assume the data is ProvisioningEntry or # QRProvisioningInformation that is not a Smart Start QR code @@ -431,7 +433,8 @@ async def async_replace_failed_node( if force_security is not None: if inclusion_strategy != InclusionStrategy.DEFAULT: raise ValueError( - "`forceSecurity` option is only supported with inclusion_strategy=DEFAULT" + "`forceSecurity` option is only supported with inclusion_strategy=" + "DEFAULT" ) options["forceSecurity"] = force_security @@ -440,7 +443,8 @@ async def async_replace_failed_node( if provisioning is not None: if inclusion_strategy != InclusionStrategy.SECURITY_S2: raise ValueError( - "`provisioning` option is only supported with inclusion_strategy=SECURITY_S2" + "`provisioning` option is only supported with inclusion_strategy=" + "SECURITY_S2" ) # Provisioning option was introduced in Schema 11 require_schema = 11 @@ -450,8 +454,8 @@ async def async_replace_failed_node( provisioning ) < MINIMUM_QR_STRING_LENGTH or not provisioning.startswith("90"): raise ValueError( - f"QR code string must be at least {MINIMUM_QR_STRING_LENGTH} characters " - "long and start with `90`" + f"QR code string must be at least {MINIMUM_QR_STRING_LENGTH} " + "characters long and start with `90`" ) options["provisioning"] = provisioning # Otherwise we assume the data is ProvisioningEntry or @@ -838,7 +842,8 @@ def receive_event(self, event: Event) -> None: if event.data["source"] != "controller": # TODO decide what to do here print( - f"Controller doesn't know how to handle/forward this event: {event.data}" + "Controller doesn't know how to handle/forward this event: " + f"{event.data}" ) CONTROLLER_EVENT_MODEL_MAP[event.type](**event.data) diff --git a/zwave_js_server/model/controller/event_model.py b/zwave_js_server/model/controller/event_model.py index 80c1ef84a..771088e45 100644 --- a/zwave_js_server/model/controller/event_model.py +++ b/zwave_js_server/model/controller/event_model.py @@ -183,7 +183,7 @@ class StatusChangedEventModel(BaseControllerEventModel): status: int -CONTROLLER_EVENT_MODEL_MAP: dict[str, type["BaseControllerEventModel"]] = { +CONTROLLER_EVENT_MODEL_MAP: dict[str, type[BaseControllerEventModel]] = { "exclusion failed": ExclusionFailedEventModel, "exclusion started": ExclusionStartedEventModel, "exclusion stopped": ExclusionStoppedEventModel, diff --git a/zwave_js_server/model/controller/inclusion_and_provisioning.py b/zwave_js_server/model/controller/inclusion_and_provisioning.py index 0ab733519..8eaed210d 100644 --- a/zwave_js_server/model/controller/inclusion_and_provisioning.py +++ b/zwave_js_server/model/controller/inclusion_and_provisioning.py @@ -1,4 +1,4 @@ -"""Provide a model for the Z-Wave JS controller's inclusion/provisioning data structures.""" +"""Provide a model for inclusion and provisioning.""" from __future__ import annotations from dataclasses import dataclass @@ -30,7 +30,7 @@ def to_dict(self) -> InclusionGrantDataType: } @classmethod - def from_dict(cls, data: InclusionGrantDataType) -> "InclusionGrant": + def from_dict(cls, data: InclusionGrantDataType) -> InclusionGrant: """Return InclusionGrant from InclusionGrantDataType dict.""" return cls( security_classes=[ @@ -65,7 +65,7 @@ def to_dict(self) -> dict[str, Any]: return data @classmethod - def from_dict(cls, data: dict[str, Any]) -> "ProvisioningEntry": + def from_dict(cls, data: dict[str, Any]) -> ProvisioningEntry: """Return ProvisioningEntry from data dict.""" cls_instance = cls( dsk=data["dsk"], @@ -140,7 +140,7 @@ def to_dict(self) -> dict[str, Any]: return data @classmethod - def from_dict(cls, data: dict[str, Any]) -> "QRProvisioningInformation": + def from_dict(cls, data: dict[str, Any]) -> QRProvisioningInformation: """Return QRProvisioningInformation from data dict.""" cls_instance = cls( version=QRCodeVersion(data["version"]), diff --git a/zwave_js_server/model/controller/rebuild_routes.py b/zwave_js_server/model/controller/rebuild_routes.py index f9ce620a1..651b25f37 100644 --- a/zwave_js_server/model/controller/rebuild_routes.py +++ b/zwave_js_server/model/controller/rebuild_routes.py @@ -19,7 +19,7 @@ class RebuildRoutesOptions: include_sleeping: bool | None = None @classmethod - def from_dict(cls, data: RebuildRoutesOptionsDataType) -> "RebuildRoutesOptions": + def from_dict(cls, data: RebuildRoutesOptionsDataType) -> RebuildRoutesOptions: """Return options from data.""" return cls(include_sleeping=data.get("includeSleeping")) diff --git a/zwave_js_server/model/controller/statistics.py b/zwave_js_server/model/controller/statistics.py index d3742e0ea..8428ffb93 100644 --- a/zwave_js_server/model/controller/statistics.py +++ b/zwave_js_server/model/controller/statistics.py @@ -22,7 +22,7 @@ class ControllerLifelineRoutesDataType(TypedDict): class ControllerLifelineRoutes: """Represent controller lifeline routes.""" - client: "Client" = field(repr=False) + client: Client = field(repr=False) data: ControllerLifelineRoutesDataType = field(repr=False) lwr: RouteStatistics | None = field(init=False, default=None) nlwr: RouteStatistics | None = field(init=False, default=None) diff --git a/zwave_js_server/model/device_config.py b/zwave_js_server/model/device_config.py index bed1c2178..a963de42d 100644 --- a/zwave_js_server/model/device_config.py +++ b/zwave_js_server/model/device_config.py @@ -164,7 +164,7 @@ def manufacturer(self) -> str | None: @property def manufacturer_id(self) -> str | None: # TODO: In the dump this is an int. - """Return manufacturer id (as defined in the specs) as a 4-digit hexadecimal string.""" + """Return manufacturer id (as defined in specs) as a 4-digit hex string.""" return self.data.get("manufacturerId") @property diff --git a/zwave_js_server/model/driver.py b/zwave_js_server/model/driver.py index cbac38fa8..08ddee438 100644 --- a/zwave_js_server/model/driver.py +++ b/zwave_js_server/model/driver.py @@ -41,7 +41,7 @@ class AllNodesReadyEventModel(BaseDriverEventModel): ) -DRIVER_EVENT_MODEL_MAP: dict[str, type["BaseDriverEventModel"]] = { +DRIVER_EVENT_MODEL_MAP: dict[str, type[BaseDriverEventModel]] = { "all nodes ready": AllNodesReadyEventModel, "log config updated": LogConfigUpdatedEventModel, "logging": LoggingEventModel, @@ -62,7 +62,7 @@ class Driver(EventBase): """Represent a Z-Wave JS driver.""" def __init__( - self, client: "Client", state: dict, log_config: LogConfigDataType + self, client: Client, state: dict, log_config: LogConfigDataType ) -> None: """Initialize driver.""" super().__init__() diff --git a/zwave_js_server/model/endpoint.py b/zwave_js_server/model/endpoint.py index d77effe6d..b354efdf8 100644 --- a/zwave_js_server/model/endpoint.py +++ b/zwave_js_server/model/endpoint.py @@ -36,7 +36,7 @@ class Endpoint(EventBase): def __init__( self, - client: "Client", + client: Client, data: EndpointDataType, values: dict[str, ConfigurationValue | Value], ) -> None: @@ -128,8 +128,8 @@ async def async_send_command( """ Send an endpoint command. For internal use only. - If wait_for_result is not None, it will take precedence, otherwise we will decide - to wait or not based on the node status. + If wait_for_result is not None, it will take precedence, otherwise we will + decide to wait or not based on the node status. """ if self.client.driver is None: raise FailedCommand( @@ -234,7 +234,7 @@ async def async_get_cc_version(self, command_class: CommandClass) -> bool: assert result return cast(bool, result["version"]) - async def async_get_node_unsafe(self) -> "NodeDataType": + async def async_get_node_unsafe(self) -> NodeDataType: """Call endpoint.get_node_unsafe command.""" result = await self.async_send_command( "get_node_unsafe", diff --git a/zwave_js_server/model/log_config.py b/zwave_js_server/model/log_config.py index d43a04fb3..f83930c6a 100644 --- a/zwave_js_server/model/log_config.py +++ b/zwave_js_server/model/log_config.py @@ -41,7 +41,7 @@ def to_dict(self) -> LogConfigDataType: return cast(LogConfigDataType, {k: v for k, v in data.items() if v is not None}) @classmethod - def from_dict(cls, data: LogConfigDataType) -> "LogConfig": + def from_dict(cls, data: LogConfigDataType) -> LogConfig: """Return LogConfig from LogConfigDataType dict.""" return cls( data.get("enabled"), diff --git a/zwave_js_server/model/node/__init__.py b/zwave_js_server/model/node/__init__.py index 0980e3002..7b2f176a9 100644 --- a/zwave_js_server/model/node/__init__.py +++ b/zwave_js_server/model/node/__init__.py @@ -3,8 +3,8 @@ import asyncio import copy -import logging from datetime import datetime +import logging from typing import TYPE_CHECKING, Any, cast from ...const import ( @@ -102,7 +102,7 @@ def _get_value_id_dict_from_value_data(value_data: ValueDataType) -> dict[str, A class Node(EventBase): """Represent a Z-Wave JS node.""" - def __init__(self, client: "Client", data: NodeDataType) -> None: + def __init__(self, client: Client, data: NodeDataType) -> None: """Initialize the node.""" super().__init__() self.client = client @@ -467,8 +467,8 @@ async def async_send_command( """ Send a node command. For internal use only. - If wait_for_result is not None, it will take precedence, otherwise we will decide - to wait or not based on the node status. + If wait_for_result is not None, it will take precedence, otherwise we will + decide to wait or not based on the node status. """ kwargs = {} message = {"command": f"node.{cmd}", "nodeId": self.node_id, **cmd_kwargs} @@ -704,7 +704,7 @@ async def async_get_highest_security_class(self) -> SecurityClass | None: return None async def async_test_power_level( - self, test_node: "Node", power_level: PowerLevel, test_frame_count: int + self, test_node: Node, power_level: PowerLevel, test_frame_count: int ) -> int: """Send testPowerLevel command to Node.""" data = await self.async_send_command( @@ -735,7 +735,7 @@ async def async_check_lifeline_health( return LifelineHealthCheckSummary(data["summary"]) async def async_check_route_health( - self, target_node: "Node", rounds: int | None = None + self, target_node: Node, rounds: int | None = None ) -> RouteHealthCheckSummary: """Send checkRouteHealth command to Node.""" kwargs = {"targetNodeId": target_node.node_id} @@ -840,7 +840,7 @@ async def async_get_value_timestamp(self, val: Value | str) -> int: return cast(int, data["timestamp"]) async def async_manually_idle_notification_value(self, val: Value | str) -> None: - """Send manuallyIdleNotificationValue command to Node for given value (or value_id).""" + """Send manuallyIdleNotificationValue cmd to Node for value (or value_id).""" # a value may be specified as value_id or the value itself if not isinstance(val, Value): val = self.values[val] diff --git a/zwave_js_server/model/node/event_model.py b/zwave_js_server/model/node/event_model.py index 7d473aeda..88a97bfc7 100644 --- a/zwave_js_server/model/node/event_model.py +++ b/zwave_js_server/model/node/event_model.py @@ -207,7 +207,7 @@ class FirmwareUpdateProgressEventModel(BaseNodeEventModel): progress: NodeFirmwareUpdateProgressDataType -NODE_EVENT_MODEL_MAP: dict[str, type["BaseNodeEventModel"]] = { +NODE_EVENT_MODEL_MAP: dict[str, type[BaseNodeEventModel]] = { "alive": AliveEventModel, "check lifeline health progress": CheckLifelineHealthProgressEventModel, "check route health progress": CheckRouteHealthProgressEventModel, diff --git a/zwave_js_server/model/node/firmware.py b/zwave_js_server/model/node/firmware.py index 61fcbb3d2..a1c72fd0c 100644 --- a/zwave_js_server/model/node/firmware.py +++ b/zwave_js_server/model/node/firmware.py @@ -146,7 +146,7 @@ class NodeFirmwareUpdateProgressDataType(TypedDict): class NodeFirmwareUpdateProgress: """Model for a node firmware update progress data.""" - node: "Node" + node: Node data: NodeFirmwareUpdateProgressDataType = field(repr=False) current_file: int = field(init=False) total_files: int = field(init=False) @@ -176,7 +176,7 @@ class NodeFirmwareUpdateResultDataType(TypedDict, total=False): class NodeFirmwareUpdateResult: """Model for node firmware update result data.""" - node: "Node" + node: Node data: NodeFirmwareUpdateResultDataType = field(repr=False) status: NodeFirmwareUpdateStatus = field(init=False) success: bool = field(init=False) @@ -210,7 +210,7 @@ class NodeFirmwareUpdateFileInfo: @classmethod def from_dict( cls, data: NodeFirmwareUpdateFileInfoDataType - ) -> "NodeFirmwareUpdateFileInfo": + ) -> NodeFirmwareUpdateFileInfo: """Initialize from dict.""" return cls(**data) @@ -242,7 +242,7 @@ class NodeFirmwareUpdateDeviceID: @classmethod def from_dict( cls, data: NodeFirmwareUpdateDeviceIDDataType - ) -> "NodeFirmwareUpdateDeviceID": + ) -> NodeFirmwareUpdateDeviceID: """Initialize from dict.""" return cls( manufacturer_id=data["manufacturerId"], @@ -290,9 +290,7 @@ class NodeFirmwareUpdateInfo: device: NodeFirmwareUpdateDeviceID @classmethod - def from_dict( - cls, data: NodeFirmwareUpdateInfoDataType - ) -> "NodeFirmwareUpdateInfo": + def from_dict(cls, data: NodeFirmwareUpdateInfoDataType) -> NodeFirmwareUpdateInfo: """Initialize from dict.""" return cls( version=data["version"], diff --git a/zwave_js_server/model/node/statistics.py b/zwave_js_server/model/node/statistics.py index 36598ec09..151393555 100644 --- a/zwave_js_server/model/node/statistics.py +++ b/zwave_js_server/model/node/statistics.py @@ -35,7 +35,7 @@ class NodeStatisticsDataType(TypedDict, total=False): class NodeStatistics: """Represent a node statistics update.""" - client: "Client" = field(repr=False) + client: Client = field(repr=False) data: NodeStatisticsDataType = field(repr=False) commands_tx: int = field(init=False) commands_rx: int = field(init=False) diff --git a/zwave_js_server/model/statistics.py b/zwave_js_server/model/statistics.py index 0b16bd916..acb1825d7 100644 --- a/zwave_js_server/model/statistics.py +++ b/zwave_js_server/model/statistics.py @@ -28,17 +28,17 @@ class RouteStatisticsDict(TypedDict): """Represent a route statistics data dict type.""" protocol_data_rate: int - repeaters: list["Node"] + repeaters: list[Node] rssi: int | None repeater_rssi: list[int] - route_failed_between: tuple["Node", "Node"] | None + route_failed_between: tuple[Node, Node] | None @dataclass class RouteStatistics: """Represent route statistics.""" - client: "Client" = field(repr=False) + client: Client = field(repr=False) data: RouteStatisticsDataType = field(repr=False) protocol_data_rate: ProtocolDataRate = field(init=False) @@ -47,7 +47,7 @@ def __post_init__(self) -> None: self.protocol_data_rate = ProtocolDataRate(self.data["protocolDataRate"]) @cached_property - def repeaters(self) -> list["Node"]: + def repeaters(self) -> list[Node]: """Return repeaters.""" assert self.client.driver return [ @@ -75,7 +75,7 @@ def repeater_rssi(self) -> list[int]: return repeater_rssi @cached_property - def route_failed_between(self) -> tuple["Node", "Node"] | None: + def route_failed_between(self) -> tuple[Node, Node] | None: """Return route failed between.""" if (node_ids := self.data.get("routeFailedBetween")) is None: return None diff --git a/zwave_js_server/model/value.py b/zwave_js_server/model/value.py index 5fb34fd7b..7ff718216 100644 --- a/zwave_js_server/model/value.py +++ b/zwave_js_server/model/value.py @@ -60,14 +60,14 @@ class ValueDataType(TypedDict, total=False): ccVersion: int # required -def _init_value(node: "Node", val: ValueDataType) -> "Value" | "ConfigurationValue": +def _init_value(node: Node, val: ValueDataType) -> Value | ConfigurationValue: """Initialize a Value object from ValueDataType.""" if val["commandClass"] == CommandClass.CONFIGURATION: return ConfigurationValue(node, val) return Value(node, val) -def _get_value_id_str_from_dict(node: "Node", val: ValueDataType) -> str: +def _get_value_id_str_from_dict(node: Node, val: ValueDataType) -> str: """Return string ID of value from ValueDataType dict.""" return get_value_id_str( node, @@ -79,7 +79,7 @@ def _get_value_id_str_from_dict(node: "Node", val: ValueDataType) -> str: def get_value_id_str( - node: "Node", + node: Node, command_class: int, property_: int | str, endpoint: int | None = None, @@ -185,7 +185,7 @@ def update(self, data: MetaDataType) -> None: class Value: """Represent a Z-Wave JS value.""" - def __init__(self, node: "Node", data: ValueDataType) -> None: + def __init__(self, node: Node, data: ValueDataType) -> None: """Initialize value.""" self.node = node self.data: ValueDataType = {} @@ -298,7 +298,8 @@ class ValueNotification(Value): https://zwave-js.github.io/node-zwave-js/#/api/node?id=quotvalue-notificationquot """ - # format is the same as a Value message, subclassed for easier identifying and future use + # format is the same as a Value message, subclassed for easier identifying and + # future use class ConfigurationValue(Value): diff --git a/zwave_js_server/model/version.py b/zwave_js_server/model/version.py index 7a411c534..9b07dac58 100644 --- a/zwave_js_server/model/version.py +++ b/zwave_js_server/model/version.py @@ -26,7 +26,7 @@ class VersionInfo: max_schema_version: int @classmethod - def from_message(cls, msg: VersionInfoDataType) -> "VersionInfo": + def from_message(cls, msg: VersionInfoDataType) -> VersionInfo: """Create a version info from a version message.""" return cls( driver_version=msg["driverVersion"], diff --git a/zwave_js_server/util/node.py b/zwave_js_server/util/node.py index 8d94ecf7d..321a46074 100644 --- a/zwave_js_server/util/node.py +++ b/zwave_js_server/util/node.py @@ -41,8 +41,7 @@ async def async_set_config_parameter( property_key: int | str | None = None, endpoint: int = 0, ) -> tuple[ConfigurationValue, CommandStatus]: - """ - Set a value for a config parameter on this node. + """Set a value for a config parameter on this node. new_value and property_ can be provided as labels, so we need to resolve them to the appropriate key