Skip to content

Commit 5b94974

Browse files
authored
Fix all lint issues (#245)
1 parent 65d9649 commit 5b94974

25 files changed

+265
-365
lines changed

.github/workflows/test.yml

+6-14
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,10 @@ jobs:
2525
run: |
2626
sudo apt-get update
2727
sudo apt-get install -y libgirepository1.0-dev
28-
python -m pip install --upgrade pip
29-
pip install -e .[server] -r requirements-test.txt
30-
- name: Flake8
31-
run: flake8 scripts/ matter_server/
32-
- name: Black
33-
run: black --check scripts/ matter_server/
34-
- name: isort
35-
run: isort --check scripts/ matter_server/
36-
- name: pylint
37-
run: pylint matter_server/
38-
- name: mypy
39-
run: mypy matter_server/
28+
python -m pip install --upgrade pip build setuptools
29+
pip install .[server] .[test]
30+
- name: Lint/test with pre-commit
31+
run: pre-commit run --all-files
4032

4133
test:
4234
runs-on: ubuntu-latest
@@ -59,7 +51,7 @@ jobs:
5951
run: |
6052
sudo apt-get update
6153
sudo apt-get install -y libgirepository1.0-dev
62-
python -m pip install --upgrade pip
63-
pip install -e .[server] -r requirements-test.txt
54+
python -m pip install --upgrade pip build setuptools
55+
pip install .[server] .[test]
6456
- name: Pytest
6557
run: pytest --durations 10 --cov-report term-missing --cov=matter_server --cov-report=xml tests/server/

.pre-commit-config.yaml

+14-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
# See https://pre-commit.com for more information
22
# See https://pre-commit.com/hooks.html for more hooks
33
repos:
4+
- repo: https://github.com/pre-commit/pre-commit-hooks
5+
rev: v4.4.0
6+
hooks:
7+
- id: end-of-file-fixer
8+
- id: trailing-whitespace
9+
- id: no-commit-to-branch
10+
args:
11+
- --branch=main
12+
- id: debug-statements
13+
- repo: https://github.com/charliermarsh/ruff-pre-commit
14+
rev: 'v0.0.259'
15+
hooks:
16+
- id: ruff
17+
files: ^(matter_server|scripts)/.+\.py$
418
- repo: https://github.com/psf/black
519
rev: 23.1.0
620
hooks:
@@ -15,23 +29,6 @@ repos:
1529
args: []
1630
exclude_types: [csv, json]
1731
exclude: ^tests/fixtures/
18-
- repo: https://github.com/PyCQA/flake8
19-
rev: 6.0.0
20-
hooks:
21-
- id: flake8
22-
files: ^(matter_server|scripts)/.+\.py$
23-
- repo: https://github.com/PyCQA/isort
24-
rev: 5.12.0
25-
hooks:
26-
- id: isort
27-
files: ^(matter_server|scripts)/.+\.py$
28-
- repo: https://github.com/pre-commit/pre-commit-hooks
29-
rev: v4.4.0
30-
hooks:
31-
- id: no-commit-to-branch
32-
args:
33-
- --branch=main
34-
- id: trailing-whitespace
3532
- repo: local
3633
hooks:
3734
- id: mypy

matter_server/client/client.py

+21-36
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33

44
import asyncio
55
import logging
6-
from types import TracebackType
7-
from typing import TYPE_CHECKING, Any, Callable, Dict, Final, Optional, cast
86
import uuid
7+
from collections.abc import Callable
8+
from types import TracebackType
9+
from typing import TYPE_CHECKING, Any, Final, cast
910

1011
from aiohttp import ClientSession
1112

1213
from matter_server.common.errors import ERROR_MAP
13-
14-
from ..common.helpers.util import dataclass_from_dict, dataclass_to_dict
15-
from ..common.models import (
14+
from matter_server.common.helpers.util import dataclass_from_dict, dataclass_to_dict
15+
from matter_server.common.models import (
1616
APICommand,
1717
CommandMessage,
1818
ErrorResultMessage,
@@ -25,6 +25,7 @@
2525
ServerInfoMessage,
2626
SuccessResultMessage,
2727
)
28+
2829
from .connection import MatterClientConnection
2930
from .exceptions import ConnectionClosed, InvalidServerVersion, InvalidState
3031
from .models.node import MatterNode
@@ -42,8 +43,8 @@ def __init__(self, ws_server_url: str, aiohttp_session: ClientSession):
4243
"""Initialize the Client class."""
4344
self.connection = MatterClientConnection(ws_server_url, aiohttp_session)
4445
self.logger = logging.getLogger(__package__)
45-
self._nodes: Dict[int, MatterNode] = {}
46-
self._result_futures: Dict[str, asyncio.Future] = {}
46+
self._nodes: dict[int, MatterNode] = {}
47+
self._result_futures: dict[str, asyncio.Future] = {}
4748
self._subscribers: dict[str, list[Callable[[EventType, Any], None]]] = {}
4849
self._stop_called: bool = False
4950
self._loop: asyncio.AbstractEventLoop | None = None
@@ -56,9 +57,9 @@ def server_info(self) -> ServerInfoMessage | None:
5657
def subscribe(
5758
self,
5859
callback: Callable[[EventType, Any], None],
59-
event_filter: Optional[EventType] = None,
60-
node_filter: Optional[int] = None,
61-
attr_path_filter: Optional[str] = None,
60+
event_filter: EventType | None = None,
61+
node_filter: int | None = None,
62+
attr_path_filter: str | None = None,
6263
) -> Callable[[], None]:
6364
"""
6465
Subscribe to node and server events.
@@ -70,16 +71,10 @@ def subscribe(
7071
# for fast lookups we create a key based on the filters, allowing
7172
# a "catch all" with a wildcard (*).
7273
_event_filter: str
73-
if event_filter is None:
74-
_event_filter = SUB_WILDCARD
75-
else:
76-
_event_filter = event_filter.value
74+
_event_filter = SUB_WILDCARD if event_filter is None else event_filter.value
7775

7876
_node_filter: str
79-
if node_filter is None:
80-
_node_filter = SUB_WILDCARD
81-
else:
82-
_node_filter = str(node_filter)
77+
_node_filter = SUB_WILDCARD if node_filter is None else str(node_filter)
8378

8479
if attr_path_filter is None:
8580
attr_path_filter = SUB_WILDCARD
@@ -123,9 +118,7 @@ async def commission_on_network(self, setup_pin_code: int) -> MatterNodeData:
123118

124119
async def set_wifi_credentials(self, ssid: str, credentials: str) -> None:
125120
"""Set WiFi credentials for commissioning to a (new) device."""
126-
await self.send_command(
127-
APICommand.SET_WIFI_CREDENTIALS, ssid=ssid, credentials=credentials
128-
)
121+
await self.send_command(APICommand.SET_WIFI_CREDENTIALS, ssid=ssid, credentials=credentials)
129122

130123
async def set_thread_operational_dataset(self, dataset: str) -> None:
131124
"""Set Thread Operational dataset in the stack."""
@@ -137,7 +130,7 @@ async def open_commissioning_window(
137130
timeout: int = 300,
138131
iteration: int = 1000,
139132
option: int = 0,
140-
discriminator: Optional[int] = None,
133+
discriminator: int | None = None,
141134
) -> int:
142135
"""
143136
Open a commissioning window to commission a device present on this controller to another.
@@ -230,10 +223,7 @@ async def send_command_no_wait(
230223
if not self.server_info:
231224
raise InvalidState("Not connected")
232225

233-
if (
234-
require_schema is not None
235-
and require_schema > self.server_info.schema_version
236-
):
226+
if require_schema is not None and require_schema > self.server_info.schema_version:
237227
raise InvalidServerVersion(
238228
"Command not available due to incompatible server version. Update the Matter "
239229
f"Server to a version that supports at least api schema {require_schema}."
@@ -268,15 +258,10 @@ async def start_listening(self, init_ready: asyncio.Event | None = None) -> None
268258
message_id=uuid.uuid4().hex, command=APICommand.START_LISTENING
269259
)
270260
await self.connection.send_message(message)
271-
nodes_msg = cast(
272-
SuccessResultMessage, await self.connection.receive_message_or_raise()
273-
)
261+
nodes_msg = cast(SuccessResultMessage, await self.connection.receive_message_or_raise())
274262
# a full dump of all nodes will be the result of the start_listening command
275263
# create MatterNode objects from the basic MatterNodeData objects
276-
nodes = [
277-
MatterNode(dataclass_from_dict(MatterNodeData, x))
278-
for x in nodes_msg.result
279-
]
264+
nodes = [MatterNode(dataclass_from_dict(MatterNodeData, x)) for x in nodes_msg.result]
280265
self._nodes = {node.node_id: node for node in nodes}
281266
# once we've hit this point we're all set
282267
self.logger.info("Matter client initialized.")
@@ -373,8 +358,8 @@ def _signal_event(
373358
self,
374359
event: EventType,
375360
data: Any = None,
376-
node_id: Optional[int] = None,
377-
attribute_path: Optional[str] = None,
361+
node_id: int | None = None,
362+
attribute_path: str | None = None,
378363
) -> None:
379364
"""Signal event to all subscribers."""
380365
# instead of iterating all subscribers we iterate over subscription keys
@@ -390,7 +375,7 @@ def _signal_event(
390375
for callback in self._subscribers.get(key, []):
391376
callback(event, data)
392377

393-
async def __aenter__(self) -> "MatterClient":
378+
async def __aenter__(self) -> MatterClient:
394379
"""Initialize and connect the Matter Websocket client."""
395380
await self.connect()
396381
return self

matter_server/client/connection.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
import asyncio
55
import logging
66
import pprint
7-
from typing import Any, Callable, Dict, Final, cast
7+
from collections.abc import Callable
8+
from typing import Any, Final, cast
89

910
from aiohttp import ClientSession, ClientWebSocketResponse, WSMsgType, client_exceptions
1011

12+
from matter_server.common.const import SCHEMA_VERSION
13+
from matter_server.common.helpers.json import json_dumps, json_loads
1114
from matter_server.common.helpers.util import dataclass_from_dict
12-
13-
from ..common.const import SCHEMA_VERSION
14-
from ..common.helpers.json import json_dumps, json_loads
15-
from ..common.models import (
15+
from matter_server.common.models import (
1616
CommandMessage,
1717
ErrorResultMessage,
1818
EventMessage,
@@ -21,6 +21,7 @@
2121
ServerInfoMessage,
2222
SuccessResultMessage,
2323
)
24+
2425
from .exceptions import (
2526
CannotConnect,
2627
ConnectionClosed,
@@ -50,8 +51,8 @@ def __init__(
5051
self.server_info: ServerInfoMessage | None = None
5152
self._aiohttp_session = aiohttp_session
5253
self._ws_client: ClientWebSocketResponse | None = None
53-
self._nodes: Dict[int, MatterNode] = {}
54-
self._result_futures: Dict[str, asyncio.Future] = {}
54+
self._nodes: dict[int, MatterNode] = {}
55+
self._result_futures: dict[str, asyncio.Future] = {}
5556
self._subscribers: dict[str, list[Callable[[EventType, Any], None]]] = {}
5657

5758
@property

matter_server/client/models/device_types.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
import typing
1010

11-
from chip.clusters import Objects as all_clusters
11+
from chip.clusters import Objects as all_clusters # noqa: N813
1212

13-
ALL_TYPES: dict[int, type["DeviceType"]] = {}
13+
ALL_TYPES: dict[int, type[DeviceType]] = {}
1414

1515

1616
class DeviceType:

matter_server/client/models/node.py

+11-20
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
from .device_types import (
1818
ALL_TYPES as DEVICE_TYPES,
19+
)
20+
from .device_types import (
1921
Aggregator,
2022
BridgedDevice,
2123
DeviceType,
@@ -142,9 +144,7 @@ def has_attribute(
142144
# allow sending None for Cluster to auto resolve it from the Attribute
143145
cluster = attribute.cluster_id
144146
cluster_id = cluster if isinstance(cluster, int) else cluster.id
145-
attribute_id = (
146-
attribute if isinstance(attribute, int) else attribute.attribute_id
147-
)
147+
attribute_id = attribute if isinstance(attribute, int) else attribute.attribute_id
148148
# the fastest way to check this is just by checking the AttributePath in the raw data...
149149
attr_path = create_attribute_path(self.endpoint_id, cluster_id, attribute_id)
150150
return attr_path in self.node.node_data.attributes
@@ -165,12 +165,10 @@ def set_attribute_value(self, attribute_path: str, attribute_value: Any) -> None
165165
self.clusters[cluster_id] = cluster_instance
166166

167167
# unpack cluster attribute, using the descriptor
168-
attribute_class: Clusters.ClusterAttributeDescriptor = ALL_ATTRIBUTES[
169-
cluster_id
170-
][attribute_id]
171-
attribute_name, attribute_type = get_object_params(
172-
cluster_class.descriptor, attribute_id
173-
)
168+
attribute_class: Clusters.ClusterAttributeDescriptor = ALL_ATTRIBUTES[cluster_id][
169+
attribute_id
170+
]
171+
attribute_name, attribute_type = get_object_params(cluster_class.descriptor, attribute_id)
174172

175173
# we only set the value at cluster instance level and we leave
176174
# the underlying Attributes classproperty alone
@@ -251,20 +249,15 @@ def get_attribute_value(
251249
"""Return Matter Cluster Attribute value for given parameters."""
252250
return self.endpoints[endpoint].get_attribute_value(cluster, attribute)
253251

254-
def has_cluster(
255-
self, cluster: type[_CLUSTER_T] | int, endpoint: int | None = None
256-
) -> bool:
252+
def has_cluster(self, cluster: type[_CLUSTER_T] | int, endpoint: int | None = None) -> bool:
257253
"""Check if node has a specific cluster on any of the endpoints."""
258254
return any(
259255
x
260256
for x in self.endpoints.values()
261-
if x.has_cluster(cluster)
262-
and (endpoint is None or x.endpoint_id == endpoint)
257+
if x.has_cluster(cluster) and (endpoint is None or x.endpoint_id == endpoint)
263258
)
264259

265-
def get_cluster(
266-
self, endpoint: int, cluster: type[_CLUSTER_T] | int
267-
) -> _CLUSTER_T | None:
260+
def get_cluster(self, endpoint: int, cluster: type[_CLUSTER_T] | int) -> _CLUSTER_T | None:
268261
"""
269262
Get a Cluster object containing all attributes.
270263
@@ -300,9 +293,7 @@ def update(self, node_data: MatterNodeData) -> None:
300293
endpoint_id=endpoint_id, attributes_data=attributes_data, node=self
301294
)
302295
# lookup if this is a bridge device
303-
self._is_bridge_device = any(
304-
Aggregator in x.device_types for x in self.endpoints.values()
305-
)
296+
self._is_bridge_device = any(Aggregator in x.device_types for x in self.endpoints.values())
306297
# composed devices reference to other endpoints through the partsList attribute
307298
# create a mapping table to quickly map this
308299
for endpoint in self.endpoints.values():

matter_server/common/errors.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
"""Matter Exceptions."""
22
from __future__ import annotations
33

4-
from typing import Type
5-
64
# mapping from error_code to Exception class
75
ERROR_MAP: dict[int, type] = {}
86

@@ -78,6 +76,6 @@ class InvalidCommand(MatterError):
7876
error_code = 9
7977

8078

81-
def exception_from_error_code(error_code: int) -> Type[MatterError]:
79+
def exception_from_error_code(error_code: int) -> type[MatterError]:
8280
"""Return correct Exception class from error_code."""
8381
return ERROR_MAP.get(error_code, MatterError)

matter_server/common/helpers/api.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""Several helpers for the WebSockets API."""
22
from __future__ import annotations
33

4-
from dataclasses import MISSING, dataclass
54
import inspect
6-
from typing import Any, Callable, Coroutine, TypeVar, get_type_hints
5+
from collections.abc import Callable, Coroutine
6+
from dataclasses import MISSING, dataclass
7+
from typing import Any, TypeVar, get_type_hints
78

89
from matter_server.common.helpers.util import parse_value
910

@@ -22,7 +23,7 @@ class APICommandHandler:
2223
@classmethod
2324
def parse(
2425
cls, command: str, func: Callable[..., Coroutine[Any, Any, Any]]
25-
) -> "APICommandHandler":
26+
) -> APICommandHandler:
2627
"""Parse APICommandHandler by providing a function."""
2728
return APICommandHandler(
2829
command=command,
@@ -60,9 +61,6 @@ def parse_arguments(
6061
# parse arguments to correct type
6162
for name, param in func_sig.parameters.items():
6263
value = args.get(name)
63-
if param.default is inspect.Parameter.empty:
64-
default = MISSING
65-
else:
66-
default = param.default
64+
default = MISSING if param.default is inspect.Parameter.empty else param.default
6765
final_args[name] = parse_value(name, value, func_types[name], default)
6866
return final_args

0 commit comments

Comments
 (0)