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
9 changes: 6 additions & 3 deletions pyoverkiz/auth/strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import json
import ssl
from collections.abc import Mapping
from http import HTTPStatus
from typing import TYPE_CHECKING, Any, cast

if TYPE_CHECKING:
Expand Down Expand Up @@ -49,6 +50,8 @@
)
from pyoverkiz.models import ServerConfig

MIN_JWT_SEGMENTS = 2


class BaseAuthStrategy(AuthStrategy):
"""Base class for authentication strategies."""
Expand Down Expand Up @@ -110,13 +113,13 @@ async def _post_login(self, data: Mapping[str, Any]) -> None:
data=data,
ssl=self._ssl,
) as response:
if response.status not in (200, 204):
if response.status not in (HTTPStatus.OK, HTTPStatus.NO_CONTENT):
raise BadCredentialsError(
f"Login failed for {self.server.name}: {response.status}"
)

# A 204 No Content response cannot have a body, so skip JSON parsing.
if response.status == 204:
if response.status == HTTPStatus.NO_CONTENT:
return

result = await response.json()
Expand Down Expand Up @@ -419,7 +422,7 @@ def auth_headers(self, path: str | None = None) -> Mapping[str, str]:
def _decode_jwt_payload(token: str) -> dict[str, Any]:
"""Decode the payload of a JWT token."""
parts = token.split(".")
if len(parts) < 2:
if len(parts) < MIN_JWT_SEGMENTS:
raise InvalidTokenError("Malformed JWT received.")

payload_segment = parts[1]
Expand Down
3 changes: 2 additions & 1 deletion pyoverkiz/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
import ssl
import urllib.parse
from http import HTTPStatus
from pathlib import Path
from types import TracebackType
from typing import Any, Self, cast
Expand Down Expand Up @@ -728,7 +729,7 @@ async def _delete(self, path: str) -> None:
async def _parse_response(response: ClientResponse) -> Any:
"""Check response status and parse JSON body (returns None for 204)."""
await check_response(response)
if response.status == 204:
if response.status == HTTPStatus.NO_CONTENT:
return None
return await response.json()

Expand Down
5 changes: 3 additions & 2 deletions pyoverkiz/response_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

from http import HTTPStatus
from json import JSONDecodeError

from aiohttp import ClientResponse
Expand Down Expand Up @@ -113,7 +114,7 @@

async def check_response(response: ClientResponse) -> None:
"""Check the response returned by the OverKiz API."""
if response.status in [200, 204]:
if response.status in (HTTPStatus.OK, HTTPStatus.NO_CONTENT):
return

try:
Expand All @@ -124,7 +125,7 @@ async def check_response(response: ClientResponse) -> None:
if "is down for maintenance" in result:
raise MaintenanceError("Server is down for maintenance") from error

if response.status == 503:
if response.status == HTTPStatus.SERVICE_UNAVAILABLE:
raise ServiceUnavailableError(result) from error

raise OverkizError(
Expand Down
9 changes: 7 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,21 @@ select = [
"PTH",
# flake8-builtins
"A",
# pylint
"PL",
]
ignore = [
"E501", # Line too long
"TRY003", # Long exception messages are acceptable for domain-specific errors
"A002", # Shadowing builtins (id, type) is intentional for API field names
"PLR0913", # Too many arguments — expected for API model constructors
"PLC0415", # Import not at top level — intentional lazy imports
"PLR0911", # Too many return statements — acceptable in factory functions
]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101", "SLF001"] # Allow assert and private member access in tests
"utils/**" = ["INP001", "BLE001"] # Utils are scripts, not packages
"tests/**" = ["S101", "SLF001", "PLR2004"] # Allow assert, private member access, and magic values in tests
"utils/**" = ["INP001", "BLE001", "PLR0912", "PLR0915"] # Utils are scripts, not packages; complex generators acceptable

[tool.ruff.lint.pydocstyle]
convention = "google" # Accepts: "google", "numpy", or "pep257".
Expand Down
Loading