Skip to content

Commit

Permalink
resolving ruff ANN issues (work in progress)
Browse files Browse the repository at this point in the history
  • Loading branch information
ClaasRostock committed Feb 12, 2025
1 parent 9845bc4 commit b163027
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 129 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ dependencies = [
"flexparser<0.4",
"pdfminer>=20191125",
"pypdf2>=3.0.1",
"parent-lookup>=0.2.0",
]

[project.optional-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion src/component_model/utils/fmu.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def model_from_fmu(
*,
provide_msg: bool = False, # TODO @EisDNV: This argument is not used. Remove? # noqa: ARG001
sep: str = ".", # TODO @EisDNV: This argument is not used. Remove? # noqa: ARG001
) -> dict:
) -> dict[str, Any]:
"""Generate a Model from an FMU (excluding the inner working functions like `do_step()`),
i.e. partially reverse-engineering a FMU.
This can be useful for convenient access to model information like variables
Expand Down
23 changes: 23 additions & 0 deletions src/component_model/utils/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from collections.abc import Sequence
from typing import (
TypeAlias,
)

# ===== Arguments (Variables) =====================================================================
TNumeric: TypeAlias = int | float
TValue: TypeAlias = int | float | bool | str # single (scalar) value, as e.g. also serialized to/from Json5 atom
TTimeColumn: TypeAlias = list[int] | list[float] # X column, but limited to numeric types. Typically indicating time.
TDataColumn: TypeAlias = list[int] | list[float] | list[bool] # | list[str] # X column
TDataRow: TypeAlias = Sequence[TValue | Sequence[TValue]] # | TNumeric # X row without variable names (just values)
TDataTable: TypeAlias = Sequence[TDataRow] # X table
# TArguments: TypeAlias = Mapping[str, TValue] # X row with variable names # noqa: ERA001
# TArgs: TypeAlias = dict[str, TValue] # noqa: ERA001


# ===== System Interface =====================================================================
#: Arguments for 'get' action functions (component_variable_name, component_name, variable_references)
TGetActionArgs: TypeAlias = tuple[str, str, tuple[int, ...]]
#: Arguments for 'set' action functions (component_variable_name, component_name, variable_references, variable_values)
TSetActionArgs: TypeAlias = tuple[str, str, tuple[int, ...], tuple[TValue, ...]]
#: Arguments for action functions
TActionArgs: TypeAlias = TGetActionArgs | TSetActionArgs
73 changes: 38 additions & 35 deletions src/component_model/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@

from component_model.caus_var_ini import Initial, check_causality_variability_initial, use_start
from component_model.utils.logger import get_module_logger
from component_model.utils.types import TValue

if TYPE_CHECKING:
from collections.abc import Callable

from pythonfmu.enums import Fmi2Causality as Causality
from pythonfmu.enums import Fmi2Variability as Variability


logger = get_module_logger(__name__, level=0)
PyType: TypeAlias = str | int | float | bool | Enum
Numeric: TypeAlias = int | float
Compound: TypeAlias = tuple[PyType, ...] | list[PyType] | np.ndarray
Compound: TypeAlias = tuple[TValue, ...] | list[TValue] | np.ndarray[Any, np.dtype[np.float64]]


class Check(IntFlag):
Expand Down Expand Up @@ -111,7 +111,7 @@ class Variable(ScalarVariable):
typ (type)=None: Optional explicit type of variable to expect as start and value.
Since initial values are often set with strings (with units, see below), this is set explicitly.
If None, _typ is set to Enum/str if derived from these after disection or float if a number. 'int' is not automatically detected.
start (PyType): The initial value of the variable.
start (TValue): The initial value of the variable.
Optionally, the unit can be included, providing the initial value as string, evaluating to quantity of type typ a display unit and base unit.
Note that the quantities are always converted to standard units of the same type, while the display unit may be different, i.e. the preferred user communication.
Expand Down Expand Up @@ -155,7 +155,7 @@ def __init__( # noqa: PLR0913
variability: str | None = "fixed",
initial: str | None = None,
typ: type | None = None,
start: PyType | Compound | None = None,
start: TValue | Compound | None = None,
rng: tuple | None = (),
annotations: dict | None = None,
value_check: Check = Check.all,
Expand All @@ -164,7 +164,7 @@ def __init__( # noqa: PLR0913
owner: Any | None = None,
value_reference: int | None = None,
):
self.model = model
self.model: Model = model
self._causality, self._variability, self._initial = check_causality_variability_initial(
causality, variability, initial
)
Expand All @@ -189,7 +189,10 @@ def __init__( # noqa: PLR0913
self.on_set = on_set
# Note: the _len is a central property, distinguishing scalar and compound variables.

self._start: tuple
self._start: tuple[TValue, ...]
self._range: tuple[tuple[TValue, ...], ...]
self._display: tuple[tuple[str, ...], ...]
self._unit: tuple[str, ...]
# First we check for str (since these are also iterable), then we can check for the presence of __getitem__
# Determine the (element) type (unique for all elements in compound variables)
if self._typ is str: # explicit free string
Expand Down Expand Up @@ -223,24 +226,24 @@ def __len__(self) -> int:
return self._len # This works also compound variables, as long as _len is properly set

@property
def start(self):
def start(self) -> TValue | tuple[TValue, ...]:
return self._start

@start.setter
def start(self, val):
if isinstance(val, str | int | float | bool | Enum):
def start(self, val: TValue | tuple[TValue, ...]) -> None:
if isinstance(val, TValue):
self._start = (val,)
elif isinstance(val, tuple | list | np.ndarray):
self._start = tuple(val)
else:
raise VariableInitError(f"Unallowed start value setting {val} for variable {self.name}") from None

@property
def unit(self):
def unit(self) -> str | tuple[str, ...]:
return self._unit

@unit.setter
def unit(self, val):
def unit(self, val: str | tuple[str, ...]) -> None:
if isinstance(val, tuple | list):
self._unit = tuple(val)
elif isinstance(val, str):
Expand All @@ -249,11 +252,11 @@ def unit(self, val):
raise VariableInitError(f"Unallowed unit setting {val} for variable {self.name}") from None

@property
def display(self):
def display(self) -> tuple[tuple[str, ...], ...]:
return self._display

@display.setter
def display(self, val):
def display(self, val: tuple[str, ...] | tuple[tuple[str, ...], ...]) -> None:
if val is None or (isinstance(val, tuple) and isinstance(val[0], str)): # single variable
self._display = (val,)
elif isinstance(val, tuple) and (val[0] is None or isinstance(val[0], (tuple))): # compound variable
Expand All @@ -262,22 +265,22 @@ def display(self, val):
raise VariableInitError(f"Unallowed display setting {val} for variable {self.name}") from None

@property
def range(self):
def range(self) -> tuple[tuple[TValue, ...], ...]:
return self._range

@range.setter
def range(self, val):
def range(self, val: tuple[TValue, ...] | tuple[tuple[TValue, ...], ...]) -> None:
if isinstance(val, tuple) and isinstance(val[0], tuple): # compound variable
self._range = val
elif isinstance(val, tuple) and all(isinstance(val[i], int | float | bool | Enum | str) for i in range(2)):
elif isinstance(val, tuple) and all(isinstance(val[i], TValue) for i in range(2)):
self._range = (val,)

@property
def typ(self):
def typ(self) -> type | None:
return self._typ

@property
def check(self):
def check(self) -> Check:
return self._check

@property
Expand All @@ -295,9 +298,9 @@ def initial(self) -> Initial:

def setter( # noqa: C901, PLR0912
self,
value: PyType | Compound,
value: TValue | Compound,
idx: int | None = None,
):
) -> None:
"""Set the value (input to model from outside), including range checking and unit conversion.
For compound values, the whole 'array' should be provided,
Expand Down Expand Up @@ -339,7 +342,7 @@ def setter( # noqa: C901, PLR0912
else:
setattr(self.owner, self.local_name, value if self.on_set is None else self.on_set(value))

def getter(self) -> PyType | list[PyType]: # noqa: C901, PLR0912
def getter(self) -> TValue | Compound: # noqa: C901, PLR0912
"""Get the value (output a value from the model), including range checking and unit conversion.
The whole variable value is returned.
The return value can be indexed/sliced to get elements of compound variables.
Expand Down Expand Up @@ -386,11 +389,11 @@ def _init_range( # noqa: C901, PLR0912
"""

def ensure_display_limits(
val: PyType,
val: TValue,
idx: int,
*,
right: bool,
):
) -> TValue:
"""Ensure that value is provided as display unit and that limits are included in range."""
if self._display[idx] is not None: # Range in display units!
_val = self._display[idx]
Expand Down Expand Up @@ -453,15 +456,15 @@ def ensure_display_limits(

def check_range( # noqa: C901, PLR0911, PLR0912
self,
value: PyType | Compound | None,
value: TValue | Compound | None,
idx: int | None = None,
*,
disp: bool = True,
) -> bool:
"""Check the provided 'value' with respect to the range.
Args:
value (PyType|Compound): the value to check. Scalars may be wrapped into a vector
value (TValue|Compound): the value to check. Scalars may be wrapped into a vector
disp (bool) = True: denotes whether the value is expected in display units (default) or units
Returns
Expand Down Expand Up @@ -506,7 +509,7 @@ def check_range( # noqa: C901, PLR0911, PLR0912
return self._range[idx] is None or self._range[idx][0] <= value <= self._range[idx][1]
raise VariableUseError(f"check_range(): value={value}, type={self.typ}, range={self.range}") from None

def fmi_type_str(self, val: PyType) -> str:
def fmi_type_str(self, val: TValue) -> str:
"""Translate the provided type to a proper fmi type and return it as string.
See types defined in schema fmi2Unit.xsd.
"""
Expand All @@ -517,10 +520,10 @@ def fmi_type_str(self, val: PyType) -> str:
@classmethod
def auto_type( # noqa: C901, PLR0912
cls,
val: PyType | Compound,
val: TValue | Compound,
*,
allow_int: bool = False,
):
) -> type[Any]:
"""Determine the Variable type from a provided example value.
Since variables can be initialized using strings with units,
the type can only be determined when the value is disected.
Expand Down Expand Up @@ -561,7 +564,7 @@ def auto_type( # noqa: C901, PLR0912
return type(val)

@classmethod
def _auto_extreme(cls, var: PyType) -> tuple:
def _auto_extreme(cls, var: TValue) -> tuple:
"""Return the extreme values of the variable.
Args:
Expand All @@ -579,12 +582,12 @@ def _auto_extreme(cls, var: PyType) -> tuple:
return (min(x.value for x in type(var)), max(x.value for x in type(var)))
return () # return an empty tuple (no range specified, e.g. for str)

def _disect_unit(self, quantity: PyType | Compound) -> tuple:
def _disect_unit(self, quantity: TValue | Compound) -> tuple:
"""Disect the provided quantity in terms of magnitude and unit, if provided as string.
If another type is provided, dimensionless units are assumed.
Args:
quantity (PyType): the quantity to disect. Should be provided as string, but also the trivial cases (int,float,Enum) are allowed.
quantity (TValue): the quantity to disect. Should be provided as string, but also the trivial cases (int,float,Enum) are allowed.
A free string should not be used and leads to a warning
Returns:
the magnitude in base units, the base unit and the unit as given (display units),
Expand Down Expand Up @@ -648,7 +651,7 @@ def _get_transformation(self, q: Quantity) -> tuple[float, str, tuple | None]:
from_base = partial(linear, b=1.0 / b, a=-a / b)
return (val, str(qb.units), (str(q.units), to_base, from_base))

def xml_scalarvariables(self): # noqa: C901
def xml_scalarvariables(self) -> list[ET.Element]: # noqa: C901
"""Generate <ScalarVariable> XML code with respect to this variable and return xml element.
For compound variables, all elements are included.
Expand All @@ -661,15 +664,15 @@ def xml_scalarvariables(self): # noqa: C901
List of ScalarVariable xml elements
"""

def substr(alt1: str, alti: str):
def substr(alt1: str, alti: str) -> str:
return alt1 if self._len == 1 else alti

declaredType = {"int": "Integer", "bool": "Boolean", "float": "Real", "str": "String", "Enum": "Enumeration"}[
self.typ.__qualname__
] # translation of python to FMI primitives. Same for all components
assert self._initial is not None, "Initial shall be properly set at this point"
do_use_start = use_start(causality=self._causality, variability=self._variability, initial=self._initial)
svars = []
svars: list[ET.Element] = []
for i in range(self._len):
sv = ET.Element(
tag="ScalarVariable",
Expand Down
15 changes: 9 additions & 6 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ class PythonSlave(Fmi2Slave):
author = "John Doe"
description = "A simple description"

def __init__(self, **kwargs: Any) -> None:
def __init__(
self,
**kwargs: Any, # noqa: ANN401
) -> None:
super().__init__(**kwargs)

self.intOut = 1
Expand Down Expand Up @@ -109,7 +112,7 @@ def enter_initialization_mode(self):
def exit_initialization_mode(self):
pass

def do_step(self, current_time, step_size):
def do_step(self, current_time: float, step_size: float) -> bool:
"""N. Happens at every communication point.
a. Inputs (signals) are set
b. Perform calculations
Expand All @@ -122,13 +125,13 @@ def do_step(self, current_time, step_size):
return True


def test_make_fmu(build_fmu):
def test_make_fmu(build_fmu: Path):
assert build_fmu.name == "PythonSlave.fmu"


def test_use_fmu(build_fmu):
def test_use_fmu(build_fmu: Path):
_ = simulate_fmu(
build_fmu,
filename=build_fmu,
stop_time=1,
step_size=0.1,
validate=True,
Expand All @@ -140,5 +143,5 @@ def test_use_fmu(build_fmu):


if __name__ == "__main__":
retcode = pytest.main(["-rA", "-v", __file__])
retcode = pytest.main(args=["-rA", "-v", __file__])
assert retcode == 0, f"Non-zero return code {retcode}"
Loading

0 comments on commit b163027

Please sign in to comment.