Skip to content

Commit 2faab71

Browse files
authored
Fix typing for composing constants with formula builders (#980)
Closes #951
2 parents 46cb78e + ab702a3 commit 2faab71

File tree

3 files changed

+46
-19
lines changed

3 files changed

+46
-19
lines changed

RELEASE_NOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717

1818
## Bug Fixes
1919

20-
<!-- Here goes notable bug fixes that are worth a special mention or explanation -->
20+
- Fixed a typing issue that occurs in some cases when composing formulas with constants.

src/frequenz/sdk/timeseries/formula_engine/_formula_engine.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -842,12 +842,12 @@ def _push(
842842

843843
@overload
844844
def __add__(
845-
self, other: _CompositionType1Phase
845+
self, other: _CompositionType1Phase | QuantityT
846846
) -> HigherOrderFormulaBuilder[QuantityT]: ...
847847

848848
@overload
849849
def __add__(
850-
self, other: _CompositionType3Phase | QuantityT
850+
self, other: _CompositionType3Phase
851851
) -> HigherOrderFormulaBuilder3Phase[QuantityT]: ...
852852

853853
def __add__(
@@ -870,12 +870,12 @@ def __add__(
870870

871871
@overload
872872
def __sub__(
873-
self, other: _CompositionType1Phase
873+
self, other: _CompositionType1Phase | QuantityT
874874
) -> HigherOrderFormulaBuilder[QuantityT]: ...
875875

876876
@overload
877877
def __sub__(
878-
self, other: _CompositionType3Phase | QuantityT
878+
self, other: _CompositionType3Phase
879879
) -> HigherOrderFormulaBuilder3Phase[QuantityT]: ...
880880

881881
def __sub__(
@@ -899,12 +899,12 @@ def __sub__(
899899

900900
@overload
901901
def __mul__(
902-
self, other: _CompositionType1Phase
902+
self, other: _CompositionType1Phase | float
903903
) -> HigherOrderFormulaBuilder[QuantityT]: ...
904904

905905
@overload
906906
def __mul__(
907-
self, other: _CompositionType3Phase | float
907+
self, other: _CompositionType3Phase
908908
) -> HigherOrderFormulaBuilder3Phase[QuantityT]: ...
909909

910910
def __mul__(
@@ -928,12 +928,12 @@ def __mul__(
928928

929929
@overload
930930
def __truediv__(
931-
self, other: _CompositionType1Phase
931+
self, other: _CompositionType1Phase | float
932932
) -> HigherOrderFormulaBuilder[QuantityT]: ...
933933

934934
@overload
935935
def __truediv__(
936-
self, other: _CompositionType3Phase | float
936+
self, other: _CompositionType3Phase
937937
) -> HigherOrderFormulaBuilder3Phase[QuantityT]: ...
938938

939939
def __truediv__(
@@ -957,12 +957,12 @@ def __truediv__(
957957

958958
@overload
959959
def max(
960-
self, other: _CompositionType1Phase
960+
self, other: _CompositionType1Phase | QuantityT
961961
) -> HigherOrderFormulaBuilder[QuantityT]: ...
962962

963963
@overload
964964
def max(
965-
self, other: _CompositionType3Phase | QuantityT
965+
self, other: _CompositionType3Phase
966966
) -> HigherOrderFormulaBuilder3Phase[QuantityT]: ...
967967

968968
def max(
@@ -985,12 +985,12 @@ def max(
985985

986986
@overload
987987
def min(
988-
self, other: _CompositionType1Phase
988+
self, other: _CompositionType1Phase | QuantityT
989989
) -> HigherOrderFormulaBuilder[QuantityT]: ...
990990

991991
@overload
992992
def min(
993-
self, other: _CompositionType3Phase | QuantityT
993+
self, other: _CompositionType3Phase
994994
) -> HigherOrderFormulaBuilder3Phase[QuantityT]: ...
995995

996996
def min(

tests/timeseries/_formula_engine/test_formula_composition.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from pytest_mock import MockerFixture
1313

1414
from frequenz.sdk import microgrid
15-
from frequenz.sdk.timeseries._quantities import Power
15+
from frequenz.sdk.timeseries import Power, Sample
1616

1717
from ..mock_microgrid import MockMicrogrid
1818
from .utils import get_resampled_stream
@@ -303,7 +303,10 @@ async def test_formula_composition_min_max_const(
303303
max_pow = await engine_max_rx.receive()
304304
assert max_pow and max_pow.value and max_pow.value.isclose(Power.zero())
305305

306-
async def test_formula_composition_constant(self, mocker: MockerFixture) -> None:
306+
async def test_formula_composition_constant( # pylint: disable=too-many-locals
307+
self,
308+
mocker: MockerFixture,
309+
) -> None:
307310
"""Test the composition of formulas with constant values."""
308311
async with (
309312
MockMicrogrid(grid_meter=True, mocker=mocker) as mockgrid,
@@ -334,40 +337,64 @@ async def test_formula_composition_constant(self, mocker: MockerFixture) -> None
334337
engine_div._stop # pylint: disable=protected-access
335338
)
336339

340+
engine_composite = (
341+
(
342+
(grid.power + Power.from_watts(50.0)) / 2.0
343+
+ grid.power
344+
- Power.from_watts(20.0)
345+
)
346+
* 2.0
347+
).build("grid_power_composite")
348+
337349
await mockgrid.mock_resampler.send_meter_power([100.0])
338350

339351
# Test addition
340-
grid_power_addition = await engine_add.new_receiver().receive()
352+
grid_power_addition: Sample[Power] = (
353+
await engine_add.new_receiver().receive()
354+
)
341355
assert grid_power_addition.value is not None
342356
assert math.isclose(
343357
grid_power_addition.value.as_watts(),
344358
150.0,
345359
)
346360

347361
# Test subtraction
348-
grid_power_subtraction = await engine_sub.new_receiver().receive()
362+
grid_power_subtraction: Sample[Power] = (
363+
await engine_sub.new_receiver().receive()
364+
)
349365
assert grid_power_subtraction.value is not None
350366
assert math.isclose(
351367
grid_power_subtraction.value.as_watts(),
352368
0.0,
353369
)
354370

355371
# Test multiplication
356-
grid_power_multiplication = await engine_mul.new_receiver().receive()
372+
grid_power_multiplication: Sample[Power] = (
373+
await engine_mul.new_receiver().receive()
374+
)
357375
assert grid_power_multiplication.value is not None
358376
assert math.isclose(
359377
grid_power_multiplication.value.as_watts(),
360378
200.0,
361379
)
362380

363381
# Test division
364-
grid_power_division = await engine_div.new_receiver().receive()
382+
grid_power_division: Sample[Power] = (
383+
await engine_div.new_receiver().receive()
384+
)
365385
assert grid_power_division.value is not None
366386
assert math.isclose(
367387
grid_power_division.value.as_watts(),
368388
50.0,
369389
)
370390

391+
# Test composite formula
392+
grid_power_composite: Sample[Power] = (
393+
await engine_composite.new_receiver().receive()
394+
)
395+
assert grid_power_composite.value is not None
396+
assert math.isclose(grid_power_composite.value.as_watts(), 310.0)
397+
371398
# Test multiplication with a Quantity
372399
with pytest.raises(RuntimeError):
373400
engine_assert = (grid.power * Power.from_watts(2.0)).build( # type: ignore

0 commit comments

Comments
 (0)