| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,7 +17,7 @@ jobs: | |||
| 17 | 17 | ||
| 18 | 18 | strategy: | |
| 19 | 19 | matrix: | |
| 20 | - python-version: ["3.12"] | ||
| 20 | + python-version: ["3.13"] | ||
| 21 | 21 | ||
| 22 | 22 | steps: | |
| 23 | 23 | - name: "Checkout source files" | |
@@ -39,37 +39,17 @@ jobs: | |||
| 39 | 39 | name: Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} | |
| 40 | 40 | needs: linting | |
| 41 | 41 | runs-on: ${{ matrix.os }} | |
| 42 | - continue-on-error: ${{ startsWith(matrix.python-version, 'pypy') }} | ||
| 43 | 42 | ||
| 44 | 43 | strategy: | |
| 45 | 44 | matrix: | |
| 46 | - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "pypy-3.9", "pypy-3.10"] | ||
| 45 | + python-version: ["3.11", "3.12", "3.13"] | ||
| 47 | 46 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 48 | 47 | extras: [false, true] | |
| 49 | 48 | exclude: | |
| 50 | 49 | - os: macos-latest | |
| 51 | 50 | extras: true | |
| 52 | 51 | - os: windows-latest | |
| 53 | 52 | extras: true | |
| 54 | - - os: ubuntu-latest | ||
| 55 | - python-version: "pypy-3.9" | ||
| 56 | - extras: true | ||
| 57 | - - os: ubuntu-latest | ||
| 58 | - python-version: "pypy-3.10" | ||
| 59 | - extras: true | ||
| 60 | - - os: ubuntu-latest | ||
| 61 | - python-version: "3.9" | ||
| 62 | - extras: true | ||
| 63 | - - os: ubuntu-latest | ||
| 64 | - python-version: "3.10" | ||
| 65 | - extras: true | ||
| 66 | - # Exclude pypy on windows due to significant performance issues | ||
| 67 | - # running pytest requires ~12 min instead of 2 min on other platforms | ||
| 68 | - - os: windows-latest | ||
| 69 | - python-version: "pypy-3.9" | ||
| 70 | - - os: windows-latest | ||
| 71 | - python-version: "pypy-3.10" | ||
| 72 | - | ||
| 73 | 53 | ||
| 74 | 54 | steps: | |
| 75 | 55 | - uses: "actions/checkout@v4" | |
@@ -79,16 +59,10 @@ jobs: | |||
| 79 | 59 | python-version: ${{ matrix.python-version }} | |
| 80 | 60 | uv-version: ${{ env.UV_VERSION }} | |
| 81 | 61 | uv-install-options: ${{ matrix.extras == true && '--all-extras' || '' }} | |
| 82 | - - name: "Run tests (no coverage)" | ||
| 83 | - if: ${{ startsWith(matrix.python-version, 'pypy') }} | ||
| 84 | - run: | | ||
| 85 | - uv run pytest -n auto | ||
| 86 | 62 | - name: "Run tests (with coverage)" | |
| 87 | - if: ${{ !startsWith(matrix.python-version, 'pypy') }} | ||
| 88 | 63 | run: | | |
| 89 | 64 | uv run pytest -n auto --cov kasa --cov-report xml | |
| 90 | 65 | - name: "Upload coverage to Codecov" | |
| 91 | - if: ${{ !startsWith(matrix.python-version, 'pypy') }} | ||
| 92 | 66 | uses: "codecov/codecov-action@v4" | |
| 93 | 67 | with: | |
| 94 | 68 | token: ${{ secrets.CODECOV_TOKEN }} | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,7 +18,7 @@ repos: | |||
| 18 | 18 | - id: check-ast | |
| 19 | 19 | ||
| 20 | 20 | - repo: https://github.com/astral-sh/ruff-pre-commit | |
| 21 | - rev: v0.3.7 | ||
| 21 | + rev: v0.7.4 | ||
| 22 | 22 | hooks: | |
| 23 | 23 | - id: ruff | |
| 24 | 24 | args: [--fix, --exit-non-zero-on-fix] | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,7 +1,7 @@ | |||
| 1 | 1 | """Original implementation of the TP-Link Smart Home protocol.""" | |
| 2 | 2 | ||
| 3 | 3 | import struct | |
| 4 | - from typing import Generator | ||
| 4 | + from collections.abc import Generator | ||
| 5 | 5 | ||
| 6 | 6 | ||
| 7 | 7 | class OriginalTPLinkSmartHomeProtocol: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -315,7 +315,7 @@ async def cli( | |||
| 315 | 315 | credentials = Credentials(username=username, password=password) | |
| 316 | 316 | if host is not None: | |
| 317 | 317 | if discovery_info: | |
| 318 | - click.echo("Host and discovery info given, trying connect on %s." % host) | ||
| 318 | + click.echo(f"Host and discovery info given, trying connect on {host}.") | ||
| 319 | 319 | ||
| 320 | 320 | di = json.loads(discovery_info) | |
| 321 | 321 | dr = DiscoveryResult.from_dict(di) | |
@@ -358,7 +358,7 @@ async def cli( | |||
| 358 | 358 | "Could not find a protocol for the given parameters." | |
| 359 | 359 | ) | |
| 360 | 360 | else: | |
| 361 | - click.echo("Host given, performing discovery on %s." % host) | ||
| 361 | + click.echo(f"Host given, performing discovery on {host}.") | ||
| 362 | 362 | device = await Discover.discover_single( | |
| 363 | 363 | host, | |
| 364 | 364 | credentials=credentials, | |
@@ -374,13 +374,13 @@ async def cli( | |||
| 374 | 374 | ) | |
| 375 | 375 | else: | |
| 376 | 376 | click.echo( | |
| 377 | - "No --host given, performing discovery on %s. Use --target to override." | ||
| 378 | - % target | ||
| 377 | + "No --host given, performing discovery on" | ||
| 378 | + f" {target}. Use --target to override." | ||
| 379 | 379 | ) | |
| 380 | 380 | devices = await Discover.discover( | |
| 381 | 381 | target=target, credentials=credentials, discovery_timeout=discovery_timeout | |
| 382 | 382 | ) | |
| 383 | - click.echo("Detected %s devices" % len(devices)) | ||
| 383 | + click.echo(f"Detected {len(devices)} devices") | ||
| 384 | 384 | for dev in devices.values(): | |
| 385 | 385 | await handle_device( | |
| 386 | 386 | basedir, | |
@@ -446,7 +446,7 @@ async def get_legacy_fixture( | |||
| 446 | 446 | dr = DiscoveryResult.from_dict(discovery_info) | |
| 447 | 447 | final["discovery_result"] = dr.to_dict() | |
| 448 | 448 | ||
| 449 | - click.echo("Got %s successes" % len(successes)) | ||
| 449 | + click.echo(f"Got {len(successes)} successes") | ||
| 450 | 450 | click.echo(click.style("## device info file ##", bold=True)) | |
| 451 | 451 | ||
| 452 | 452 | sysinfo = final["system"]["get_sysinfo"] | |
@@ -959,7 +959,7 @@ async def get_smart_fixtures( | |||
| 959 | 959 | dr = DiscoveryResult.from_dict(discovery_info) # type: ignore | |
| 960 | 960 | final["discovery_result"] = dr.to_dict() | |
| 961 | 961 | ||
| 962 | - click.echo("Got %s successes" % len(successes)) | ||
| 962 | + click.echo(f"Got {len(successes)} successes") | ||
| 963 | 963 | click.echo(click.style("## device info file ##", bold=True)) | |
| 964 | 964 | ||
| 965 | 965 | if "get_device_info" in final: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -67,7 +67,7 @@ def parse_pcap(file): | |||
| 67 | 67 | for module, cmds in json_payload.items(): | |
| 68 | 68 | seen_items["modules"][module] += 1 | |
| 69 | 69 | if "err_code" in cmds: | |
| 70 | - echo("[red]Got error for module: %s[/red]" % cmds) | ||
| 70 | + echo(f"[red]Got error for module: {cmds}[/red]") | ||
| 71 | 71 | continue | |
| 72 | 72 | ||
| 73 | 73 | for cmd, response in cmds.items(): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,7 +3,6 @@ | |||
| 3 | 3 | from __future__ import annotations | |
| 4 | 4 | ||
| 5 | 5 | import asyncio | |
| 6 | - | ||
| 7 | 6 | from zoneinfo import ZoneInfo | |
| 8 | 7 | ||
| 9 | 8 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,9 +5,10 @@ | |||
| 5 | 5 | import json | |
| 6 | 6 | import re | |
| 7 | 7 | import sys | |
| 8 | + from collections.abc import Callable | ||
| 8 | 9 | from contextlib import contextmanager | |
| 9 | 10 | from functools import singledispatch, update_wrapper, wraps | |
| 10 | - from typing import TYPE_CHECKING, Any, Callable, Final | ||
| 11 | + from typing import TYPE_CHECKING, Any, Final | ||
| 11 | 12 | ||
| 12 | 13 | import asyncclick as click | |
| 13 | 14 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,10 +2,10 @@ | |||
| 2 | 2 | ||
| 3 | 3 | from __future__ import annotations | |
| 4 | 4 | ||
| 5 | + import zoneinfo | ||
| 5 | 6 | from datetime import datetime | |
| 6 | 7 | ||
| 7 | 8 | import asyncclick as click | |
| 8 | - import zoneinfo | ||
| 9 | 9 | ||
| 10 | 10 | from kasa import ( | |
| 11 | 11 | Device, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -110,11 +110,9 @@ | |||
| 110 | 110 | from collections.abc import Mapping, Sequence | |
| 111 | 111 | from dataclasses import dataclass | |
| 112 | 112 | from datetime import datetime, tzinfo | |
| 113 | - from typing import TYPE_CHECKING, Any | ||
| 113 | + from typing import TYPE_CHECKING, Any, TypeAlias | ||
| 114 | 114 | from warnings import warn | |
| 115 | 115 | ||
| 116 | - from typing_extensions import TypeAlias | ||
| 117 | - | ||
| 118 | 116 | from .credentials import Credentials as _Credentials | |
| 119 | 117 | from .device_type import DeviceType | |
| 120 | 118 | from .deviceconfig import ( | |
@@ -213,7 +211,7 @@ def __init__( | |||
| 213 | 211 | self._last_update: Any = None | |
| 214 | 212 | _LOGGER.debug("Initializing %s of type %s", host, type(self)) | |
| 215 | 213 | self._device_type = DeviceType.Unknown | |
| 216 | - # TODO: typing Any is just as using Optional[Dict] would require separate | ||
| 214 | + # TODO: typing Any is just as using dict | None would require separate | ||
| 217 | 215 | # checks in accessors. the @updated_required decorator does not ensure | |
| 218 | 216 | # mypy that these are not accessed incorrectly. | |
| 219 | 217 | self._discovery_info: dict[str, Any] | None = None | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -27,14 +27,12 @@ | |||
| 27 | 27 | ||
| 28 | 28 | """ | |
| 29 | 29 | ||
| 30 | - # Note that this module does not work with from __future__ import annotations | ||
| 31 | - # due to it's use of type returned by fields() which becomes a string with the import. | ||
| 32 | - # https://bugs.python.org/issue39442 | ||
| 33 | - # ruff: noqa: FA100 | ||
| 30 | + # Module cannot use from __future__ import annotations until migrated to mashumaru | ||
| 31 | + # as dataclass.fields() will not resolve the type. | ||
| 34 | 32 | import logging | |
| 35 | 33 | from dataclasses import asdict, dataclass, field, fields, is_dataclass | |
| 36 | 34 | from enum import Enum | |
| 37 | - from typing import TYPE_CHECKING, Any, Dict, Optional, TypedDict, Union | ||
| 35 | + from typing import TYPE_CHECKING, Any, Optional, TypedDict | ||
| 38 | 36 | ||
| 39 | 37 | from .credentials import Credentials | |
| 40 | 38 | from .exceptions import KasaException | |
@@ -118,15 +116,15 @@ class DeviceConnectionParameters: | |||
| 118 | 116 | ||
| 119 | 117 | device_family: DeviceFamily | |
| 120 | 118 | encryption_type: DeviceEncryptionType | |
| 121 | - login_version: Optional[int] = None | ||
| 119 | + login_version: int | None = None | ||
| 122 | 120 | https: bool = False | |
| 123 | 121 | ||
| 124 | 122 | @staticmethod | |
| 125 | 123 | def from_values( | |
| 126 | 124 | device_family: str, | |
| 127 | 125 | encryption_type: str, | |
| 128 | - login_version: Optional[int] = None, | ||
| 129 | - https: Optional[bool] = None, | ||
| 126 | + login_version: int | None = None, | ||
| 127 | + https: bool | None = None, | ||
| 130 | 128 | ) -> "DeviceConnectionParameters": | |
| 131 | 129 | """Return connection parameters from string values.""" | |
| 132 | 130 | try: | |
@@ -145,7 +143,7 @@ def from_values( | |||
| 145 | 143 | ) from ex | |
| 146 | 144 | ||
| 147 | 145 | @staticmethod | |
| 148 | - def from_dict(connection_type_dict: Dict[str, Any]) -> "DeviceConnectionParameters": | ||
| 146 | + def from_dict(connection_type_dict: dict[str, Any]) -> "DeviceConnectionParameters": | ||
| 149 | 147 | """Return connection parameters from dict.""" | |
| 150 | 148 | if ( | |
| 151 | 149 | isinstance(connection_type_dict, dict) | |
@@ -163,9 +161,9 @@ def from_dict(connection_type_dict: Dict[str, Any]) -> "DeviceConnectionParamete | |||
| 163 | 161 | ||
| 164 | 162 | raise KasaException(f"Invalid connection type data for {connection_type_dict}") | |
| 165 | 163 | ||
| 166 | - def to_dict(self) -> Dict[str, Union[str, int, bool]]: | ||
| 164 | + def to_dict(self) -> dict[str, str | int | bool]: | ||
| 167 | 165 | """Convert connection params to dict.""" | |
| 168 | - result: Dict[str, Union[str, int]] = { | ||
| 166 | + result: dict[str, str | int] = { | ||
| 169 | 167 | "device_family": self.device_family.value, | |
| 170 | 168 | "encryption_type": self.encryption_type.value, | |
| 171 | 169 | "https": self.https, | |
@@ -183,17 +181,17 @@ class DeviceConfig: | |||
| 183 | 181 | #: IP address or hostname | |
| 184 | 182 | host: str | |
| 185 | 183 | #: Timeout for querying the device | |
| 186 | - timeout: Optional[int] = DEFAULT_TIMEOUT | ||
| 184 | + timeout: int | None = DEFAULT_TIMEOUT | ||
| 187 | 185 | #: Override the default 9999 port to support port forwarding | |
| 188 | - port_override: Optional[int] = None | ||
| 186 | + port_override: int | None = None | ||
| 189 | 187 | #: Credentials for devices requiring authentication | |
| 190 | - credentials: Optional[Credentials] = None | ||
| 188 | + credentials: Credentials | None = None | ||
| 191 | 189 | #: Credentials hash for devices requiring authentication. | |
| 192 | 190 | #: If credentials are also supplied they take precendence over credentials_hash. | |
| 193 | 191 | #: Credentials hash can be retrieved from :attr:`Device.credentials_hash` | |
| 194 | - credentials_hash: Optional[str] = None | ||
| 192 | + credentials_hash: str | None = None | ||
| 195 | 193 | #: The protocol specific type of connection. Defaults to the legacy type. | |
| 196 | - batch_size: Optional[int] = None | ||
| 194 | + batch_size: int | None = None | ||
| 197 | 195 | #: The batch size for protoools supporting multiple request batches. | |
| 198 | 196 | connection_type: DeviceConnectionParameters = field( | |
| 199 | 197 | default_factory=lambda: DeviceConnectionParameters( | |
@@ -208,7 +206,7 @@ class DeviceConfig: | |||
| 208 | 206 | #: Set a custom http_client for the device to use. | |
| 209 | 207 | http_client: Optional["ClientSession"] = field(default=None, compare=False) | |
| 210 | 208 | ||
| 211 | - aes_keys: Optional[KeyPairDict] = None | ||
| 209 | + aes_keys: KeyPairDict | None = None | ||
| 212 | 210 | ||
| 213 | 211 | def __post_init__(self) -> None: | |
| 214 | 212 | if self.connection_type is None: | |
@@ -219,9 +217,9 @@ def __post_init__(self) -> None: | |||
| 219 | 217 | def to_dict( | |
| 220 | 218 | self, | |
| 221 | 219 | *, | |
| 222 | - credentials_hash: Optional[str] = None, | ||
| 220 | + credentials_hash: str | None = None, | ||
| 223 | 221 | exclude_credentials: bool = False, | |
| 224 | - ) -> Dict[str, Dict[str, str]]: | ||
| 222 | + ) -> dict[str, dict[str, str]]: | ||
| 225 | 223 | """Convert device config to dict.""" | |
| 226 | 224 | if credentials_hash is not None or exclude_credentials: | |
| 227 | 225 | self.credentials = None | |
@@ -230,7 +228,7 @@ def to_dict( | |||
| 230 | 228 | return _dataclass_to_dict(self) | |
| 231 | 229 | ||
| 232 | 230 | @staticmethod | |
| 233 | - def from_dict(config_dict: Dict[str, Dict[str, str]]) -> "DeviceConfig": | ||
| 231 | + def from_dict(config_dict: dict[str, dict[str, str]]) -> "DeviceConfig": | ||
| 234 | 232 | """Return device config from dict.""" | |
| 235 | 233 | if isinstance(config_dict, dict): | |
| 236 | 234 | return _dataclass_from_dict(DeviceConfig, config_dict) | |
| Back | FazBrowse Home | New Git URL |
0 commit comments