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
10 changes: 6 additions & 4 deletions .github/workflows/typing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ env:
DEFAULT_PYTHON: "3.13"

jobs:
mypy:
name: mypy
ty:
name: ty
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: ⤵️ Check out code from GitHub
uses: actions/checkout@v6.0.1
Expand All @@ -30,5 +32,5 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: 🏗 Install dependencies
run: uv sync --dev
- name: 🚀 Run mypy
run: uv run mypy examples src tests
- name: 🚀 Run ty
run: uv run ty check examples src tests
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ repos:
types: [text]
entry: uv run end-of-file-fixer
stages: [pre-commit, pre-push, manual]
- id: mypy
name: 🆎 Static type checking using mypy
- id: ty
name: 🆎 Static type checking using ty
language: system
types: [python]
entry: uv run mypy
entry: uv run ty check
require_serial: true
- id: no-commit-to-branch
name: 🛑 Don't commit to main branch
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ dev = [
# hatch is required to support type hinting and proper packaging of the py.typed file.
"hatch>=1.14.1",
"isort==6.1.0",
"mypy==1.19.0",
"ty==0.0.5",
"pre-commit==4.5.0",
"pre-commit-hooks==6.0.0",
"pylint==3.3.9",
Expand Down
12 changes: 5 additions & 7 deletions src/bsblan/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
import logging
import re
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
from constants import APIConfig
from typing import Any

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -76,7 +73,8 @@ def validate_time_format(
class APIValidator:
"""Validates and maintains BSB-LAN API configuration."""

api_config: APIConfig
# Flexible type for API data (accepts `APIConfig`, plain dicts, or None)
api_config: Any # intentionally permissive to support tests and dynamic data
validated_sections: set[str] = field(default_factory=set)

def validate_section(self, section: str, request_data: dict[str, Any]) -> None:
Expand All @@ -89,7 +87,7 @@ def validate_section(self, section: str, request_data: dict[str, Any]) -> None:

"""
# Check if the section exists in the APIConfig object
if section not in self.api_config:
if not self.api_config or section not in self.api_config:
logger.warning("Unknown section '%s' in API configuration", section)
return

Expand Down Expand Up @@ -141,7 +139,7 @@ def _is_valid_param(self, param: dict[str, Any]) -> bool:

def get_section_params(self, section: str) -> Any:
"""Get the parameter mapping for a section."""
return self.api_config.get(section, {}).copy()
return (self.api_config or {}).get(section, {}).copy()

def is_section_validated(self, section: str) -> bool:
"""Check if a section has been validated."""
Expand Down
16 changes: 13 additions & 3 deletions tests/test_thermostat.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@

import json
import logging
from collections.abc import Awaitable, Callable
from typing import TYPE_CHECKING, Any

import aiohttp
import pytest
from aiohttp.web_request import Request
from aresponses import Response, ResponsesMockServer

from bsblan import BSBLAN, BSBLANConfig
Expand All @@ -21,7 +23,13 @@
)

if TYPE_CHECKING:
from collections.abc import AsyncGenerator
from collections.abc import (
AsyncGenerator,
Awaitable,
Callable,
)

from aiohttp.web_request import Request

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -59,10 +67,12 @@ async def mock_aresponses() -> AsyncGenerator[ResponsesMockServer, None]:
yield server


def create_response_handler(expected_data: dict[str, Any]) -> Response:
def create_response_handler(
expected_data: dict[str, Any],
) -> Callable[[Request], Awaitable[Response]]:
"""Create a response handler that checks the request data."""

async def response_handler(request: aiohttp.web.Request) -> Response:
async def response_handler(request: Request) -> Response:
"""Check the request data."""
assert request.method == "POST"
assert request.host == "example.com"
Expand Down
Loading
Loading