diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 132df750..93754118 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -55,6 +55,27 @@ jobs: run: | ruff check src/pyatmo + typechecker: + runs-on: ubuntu-latest + strategy: + max-parallel: 1 + matrix: + python-version: [3.11.4] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5.2.0 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install mypy types-requests + - name: Typecheck with mypy + run: | + mypy src/pyatmo + build: runs-on: ubuntu-latest strategy: diff --git a/pyproject.toml b/pyproject.toml index 9d528a67..74f17af4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["wheel", "setuptools", "attrs>=17.1"] +requires = ["setuptools>=64", "setuptools_scm>=8"] build-backend = "setuptools.build_meta" [project] @@ -54,16 +54,6 @@ dev-dependencies = [ ] [tool.setuptools_scm] -local_scheme = "no-local-version" -tag_regex = "^(?Pv)?(?P[^\\+]+)(?P.*)?$" -write_to = "src/pyatmo/__version__.py" -write_to_template = '''""" -Pyatmo: Simple API to access Netatmo devices and data - -DO NO EDIT THIS FILE - VERSION IS MANAGED BY SETUPTOOLS_SCM -""" -__version__ = "{version}" -''' [tool.pytest.ini_options] minversion = "8.0" @@ -78,66 +68,7 @@ fix = true line-length = 88 [tool.ruff.lint] -select = [ - "B002", # Python does not support the unary prefix increment - "B007", # Loop control variable {name} not used within loop body - "B014", # Exception handler with duplicate exception - "B023", # Function definition does not bind loop variable {name} - "B026", # Star-arg unpacking after a keyword argument is strongly discouraged - "C", # complexity - "COM818", # Trailing comma on bare tuple prohibited - "D", # docstrings - "DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow() - "DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts) - "E", # pycodestyle - "F", # pyflakes/autoflake - "G", # flake8-logging-format - "I", # isort - "ICN001", # import concentions; {name} should be imported as {asname} - "N804", # First argument of a class method should be named cls - "N805", # First argument of a method should be named self - "N815", # Variable {name} in class scope should not be mixedCase - "S307", # No builtin eval() allowed - "PGH004", # Use specific rule codes when using noqa - "PLC0414", # Useless import alias. Import alias does not rename original package. - "PL", # pylint - "Q000", # Double quotes found but single quotes preferred - "RUF006", # Store a reference to the return value of asyncio.create_task - "S102", # Use of exec detected - "S103", # bad-file-permissions - "S108", # hardcoded-temp-file - "S306", # suspicious-mktemp-usage - "S307", # suspicious-eval-usage - "S313", # suspicious-xmlc-element-tree-usage - "S314", # suspicious-xml-element-tree-usage - "S315", # suspicious-xml-expat-reader-usage - "S316", # suspicious-xml-expat-builder-usage - "S317", # suspicious-xml-sax-usage - "S318", # suspicious-xml-mini-dom-usage - "S319", # suspicious-xml-pull-dom-usage - "S320", # suspicious-xmle-tree-usage - "S601", # paramiko-call - "S602", # subprocess-popen-with-shell-equals-true - "S604", # call-with-shell-equals-true - "S608", # hardcoded-sql-expression - "S609", # unix-command-wildcard-injection - "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass - "SIM117", # Merge with-statements that use the same scope - "SIM118", # Use {key} in {dict} instead of {key} in {dict}.keys() - "SIM201", # Use {left} != {right} instead of not {left} == {right} - "SIM208", # Use {expr} instead of not (not {expr}) - "SIM212", # Use {a} if {a} else {b} instead of {b} if not {a} else {a} - "SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'. - "SIM401", # Use get from dict with default instead of an if block - "T100", # Trace found: {name} used - "T20", # flake8-print - "TID251", # Banned imports - "TRY004", # Prefer TypeError exception for invalid type - "B904", # Use raise from to specify exception cause - "TRY302", # Remove exception handler; error is immediately re-raised - "UP", # pyupgrade - "W", # pycodestyle -] +select = ["ALL"] ignore = [ "D202", # No blank lines allowed after function docstring @@ -147,6 +78,7 @@ ignore = [ "D407", # Section name underlining "E501", # line too long "E731", # do not assign a lambda expression, use a def + "N818", # Exception should be named with an Error suffix # False positives https://github.com/astral-sh/ruff/issues/5386 "PLC0208", # Use a sequence type instead of a `set` when iterating over values "PLR0911", # Too many return statements ({returns} > {max_returns}) @@ -175,9 +107,28 @@ split-on-trailing-comma = false [tool.ruff.lint.per-file-ignores] # Allow for main entry & scripts to write to stdout "src/pyatmo/__main__.py" = ["T201"] +"src/pyatmo/modules/module.py" = ["PGH003"] +"src/pyatmo/auth.py" = ["ASYNC109"] # Exceptions for tests -"tests/*" = ["D10"] +"tests/*" = [ + "D10", + "S105", + "S101", + "ANN201", + "ANN001", + "N802", + "ANN202", + "PTH123", + "ASYNC230", + "PT012", + "DTZ001", + "ANN003", + "ANN002", + "A001", + "ARG001", + "ANN204", +] [tool.ruff.lint.mccabe] max-complexity = 25 diff --git a/src/pyatmo/account.py b/src/pyatmo/account.py index ff871573..019b79ff 100644 --- a/src/pyatmo/account.py +++ b/src/pyatmo/account.py @@ -3,7 +3,7 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, cast from uuid import uuid4 from pyatmo import modules @@ -20,7 +20,7 @@ ) from pyatmo.helpers import extract_raw_data from pyatmo.home import Home -from pyatmo.modules.module import MeasureInterval, Module +from pyatmo.modules.module import Energy, MeasureInterval, Module if TYPE_CHECKING: from pyatmo.auth import AbstractAsyncAuth @@ -31,7 +31,11 @@ class AsyncAccount: """Async class of a Netatmo account.""" - def __init__(self, auth: AbstractAsyncAuth, favorite_stations: bool = True) -> None: + def __init__( + self, + auth: AbstractAsyncAuth, + favorite_stations: bool = True, # noqa: FBT001, FBT002 + ) -> None: """Initialize the Netatmo account.""" self.auth: AbstractAsyncAuth = auth @@ -72,7 +76,8 @@ def process_topology(self, disabled_homes_ids: list[str] | None = None) -> None: self.homes[home_id] = Home(self.auth, raw_data=home) async def async_update_topology( - self, disabled_homes_ids: list[str] | None = None + self, + disabled_homes_ids: list[str] | None = None, ) -> None: """Retrieve topology data from /homesdata.""" @@ -126,12 +131,15 @@ async def async_update_measures( ) -> None: """Retrieve measures data from /getmeasure.""" - await getattr(self.homes[home_id].modules[module_id], "async_update_measures")( - start_time=start_time, - end_time=end_time, - interval=interval, - days=days, - ) + module = self.homes[home_id].modules[module_id] + if module.has_feature("historical_data"): + module = cast(Energy, module) + await module.async_update_measures( + start_time=start_time, + end_time=end_time, + interval=interval, + days=days, + ) def register_public_weather_area( self, @@ -140,7 +148,7 @@ def register_public_weather_area( lat_sw: str, lon_sw: str, required_data_type: str | None = None, - filtering: bool = False, + filtering: bool = False, # noqa: FBT001, FBT002 *, area_id: str = str(uuid4()), ) -> str: diff --git a/src/pyatmo/auth.py b/src/pyatmo/auth.py index e401124d..0afde5a6 100644 --- a/src/pyatmo/auth.py +++ b/src/pyatmo/auth.py @@ -8,7 +8,13 @@ import logging from typing import Any -from aiohttp import ClientError, ClientResponse, ClientSession, ContentTypeError +from aiohttp import ( + ClientError, + ClientResponse, + ClientSession, + ClientTimeout, + ContentTypeError, +) from pyatmo.const import ( AUTHORIZATION_HEADER, @@ -51,7 +57,9 @@ async def async_get_image( try: access_token = await self.async_get_access_token() except ClientError as err: - raise ApiError(f"Access token failure: {err}") from err + error_type = type(err).__name__ + msg = f"Access token failure: {error_type} - {err}" + raise ApiError(msg) from err headers = {AUTHORIZATION_HEADER: f"Bearer {access_token}"} req_args = {"data": params if params is not None else {}} @@ -59,19 +67,22 @@ async def async_get_image( url = (base_url or self.base_url) + endpoint async with self.websession.get( url, - **req_args, # type: ignore + **req_args, # type: ignore # noqa: PGH003 headers=headers, - timeout=timeout, + timeout=ClientTimeout(total=timeout), ) as resp: resp_content = await resp.read() if resp.headers.get("content-type") == "image/jpeg": return resp_content - raise ApiError( + msg = ( f"{resp.status} - " f"invalid content-type in response" - f"when accessing '{url}'", + f"when accessing '{url}'" + ) + raise ApiError( + msg, ) async def async_post_api_request( @@ -104,20 +115,21 @@ async def async_post_request( async with self.websession.post( url, - **req_args, + **req_args, # type: ignore # noqa: PGH003 headers=headers, - timeout=timeout, + timeout=ClientTimeout(total=timeout), ) as resp: return await self.process_response(resp, url) - async def get_access_token(self): + async def get_access_token(self) -> str: """Get access token.""" try: return await self.async_get_access_token() except ClientError as err: - raise ApiError(f"Access token failure: {err}") from err + msg = f"Access token failure: {err}" + raise ApiError(msg) from err - def prepare_request_arguments(self, params): + def prepare_request_arguments(self, params: dict | None) -> dict: """Prepare request arguments.""" req_args = {"data": params if params is not None else {}} @@ -131,7 +143,7 @@ def prepare_request_arguments(self, params): return req_args - async def process_response(self, resp, url): + async def process_response(self, resp: ClientResponse, url: str) -> ClientResponse: """Process response.""" resp_status = resp.status resp_content = await resp.read() @@ -142,7 +154,12 @@ async def process_response(self, resp, url): return await self.handle_success_response(resp, resp_content) - async def handle_error_response(self, resp, resp_status, url): + async def handle_error_response( + self, + resp: ClientResponse, + resp_status: int, + url: str, + ) -> None: """Handle error response.""" try: resp_json = await resp.json() @@ -159,19 +176,25 @@ async def handle_error_response(self, resp, resp_status, url): raise ApiErrorThrottling( message, ) - else: - raise ApiError( - message, - ) + raise ApiError( + message, + ) except (JSONDecodeError, ContentTypeError) as exc: - raise ApiError( + msg = ( f"{resp_status} - " f"{ERRORS.get(resp_status, '')} - " - f"when accessing '{url}'", + f"when accessing '{url}'" + ) + raise ApiError( + msg, ) from exc - async def handle_success_response(self, resp, resp_content): + async def handle_success_response( + self, + resp: ClientResponse, + resp_content: bytes, + ) -> ClientResponse: """Handle success response.""" try: if "application/json" in resp.headers.get("content-type", []): @@ -193,7 +216,8 @@ async def async_addwebhook(self, webhook_url: str) -> None: params={"url": webhook_url}, ) except asyncio.exceptions.TimeoutError as exc: - raise ApiError("Webhook registration timed out") from exc + msg = "Webhook registration timed out" + raise ApiError(msg) from exc else: LOG.debug("addwebhook: %s", resp) @@ -205,6 +229,7 @@ async def async_dropwebhook(self) -> None: params={"app_types": "app_security"}, ) except asyncio.exceptions.TimeoutError as exc: - raise ApiError("Webhook registration timed out") from exc + msg = "Webhook registration timed out" + raise ApiError(msg) from exc else: LOG.debug("dropwebhook: %s", resp) diff --git a/src/pyatmo/const.py b/src/pyatmo/const.py index 0b141743..71402d45 100644 --- a/src/pyatmo/const.py +++ b/src/pyatmo/const.py @@ -106,3 +106,6 @@ MAX_HISTORY_TIME_FRAME = 24 * 2 * 3600 UNKNOWN = "unknown" + +ON = True +OFF = False diff --git a/src/pyatmo/event.py b/src/pyatmo/event.py index c2fa97ed..b52232ba 100644 --- a/src/pyatmo/event.py +++ b/src/pyatmo/event.py @@ -4,8 +4,10 @@ from dataclasses import dataclass from enum import Enum +from typing import TYPE_CHECKING -from pyatmo.const import RawData +if TYPE_CHECKING: + from pyatmo.const import RawData EVENT_ATTRIBUTES_MAP = {"id": "entity_id", "type": "event_type", "time": "event_time"} @@ -86,6 +88,7 @@ class Event: message: str | None = None camera_id: str | None = None device_id: str | None = None + module_id: str | None = None person_id: str | None = None video_id: str | None = None sub_type: int | None = None diff --git a/src/pyatmo/exceptions.py b/src/pyatmo/exceptions.py index 31cc8c69..cbf74a07 100644 --- a/src/pyatmo/exceptions.py +++ b/src/pyatmo/exceptions.py @@ -4,52 +4,34 @@ class NoSchedule(Exception): """Raised when no schedule is found.""" - pass - class InvalidSchedule(Exception): """Raised when an invalid schedule is encountered.""" - pass - class InvalidHome(Exception): """Raised when an invalid home is encountered.""" - pass - class InvalidRoom(Exception): """Raised when an invalid room is encountered.""" - pass - class NoDevice(Exception): """Raised when no device is found.""" - pass - class ApiError(Exception): """Raised when an API error is encountered.""" - pass - class ApiErrorThrottling(ApiError): """Raised when an API error is encountered.""" - pass - class ApiHomeReachabilityError(ApiError): """Raised when an API error is encountered.""" - pass - class InvalidState(Exception): """Raised when an invalid state is encountered.""" - - pass diff --git a/src/pyatmo/helpers.py b/src/pyatmo/helpers.py index 9833d5b0..a778449d 100644 --- a/src/pyatmo/helpers.py +++ b/src/pyatmo/helpers.py @@ -3,11 +3,13 @@ from __future__ import annotations import logging -from typing import Any, cast +from typing import TYPE_CHECKING, Any, cast -from pyatmo.const import RawData from pyatmo.exceptions import NoDevice +if TYPE_CHECKING: + from pyatmo.const import RawData + LOG: logging.Logger = logging.getLogger(__name__) @@ -31,7 +33,7 @@ def fix_id(raw_data: RawData) -> dict[str, Any]: return raw_data -def extract_raw_data(resp: Any, tag: str) -> dict[str, Any]: +def extract_raw_data(resp: Any, tag: str) -> dict[str, Any]: # noqa: ANN401 """Extract raw data from server response.""" raw_data = {} @@ -40,7 +42,8 @@ def extract_raw_data(resp: Any, tag: str) -> dict[str, Any]: if resp is None or "body" not in resp or tag not in resp["body"]: LOG.debug("Server response (tag: %s): %s", tag, resp) - raise NoDevice("No device found, errors in response") + msg = "No device found, errors in response" + raise NoDevice(msg) if tag == "homes": return { @@ -50,6 +53,7 @@ def extract_raw_data(resp: Any, tag: str) -> dict[str, Any]: if not (raw_data := fix_id(resp["body"].get(tag))): LOG.debug("Server response (tag: %s): %s", tag, resp) - raise NoDevice("No device data available") + msg = "No device data available" + raise NoDevice(msg) return {tag: raw_data, "errors": resp["body"].get("errors", [])} diff --git a/src/pyatmo/home.py b/src/pyatmo/home.py index 17a3bc80..20240ece 100644 --- a/src/pyatmo/home.py +++ b/src/pyatmo/home.py @@ -3,9 +3,7 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, Any - -from aiohttp import ClientResponse +from typing import TYPE_CHECKING, Any, cast from pyatmo import modules from pyatmo.const import ( @@ -26,13 +24,16 @@ InvalidState, NoSchedule, ) -from pyatmo.modules import Module +from pyatmo.modules.netatmo import NACamera from pyatmo.person import Person from pyatmo.room import Room from pyatmo.schedule import Schedule if TYPE_CHECKING: + from aiohttp import ClientResponse + from pyatmo.auth import AbstractAsyncAuth + from pyatmo.modules import Module LOG = logging.getLogger(__name__) @@ -98,7 +99,7 @@ def get_module(self, module: dict) -> Module: ) except AttributeError: LOG.info("Unknown device type %s", module["type"]) - return getattr(modules, "NLunknown")( + return modules.NLunknown( home=self, module=module, ) @@ -150,7 +151,7 @@ def update_topology(self, raw_data: RawData) -> None: async def update( self, raw_data: RawData, - do_raise_for_reachability_error=False, + do_raise_for_reachability_error: bool = False, # noqa: FBT002, FBT001 ) -> None: """Update home with the latest data.""" has_error = False @@ -187,15 +188,12 @@ async def update( if module.reachable: has_one_module_reachable = True if hasattr(module, "events"): - setattr( - module, - "events", - [ - event - for event in self.events.values() - if getattr(event, "module_id") == module.entity_id - ], - ) + module = cast(NACamera, module) + module.events = [ + event + for event in self.events.values() + if event.module_id == module.entity_id + ] if ( do_raise_for_reachability_error @@ -203,8 +201,9 @@ async def update( and has_one_module_reachable is False and has_an_update is False ): + msg = "No Home update could be performed, all modules unreachable and not updated" raise ApiHomeReachabilityError( - "No Home update could be performed, all modules unreachable and not updated", + msg, ) def get_selected_schedule(self) -> Schedule | None: @@ -252,9 +251,11 @@ async def async_set_thermmode( ) -> bool: """Set thermotat mode.""" if schedule_id is not None and not self.is_valid_schedule(schedule_id): - raise NoSchedule(f"{schedule_id} is not a valid schedule id.") + msg = f"{schedule_id} is not a valid schedule id." + raise NoSchedule(msg) if mode is None: - raise NoSchedule(f"{mode} is not a valid mode.") + msg = f"{mode} is not a valid mode." + raise NoSchedule(msg) post_params = {"home_id": self.entity_id, "mode": mode} if end_time is not None and mode in {"hg", "away"}: post_params["endtime"] = str(end_time) @@ -277,7 +278,8 @@ async def async_set_thermmode( async def async_switch_schedule(self, schedule_id: str) -> bool: """Switch the schedule.""" if not self.is_valid_schedule(schedule_id): - raise NoSchedule(f"{schedule_id} is not a valid schedule id") + msg = f"{schedule_id} is not a valid schedule id" + raise NoSchedule(msg) LOG.debug("Setting home (%s) schedule to %s", self.entity_id, schedule_id) resp = await self.auth.async_post_api_request( endpoint=SWITCHHOMESCHEDULE_ENDPOINT, @@ -289,7 +291,8 @@ async def async_switch_schedule(self, schedule_id: str) -> bool: async def async_set_state(self, data: dict[str, Any]) -> bool: """Set state using given data.""" if not is_valid_state(data): - raise InvalidState("Data for '/set_state' contains errors.") + msg = "Data for '/set_state' contains errors." + raise InvalidState(msg) LOG.debug("Setting state for home (%s) according to %s", self.entity_id, data) resp = await self.auth.async_post_api_request( endpoint=SETSTATE_ENDPOINT, @@ -335,7 +338,8 @@ async def async_set_schedule_temperatures( selected_schedule = self.get_selected_schedule() if selected_schedule is None: - raise NoSchedule("Could not determine selected schedule.") + msg = "Could not determine selected schedule." + raise NoSchedule(msg) zones = [] @@ -383,7 +387,8 @@ async def async_sync_schedule( ) -> None: """Modify an existing schedule.""" if not is_valid_schedule(schedule): - raise InvalidSchedule("Data for '/synchomeschedule' contains errors.") + msg = "Data for '/synchomeschedule' contains errors." + raise InvalidSchedule(msg) LOG.debug( "Setting schedule (%s) for home (%s) to %s", schedule_id, diff --git a/src/pyatmo/modules/base_class.py b/src/pyatmo/modules/base_class.py index db4446d4..2e59f7ca 100644 --- a/src/pyatmo/modules/base_class.py +++ b/src/pyatmo/modules/base_class.py @@ -4,7 +4,6 @@ from abc import ABC import bisect -from collections.abc import Iterable from dataclasses import dataclass import logging from operator import itemgetter @@ -14,11 +13,14 @@ from pyatmo.modules.device_types import DeviceType if TYPE_CHECKING: - from pyatmo.event import EventTypes + from collections.abc import Iterator + from pyatmo.home import Home from time import time +from pyatmo.event import EventTypes + LOG = logging.getLogger(__name__) @@ -29,13 +31,13 @@ "event_type": lambda x, y: EventTypes(x.get("type", y)), "reachable": lambda x, _: x.get("reachable", False), "monitoring": lambda x, _: x.get("monitoring", False) == "on", - "battery_level": lambda x, y: x.get("battery_vp", x.get("battery_level")), + "battery_level": lambda x, _: x.get("battery_vp", x.get("battery_level")), "place": lambda x, _: Place(x.get("place")), "target_position__step": lambda x, _: x.get("target_position:step"), } -def default(key: str, val: Any) -> Any: +def default(key: str, val: Any) -> Any: # noqa: ANN401 """Return default value.""" return lambda x, _: x.get(key, val) @@ -59,6 +61,11 @@ class EntityBase: history_features_values: dict name: str + def has_feature(self, feature: str) -> bool: + """Check if the entity has the given feature.""" + + return hasattr(self, feature) or feature in self.history_features + class NetatmoBase(EntityBase, ABC): """Base class for Netatmo entities.""" @@ -76,13 +83,6 @@ def update_topology(self, raw_data: RawData) -> None: self._update_attributes(raw_data) - if ( - self.bridge - and self.bridge in self.home.modules - and getattr(self, "device_category") == "weather" - ): - self.name = update_name(self.name, self.home.modules[self.bridge].name) - def _update_attributes(self, raw_data: RawData) -> None: """Update attributes.""" @@ -100,7 +100,12 @@ def _update_attributes(self, raw_data: RawData) -> None: self.add_history_data(hist_feature, val, now) - def add_history_data(self, feature: str, value, time: int) -> None: + def add_history_data( + self, + feature: str, + value: Any, # noqa: ANN401 + time: int, + ) -> None: """Add historical data at the given time.""" # get the feature values rolling buffer @@ -119,7 +124,12 @@ def add_history_data(self, feature: str, value, time: int) -> None: while len(hist_f) > 0 and hist_f[-1][0] - hist_f[0][0] > MAX_HISTORY_TIME_FRAME: hist_f.pop(0) - def get_history_data(self, feature: str, from_ts: int, to_ts: int | None = None): + def get_history_data( + self, + feature: str, + from_ts: float, + to_ts: float | None = None, + ) -> list[Any]: """Retrieve historical data.""" hist_f = self.history_features_values.get(feature, []) @@ -150,7 +160,7 @@ def __init__(self, longitude: float, latitude: float) -> None: self.latitude = latitude self.longitude = longitude - def __iter__(self) -> Iterable[float]: + def __iter__(self) -> Iterator[float]: """Iterate over latitude and longitude.""" yield self.longitude diff --git a/src/pyatmo/modules/device_types.py b/src/pyatmo/modules/device_types.py index 6b2a27a3..4b904774 100644 --- a/src/pyatmo/modules/device_types.py +++ b/src/pyatmo/modules/device_types.py @@ -4,6 +4,7 @@ from enum import Enum import logging +from typing import Literal LOG = logging.getLogger(__name__) @@ -116,7 +117,7 @@ class DeviceType(str, Enum): # pylint: enable=C0103 @classmethod - def _missing_(cls, key): + def _missing_(cls, key) -> Literal[DeviceType.NLunknown]: # noqa: ANN001 """Handle unknown device types.""" msg = f"{key} device is unknown" diff --git a/src/pyatmo/modules/idiamant.py b/src/pyatmo/modules/idiamant.py index a968205e..7fdeac9e 100644 --- a/src/pyatmo/modules/idiamant.py +++ b/src/pyatmo/modules/idiamant.py @@ -18,22 +18,14 @@ class NBG(FirmwareMixin, WifiMixin, Module): """Class to represent a iDiamant NBG.""" - ... - class NBR(FirmwareMixin, RfMixin, ShutterMixin, Module): """Class to represent a iDiamant NBR.""" - ... - class NBO(FirmwareMixin, RfMixin, ShutterMixin, Module): """Class to represent a iDiamant NBO.""" - ... - class NBS(FirmwareMixin, RfMixin, ShutterMixin, Module): """Class to represent a iDiamant NBS.""" - - ... diff --git a/src/pyatmo/modules/module.py b/src/pyatmo/modules/module.py index a64bd3c5..c3d19a7e 100644 --- a/src/pyatmo/modules/module.py +++ b/src/pyatmo/modules/module.py @@ -5,13 +5,13 @@ from datetime import UTC, datetime, timedelta from enum import Enum import logging -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, LiteralString from aiohttp import ClientConnectorError -from pyatmo.const import GETMEASURE_ENDPOINT, RawData +from pyatmo.const import GETMEASURE_ENDPOINT, OFF, ON, RawData from pyatmo.exceptions import ApiError -from pyatmo.modules.base_class import EntityBase, NetatmoBase, Place +from pyatmo.modules.base_class import EntityBase, NetatmoBase, Place, update_name from pyatmo.modules.device_types import DEVICE_CATEGORY_MAP, DeviceCategory, DeviceType if TYPE_CHECKING: @@ -66,7 +66,7 @@ def process_battery_state(data: str) -> int: class FirmwareMixin(EntityBase): """Mixin for firmware data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize firmware mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 self.firmware_revision: int | None = None @@ -76,7 +76,7 @@ def __init__(self, home: Home, module: ModuleT): class WifiMixin(EntityBase): """Mixin for wifi data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize wifi mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 self.wifi_strength: int | None = None @@ -85,7 +85,7 @@ def __init__(self, home: Home, module: ModuleT): class RfMixin(EntityBase): """Mixin for rf data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize rf mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -95,7 +95,7 @@ def __init__(self, home: Home, module: ModuleT): class RainMixin(EntityBase): """Mixin for rain data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize rain mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -107,7 +107,7 @@ def __init__(self, home: Home, module: ModuleT): class WindMixin(EntityBase): """Mixin for wind data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize wind mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -152,7 +152,7 @@ def process_angle(angle: int) -> str: class TemperatureMixin(EntityBase): """Mixin for temperature data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize temperature mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -169,7 +169,7 @@ def __init__(self, home: Home, module: ModuleT): class HumidityMixin(EntityBase): """Mixin for humidity data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize humidity mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -179,7 +179,7 @@ def __init__(self, home: Home, module: ModuleT): class CO2Mixin(EntityBase): """Mixin for CO2 data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize CO2 mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -189,7 +189,7 @@ def __init__(self, home: Home, module: ModuleT): class HealthIndexMixin(EntityBase): """Mixin for health index data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize health index mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -199,7 +199,7 @@ def __init__(self, home: Home, module: ModuleT): class NoiseMixin(EntityBase): """Mixin for noise data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize noise mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -209,7 +209,7 @@ def __init__(self, home: Home, module: ModuleT): class PressureMixin(EntityBase): """Mixin for pressure data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize pressure mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -221,7 +221,7 @@ def __init__(self, home: Home, module: ModuleT): class BoilerMixin(EntityBase): """Mixin for boiler data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize boiler mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -231,7 +231,7 @@ def __init__(self, home: Home, module: ModuleT): class CoolerMixin(EntityBase): """Mixin for cooler data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize cooler mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -241,7 +241,7 @@ def __init__(self, home: Home, module: ModuleT): class BatteryMixin(EntityBase): """Mixin for battery data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize battery mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -263,7 +263,7 @@ def battery(self) -> int: class PlaceMixin(EntityBase): """Mixin for place data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize place mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -273,7 +273,7 @@ def __init__(self, home: Home, module: ModuleT): class DimmableMixin(EntityBase): """Mixin for dimmable data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize dimmable mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -297,7 +297,7 @@ async def async_set_brightness(self, brightness: int) -> bool: class ApplianceTypeMixin(EntityBase): """Mixin for appliance type data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize appliance type mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -307,7 +307,7 @@ def __init__(self, home: Home, module: ModuleT): class PowerMixin(EntityBase): """Mixin for power data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize power mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -318,7 +318,7 @@ def __init__(self, home: Home, module: ModuleT): class EventMixin(EntityBase): """Mixin for event data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize event mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -328,7 +328,7 @@ def __init__(self, home: Home, module: ModuleT): class ContactorMixin(EntityBase): """Mixin for contactor data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize contactor mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -338,7 +338,7 @@ def __init__(self, home: Home, module: ModuleT): class OffloadMixin(EntityBase): """Mixin for offload data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize offload mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -348,13 +348,13 @@ def __init__(self, home: Home, module: ModuleT): class SwitchMixin(EntityBase): """Mixin for switch data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize switch mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 self.on: bool | None = None - async def async_set_switch(self, target_position: int) -> bool: + async def async_set_switch(self, target_position: bool) -> bool: # noqa: FBT001 """Set switch to target position.""" json_switch = { @@ -371,18 +371,18 @@ async def async_set_switch(self, target_position: int) -> bool: async def async_on(self) -> bool: """Switch on.""" - return await self.async_set_switch(True) + return await self.async_set_switch(ON) async def async_off(self) -> bool: """Switch off.""" - return await self.async_set_switch(False) + return await self.async_set_switch(OFF) class FanSpeedMixin(EntityBase): """Mixin for fan speed data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize fan speed mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -414,7 +414,7 @@ class ShutterMixin(EntityBase): __stop_position = -1 __preferred_position = -2 - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize shutter mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -465,7 +465,7 @@ async def async_move_to_preferred_position(self) -> bool: class CameraMixin(EntityBase): """Mixin for camera data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize camera mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -520,7 +520,10 @@ async def _async_check_url(self, url: str) -> str | None: LOG.debug("Api error for camera url %s", url) return None - assert not isinstance(resp, bytes) + if isinstance(resp, bytes): + msg = "Invalid response from camera url" + raise ApiError(msg) + resp_data = await resp.json() return resp_data.get("local_url") if resp_data else None @@ -528,7 +531,7 @@ async def _async_check_url(self, url: str) -> str | None: class FloodlightMixin(EntityBase): """Mixin for floodlight data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize floodlight mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -566,7 +569,7 @@ async def async_floodlight_auto(self) -> bool: class StatusMixin(EntityBase): """Mixin for status data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize status mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -576,7 +579,7 @@ def __init__(self, home: Home, module: ModuleT): class MonitoringMixin(EntityBase): """Mixin for monitoring data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize monitoring mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 @@ -654,8 +657,8 @@ class MeasureType(Enum): def compute_riemann_sum( power_data: list[tuple[int, float]], - conservative: bool = False, -): + conservative: bool = False, # noqa: FBT001, FBT002 +) -> float: """Compute energy from power with a rieman sum.""" delta_energy = 0.0 @@ -684,44 +687,48 @@ def compute_riemann_sum( class EnergyHistoryMixin(EntityBase): """Mixin for Energy history data.""" - def __init__(self, home: Home, module: ModuleT): + def __init__(self, home: Home, module: ModuleT) -> None: """Initialize history mixin.""" super().__init__(home, module) # type: ignore # mypy issue 4335 - self.historical_data: list[dict[str, Any]] = [] - self.start_time: int | None = None - self.end_time: int | None = None + self.historical_data: list[dict[str, Any]] | None = None + self.start_time: float | None = None + self.end_time: float | None = None self.interval: MeasureInterval | None = None - self.sum_energy_elec: int | None = None - self.sum_energy_elec_peak: int | None = None - self.sum_energy_elec_off_peak: int | None = None - self._anchor_for_power_adjustment: int | None = None + self.sum_energy_elec: float = 0.0 + self.sum_energy_elec_peak: float = 0.0 + self.sum_energy_elec_off_peak: float = 0.0 + self._anchor_for_power_adjustment: float | None = None self.in_reset: bool = False - def reset_measures(self, start_power_time, in_reset=True): + def reset_measures( + self, + start_power_time: datetime, + in_reset: bool = True, # noqa: FBT001, FBT002 + ) -> None: """Reset energy measures.""" self.in_reset = in_reset self.historical_data = [] + self.sum_energy_elec = 0.0 + self.sum_energy_elec_peak = 0.0 + self.sum_energy_elec_off_peak = 0.0 if start_power_time is None: self._anchor_for_power_adjustment = start_power_time else: self._anchor_for_power_adjustment = int(start_power_time.timestamp()) - self.sum_energy_elec = 0 - self.sum_energy_elec_peak = 0 - self.sum_energy_elec_off_peak = 0 def get_sum_energy_elec_power_adapted( self, - to_ts: int | None = None, - conservative: bool = False, - ): + to_ts: float | None = None, + conservative: bool = False, # noqa: FBT001, FBT002 + ) -> tuple[None, float] | tuple[float, float]: """Compute proper energy value with adaptation from power.""" v = self.sum_energy_elec if v is None: - return None, 0 + return None, 0.0 - delta_energy = 0 + delta_energy = 0.0 if not self.in_reset: if to_ts is None: @@ -748,18 +755,22 @@ def get_sum_energy_elec_power_adapted( return v, delta_energy - def _log_energy_error(self, start_time, end_time, msg=None, body=None): - if body is None: - body = "NO BODY" + def _log_energy_error( + self, + start_time: float, + end_time: float, + msg: str | None = None, + body: dict | None = None, + ) -> None: LOG.debug( "ENERGY collection error %s %s %s %s %s %s %s", msg, self.name, - datetime.fromtimestamp(start_time), - datetime.fromtimestamp(end_time), + datetime.fromtimestamp(start_time), # noqa: DTZ006 + datetime.fromtimestamp(end_time), # noqa: DTZ006 start_time, end_time, - body, + body or "NO BODY", ) async def async_update_measures( @@ -772,10 +783,10 @@ async def async_update_measures( """Update historical data.""" if end_time is None: - end_time = int(datetime.now().timestamp()) + end_time = int(datetime.now().timestamp()) # noqa: DTZ005 if start_time is None: - end = datetime.fromtimestamp(end_time) + end = datetime.fromtimestamp(end_time) # noqa: DTZ006 start_time = int((end - timedelta(days=days)).timestamp()) prev_start_time = self.start_time @@ -793,7 +804,7 @@ async def async_update_measures( delta_range = MEASURE_INTERVAL_TO_SECONDS.get(interval, 0) // 2 - filters, raw_data = await self._energy_API_calls(start_time, end_time, interval) + filters, raw_data = await self._energy_api_calls(start_time, end_time, interval) hist_good_vals = await self._get_aligned_energy_values_and_mode( start_time, @@ -802,11 +813,10 @@ async def async_update_measures( raw_data, ) - self.historical_data = [] prev_sum_energy_elec = self.sum_energy_elec - self.sum_energy_elec = 0 - self.sum_energy_elec_peak = 0 - self.sum_energy_elec_off_peak = 0 + self.sum_energy_elec = 0.0 + self.sum_energy_elec_peak = 0.0 + self.sum_energy_elec_off_peak = 0.0 # no data at all: we know nothing for the end: best guess, it is the start self._anchor_for_power_adjustment = start_time @@ -821,8 +831,8 @@ async def async_update_measures( LOG.debug( "NO VALUES energy update %s from: %s to %s, prev_sum=%s", self.name, - datetime.fromtimestamp(start_time), - datetime.fromtimestamp(end_time), + datetime.fromtimestamp(start_time), # noqa: DTZ006 + datetime.fromtimestamp(end_time), # noqa: DTZ006 prev_sum_energy_elec if prev_sum_energy_elec is not None else "NOTHING", ) else: @@ -831,24 +841,25 @@ async def async_update_measures( end_time, delta_range, hist_good_vals, - prev_end_time, - prev_start_time, - prev_sum_energy_elec, + prev_end_time or 0.0, + prev_start_time or 0.0, + prev_sum_energy_elec or 0.0, ) async def _prepare_exported_historical_data( self, - start_time, - end_time, - delta_range, - hist_good_vals, - prev_end_time, - prev_start_time, - prev_sum_energy_elec, - ): - computed_start = 0 - computed_end = 0 - computed_end_for_calculus = 0 + start_time: float, + end_time: float, + delta_range: float, + hist_good_vals: list[tuple[int, float, list[float]]], + prev_end_time: float, + prev_start_time: float, + prev_sum_energy_elec: float | None, + ) -> None: + self.historical_data = [] + computed_start = 0.0 + computed_end = 0.0 + computed_end_for_calculus = 0.0 for cur_start_time, val, vals in hist_good_vals: self.sum_energy_elec += val @@ -871,8 +882,7 @@ async def _prepare_exported_historical_data( computed_start = c_start computed_end = c_end - # - delta_range not sure, revert ... it seems the energy value effectively stops at those mid values - computed_end_for_calculus = c_end # - delta_range + computed_end_for_calculus = c_end start_time_string = f"{datetime.fromtimestamp(c_start + 1, tz=UTC).isoformat().split('+')[0]}Z" end_time_string = ( @@ -902,14 +912,14 @@ async def _prepare_exported_historical_data( LOG.debug( msg, self.name, - datetime.fromtimestamp(start_time), - datetime.fromtimestamp(end_time), - datetime.fromtimestamp(computed_start), - datetime.fromtimestamp(computed_end), + datetime.fromtimestamp(start_time), # noqa: DTZ006 + datetime.fromtimestamp(end_time), # noqa: DTZ006 + datetime.fromtimestamp(computed_start), # noqa: DTZ006 + datetime.fromtimestamp(computed_end), # noqa: DTZ006 self.sum_energy_elec, prev_sum_energy_elec, - datetime.fromtimestamp(prev_start_time), - datetime.fromtimestamp(prev_end_time), + datetime.fromtimestamp(prev_start_time), # noqa: DTZ006 + datetime.fromtimestamp(prev_end_time), # noqa: DTZ006 ) else: msg = ( @@ -919,10 +929,10 @@ async def _prepare_exported_historical_data( LOG.debug( msg, self.name, - datetime.fromtimestamp(start_time), - datetime.fromtimestamp(end_time), - datetime.fromtimestamp(computed_start), - datetime.fromtimestamp(computed_end), + datetime.fromtimestamp(start_time), # noqa: DTZ006 + datetime.fromtimestamp(end_time), # noqa: DTZ006 + datetime.fromtimestamp(computed_start), # noqa: DTZ006 + datetime.fromtimestamp(computed_end), # noqa: DTZ006 self.sum_energy_elec, prev_sum_energy_elec if prev_sum_energy_elec is not None else "NOTHING", ) @@ -931,27 +941,30 @@ async def _prepare_exported_historical_data( async def _get_aligned_energy_values_and_mode( self, - start_time, - end_time, - delta_range, - raw_data, - ): + start_time: float, + end_time: float, + delta_range: float, + raw_data: dict, + ) -> list[Any]: hist_good_vals = [] values_lots = raw_data for values_lot in values_lots: try: start_lot_time = int(values_lot["beg_time"]) - except Exception: + except KeyError: self._log_energy_error( start_time, end_time, msg="beg_time missing", body=values_lots, ) - raise ApiError( + msg = ( f"Energy badly formed resp beg_time missing: {values_lots} - " - f"module: {self.name}", + f"module: {self.name}" + ) + raise ApiError( + msg, ) from None interval_sec = values_lot.get("step_time") @@ -983,13 +996,17 @@ async def _get_aligned_energy_values_and_mode( hist_good_vals.append((cur_start_time, val, vals)) cur_start_time = cur_start_time + interval_sec - hist_good_vals = sorted(hist_good_vals, key=itemgetter(0)) - return hist_good_vals + return sorted(hist_good_vals, key=itemgetter(0)) - def _get_energy_filers(self): + def _get_energy_filers(self) -> LiteralString: return ENERGY_FILTERS - async def _energy_API_calls(self, start_time, end_time, interval): + async def _energy_api_calls( + self, + start_time: float, + end_time: float, + interval: MeasureInterval, + ) -> tuple[LiteralString, Any]: filters = self._get_energy_filers() params = { @@ -1016,11 +1033,12 @@ async def _energy_API_calls(self, start_time, end_time, interval): msg=f"direct from {filters}", body=rw_dt_f, ) - raise ApiError( + msg = ( f"Energy badly formed resp: {rw_dt_f} - " f"module: {self.name} - " - f"when accessing '{filters}'", + f"when accessing '{filters}'" ) + raise ApiError(msg) raw_data = rw_dt @@ -1030,7 +1048,7 @@ async def _energy_API_calls(self, start_time, end_time, interval): class EnergyHistoryLegacyMixin(EnergyHistoryMixin): """Mixin for Energy history data, Using legacy APis (used for NLE).""" - def _get_energy_filers(self): + def _get_energy_filers(self) -> LiteralString: return ENERGY_FILTERS_LEGACY @@ -1064,6 +1082,15 @@ async def update(self, raw_data: RawData) -> None: """Update module with the latest data.""" self.update_topology(raw_data) + + if ( + self.bridge + and self.bridge in self.home.modules + and hasattr(self, "device_category") + and self.device_category == "weather" + ): + self.name = update_name(self.name, self.home.modules[self.bridge].name) + self.update_features() # If we have an NLE as a bridge all its bridged modules will have to be reachable @@ -1117,25 +1144,21 @@ async def update(self, raw_data: RawData) -> None: class Switch(FirmwareMixin, EnergyHistoryMixin, PowerMixin, SwitchMixin, Module): """Class to represent a Netatmo switch.""" - ... - class Dimmer(DimmableMixin, Switch): """Class to represent a Netatmo dimmer.""" - ... - class Shutter(FirmwareMixin, ShutterMixin, Module): """Class to represent a Netatmo shutter.""" - ... - class Fan(FirmwareMixin, FanSpeedMixin, PowerMixin, Module): """Class to represent a Netatmo ventilation device.""" - ... + +class Energy(EnergyHistoryMixin, Module): + """Class to represent a Netatmo energy module.""" # pylint: enable=too-many-ancestors diff --git a/src/pyatmo/modules/netatmo.py b/src/pyatmo/modules/netatmo.py index 047c4eb1..297f758d 100644 --- a/src/pyatmo/modules/netatmo.py +++ b/src/pyatmo/modules/netatmo.py @@ -49,50 +49,34 @@ class NRV(FirmwareMixin, RfMixin, BatteryMixin, Module): """Class to represent a Netatmo NRV.""" - ... - class NATherm1(FirmwareMixin, RfMixin, BatteryMixin, BoilerMixin, Module): """Class to represent a Netatmo NATherm1.""" - ... - class NAPlug(FirmwareMixin, RfMixin, WifiMixin, Module): """Class to represent a Netatmo NAPlug.""" - ... - class OTH(FirmwareMixin, WifiMixin, Module): """Class to represent a Netatmo OTH.""" - ... - class OTM(FirmwareMixin, RfMixin, BatteryMixin, BoilerMixin, Module): """Class to represent a Netatmo OTM.""" - ... - class NACamera(Camera): """Class to represent a Netatmo NACamera.""" - ... - class NOC(FloodlightMixin, Camera): """Class to represent a Netatmo NOC.""" - ... - class NDB(Camera): """Class to represent a Netatmo NDB.""" - ... - class NAMain( TemperatureMixin, @@ -107,8 +91,6 @@ class NAMain( ): """Class to represent a Netatmo NAMain.""" - ... - class NAModule1( TemperatureMixin, @@ -121,20 +103,14 @@ class NAModule1( ): """Class to represent a Netatmo NAModule1.""" - ... - class NAModule2(WindMixin, RfMixin, FirmwareMixin, BatteryMixin, PlaceMixin, Module): """Class to represent a Netatmo NAModule2.""" - ... - class NAModule3(RainMixin, RfMixin, FirmwareMixin, BatteryMixin, PlaceMixin, Module): """Class to represent a Netatmo NAModule3.""" - ... - class NAModule4( TemperatureMixin, @@ -148,8 +124,6 @@ class NAModule4( ): """Class to represent a Netatmo NAModule4.""" - ... - class NHC( TemperatureMixin, @@ -165,14 +139,10 @@ class NHC( ): """Class to represent a Netatmo NHC.""" - ... - class NACamDoorTag(StatusMixin, FirmwareMixin, BatteryMixin, RfMixin, Module): """Class to represent a Netatmo NACamDoorTag.""" - ... - class NIS( StatusMixin, @@ -184,8 +154,6 @@ class NIS( ): """Class to represent a Netatmo NIS.""" - ... - class NSD( FirmwareMixin, @@ -193,8 +161,6 @@ class NSD( ): """Class to represent a Netatmo NSD.""" - ... - class NCO( FirmwareMixin, @@ -202,8 +168,6 @@ class NCO( ): """Class to represent a Netatmo NCO.""" - ... - @dataclass class Location: @@ -230,7 +194,7 @@ def __init__( lat_sw: str, lon_sw: str, required_data_type: str | None = None, - filtering: bool = False, + filtering: bool = False, # noqa: FBT001, FBT002 ) -> None: """Initialize self.""" diff --git a/src/pyatmo/modules/somfy.py b/src/pyatmo/modules/somfy.py index 67c69f98..4d76042e 100644 --- a/src/pyatmo/modules/somfy.py +++ b/src/pyatmo/modules/somfy.py @@ -11,5 +11,3 @@ class TPSRS(RfMixin, Shutter): """Class to represent a somfy TPSRS.""" - - ... diff --git a/src/pyatmo/person.py b/src/pyatmo/person.py index 9ab89b2c..b15cae4b 100644 --- a/src/pyatmo/person.py +++ b/src/pyatmo/person.py @@ -6,10 +6,11 @@ import logging from typing import TYPE_CHECKING -from pyatmo.const import RawData from pyatmo.modules.base_class import NetatmoBase if TYPE_CHECKING: + from pyatmo.const import RawData + from .home import Home LOG = logging.getLogger(__name__) diff --git a/src/pyatmo/schedule.py b/src/pyatmo/schedule.py index d603801e..2df909a6 100644 --- a/src/pyatmo/schedule.py +++ b/src/pyatmo/schedule.py @@ -6,11 +6,12 @@ import logging from typing import TYPE_CHECKING -from pyatmo.const import RawData from pyatmo.modules.base_class import NetatmoBase from pyatmo.room import Room if TYPE_CHECKING: + from pyatmo.const import RawData + from .home import Home LOG = logging.getLogger(__name__) @@ -65,7 +66,7 @@ def __init__(self, home: Home, raw_data: RawData) -> None: self.home = home self.type = raw_data.get("type", 0) - def room_factory(home: Home, room_raw_data: RawData): + def room_factory(home: Home, room_raw_data: RawData) -> Room: room = Room(home, room_raw_data, {}) room.update(room_raw_data) return room diff --git a/tests/common.py b/tests/common.py index 3bff5404..7fb2f026 100644 --- a/tests/common.py +++ b/tests/common.py @@ -78,5 +78,4 @@ async def fake_post_request(*args, **kwargs): async def fake_post_request_multi(*args, **kwargs): kwargs["POSTFIX"] = "multi" - r = await fake_post_request(*args, **kwargs) - return r + return await fake_post_request(*args, **kwargs) diff --git a/tests/conftest.py b/tests/conftest.py index 6b7c53a7..f1111090 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,14 +16,14 @@ def does_not_raise(): yield -@pytest.fixture(scope="function") +@pytest.fixture async def async_auth(): """AsyncAuth fixture.""" with patch("pyatmo.auth.AbstractAsyncAuth", AsyncMock()) as auth: yield auth -@pytest.fixture(scope="function") +@pytest.fixture async def async_account(async_auth): """AsyncAccount fixture.""" account = pyatmo.AsyncAccount(async_auth) @@ -42,15 +42,15 @@ async def async_account(async_auth): yield account -@pytest.fixture(scope="function") +@pytest.fixture async def async_home(async_account): """AsyncClimate fixture for home_id 91763b24c43d3e344f424e8b.""" home_id = "91763b24c43d3e344f424e8b" await async_account.async_update_status(home_id) - yield async_account.homes[home_id] + return async_account.homes[home_id] -@pytest.fixture(scope="function") +@pytest.fixture async def async_account_multi(async_auth): """AsyncAccount fixture.""" account = pyatmo.AsyncAccount(async_auth) @@ -71,9 +71,9 @@ async def async_account_multi(async_auth): yield account -@pytest.fixture(scope="function") +@pytest.fixture async def async_home_multi(async_account_multi): """AsyncClimate fixture for home_id 91763b24c43d3e344f424e8b.""" home_id = "aaaaaaaaaaabbbbbbbbbbccc" await async_account_multi.async_update_status(home_id) - yield async_account_multi.homes[home_id] + return async_account_multi.homes[home_id] diff --git a/tests/test_camera.py b/tests/test_camera.py index 79b7c01e..c12ecf89 100644 --- a/tests/test_camera.py +++ b/tests/test_camera.py @@ -11,7 +11,7 @@ # pylint: disable=F6401 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_camera_NACamera(async_home): # pylint: disable=invalid-name """Test Netatmo indoor camera module.""" module_id = "12:34:56:00:f1:62" @@ -29,7 +29,7 @@ async def test_async_camera_NACamera(async_home): # pylint: disable=invalid-nam assert person.last_seen == 1557071156 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_NOC(async_home): # pylint: disable=invalid-name """Test basic outdoor camera functionality.""" module_id = "12:34:56:10:b9:0e" @@ -84,7 +84,7 @@ def gen_json_data(state): ) -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_camera_monitoring(async_home): """Test basic camera monitoring functionality.""" module_id = "12:34:56:10:b9:0e" diff --git a/tests/test_climate.py b/tests/test_climate.py index e0303840..64eb9acd 100644 --- a/tests/test_climate.py +++ b/tests/test_climate.py @@ -14,7 +14,7 @@ # pylint: disable=F6401 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_room(async_home): """Test room with climate devices.""" room_id = "2746182631" @@ -29,7 +29,7 @@ async def test_async_climate_room(async_home): assert len(room.modules) == 1 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_NATherm1(async_home): # pylint: disable=invalid-name """Test NATherm1 climate device.""" module_id = "12:34:56:00:01:ae" @@ -43,7 +43,7 @@ async def test_async_climate_NATherm1(async_home): # pylint: disable=invalid-na assert module.rf_strength == 58 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_NRV(async_home): # pylint: disable=invalid-name """Test NRV climate device.""" module_id = "12:34:56:03:a5:54" @@ -57,7 +57,7 @@ async def test_async_climate_NRV(async_home): # pylint: disable=invalid-name assert module.firmware_revision == 79 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_NAPlug(async_home): # pylint: disable=invalid-name """Test NAPlug climate device.""" module_id = "12:34:56:00:fa:d0" @@ -70,7 +70,7 @@ async def test_async_climate_NAPlug(async_home): # pylint: disable=invalid-name assert module.firmware_revision == 174 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_NIS(async_home): # pylint: disable=invalid-name """Test Netatmo siren.""" module_id = "12:34:56:00:e3:9b" @@ -82,7 +82,7 @@ async def test_async_climate_NIS(async_home): # pylint: disable=invalid-name assert module.monitoring is False -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_OTM(async_home): # pylint: disable=invalid-name """Test OTM climate device.""" module_id = "12:34:56:20:f5:8c" @@ -96,7 +96,7 @@ async def test_async_climate_OTM(async_home): # pylint: disable=invalid-name assert module.rf_strength == 64 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_OTH(async_home): # pylint: disable=invalid-name """Test OTH climate device.""" module_id = "12:34:56:20:f5:44" @@ -108,7 +108,7 @@ async def test_async_climate_OTH(async_home): # pylint: disable=invalid-name assert module.firmware_revision == 22 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_BNS(async_home): # pylint: disable=invalid-name """Test Smarther BNS climate module.""" module_id = "10:20:30:bd:b8:1e" @@ -125,7 +125,7 @@ async def test_async_climate_BNS(async_home): # pylint: disable=invalid-name assert room.features == {"humidity", DeviceCategory.climate} -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_update(async_account): """Test basic climate state update.""" home_id = "91763b24c43d3e344f424e8b" @@ -182,7 +182,7 @@ async def test_async_climate_update(async_account): @pytest.mark.parametrize( - "t_sched_id, expected", + ("t_sched_id", "expected"), [ ("591b54a2764ff4d50d8b5795", does_not_raise()), ( @@ -191,7 +191,7 @@ async def test_async_climate_update(async_account): ), ], ) -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_switch_schedule( async_home, t_sched_id, @@ -213,7 +213,7 @@ async def test_async_climate_switch_schedule( @pytest.mark.parametrize( - "temp, end_time", + ("temp", "end_time"), [ ( 14, @@ -233,7 +233,7 @@ async def test_async_climate_switch_schedule( ), ], ) -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_room_therm_set( async_home, temp, @@ -273,7 +273,7 @@ async def test_async_climate_room_therm_set( @pytest.mark.parametrize( - "mode, end_time, schedule_id, json_fixture, expected, exception", + ("mode", "end_time", "schedule_id", "json_fixture", "expected", "exception"), [ ( "away", @@ -315,14 +315,6 @@ async def test_async_climate_room_therm_set( False, pytest.raises(NoSchedule), ), - ( - None, - None, - None, - "home_status_error_mode_is_missing.json", - False, - pytest.raises(NoSchedule), - ), ( "away", 1559162650, @@ -341,7 +333,7 @@ async def test_async_climate_room_therm_set( ), ], ) -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_set_thermmode( async_home, mode, @@ -369,7 +361,7 @@ async def test_async_climate_set_thermmode( assert expected is resp -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_climate_empty_home(async_account): """Test climate setup with empty home.""" home_id = "91763b24c43d3e344f424e8c" diff --git a/tests/test_energy.py b/tests/test_energy.py index 735d34f6..3210806a 100644 --- a/tests/test_energy.py +++ b/tests/test_energy.py @@ -2,7 +2,7 @@ import datetime as dt import json -from unittest.mock import AsyncMock, patch +from unittest.mock import patch import pytest import time_machine @@ -14,7 +14,7 @@ # pylint: disable=F6401 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_energy_NLPC(async_home): # pylint: disable=invalid-name """Test Legrand / BTicino connected energy meter module.""" module_id = "12:34:56:00:00:a1:4c:da" @@ -25,7 +25,7 @@ async def test_async_energy_NLPC(async_home): # pylint: disable=invalid-name @time_machine.travel(dt.datetime(2022, 2, 12, 7, 59, 49)) -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_historical_data_retrieval(async_account): """Test retrieval of historical measurements.""" home_id = "91763b24c43d3e344f424e8b" @@ -63,7 +63,7 @@ async def test_historical_data_retrieval(async_account): @time_machine.travel(dt.datetime(2024, 7, 24, 22, 00, 10)) -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_historical_data_retrieval_multi(async_account_multi): """Test retrieval of historical measurements.""" home_id = "aaaaaaaaaaabbbbbbbbbbccc" @@ -125,7 +125,8 @@ async def test_historical_data_retrieval_multi(async_account_multi): assert module.sum_energy_elec_peak == 10177 -async def test_disconnected_main_bridge(async_account_multi): +@patch("pyatmo.auth.AbstractAsyncAuth.async_post_api_request") +async def test_disconnected_main_bridge(mock_home_status, async_account_multi): """Test retrieval of historical measurements.""" home_id = "aaaaaaaaaaabbbbbbbbbbccc" @@ -135,14 +136,7 @@ async def test_disconnected_main_bridge(async_account_multi): ) as json_file: home_status_fixture = json.load(json_file) mock_home_status_resp = MockResponse(home_status_fixture, 200) + mock_home_status.return_value = mock_home_status_resp - with patch( - "pyatmo.auth.AbstractAsyncAuth.async_post_api_request", - AsyncMock(return_value=mock_home_status_resp), - ): - try: - await async_account_multi.async_update_status(home_id) - except ApiHomeReachabilityError: - pass # expected error - else: - assert False + with pytest.raises(ApiHomeReachabilityError): + await async_account_multi.async_update_status(home_id) diff --git a/tests/test_fan.py b/tests/test_fan.py index a6e5535f..27b975e5 100644 --- a/tests/test_fan.py +++ b/tests/test_fan.py @@ -7,7 +7,7 @@ # pylint: disable=F6401 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_fan_NLLF(async_home): # pylint: disable=invalid-name """Test NLLF Legrand centralized ventilation controller.""" module_id = "12:34:56:00:01:01:01:b1" diff --git a/tests/test_home.py b/tests/test_home.py index dc77d8cb..c7cebf1c 100644 --- a/tests/test_home.py +++ b/tests/test_home.py @@ -1,6 +1,5 @@ """Define tests for home module.""" -# import datetime as dt import json from unittest.mock import AsyncMock, patch @@ -10,10 +9,8 @@ from pyatmo import DeviceType, NoDevice from tests.common import MockResponse -# pylint: disable=F6401 - -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_home(async_home): """Test basic home setup.""" room_id = "3688132631" @@ -42,7 +39,7 @@ async def test_async_home(async_home): assert async_home.temperature_control_mode == "cooling" -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_home_set_schedule(async_home): """Test home schedule.""" schedule_id = "591b54a2764ff4d50d8b5795" @@ -54,7 +51,7 @@ async def test_async_home_set_schedule(async_home): assert async_home.get_away_temp() == 14 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_home_data_no_body(async_auth): with open("fixtures/homesdata_emtpy_home.json", encoding="utf-8") as fixture_file: json_fixture = json.load(fixture_file) @@ -70,7 +67,7 @@ async def test_async_home_data_no_body(async_auth): mock_request.assert_called() -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_set_persons_home(async_account): """Test marking a person being at home.""" home_id = "91763b24c43d3e344f424e8b" @@ -96,7 +93,7 @@ async def test_async_set_persons_home(async_account): ) -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_set_persons_away(async_account): """Test marking a set of persons being away.""" home_id = "91763b24c43d3e344f424e8b" @@ -125,7 +122,7 @@ async def test_async_set_persons_away(async_account): ) -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_home_event_update(async_account): """Test basic event update.""" home_id = "91763b24c43d3e344f424e8b" diff --git a/tests/test_shutter.py b/tests/test_shutter.py index 004f9648..94486561 100644 --- a/tests/test_shutter.py +++ b/tests/test_shutter.py @@ -11,7 +11,7 @@ # pylint: disable=F6401 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_shutter_NBR(async_home): # pylint: disable=invalid-name """Test NLP Bubendorf iDiamant roller shutter.""" module_id = "0009999992" @@ -22,7 +22,7 @@ async def test_async_shutter_NBR(async_home): # pylint: disable=invalid-name assert module.current_position == 0 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_shutter_NBO(async_home): # pylint: disable=invalid-name """Test NBO Bubendorf iDiamant roller shutter.""" module_id = "0009999993" @@ -33,7 +33,7 @@ async def test_async_shutter_NBO(async_home): # pylint: disable=invalid-name assert module.current_position == 0 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_shutters(async_home): """Test basic shutter functionality.""" room_id = "3688132631" diff --git a/tests/test_switch.py b/tests/test_switch.py index 846bd57f..64cbae5f 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -7,7 +7,7 @@ # pylint: disable=F6401 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_switch_NLP(async_home): # pylint: disable=invalid-name """Test NLP Legrand plug.""" module_id = "12:34:56:80:00:12:ac:f2" @@ -19,7 +19,7 @@ async def test_async_switch_NLP(async_home): # pylint: disable=invalid-name assert module.power == 0 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_switch_NLF(async_home): # pylint: disable=invalid-name """Test NLF Legrand dimmer.""" module_id = "00:11:22:33:00:11:45:fe" diff --git a/tests/test_weather.py b/tests/test_weather.py index c83fb0d1..c2d297f3 100644 --- a/tests/test_weather.py +++ b/tests/test_weather.py @@ -9,7 +9,7 @@ # pylint: disable=F6401 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_weather_NAMain(async_home): # pylint: disable=invalid-name """Test Netatmo weather station main module.""" module_id = "12:34:56:80:bb:26" @@ -18,7 +18,7 @@ async def test_async_weather_NAMain(async_home): # pylint: disable=invalid-name assert module.device_type == DeviceType.NAMain -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_weather_update(async_account): """Test basic weather station update.""" home_id = "91763b24c43d3e344f424e8b" @@ -165,7 +165,7 @@ async def test_async_weather_update(async_account): assert module.gust_angle == 206 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_weather_favorite(async_account): """Test favorite weather station.""" await async_account.async_update_weather_stations() @@ -231,7 +231,7 @@ async def test_async_weather_favorite(async_account): assert module.humidity == 87 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_air_care_update(async_account): """Test basic air care update.""" await async_account.async_update_air_care() @@ -273,7 +273,7 @@ async def test_async_air_care_update(async_account): assert module.health_idx == 1 -@pytest.mark.asyncio +@pytest.mark.asyncio() async def test_async_public_weather_update(async_account): """Test basic public weather update.""" lon_ne = "6.221652" diff --git a/tests/testing_main_template.py b/tests/testing_main_template.py index 329ace5b..b4e3e81f 100644 --- a/tests/testing_main_template.py +++ b/tests/testing_main_template.py @@ -37,10 +37,6 @@ async def main(): end_time=end, ) - # print(account) - if __name__ == "__main__": topology = asyncio.run(main()) - - # print(topology) diff --git a/uv.lock b/uv.lock index f38eeece..023b9dc0 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -requires-python = ">=3.10, <3.13" +requires-python = ">=3.11, <3.13" [[package]] name = "aiohappyeyeballs" @@ -17,7 +17,6 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, { name = "aiosignal" }, - { name = "async-timeout", marker = "python_full_version < '3.11'" }, { name = "attrs" }, { name = "frozenlist" }, { name = "multidict" }, @@ -25,21 +24,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/ca/28/ca549838018140b92a19001a8628578b0f2a3b38c16826212cc6f706e6d4/aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691", size = 7524360 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/4a/b27dd9b88fe22dde88742b341fd10251746a6ffcfe1c0b8b15b4a8cbd7c1/aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3", size = 587010 }, - { url = "https://files.pythonhosted.org/packages/de/a9/0f7e2b71549c9d641086c423526ae7a10de3b88d03ba104a3df153574d0d/aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6", size = 397698 }, - { url = "https://files.pythonhosted.org/packages/3b/52/26baa486e811c25b0cd16a494038260795459055568713f841e78f016481/aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699", size = 389052 }, - { url = "https://files.pythonhosted.org/packages/33/df/71ba374a3e925539cb2f6e6d4f5326e7b6b200fabbe1b3cc5e6368f07ce7/aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6", size = 1248615 }, - { url = "https://files.pythonhosted.org/packages/67/02/bb89c1eba08a27fc844933bee505d63d480caf8e2816c06961d2941cd128/aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1", size = 1282930 }, - { url = "https://files.pythonhosted.org/packages/db/36/07d8cfcc37f39c039f93a4210cc71dadacca003609946c63af23659ba656/aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f", size = 1317250 }, - { url = "https://files.pythonhosted.org/packages/9a/44/cabeac994bef8ba521b552ae996928afc6ee1975a411385a07409811b01f/aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb", size = 1243212 }, - { url = "https://files.pythonhosted.org/packages/5a/11/23f1e31f5885ac72be52fd205981951dd2e4c87c5b1487cf82fde5bbd46c/aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91", size = 1213401 }, - { url = "https://files.pythonhosted.org/packages/3f/e7/6e69a0b0d896fbaf1192d492db4c21688e6c0d327486da610b0e8195bcc9/aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f", size = 1212450 }, - { url = "https://files.pythonhosted.org/packages/a9/7f/a42f51074c723ea848254946aec118f1e59914a639dc8ba20b0c9247c195/aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c", size = 1211324 }, - { url = "https://files.pythonhosted.org/packages/d5/43/c2f9d2f588ccef8f028f0a0c999b5ceafecbda50b943313faee7e91f3e03/aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69", size = 1266838 }, - { url = "https://files.pythonhosted.org/packages/c1/a7/ff9f067ecb06896d859e4f2661667aee4bd9c616689599ff034b63cbd9d7/aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3", size = 1285301 }, - { url = "https://files.pythonhosted.org/packages/9a/e3/dd56bb4c67d216046ce61d98dec0f3023043f1de48f561df1bf93dd47aea/aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683", size = 1235806 }, - { url = "https://files.pythonhosted.org/packages/a7/64/90dcd42ac21927a49ba4140b2e4d50e1847379427ef6c43eb338ef9960e3/aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef", size = 360162 }, - { url = "https://files.pythonhosted.org/packages/f3/45/145d8b4853fc92c0c8509277642767e7726a085e390ce04353dc68b0f5b5/aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088", size = 379173 }, { url = "https://files.pythonhosted.org/packages/f1/90/54ccb1e4eadfb6c95deff695582453f6208584431d69bf572782e9ae542b/aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2", size = 586455 }, { url = "https://files.pythonhosted.org/packages/c3/7a/95e88c02756e7e718f054e1bb3ec6ad5d0ee4a2ca2bb1768c5844b3de30a/aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf", size = 397255 }, { url = "https://files.pythonhosted.org/packages/07/4f/767387b39990e1ee9aba8ce642abcc286d84d06e068dc167dab983898f18/aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e", size = 388973 }, @@ -99,15 +83,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/ac/a7305707cb852b7e16ff80eaf5692309bde30e2b1100a1fcacdc8f731d97/aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17", size = 7617 }, ] -[[package]] -name = "async-timeout" -version = "4.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/d6/21b30a550dafea84b1b8eee21b5e23fa16d010ae006011221f33dcd8d7f8/async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", size = 8345 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/fa/e01228c2938de91d47b307831c62ab9e4001e747789d0b05baf779a6488c/async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028", size = 5721 }, -] - [[package]] name = "attrs" version = "24.2.0" @@ -136,15 +111,9 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/04/b0/46fb0d4e00372f4a86a6f8efa3cb193c9f64863615e39010b1477e010578/black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f", size = 644810 } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/6e/74e29edf1fba3887ed7066930a87f698ffdcd52c5dbc263eabb06061672d/black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6", size = 1632092 }, - { url = "https://files.pythonhosted.org/packages/ab/49/575cb6c3faee690b05c9d11ee2e8dba8fbd6d6c134496e644c1feb1b47da/black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb", size = 1457529 }, - { url = "https://files.pythonhosted.org/packages/7a/b4/d34099e95c437b53d01c4aa37cf93944b233066eb034ccf7897fa4e5f286/black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42", size = 1757443 }, - { url = "https://files.pythonhosted.org/packages/87/a0/6d2e4175ef364b8c4b64f8441ba041ed65c63ea1db2720d61494ac711c15/black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a", size = 1418012 }, { url = "https://files.pythonhosted.org/packages/08/a6/0a3aa89de9c283556146dc6dbda20cd63a9c94160a6fbdebaf0918e4a3e1/black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1", size = 1615080 }, { url = "https://files.pythonhosted.org/packages/db/94/b803d810e14588bb297e565821a947c108390a079e21dbdcb9ab6956cd7a/black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af", size = 1438143 }, { url = "https://files.pythonhosted.org/packages/a5/b5/f485e1bbe31f768e2e5210f52ea3f432256201289fd1a3c0afda693776b0/black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4", size = 1738774 }, @@ -183,14 +152,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 }, - { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 }, - { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 }, - { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 }, - { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 }, - { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 }, - { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 }, - { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 }, { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, @@ -239,21 +200,6 @@ version = "3.3.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219 }, - { url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521 }, - { url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383 }, - { url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223 }, - { url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101 }, - { url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699 }, - { url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065 }, - { url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505 }, - { url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425 }, - { url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287 }, - { url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929 }, - { url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605 }, - { url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646 }, - { url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846 }, - { url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343 }, { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 }, { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 }, { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 }, @@ -323,16 +269,6 @@ version = "7.6.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/61/eb7ce5ed62bacf21beca4937a90fe32545c91a3c8a42a30c6616d48fc70d/coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16", size = 206690 }, - { url = "https://files.pythonhosted.org/packages/7d/73/041928e434442bd3afde5584bdc3f932fb4562b1597629f537387cec6f3d/coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36", size = 207127 }, - { url = "https://files.pythonhosted.org/packages/c7/c8/6ca52b5147828e45ad0242388477fdb90df2c6cbb9a441701a12b3c71bc8/coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02", size = 235654 }, - { url = "https://files.pythonhosted.org/packages/d5/da/9ac2b62557f4340270942011d6efeab9833648380109e897d48ab7c1035d/coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc", size = 233598 }, - { url = "https://files.pythonhosted.org/packages/53/23/9e2c114d0178abc42b6d8d5281f651a8e6519abfa0ef460a00a91f80879d/coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23", size = 234732 }, - { url = "https://files.pythonhosted.org/packages/0f/7e/a0230756fb133343a52716e8b855045f13342b70e48e8ad41d8a0d60ab98/coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34", size = 233816 }, - { url = "https://files.pythonhosted.org/packages/28/7c/3753c8b40d232b1e5eeaed798c875537cf3cb183fb5041017c1fdb7ec14e/coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c", size = 232325 }, - { url = "https://files.pythonhosted.org/packages/57/e3/818a2b2af5b7573b4b82cf3e9f137ab158c90ea750a8f053716a32f20f06/coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959", size = 233418 }, - { url = "https://files.pythonhosted.org/packages/c8/fb/4532b0b0cefb3f06d201648715e03b0feb822907edab3935112b61b885e2/coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232", size = 209343 }, - { url = "https://files.pythonhosted.org/packages/5a/25/af337cc7421eca1c187cc9c315f0a755d48e755d2853715bfe8c418a45fa/coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0", size = 210136 }, { url = "https://files.pythonhosted.org/packages/ad/5f/67af7d60d7e8ce61a4e2ddcd1bd5fb787180c8d0ae0fbd073f903b3dd95d/coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", size = 206796 }, { url = "https://files.pythonhosted.org/packages/e1/0e/e52332389e057daa2e03be1fbfef25bb4d626b37d12ed42ae6281d0a274c/coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", size = 207244 }, { url = "https://files.pythonhosted.org/packages/aa/cd/766b45fb6e090f20f8927d9c7cb34237d41c73a939358bc881883fd3a40d/coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", size = 239279 }, @@ -373,7 +309,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598 }, { url = "https://files.pythonhosted.org/packages/66/8b/f54f8db2ae17188be9566e8166ac6df105c1c611e25da755738025708d54/coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", size = 210307 }, { url = "https://files.pythonhosted.org/packages/9f/b0/e0dca6da9170aefc07515cce067b97178cefafb512d00a87a1c717d2efd5/coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", size = 211453 }, - { url = "https://files.pythonhosted.org/packages/a5/2b/0354ed096bca64dc8e32a7cbcae28b34cb5ad0b1fe2125d6d99583313ac0/coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df", size = 198926 }, ] [package.optional-dependencies] @@ -402,8 +337,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ac/7e/ebda4dd4ae098a0990753efbb4b50954f1d03003846b943ea85070782da7/cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1", size = 3993152 }, { url = "https://files.pythonhosted.org/packages/43/f6/feebbd78a3e341e3913846a3bb2c29d0b09b1b3af1573c6baabc2533e147/cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa", size = 3886392 }, { url = "https://files.pythonhosted.org/packages/bd/4c/ab0b9407d5247576290b4fd8abd06b7f51bd414f04eef0f2800675512d61/cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4", size = 4082606 }, - { url = "https://files.pythonhosted.org/packages/ea/45/967da50269954b993d4484bf85026c7377bd551651ebdabba94905972556/cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d", size = 3713077 }, - { url = "https://files.pythonhosted.org/packages/df/e6/ccd29a1f9a6b71294e1e9f530c4d779d5dd37c8bb736c05d5fb6d98a971b/cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289", size = 3915597 }, ] [[package]] @@ -424,15 +357,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, ] -[[package]] -name = "exceptiongroup" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, -] - [[package]] name = "filelock" version = "3.16.0" @@ -448,21 +372,6 @@ version = "1.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/cf/3d/2102257e7acad73efc4a0c306ad3953f68c504c16982bbdfee3ad75d8085/frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", size = 37820 } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/35/1328c7b0f780d34f8afc1d87ebdc2bb065a123b24766a0b475f0d67da637/frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac", size = 94315 }, - { url = "https://files.pythonhosted.org/packages/f4/d6/ca016b0adcf8327714ccef969740688808c86e0287bf3a639ff582f24e82/frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868", size = 53805 }, - { url = "https://files.pythonhosted.org/packages/ae/83/bcdaa437a9bd693ba658a0310f8cdccff26bd78e45fccf8e49897904a5cd/frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776", size = 52163 }, - { url = "https://files.pythonhosted.org/packages/d4/e9/759043ab7d169b74fe05ebfbfa9ee5c881c303ebc838e308346204309cd0/frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a", size = 238595 }, - { url = "https://files.pythonhosted.org/packages/f8/ce/b9de7dc61e753dc318cf0de862181b484178210c5361eae6eaf06792264d/frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad", size = 262428 }, - { url = "https://files.pythonhosted.org/packages/36/ce/dc6f29e0352fa34ebe45421960c8e7352ca63b31630a576e8ffb381e9c08/frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c", size = 258867 }, - { url = "https://files.pythonhosted.org/packages/51/47/159ac53faf8a11ae5ee8bb9db10327575557504e549cfd76f447b969aa91/frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe", size = 229412 }, - { url = "https://files.pythonhosted.org/packages/ec/25/0c87df2e53c0c5d90f7517ca0ff7aca78d050a8ec4d32c4278e8c0e52e51/frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a", size = 239539 }, - { url = "https://files.pythonhosted.org/packages/97/94/a1305fa4716726ae0abf3b1069c2d922fcfd442538cb850f1be543f58766/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98", size = 253379 }, - { url = "https://files.pythonhosted.org/packages/53/82/274e19f122e124aee6d113188615f63b0736b4242a875f482a81f91e07e2/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75", size = 245901 }, - { url = "https://files.pythonhosted.org/packages/b8/28/899931015b8cffbe155392fe9ca663f981a17e1adc69589ee0e1e7cdc9a2/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5", size = 263797 }, - { url = "https://files.pythonhosted.org/packages/6e/4f/b8a5a2f10c4a58c52a52a40cf6cf1ffcdbf3a3b64f276f41dab989bf3ab5/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950", size = 264415 }, - { url = "https://files.pythonhosted.org/packages/b0/2c/7be3bdc59dbae444864dbd9cde82790314390ec54636baf6b9ce212627ad/frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc", size = 253964 }, - { url = "https://files.pythonhosted.org/packages/2e/ec/4fb5a88f6b9a352aed45ab824dd7ce4801b7bcd379adcb927c17a8f0a1a8/frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1", size = 44559 }, - { url = "https://files.pythonhosted.org/packages/61/15/2b5d644d81282f00b61e54f7b00a96f9c40224107282efe4cd9d2bf1433a/frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439", size = 50434 }, { url = "https://files.pythonhosted.org/packages/01/bc/8d33f2d84b9368da83e69e42720cff01c5e199b5a868ba4486189a4d8fa9/frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", size = 97060 }, { url = "https://files.pythonhosted.org/packages/af/b2/904500d6a162b98a70e510e743e7ea992241b4f9add2c8063bf666ca21df/frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", size = 55347 }, { url = "https://files.pythonhosted.org/packages/5b/9c/f12b69997d3891ddc0d7895999a00b0c6a67f66f79498c0e30f27876435d/frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", size = 53374 }, @@ -632,26 +541,8 @@ wheels = [ name = "multidict" version = "6.1.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/68/259dee7fd14cf56a17c554125e534f6274c2860159692a414d0b402b9a6d/multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60", size = 48628 }, - { url = "https://files.pythonhosted.org/packages/50/79/53ba256069fe5386a4a9e80d4e12857ced9de295baf3e20c68cdda746e04/multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1", size = 29327 }, - { url = "https://files.pythonhosted.org/packages/ff/10/71f1379b05b196dae749b5ac062e87273e3f11634f447ebac12a571d90ae/multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53", size = 29689 }, - { url = "https://files.pythonhosted.org/packages/71/45/70bac4f87438ded36ad4793793c0095de6572d433d98575a5752629ef549/multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5", size = 126639 }, - { url = "https://files.pythonhosted.org/packages/80/cf/17f35b3b9509b4959303c05379c4bfb0d7dd05c3306039fc79cf035bbac0/multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581", size = 134315 }, - { url = "https://files.pythonhosted.org/packages/ef/1f/652d70ab5effb33c031510a3503d4d6efc5ec93153562f1ee0acdc895a57/multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56", size = 129471 }, - { url = "https://files.pythonhosted.org/packages/a6/64/2dd6c4c681688c0165dea3975a6a4eab4944ea30f35000f8b8af1df3148c/multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429", size = 124585 }, - { url = "https://files.pythonhosted.org/packages/87/56/e6ee5459894c7e554b57ba88f7257dc3c3d2d379cb15baaa1e265b8c6165/multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748", size = 116957 }, - { url = "https://files.pythonhosted.org/packages/36/9e/616ce5e8d375c24b84f14fc263c7ef1d8d5e8ef529dbc0f1df8ce71bb5b8/multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db", size = 128609 }, - { url = "https://files.pythonhosted.org/packages/8c/4f/4783e48a38495d000f2124020dc96bacc806a4340345211b1ab6175a6cb4/multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056", size = 123016 }, - { url = "https://files.pythonhosted.org/packages/3e/b3/4950551ab8fc39862ba5e9907dc821f896aa829b4524b4deefd3e12945ab/multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76", size = 133542 }, - { url = "https://files.pythonhosted.org/packages/96/4d/f0ce6ac9914168a2a71df117935bb1f1781916acdecbb43285e225b484b8/multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160", size = 130163 }, - { url = "https://files.pythonhosted.org/packages/be/72/17c9f67e7542a49dd252c5ae50248607dfb780bcc03035907dafefb067e3/multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7", size = 126832 }, - { url = "https://files.pythonhosted.org/packages/71/9f/72d719e248cbd755c8736c6d14780533a1606ffb3fbb0fbd77da9f0372da/multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0", size = 26402 }, - { url = "https://files.pythonhosted.org/packages/04/5a/d88cd5d00a184e1ddffc82aa2e6e915164a6d2641ed3606e766b5d2f275a/multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d", size = 28800 }, { url = "https://files.pythonhosted.org/packages/93/13/df3505a46d0cd08428e4c8169a196131d1b0c4b515c3649829258843dde6/multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6", size = 48570 }, { url = "https://files.pythonhosted.org/packages/f0/e1/a215908bfae1343cdb72f805366592bdd60487b4232d039c437fe8f5013d/multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156", size = 29316 }, { url = "https://files.pythonhosted.org/packages/70/0f/6dc70ddf5d442702ed74f298d69977f904960b82368532c88e854b79f72b/multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb", size = 29640 }, @@ -706,16 +597,10 @@ version = "1.11.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5c/86/5d7cbc4974fd564550b80fbb8103c05501ea11aa7835edf3351d90095896/mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79", size = 3078806 } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/cd/815368cd83c3a31873e5e55b317551500b12f2d1d7549720632f32630333/mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a", size = 10939401 }, - { url = "https://files.pythonhosted.org/packages/f1/27/e18c93a195d2fad75eb96e1f1cbc431842c332e8eba2e2b77eaf7313c6b7/mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef", size = 10111697 }, - { url = "https://files.pythonhosted.org/packages/dc/08/cdc1fc6d0d5a67d354741344cc4aa7d53f7128902ebcbe699ddd4f15a61c/mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383", size = 12500508 }, - { url = "https://files.pythonhosted.org/packages/64/12/aad3af008c92c2d5d0720ea3b6674ba94a98cdb86888d389acdb5f218c30/mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8", size = 13020712 }, - { url = "https://files.pythonhosted.org/packages/03/e6/a7d97cc124a565be5e9b7d5c2a6ebf082379ffba99646e4863ed5bbcb3c3/mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7", size = 9567319 }, { url = "https://files.pythonhosted.org/packages/e2/aa/cc56fb53ebe14c64f1fe91d32d838d6f4db948b9494e200d2f61b820b85d/mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385", size = 10859630 }, { url = "https://files.pythonhosted.org/packages/04/c8/b19a760fab491c22c51975cf74e3d253b8c8ce2be7afaa2490fbf95a8c59/mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca", size = 10037973 }, { url = "https://files.pythonhosted.org/packages/88/57/7e7e39f2619c8f74a22efb9a4c4eff32b09d3798335625a124436d121d89/mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104", size = 12416659 }, @@ -842,7 +727,7 @@ wheels = [ [[package]] name = "pyatmo" -version = "8.1.1.dev4" +version = "0.0.0" source = { editable = "." } dependencies = [ { name = "aiohttp" }, @@ -919,7 +804,6 @@ version = "1.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/41/43/5581b42a96c5ee7bf2b22d3b08b34c8a54dfe6591d8b9a4314c890bd4a0d/pyproject_api-1.7.1.tar.gz", hash = "sha256:7ebc6cd10710f89f4cf2a2731710a98abce37ebff19427116ff2174c9236a827", size = 22271 } wheels = [ @@ -932,11 +816,9 @@ version = "8.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } wheels = [ @@ -1007,15 +889,6 @@ version = "6.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, @@ -1189,17 +1062,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/82d358c4d53555f031c2343d1c235b56b9f3b0a60ac3adc555778fe87506/time_machine-2.15.0.tar.gz", hash = "sha256:ebd2e63baa117ded04b978813fcd1279d3fc6be2149c9cac75c716b6f1db774c", size = 25067 } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/47/35413db37da55865fdbf60649bcb948cc2559f420ef4e91e77e4e24c71b8/time_machine-2.15.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:892d016789b59950989b2db188dcd46cf16d34e8daf2343e33b679b0c5fd1001", size = 20779 }, - { url = "https://files.pythonhosted.org/packages/e0/c3/fda6d2336737d0331eb55357db1dc916af14c4fda77c69ad8b3733b003c4/time_machine-2.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4428bdae507996aa3fdeb4727bca09e26306fa64a502e7335207252684516cbf", size = 17040 }, - { url = "https://files.pythonhosted.org/packages/36/e1/71200f24d668e5183e875a08ba5e557b6107c1b7d57fa6d54ac24ad10234/time_machine-2.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0302568338c8bd333ed0698231dbb781b70ead1a5579b4ac734b9bf88313229f", size = 34811 }, - { url = "https://files.pythonhosted.org/packages/9e/2f/4b9289ea07978ad5c3469c872c7eeadf5f5b3a1dcebe2fb274c2486fc220/time_machine-2.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18fc4740073e67071472c48355775ec6d1b93af5c675524b7de2474e0dcd8741", size = 32820 }, - { url = "https://files.pythonhosted.org/packages/5f/9e/9f838c91d2248d716281af60dfea4131438c6ad6d7405ebc6e47f8c25c3b/time_machine-2.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:768d33b484a35da93731cc99bdc926b539240a78673216cdc6306833d9072350", size = 34635 }, - { url = "https://files.pythonhosted.org/packages/f3/10/1048b5ba6de55779563f005de5fbfb764727bf9678ad7701cea480b3816c/time_machine-2.15.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:73a8c8160d2a170dadcad5b82fb5ee53236a19cec0996651cf4d21da0a2574d5", size = 34326 }, - { url = "https://files.pythonhosted.org/packages/13/82/6b4df8e5abf754b0ccceeb59fa32486d28c65f67d4ada37ff8b1e9f52006/time_machine-2.15.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09fd839a321a92aa8183206c383b9725eaf4e0a28a70e4cb87db292b352eeefb", size = 32639 }, - { url = "https://files.pythonhosted.org/packages/cf/07/95e380c46136252401d97f613782a10061b3c11b61edaeb78e83aedc1a88/time_machine-2.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:838a6d117739f1ae6ecc45ec630fa694f41a85c0d07b1f3b1db2a6cc52c1808b", size = 34021 }, - { url = "https://files.pythonhosted.org/packages/b6/0c/6595fa82bd70bc7e8065bfc6534e51a27c18978f7c158d6392c979cace2c/time_machine-2.15.0-cp310-cp310-win32.whl", hash = "sha256:d24d2ec74923b49bce7618e3e7762baa6be74e624d9829d5632321de102bf386", size = 19413 }, - { url = "https://files.pythonhosted.org/packages/2f/3d/cb3c1cecfeb4b6423302ee1b2863617390500f67526f0fc1fb5641db05f6/time_machine-2.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:95c8e7036cf442480d0bf6f5fde371e1eb6dbbf5391d7bdb8db73bd8a732b538", size = 20280 }, - { url = "https://files.pythonhosted.org/packages/22/aa/96aaac88738369fba43d5cb076bb09290b1a2cbd84210bcc0a9a519c7970/time_machine-2.15.0-cp310-cp310-win_arm64.whl", hash = "sha256:660810cd27a8a94cb5e845e8f28a95e70b01ff0c45466d394c4a0cba5a0ae279", size = 18392 }, { url = "https://files.pythonhosted.org/packages/ce/54/829ab196c3306eb4cee95e3c8e7d004e15877b36479de5d2ecc72fc1d3d4/time_machine-2.15.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:674097dd54a0bbd555e7927092c74428c4c07268ad52bca38cfccc3214707e50", size = 20448 }, { url = "https://files.pythonhosted.org/packages/e1/48/a06f8c7db768db501a60210a48f3d37b7b3d65ca85aa8dc08147eb204b4a/time_machine-2.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4e83fd6112808d1d14d1a57397c6fa3bd71bb2f3b8800036e12366e3680819b9", size = 16897 }, { url = "https://files.pythonhosted.org/packages/e7/f8/73265927e3da54a417536dc3d8c9aad806b62b8133099a7ee12661aba1a3/time_machine-2.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b095a1de40ca1afaeae8df3f45e26b645094a1912e6e6871e725fcf06ecdb74a", size = 32789 }, @@ -1258,7 +1120,6 @@ dependencies = [ { name = "platformdirs" }, { name = "pluggy" }, { name = "pyproject-api" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "virtualenv" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e1/cc/272e73f90be0f6df89efaf82e5d804b90b4e39ceb0ef1621486bb0e921e8/tox-4.18.1.tar.gz", hash = "sha256:3c0c96bc3a568a5c7e66387a4cfcf8c875b52e09f4d47c9f7a277ec82f1a0b11", size = 181159 } @@ -1328,21 +1189,6 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/e4/3d/4924f9ed49698bac5f112bc9b40aa007bbdcd702462c1df3d2e1383fb158/yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53", size = 162095 } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/a3/4e67b1463c12ba178aace33b62468377473c77b33a95bcb12b67b2b93817/yarl-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:400cd42185f92de559d29eeb529e71d80dfbd2f45c36844914a4a34297ca6f00", size = 188473 }, - { url = "https://files.pythonhosted.org/packages/f3/86/c0c76e69a390fb43533783582714e8a58003f443b81cac1605ce71cade00/yarl-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8258c86f47e080a258993eed877d579c71da7bda26af86ce6c2d2d072c11320d", size = 114362 }, - { url = "https://files.pythonhosted.org/packages/07/ef/e6bee78c1bf432de839148fe9fdc1cf5e7fbd6402d8b0b7d7a1522fb9733/yarl-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2164cd9725092761fed26f299e3f276bb4b537ca58e6ff6b252eae9631b5c96e", size = 112537 }, - { url = "https://files.pythonhosted.org/packages/37/f4/3406e76ed71e4d3023dbae4514513a387e2e753cb8a4cadd6ff9ba08a046/yarl-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08ea567c16f140af8ddc7cb58e27e9138a1386e3e6e53982abaa6f2377b38cc", size = 442573 }, - { url = "https://files.pythonhosted.org/packages/37/15/98b4951271a693142e551fea24bca1e96be71b5256b3091dbe8433532a45/yarl-1.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:768ecc550096b028754ea28bf90fde071c379c62c43afa574edc6f33ee5daaec", size = 468046 }, - { url = "https://files.pythonhosted.org/packages/88/1a/f10b88c4d8200708cbc799aad978a37a0ab15a4a72511c60bed11ee585c4/yarl-1.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2909fa3a7d249ef64eeb2faa04b7957e34fefb6ec9966506312349ed8a7e77bf", size = 462124 }, - { url = "https://files.pythonhosted.org/packages/02/a3/97b527b5c4551c3b17fd095fe019435664330060b3879c8c1ae80985d4bc/yarl-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01a8697ec24f17c349c4f655763c4db70eebc56a5f82995e5e26e837c6eb0e49", size = 446807 }, - { url = "https://files.pythonhosted.org/packages/40/06/da47aae54f1bb8ac0668d68bbdde40ba761643f253b2c16fdb4362af8ca3/yarl-1.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e286580b6511aac7c3268a78cdb861ec739d3e5a2a53b4809faef6b49778eaff", size = 431778 }, - { url = "https://files.pythonhosted.org/packages/ba/a1/54992cd68f61c11d975184f4c8a4c7f43a838e7c6ce183030a3fc0a257a6/yarl-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4179522dc0305c3fc9782549175c8e8849252fefeb077c92a73889ccbcd508ad", size = 443702 }, - { url = "https://files.pythonhosted.org/packages/5c/8b/adf290dc272a1a30a0e9dc04e2e62486be80f371bd9da2e9899f8e6181f3/yarl-1.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27fcb271a41b746bd0e2a92182df507e1c204759f460ff784ca614e12dd85145", size = 448289 }, - { url = "https://files.pythonhosted.org/packages/fc/98/e6ad935fa009890b9ef2769266dc9dceaeee5a7f9a57bc7daf50b5b6c305/yarl-1.11.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd", size = 471660 }, - { url = "https://files.pythonhosted.org/packages/91/5d/1ad82849ce3c02661395f5097878c58ecabc4dac5d2d98e4f85949386448/yarl-1.11.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c92261eb2ad367629dc437536463dc934030c9e7caca861cc51990fe6c565f26", size = 469830 }, - { url = "https://files.pythonhosted.org/packages/e0/70/376046a7f69cfec814b97fb8bf1af6f16dcbe37fd0ef89a9f87b04156923/yarl-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d95b52fbef190ca87d8c42f49e314eace4fc52070f3dfa5f87a6594b0c1c6e46", size = 457671 }, - { url = "https://files.pythonhosted.org/packages/33/49/825f84f9a5d26d26fbf82531cee3923f356e2d8efc1819b85ada508fa91f/yarl-1.11.1-cp310-cp310-win32.whl", hash = "sha256:489fa8bde4f1244ad6c5f6d11bb33e09cf0d1d0367edb197619c3e3fc06f3d91", size = 101184 }, - { url = "https://files.pythonhosted.org/packages/b0/29/2a08a45b9f2eddd1b840813698ee655256f43b507c12f7f86df947cf5f8f/yarl-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:476e20c433b356e16e9a141449f25161e6b69984fb4cdbd7cd4bd54c17844998", size = 110175 }, { url = "https://files.pythonhosted.org/packages/af/f1/f3e6be722461cab1e7c6aea657685897956d6e4743940d685d167914e31c/yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68", size = 188410 }, { url = "https://files.pythonhosted.org/packages/4b/c1/21cc66b263fdc2ec10b6459aed5b239f07eed91a77438d88f0e1bd70e202/yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe", size = 114293 }, { url = "https://files.pythonhosted.org/packages/31/7a/0ecab63a166a22357772f4a2852c859e2d5a7b02a5c58803458dd516e6b4/yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675", size = 112548 },