diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 425ddc3..24eb6d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - run: uv python install ${{ matrix.python }} - run: uv run ruff check src/obelisk/ - run: uv run ruff format --check src/obelisk/ - - run: uv run mypy src/obelisk/ + - run: uv run mypy test: name: Run tests strategy: diff --git a/hooks/pre-commit b/hooks/pre-commit index f85e1ca..5fcd963 100755 --- a/hooks/pre-commit +++ b/hooks/pre-commit @@ -11,4 +11,4 @@ exec 1>&2 uv run ruff format --check src/obelisk/ -uv run mypy src/obelisk/ +uv run mypy diff --git a/pyproject.toml b/pyproject.toml index 4b88d89..41d965b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,3 +50,7 @@ dev = [ [tool.hatch.build.targets.wheel] packages = ["src/obelisk"] + +[tool.mypy] +files = "src/obelisk" +strict = true diff --git a/src/obelisk/asynchronous/base.py b/src/obelisk/asynchronous/base.py index 6a72a95..131d995 100644 --- a/src/obelisk/asynchronous/base.py +++ b/src/obelisk/asynchronous/base.py @@ -5,7 +5,7 @@ import httpx -from obelisk.exceptions import AuthenticationError +from obelisk.exceptions import AuthenticationError, ObeliskError from obelisk.strategies.retry import RetryStrategy, NoRetryStrategy from obelisk.types import ObeliskKind @@ -45,7 +45,7 @@ def __init__( self.log = logging.getLogger("obelisk") - async def _get_token(self): + async def _get_token(self) -> None: auth_string = str( base64.b64encode(f"{self._client}:{self._secret}".encode("utf-8")), "utf-8" ) @@ -60,6 +60,7 @@ async def _get_token(self): payload = {"grant_type": "client_credentials"} async with httpx.AsyncClient() as client: + request: httpx.Response | None = None response = None last_error = None retry = self.retry_strategy.make() @@ -81,6 +82,9 @@ async def _get_token(self): if response is None and last_error is not None: raise last_error + if request is None: + raise last_error or ObeliskError("Could not create HTTP request") + if request.status_code != 200: if "error" in response: self.log.warning(f"Could not authenticate, {response['error']}") @@ -91,9 +95,11 @@ async def _get_token(self): seconds=response["expires_in"] ) - async def _verify_token(self): - if self._token is None or self._token_expires < ( - datetime.now() - self.grace_period + async def _verify_token(self) -> None: + if ( + self._token is None + or self._token_expires is None + or self._token_expires < (datetime.now() - self.grace_period) ): retry = self.retry_strategy.make() first = True @@ -107,7 +113,7 @@ async def _verify_token(self): continue async def http_post( - self, url: str, data: Any = None, params: Optional[dict] = None + self, url: str, data: Any = None, params: Optional[dict[str, str]] = None ) -> httpx.Response: """ Send an HTTP POST request to Obelisk, @@ -155,7 +161,9 @@ async def http_post( raise last_error return response - async def http_get(self, url: str, params: Optional[dict] = None) -> httpx.Response: + async def http_get( + self, url: str, params: Optional[dict[str, str]] = None + ) -> httpx.Response: """ Send an HTTP GET request to Obelisk, with proper auth. diff --git a/src/obelisk/asynchronous/client.py b/src/obelisk/asynchronous/client.py index 263928d..c900673 100644 --- a/src/obelisk/asynchronous/client.py +++ b/src/obelisk/asynchronous/client.py @@ -1,7 +1,7 @@ import json from datetime import datetime, timedelta from math import floor -from typing import AsyncGenerator, List, Literal, Optional +from typing import Any, AsyncGenerator, List, Literal, Optional import httpx from pydantic import ValidationError @@ -28,10 +28,10 @@ async def fetch_single_chunk( fields: Optional[List[str]] = None, from_timestamp: Optional[int] = None, to_timestamp: Optional[int] = None, - order_by: Optional[dict] = None, - filter_: Optional[dict] = None, + order_by: Optional[dict[str, Any]] = None, + filter_: Optional[dict[str, Any]] = None, limit: Optional[int] = None, - limit_by: Optional[dict] = None, + limit_by: Optional[dict[str, Any]] = None, cursor: Optional[str] = None, ) -> QueryResult: """ @@ -116,10 +116,10 @@ async def query( fields: Optional[List[str]] = None, from_timestamp: Optional[int] = None, to_timestamp: Optional[int] = None, - order_by: Optional[dict] = None, - filter_: Optional[dict] = None, + order_by: Optional[dict[str, Any]] = None, + filter_: Optional[dict[str, Any]] = None, limit: Optional[int] = None, - limit_by: Optional[dict] = None, + limit_by: Optional[dict[str, Any]] = None, ) -> List[Datapoint]: """ Queries data from obelisk, @@ -196,7 +196,7 @@ async def query_time_chunked( from_time: datetime, to_time: datetime, jump: timedelta, - filter_: Optional[dict] = None, + filter_: Optional[dict[str, Any]] = None, direction: Literal["asc", "desc"] = "asc", ) -> AsyncGenerator[List[Datapoint], None]: """ @@ -239,7 +239,7 @@ async def query_time_chunked( async def send( self, dataset: str, - data: List[dict], + data: List[dict[str, Any]], precision: TimestampPrecision = TimestampPrecision.MILLISECONDS, mode: IngestMode = IngestMode.DEFAULT, ) -> httpx.Response: diff --git a/src/obelisk/asynchronous/core.py b/src/obelisk/asynchronous/core.py index b8c1dc3..b342120 100644 --- a/src/obelisk/asynchronous/core.py +++ b/src/obelisk/asynchronous/core.py @@ -159,7 +159,7 @@ def check_datatype_needed(self) -> Self: return self - def to_dict(self) -> Dict: + def to_dict(self) -> dict[str, Any]: return self.model_dump(exclude_none=True, by_alias=True) diff --git a/src/obelisk/sync/client.py b/src/obelisk/sync/client.py index d145660..90d7bf0 100644 --- a/src/obelisk/sync/client.py +++ b/src/obelisk/sync/client.py @@ -1,7 +1,7 @@ import asyncio from datetime import datetime, timedelta from math import floor -from typing import Generator, List, Literal, Optional +from typing import Any, Generator, List, Literal, Optional import httpx @@ -49,10 +49,10 @@ def fetch_single_chunk( fields: Optional[List[str]] = None, from_timestamp: Optional[int] = None, to_timestamp: Optional[int] = None, - order_by: Optional[dict] = None, - filter_: Optional[dict] = None, + order_by: Optional[dict[str, Any]] = None, + filter_: Optional[dict[str, Any]] = None, limit: Optional[int] = None, - limit_by: Optional[dict] = None, + limit_by: Optional[dict[str, Any]] = None, cursor: Optional[str] = None, ) -> QueryResult: """ @@ -119,10 +119,10 @@ def query( fields: Optional[List[str]] = None, from_timestamp: Optional[int] = None, to_timestamp: Optional[int] = None, - order_by: Optional[dict] = None, - filter_: Optional[dict] = None, + order_by: Optional[dict[str, Any]] = None, + filter_: Optional[dict[str, Any]] = None, limit: Optional[int] = None, - limit_by: Optional[dict] = None, + limit_by: Optional[dict[str, Any]] = None, ) -> List[Datapoint]: """ Queries data from obelisk, @@ -182,7 +182,7 @@ def query_time_chunked( from_time: datetime, to_time: datetime, jump: timedelta, - filter_: Optional[dict] = None, + filter_: Optional[dict[str, Any]] = None, direction: Literal["asc", "desc"] = "asc", ) -> Generator[List[Datapoint], None, None]: """ @@ -225,7 +225,7 @@ def query_time_chunked( def send( self, dataset: str, - data: List[dict], + data: List[dict[str, Any]], precision: TimestampPrecision = TimestampPrecision.MILLISECONDS, mode: IngestMode = IngestMode.DEFAULT, ) -> httpx.Response: diff --git a/src/obelisk/types/core.py b/src/obelisk/types/core.py index 3630e01..d00ce55 100644 --- a/src/obelisk/types/core.py +++ b/src/obelisk/types/core.py @@ -69,7 +69,7 @@ def __str__(self) -> str: return f"'{self.left}'{self.op}{right}" @staticmethod - def _sstr(item: Any): + def _sstr(item: Any) -> str: """Smart string conversion""" if isinstance(item, datetime): return item.isoformat()