Skip to content

Commit

Permalink
Enable more strict mypy rules
Browse files Browse the repository at this point in the history
  • Loading branch information
glatterf42 committed Nov 20, 2024
1 parent 3ae106a commit 8190d7a
Show file tree
Hide file tree
Showing 154 changed files with 1,516 additions and 802 deletions.
9 changes: 7 additions & 2 deletions ixmp4/cli/platforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from collections.abc import Generator, Iterator

Check warning on line 2 in ixmp4/cli/platforms.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/cli/platforms.py#L2

Added line #L2 was not covered by tests
from itertools import cycle
from pathlib import Path
from typing import Optional
from typing import Any, Optional, TypeVar

Check warning on line 5 in ixmp4/cli/platforms.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/cli/platforms.py#L5

Added line #L5 was not covered by tests

import typer
from rich.progress import Progress, track
Expand Down Expand Up @@ -272,7 +272,12 @@ def generate(
utils.good("Done!")


def create_cycle(generator: Generator, name: str, total: int) -> Iterator:
T = TypeVar("T")

Check warning on line 275 in ixmp4/cli/platforms.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/cli/platforms.py#L275

Added line #L275 was not covered by tests


def create_cycle(

Check warning on line 278 in ixmp4/cli/platforms.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/cli/platforms.py#L278

Added line #L278 was not covered by tests
generator: Generator[T, Any, None], name: str, total: int
) -> Iterator[T]:
return cycle(
[
m
Expand Down
12 changes: 7 additions & 5 deletions ixmp4/conf/auth.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
from collections.abc import Generator
from datetime import datetime, timedelta
from typing import Any
from typing import Any, cast
from uuid import uuid4

import httpx
Expand Down Expand Up @@ -161,11 +161,13 @@ def refresh_jwt(self) -> None:
self.set_user(self.access_token)

def decode_token(self, token: str) -> dict[str, Any]:
decoded: dict = jwt.decode(
token,
options={"verify_signature": False, "verify_exp": False},
return cast(

Check warning on line 164 in ixmp4/conf/auth.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/conf/auth.py#L164

Added line #L164 was not covered by tests
dict[str, Any],
jwt.decode(
token,
options={"verify_signature": False, "verify_exp": False},
),
)
return decoded

def set_user(self, token: str) -> None:
token_dict = self.decode_token(token)
Expand Down
2 changes: 1 addition & 1 deletion ixmp4/conf/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


class Credentials(object):
credentials: dict
credentials: dict[str, dict[str, str]]

def __init__(self, toml_file: Path) -> None:
self.path = toml_file
Expand Down
22 changes: 10 additions & 12 deletions ixmp4/conf/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os
import re
from functools import lru_cache
from typing import Any
from typing import Any, cast

import httpx
import pandas as pd
Expand All @@ -21,7 +21,7 @@
logger = logging.getLogger(__name__)


class hashabledict(dict):
class hashabledict(dict[str, Any]):
"""Hashable dict type used for caching."""

def __hash__(self) -> int:
Expand Down Expand Up @@ -51,7 +51,7 @@ class JtiKwargs(TypedDict, total=False):
class ManagerConfig(Config):
template_pattern = re.compile(r"(\{env\:(\w+)\})")

def __init__(self, url: str, auth: BaseAuth, remote: bool = False) -> None:
def __init__(self, url: str, auth: BaseAuth | None, remote: bool = False) -> None:
# TODO: Find the sweet-spot for `maxsize`
# -> a trade-off between memory usage
# and load on the management service
Expand Down Expand Up @@ -86,7 +86,7 @@ def _uncached_request(
method: str,
path: str,
jti: str | None = None,
) -> dict:
) -> dict[str, Any]:
del jti
# `jti` is only used to affect `@lru_cache`
# if the token id changes a new cache entry will be created
Expand All @@ -99,15 +99,16 @@ def _uncached_request(
if res.status_code != 200:
raise ManagerApiError(f"[{str(res.status_code)}] {res.text}")
# TODO Can we really assume this type?
json: dict = res.json()
return json
return cast(dict[str, Any], res.json())

Check warning on line 102 in ixmp4/conf/manager.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/conf/manager.py#L102

Added line #L102 was not covered by tests

def _request(
self,
method: str,
path: str,
params: dict | None = None,
json: dict | list | tuple | None = None,
# Seems to be just that based on references
params: dict[str, int | None] | None = None,
# Seems to not be included with any references?
json: dict[str, Any] | list[Any] | tuple[Any] | None = None,
**kwargs: Unpack[JtiKwargs],
) -> dict[str, Any]:
if params is not None:
Expand All @@ -117,10 +118,7 @@ def _request(
json = hashabledict(json) if isinstance(json, dict) else tuple(json)

Check warning on line 118 in ixmp4/conf/manager.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/conf/manager.py#L118

Added line #L118 was not covered by tests

logger.debug(f"Trying cache: {method} {path} {params} {json}")
cached_request: dict[str, Any] = self._cached_request(
method, path, params=params, json=json, **kwargs
)
return cached_request
return self._cached_request(method, path, params=params, json=json, **kwargs)

Check warning on line 121 in ixmp4/conf/manager.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/conf/manager.py#L121

Added line #L121 was not covered by tests

def fetch_platforms(self, **kwargs: Unpack[JtiKwargs]) -> list[ManagerPlatformInfo]:
json = self._request("GET", "/ixmp4", params={"page_size": -1}, **kwargs)
Expand Down
4 changes: 2 additions & 2 deletions ixmp4/conf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ def toml(self) -> TomlConfig:
return self._toml # type: ignore[return-value]

Check warning on line 82 in ixmp4/conf/settings.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/conf/settings.py#L82

Added line #L82 was not covered by tests

@property
def default_auth(self) -> ManagerAuth | AnonymousAuth:
def default_auth(self) -> ManagerAuth | AnonymousAuth | None:
if self._default_auth is None:
self.get_auth()
return self._default_auth # type: ignore[return-value]
return self._default_auth

@property
def manager(self) -> ManagerConfig:
Expand Down
10 changes: 6 additions & 4 deletions ixmp4/core/decorators.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import functools
from collections.abc import Callable
from typing import Any
from typing import Any, TypeVar

import pandera as pa
from pandera.errors import SchemaError as PanderaSchemaError
Expand All @@ -13,15 +13,17 @@

from .exceptions import SchemaError

T = TypeVar("T")


def check_types(
func: Callable[[BaseRepository, DataFrame], None],
) -> Callable[[BaseRepository, DataFrame], None]:
func: Callable[[BaseRepository, DataFrame[T]], None],
) -> Callable[[BaseRepository, DataFrame[T]], None]:
checked_func = pa.check_types(func)

@functools.wraps(func)
def wrapper(
*args: Unpack[tuple[BaseRepository, DataFrame]],
*args: Unpack[tuple[BaseRepository, DataFrame[T]]],
skip_validation: bool = False,
**kwargs: Any,
) -> None:
Expand Down
11 changes: 7 additions & 4 deletions ixmp4/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ class ProgrammingError(Exception):
ExcMeta: type = type(Exception)


class RemoteExceptionMeta(ExcMeta):
# TODO can't find a similar open issues, should I open one?
# For reference, this occurred after adding disallow_subclassing_any
# NOTE mypy seems to say Exception has type Any, don't see how we could change that
class RemoteExceptionMeta(ExcMeta): # type: ignore[misc]
def __new__(
cls, name: str, bases: tuple, namespace: dict, **kwargs: Any
cls, name: str, bases: tuple[object], namespace: dict[str, Any], **kwargs: Any
) -> type["IxmpError"]:
http_error_name = namespace.get("http_error_name", None)
if http_error_name is not None:
Expand All @@ -34,7 +37,7 @@ class IxmpError(Exception, metaclass=RemoteExceptionMeta):
_message: str = ""
http_status_code: int = 500
http_error_name: ClassVar[str] = "ixmp_error"
kwargs: dict
kwargs: dict[str, Any]

def __init__(
self,
Expand Down Expand Up @@ -69,7 +72,7 @@ def message(self) -> str:
return message

@classmethod
def from_dict(cls, dict_: dict) -> Self:
def from_dict(cls, dict_: dict[str, Any]) -> Self:
return cls(message=dict_["message"], **dict_["kwargs"])


Expand Down
14 changes: 8 additions & 6 deletions ixmp4/core/iamc/data.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Optional
from collections.abc import Iterable
from typing import Optional, TypeVar

import pandas as pd
import pandera as pa
Expand Down Expand Up @@ -49,9 +50,10 @@ def convert_to_std_format(df: pd.DataFrame, join_runs: bool) -> pd.DataFrame:
df.rename(columns={"step_year": "year"}, inplace=True)
time_col = "year"
else:
T = TypeVar("T", bool, float, int, str)

Check warning on line 53 in ixmp4/core/iamc/data.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/core/iamc/data.py#L53

Added line #L53 was not covered by tests

def map_step_column(df: pd.Series) -> pd.Series:
df["time"] = df[MAP_STEP_COLUMN[df.type]]
def map_step_column(df: pd.Series[T]) -> pd.Series[T]:
df["time"] = df[MAP_STEP_COLUMN[str(df.type)]]

Check warning on line 56 in ixmp4/core/iamc/data.py

View check run for this annotation

Codecov / codecov/patch

ixmp4/core/iamc/data.py#L55-L56

Added lines #L55 - L56 were not covered by tests
return df

df = df.apply(map_step_column, axis=1)
Expand Down Expand Up @@ -137,9 +139,9 @@ def remove(
def tabulate(
self,
*,
variable: dict | None = None,
region: dict | None = None,
unit: dict | None = None,
variable: dict[str, str | Iterable[str]] | None = None,
region: dict[str, str | Iterable[str]] | None = None,
unit: dict[str, str | Iterable[str]] | None = None,
raw: bool = False,
) -> pd.DataFrame:
df = self.backend.iamc.datapoints.tabulate(
Expand Down
7 changes: 3 additions & 4 deletions ixmp4/core/iamc/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def docs(self) -> str | None:
return None

@docs.setter
def docs(self, description: str) -> None:
def docs(self, description: str | None) -> None:
if description is None:
self.backend.iamc.variables.docs.delete(self.id)
else:
Expand Down Expand Up @@ -72,9 +72,8 @@ def tabulate(self, name: str | None = None) -> pd.DataFrame:
return self.backend.iamc.variables.tabulate(name=name)

def _get_variable_id(self, variable: str) -> int | None:
if variable is None:
return None
elif isinstance(variable, str):
# TODO this check seems kind of redundant...
if isinstance(variable, str):
obj = self.backend.iamc.variables.get(variable)
return obj.id
else:
Expand Down
7 changes: 3 additions & 4 deletions ixmp4/core/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def docs(self) -> str | None:
return None

@docs.setter
def docs(self, description: str) -> None:
def docs(self, description: str | None) -> None:
if description is None:
self.backend.models.docs.delete(self.id)
else:
Expand Down Expand Up @@ -75,9 +75,8 @@ def tabulate(self, name: str | None = None) -> pd.DataFrame:
return self.backend.models.tabulate(name=name)

def _get_model_id(self, model: str) -> int | None:
if model is None:
return None
elif isinstance(model, str):
# TODO this check seems kind of redundant...
if isinstance(model, str):
obj = self.backend.models.get(model)
return obj.id
else:
Expand Down
10 changes: 5 additions & 5 deletions ixmp4/core/optimization/equation.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ def remove_data(self) -> None:
).data

@property
def levels(self) -> list:
levels: list = self._model.data.get("levels", [])
def levels(self) -> list[float]:
levels: list[float] = self._model.data.get("levels", [])
return levels

@property
def marginals(self) -> list:
marginals: list = self._model.data.get("marginals", [])
def marginals(self) -> list[float]:
marginals: list[float] = self._model.data.get("marginals", [])
return marginals

@property
Expand All @@ -88,7 +88,7 @@ def docs(self) -> str | None:
return None

@docs.setter
def docs(self, description: str) -> None:
def docs(self, description: str | None) -> None:
if description is None:
self.backend.optimization.equations.docs.delete(self.id)
else:
Expand Down
12 changes: 6 additions & 6 deletions ixmp4/core/optimization/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from ixmp4.core.base import BaseFacade, BaseModelFacade
from ixmp4.data.abstract import Docs as DocsModel
from ixmp4.data.abstract import Parameter as ParameterModel
from ixmp4.data.abstract import Run
from ixmp4.data.abstract import Run, Unit
from ixmp4.data.abstract.optimization import Column


Expand Down Expand Up @@ -48,13 +48,13 @@ def add(self, data: dict[str, Any] | pd.DataFrame) -> None:
).data

@property
def values(self) -> list:
values: list = self._model.data.get("values", [])
def values(self) -> list[float]:
values: list[float] = self._model.data.get("values", [])
return values

@property
def units(self) -> list:
units: list = self._model.data.get("units", [])
def units(self) -> list[Unit]:
units: list[Unit] = self._model.data.get("units", [])
return units

@property
Expand All @@ -81,7 +81,7 @@ def docs(self) -> str | None:
return None

@docs.setter
def docs(self, description: str) -> None:
def docs(self, description: str | None) -> None:
if description is None:
self.backend.optimization.parameters.docs.delete(self.id)
else:
Expand Down
2 changes: 1 addition & 1 deletion ixmp4/core/optimization/scalar.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def docs(self) -> str | None:
return None

@docs.setter
def docs(self, description: str) -> None:
def docs(self, description: str | None) -> None:
if description is None:
self.backend.optimization.scalars.docs.delete(self.id)
else:
Expand Down
2 changes: 1 addition & 1 deletion ixmp4/core/optimization/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def docs(self) -> str | None:
return None

@docs.setter
def docs(self, description: str) -> None:
def docs(self, description: str | None) -> None:
if description is None:
self.backend.optimization.tables.docs.delete(self.id)
else:
Expand Down
10 changes: 5 additions & 5 deletions ixmp4/core/optimization/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ def remove_data(self) -> None:
).data

@property
def levels(self) -> list:
levels: list = self._model.data.get("levels", [])
def levels(self) -> list[float]:
levels: list[float] = self._model.data.get("levels", [])
return levels

@property
def marginals(self) -> list:
marginals: list = self._model.data.get("marginals", [])
def marginals(self) -> list[float]:
marginals: list[float] = self._model.data.get("marginals", [])
return marginals

@property
Expand Down Expand Up @@ -92,7 +92,7 @@ def docs(self) -> str | None:
return None

@docs.setter
def docs(self, description: str) -> None:
def docs(self, description: str | None) -> None:
if description is None:
self.backend.optimization.variables.docs.delete(self.id)
else:
Expand Down
2 changes: 1 addition & 1 deletion ixmp4/core/region.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def docs(self) -> str | None:
return None

@docs.setter
def docs(self, description: str) -> None:
def docs(self, description: str | None) -> None:
if description is None:
self.backend.regions.docs.delete(self.id)
else:
Expand Down
Loading

0 comments on commit 8190d7a

Please sign in to comment.