Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 31 additions & 46 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,104 +1,94 @@
---
repos:
- repo: builtin
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-case-conflict
name: 🔠 Check for case conflicts
- id: check-executables-have-shebangs
name: 🧐 Check that executables have shebangs
language: system
types: [text, executable]
stages: [commit, push, manual]
- id: check-json
name: { Check JSON files
language: system
types: [json]
- id: check-merge-conflict
name: 💥 Check for merge conflicts
language: system
types: [text]
- id: check-symlinks
name: 🔗 Check for broken symlinks
language: system
types: [symlink]
- id: check-toml
name: ✅ Check TOML files
language: system
types: [toml]
- id: check-xml
name: ✅ Check XML files
language: system
types: [xml]
- id: check-yaml
name: ✅ Check YAML files
language: system
types: [yaml]
- id: detect-private-key
name: 🕵️ Detect Private Keys
language: system
types: [text]
- id: end-of-file-fixer
name: ⮐ Fix End of Files
language: system
types: [text]
stages: [commit, push, manual]
- id: no-commit-to-branch
name: 🛑 Don't commit to main branch
language: system
pass_filenames: false
always_run: true
args:
- --branch=main
- id: trailing-whitespace
name: ✄ Trim Trailing Whitespace
language: system
types: [text]
stages: [commit, push, manual]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-ast
name: 🐍 Check Python AST
types: [python]
- id: check-docstring-first
name: ℹ️ Check docstring is first
types: [python]
- id: no-commit-to-branch
name: 🛑 Don't commit to main branch
args: [--branch=main]

- repo: local
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.8
hooks:
- id: ruff-check
- id: ruff
name: 🐶 Ruff Linter
language: system
types: [python]
entry: uv run ruff check --fix
require_serial: true
stages: [commit, push, manual]
args: [--fix]
- id: ruff-format
name: 🐶 Ruff Formatter
language: system
types: [python]
entry: uv run ruff format
require_serial: true
stages: [commit, push, manual]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.14.1
hooks:
- id: mypy
name: 🆎 Static type checking using mypy
language: system
types: [python]
entry: uv run mypy
require_serial: true
additional_dependencies: []

- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0
hooks:
- id: prettier
name: 💄 Ensuring files are prettier
types: [yaml, json, markdown]

- repo: https://github.com/adrienverge/yamllint
rev: v1.35.1
hooks:
- id: yamllint
name: 🎗 Check YAML files with yamllint
types: [yaml]

- repo: local
hooks:
- id: uv
name: 📜 Check pyproject with uv
language: system
entry: uv lock --check
pass_filenames: false
always_run: true
- id: prettier
name: 💄 Ensuring files are prettier
language: system
types: [yaml, json, markdown]
entry: npm run prettier
pass_filenames: false
- id: pylint
name: 🌟 Starring code with pylint
language: system
Expand All @@ -110,8 +100,3 @@ repos:
types: [python]
entry: uv run pytest --cov=tadoasync --cov-report=term-missing --cov-report=xml --cov-fail-under=95
pass_filenames: false
- id: yamllint
name: 🎗 Check YAML files with yamllint
language: system
types: [yaml]
entry: uv run yamllint
1 change: 1 addition & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Config file for Sphinx and documentation."""

import os
import sys

Expand Down
37 changes: 33 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,34 @@ warn_return_any = true
warn_unused_configs = true
warn_unused_ignores = true

# Per-module overrides for libraries without type stubs
[[tool.mypy.overrides]]
module = "mashumaro.*"
ignore_errors = true

[[tool.mypy.overrides]]
module = "syrupy"
ignore_errors = true

[[tool.mypy.overrides]]
module = "aresponses"
ignore_errors = true

[[tool.mypy.overrides]]
module = "tadoasync.models"
disallow_any_generics = false
disallow_subclassing_any = false

[[tool.mypy.overrides]]
module = "tadoasync.tadoasync"
disallow_any_generics = false
warn_return_any = false

[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_decorators = false
ignore_errors = true

[tool.pylint.MASTER]
ignore = ["tests"]

Expand All @@ -120,7 +148,7 @@ good-names = ["_", "ex", "fp", "i", "id", "j", "k", "on", "Run", "T"]
max-attributes = 8

[tool.pylint."MESSAGES CONTROL"]
disable = ["duplicate-code", "format", "unsubscriptable-object", "no-member", "too-many-arguments", "protected-access", "invalid-name", "wrong-import-order", "too-many-statements", "too-many-public-methods", "too-many-instance-attributes", "redefined-builtin"]
disable = ["duplicate-code", "format", "unsubscriptable-object", "no-member", "too-many-arguments", "protected-access", "invalid-name", "wrong-import-order", "too-many-statements", "too-many-public-methods", "too-many-instance-attributes", "redefined-builtin", "import-error"]

[tool.pylint.SIMILARITIES]
ignore-imports = true
Expand All @@ -133,6 +161,7 @@ addopts = "--cov"
asyncio_mode = "auto"

[tool.ruff]
[tool.ruff.lint]
ignore = [
"ANN101", # Self... explanatory
"ANN102", # cls... just as useless
Expand All @@ -158,17 +187,17 @@ ignore = [
]
select = ["ALL"]

[tool.ruff.flake8-pytest-style]
[tool.ruff.lint.flake8-pytest-style]
fixture-parentheses = false
mark-parentheses = false

[tool.ruff.isort]
[tool.ruff.lint.isort]
known-first-party = ["tado"]

[tool.ruff.lint.flake8-type-checking]
runtime-evaluated-base-classes = [
"mashumaro.mixins.orjson.DataClassORJSONMixin",
]

[tool.ruff.mccabe]
[tool.ruff.lint.mccabe]
max-complexity = 25
10 changes: 5 additions & 5 deletions src/tadoasync/tadoasync.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ async def login_device_flow(self) -> DeviceActivationStatus:
session = self._ensure_session()
request = await session.post(url=DEVICE_AUTH_URL, data=data)
request.raise_for_status()
except asyncio.TimeoutError as err:
except TimeoutError as err:
raise TadoConnectionError(
"Timeout occurred while connecting to Tado."
) from err
Expand Down Expand Up @@ -219,7 +219,7 @@ async def _check_device_activation(self) -> bool:
_LOGGER.info("Authorization pending. Continuing polling...")
return False
request.raise_for_status()
except asyncio.TimeoutError as err:
except TimeoutError as err:
raise TadoConnectionError(
"Timeout occurred while connecting to Tado."
) from err
Expand Down Expand Up @@ -281,7 +281,7 @@ async def login(self) -> None:
async with asyncio.timeout(self._request_timeout):
request = await self._session.post(url=TOKEN_URL, data=data)
request.raise_for_status()
except asyncio.TimeoutError as err:
except TimeoutError as err:
raise TadoConnectionError(
"Timeout occurred while connecting to Tado."
) from err
Expand Down Expand Up @@ -355,7 +355,7 @@ async def _refresh_auth(self) -> None:
session = self._ensure_session()
request = await session.post(url=TOKEN_URL, data=data)
request.raise_for_status()
except asyncio.TimeoutError as err:
except TimeoutError as err:
raise TadoConnectionError(
"Timeout occurred while connecting to Tado."
) from err
Expand Down Expand Up @@ -587,7 +587,7 @@ async def _request(
method=method.value, url=str(url), headers=headers, json=data
)
request.raise_for_status()
except asyncio.TimeoutError as err:
except TimeoutError as err:
raise TadoConnectionError(
"Timeout occurred while connecting to Tado."
) from err
Expand Down
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Tests for Python Tado client."""

from pathlib import Path


Expand Down
11 changes: 7 additions & 4 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ def snapshot_assertion(snapshot: SnapshotAssertion) -> SnapshotAssertion:
@pytest.fixture(name="python_tado")
async def client() -> AsyncGenerator[Tado, None]:
"""Return a Tado client."""
async with aiohttp.ClientSession() as session, Tado(
session=session,
request_timeout=10,
) as tado:
async with (
aiohttp.ClientSession() as session,
Tado(
session=session,
request_timeout=10,
) as tado,
):
await tado.device_activation()
yield tado

Expand Down
1 change: 1 addition & 0 deletions tests/ruff.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# This extend our general Ruff rules specifically for tests
extend = "../pyproject.toml"

[lint]
extend-select = [
"PT", # Use @pytest.fixture without parentheses
]
Expand Down
1 change: 1 addition & 0 deletions tests/syrupy.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Asynchronous Python client for Tado."""

from __future__ import annotations

from dataclasses import asdict, is_dataclass
Expand Down
24 changes: 14 additions & 10 deletions tests/test_tado.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ async def test_login_device_flow_timeout(responses: aioresponses) -> None:
responses._matches.clear()
responses.post(
DEVICE_AUTH_URL,
exception=asyncio.TimeoutError(),
exception=TimeoutError(),
repeat=True,
)

Expand Down Expand Up @@ -161,8 +161,9 @@ async def mock_post(*args: Any, **kwargs: Any) -> ClientResponse: # noqa: ARG00

async with aiohttp.ClientSession() as session:
tado = Tado(session=session)
with patch("aiohttp.ClientSession.post", new=mock_post), pytest.raises(
TadoError
with (
patch("aiohttp.ClientSession.post", new=mock_post),
pytest.raises(TadoError),
):
await tado.async_init()

Expand All @@ -182,8 +183,9 @@ async def mock_post(*args: Any, **kwargs: Any) -> ClientResponse: # noqa: ARG00

async with aiohttp.ClientSession() as session:
tado = Tado(session=session)
with patch("aiohttp.ClientSession.post", new=mock_post), pytest.raises(
TadoAuthenticationError
with (
patch("aiohttp.ClientSession.post", new=mock_post),
pytest.raises(TadoAuthenticationError),
):
await tado.async_init()

Expand All @@ -203,8 +205,9 @@ async def mock_post(*args: Any, **kwargs: Any) -> ClientResponse: # noqa: ARG00

async with aiohttp.ClientSession() as session:
tado = Tado(session=session)
with patch("aiohttp.ClientSession.post", new=mock_post), pytest.raises(
TadoAuthenticationError
with (
patch("aiohttp.ClientSession.post", new=mock_post),
pytest.raises(TadoAuthenticationError),
):
await tado.async_init()

Expand Down Expand Up @@ -236,7 +239,7 @@ async def test_refresh_auth_timeout(python_tado: Tado, responses: aioresponses)
"""Test timeout during refresh of auth token."""
responses.post(
TADO_TOKEN_URL,
exception=asyncio.TimeoutError(),
exception=TimeoutError(),
)
async with aiohttp.ClientSession():
python_tado._access_token = "old_test_access_token"
Expand Down Expand Up @@ -635,8 +638,9 @@ async def test_request_client_response_error(python_tado: Tado) -> None:
async def mock_get(*args: Any, **kwargs: Any) -> ClientResponse: # noqa: ARG001 # pylint: disable=unused-argument
return mock_response

with patch("aiohttp.ClientSession.request", new=mock_get), pytest.raises(
TadoBadRequestError
with (
patch("aiohttp.ClientSession.request", new=mock_get),
pytest.raises(TadoBadRequestError),
):
await python_tado._request("me")

Expand Down
Loading