Skip to content

Commit 7f647fa

Browse files
Make nones_are_zeros a keyword-only parameter (#611)
Accept keyword-only argument `nones_are_zeros` in `FormulaEngine` and related classes and methods in order to clarify behavior at the caller's end, eliminating the need to inspect function definitions for understanding. Fixes #100
2 parents 1fe5cb8 + 4a92db7 commit 7f647fa

File tree

7 files changed

+51
-22
lines changed

7 files changed

+51
-22
lines changed

RELEASE_NOTES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ This release replaces the `@actor` decorator with a new `Actor` class.
9090

9191
- Within the `EVChargerPool.set_bounds` method, the parameter `max_amps` has been redefined as `max_current`, and it is now represented using a `Current` object instead of a `float`.
9292

93+
- The argument `nones_are_zeros` in `FormulaEngine` and related classes and methods is now a keyword-only argument.
94+
9395
## New Features
9496

9597
- Added `DFS` to the component graph

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ def from_receiver(
209209
name: str,
210210
receiver: Receiver[Sample[QuantityT]],
211211
create_method: Callable[[float], QuantityT],
212+
*,
212213
nones_are_zeros: bool = False,
213214
) -> FormulaEngine[QuantityT]:
214215
"""
@@ -471,6 +472,7 @@ def push_metric(
471472
self,
472473
name: str,
473474
data_stream: Receiver[Sample[QuantityT]],
475+
*,
474476
nones_are_zeros: bool,
475477
) -> None:
476478
"""Push a metric receiver into the engine.
@@ -482,7 +484,7 @@ def push_metric(
482484
False, the returned value will be a None.
483485
"""
484486
fetcher = self._metric_fetchers.setdefault(
485-
name, MetricFetcher(name, data_stream, nones_are_zeros)
487+
name, MetricFetcher(name, data_stream, nones_are_zeros=nones_are_zeros)
486488
)
487489
self._steps.append(fetcher)
488490

@@ -554,7 +556,8 @@ def push_average(
554556
fetchers: List[MetricFetcher[QuantityT]] = []
555557
for metric in metrics:
556558
fetcher = self._metric_fetchers.setdefault(
557-
metric[0], MetricFetcher(*metric)
559+
metric[0],
560+
MetricFetcher(metric[0], metric[1], nones_are_zeros=metric[2]),
558561
)
559562
fetchers.append(fetcher)
560563
self._steps.append(Averager(fetchers))
@@ -806,7 +809,7 @@ class HigherOrderFormulaBuilder(Generic[QuantityT], _BaseHOFormulaBuilder[Quanti
806809
"""A specialization of the _BaseHOFormulaBuilder for `FormulaReceiver`."""
807810

808811
def build(
809-
self, name: str, nones_are_zeros: bool = False
812+
self, name: str, *, nones_are_zeros: bool = False
810813
) -> FormulaEngine[QuantityT]:
811814
"""Build a `FormulaEngine` instance from the builder.
812815
@@ -825,7 +828,7 @@ def build(
825828
builder.push_metric(
826829
value._name, # pylint: disable=protected-access
827830
value.new_receiver(),
828-
nones_are_zeros,
831+
nones_are_zeros=nones_are_zeros,
829832
)
830833
elif typ == TokenType.OPER:
831834
assert isinstance(value, str)
@@ -844,7 +847,7 @@ class HigherOrderFormulaBuilder3Phase(
844847
"""A specialization of the _BaseHOFormulaBuilder for `FormulaReceiver3Phase`."""
845848

846849
def build(
847-
self, name: str, nones_are_zeros: bool = False
850+
self, name: str, *, nones_are_zeros: bool = False
848851
) -> FormulaEngine3Phase[QuantityT]:
849852
"""Build a `FormulaEngine3Phase` instance from the builder.
850853
@@ -870,7 +873,7 @@ def build(
870873
value._streams[ # pylint: disable=protected-access
871874
phase
872875
].new_receiver(),
873-
nones_are_zeros,
876+
nones_are_zeros=nones_are_zeros,
874877
)
875878
elif typ == TokenType.OPER:
876879
assert isinstance(value, str)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def from_string(
5555
self,
5656
formula: str,
5757
component_metric_id: ComponentMetricId,
58+
*,
5859
nones_are_zeros: bool = False,
5960
) -> FormulaEngine[Quantity]:
6061
"""Get a receiver for a manual formula.
@@ -81,7 +82,7 @@ def from_string(
8182
component_metric_id,
8283
Quantity,
8384
)
84-
formula_engine = builder.from_string(formula, nones_are_zeros)
85+
formula_engine = builder.from_string(formula, nones_are_zeros=nones_are_zeros)
8586
self._string_engines[channel_key] = formula_engine
8687

8788
return formula_engine

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ def __init__(
268268
self,
269269
name: str,
270270
stream: Receiver[Sample[QuantityT]],
271+
*,
271272
nones_are_zeros: bool,
272273
) -> None:
273274
"""Create a `MetricFetcher` instance.

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,12 @@ def push_component_metric(
8484
False, the returned value will be a None.
8585
"""
8686
receiver = self._get_resampled_receiver(component_id, self._metric_id)
87-
self.push_metric(f"#{component_id}", receiver, nones_are_zeros)
87+
self.push_metric(f"#{component_id}", receiver, nones_are_zeros=nones_are_zeros)
8888

8989
def from_string(
9090
self,
9191
formula: str,
92+
*,
9293
nones_are_zeros: bool,
9394
) -> FormulaEngine[QuantityT]:
9495
"""Construct a `FormulaEngine` from the given formula string.

src/frequenz/sdk/timeseries/logical_meter/_logical_meter.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ def start_formula(
123123
self,
124124
formula: str,
125125
component_metric_id: ComponentMetricId,
126+
*,
126127
nones_are_zeros: bool = False,
127128
) -> FormulaEngine[Quantity]:
128129
"""Start execution of the given formula.
@@ -144,7 +145,7 @@ def start_formula(
144145
A FormulaEngine that applies the formula and streams values.
145146
"""
146147
return self._formula_pool.from_string(
147-
formula, component_metric_id, nones_are_zeros
148+
formula, component_metric_id, nones_are_zeros=nones_are_zeros
148149
)
149150

150151
@property

tests/timeseries/test_formula_engine.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ async def run_test( # pylint: disable=too-many-locals
6565
builder.push_metric(
6666
f"#{token.value}",
6767
channels[token.value].new_receiver(),
68-
nones_are_zeros,
68+
nones_are_zeros=nones_are_zeros,
6969
)
7070
elif token.type == TokenType.OPER:
7171
builder.push_oper(token.value)
@@ -345,7 +345,7 @@ async def run_test( # pylint: disable=too-many-locals
345345
for ctr in range(num_items)
346346
]
347347
builder = make_builder(*l1_engines)
348-
engine = builder.build("l2 formula", nones_are_zeros)
348+
engine = builder.build("l2 formula", nones_are_zeros=nones_are_zeros)
349349
result_chan = engine.new_receiver()
350350

351351
now = datetime.now()
@@ -651,11 +651,15 @@ async def test_constant_value(self) -> None:
651651
sender_2 = channel_2.new_sender()
652652

653653
builder = FormulaBuilder("test_constant_value", create_method=Quantity)
654-
builder.push_metric("channel_1", channel_1.new_receiver(), False)
654+
builder.push_metric(
655+
"channel_1", channel_1.new_receiver(), nones_are_zeros=False
656+
)
655657
builder.push_oper("+")
656658
builder.push_constant(2.0)
657659
builder.push_oper("*")
658-
builder.push_metric("channel_2", channel_2.new_receiver(), False)
660+
builder.push_metric(
661+
"channel_2", channel_2.new_receiver(), nones_are_zeros=False
662+
)
659663

660664
engine = builder.build()
661665

@@ -672,12 +676,16 @@ async def test_constant_value(self) -> None:
672676

673677
builder = FormulaBuilder("test_constant_value", create_method=Quantity)
674678
builder.push_oper("(")
675-
builder.push_metric("channel_1", channel_1.new_receiver(), False)
679+
builder.push_metric(
680+
"channel_1", channel_1.new_receiver(), nones_are_zeros=False
681+
)
676682
builder.push_oper("+")
677683
builder.push_constant(2.0)
678684
builder.push_oper(")")
679685
builder.push_oper("*")
680-
builder.push_metric("channel_2", channel_2.new_receiver(), False)
686+
builder.push_metric(
687+
"channel_2", channel_2.new_receiver(), nones_are_zeros=False
688+
)
681689

682690
engine = builder.build()
683691

@@ -705,9 +713,13 @@ async def test_clipper(self) -> None:
705713
sender_2 = channel_2.new_sender()
706714

707715
builder = FormulaBuilder("test_clipper", create_method=Quantity)
708-
builder.push_metric("channel_1", channel_1.new_receiver(), False)
716+
builder.push_metric(
717+
"channel_1", channel_1.new_receiver(), nones_are_zeros=False
718+
)
709719
builder.push_oper("+")
710-
builder.push_metric("channel_2", channel_2.new_receiver(), False)
720+
builder.push_metric(
721+
"channel_2", channel_2.new_receiver(), nones_are_zeros=False
722+
)
711723
builder.push_clipper(0.0, 100.0)
712724
engine = builder.build()
713725

@@ -728,9 +740,13 @@ async def test_clipper(self) -> None:
728740

729741
builder = FormulaBuilder("test_clipper", create_method=Quantity)
730742
builder.push_oper("(")
731-
builder.push_metric("channel_1", channel_1.new_receiver(), False)
743+
builder.push_metric(
744+
"channel_1", channel_1.new_receiver(), nones_are_zeros=False
745+
)
732746
builder.push_oper("+")
733-
builder.push_metric("channel_2", channel_2.new_receiver(), False)
747+
builder.push_metric(
748+
"channel_2", channel_2.new_receiver(), nones_are_zeros=False
749+
)
734750
builder.push_oper(")")
735751
builder.push_clipper(0.0, 100.0)
736752
engine = builder.build()
@@ -763,9 +779,13 @@ async def test_types(self) -> None:
763779
sender_2 = channel_2.new_sender()
764780

765781
builder = FormulaBuilder("test_typing", create_method=Power.from_watts)
766-
builder.push_metric("channel_1", channel_1.new_receiver(), False)
782+
builder.push_metric(
783+
"channel_1", channel_1.new_receiver(), nones_are_zeros=False
784+
)
767785
builder.push_oper("+")
768-
builder.push_metric("channel_2", channel_2.new_receiver(), False)
786+
builder.push_metric(
787+
"channel_2", channel_2.new_receiver(), nones_are_zeros=False
788+
)
769789
engine = builder.build()
770790

771791
results_rx = engine.new_receiver()
@@ -787,7 +807,7 @@ async def test_from_receiver(self) -> None:
787807
sender = channel.new_sender()
788808

789809
builder = FormulaBuilder("test_from_receiver", create_method=Power.from_watts)
790-
builder.push_metric("channel_1", channel.new_receiver(), False)
810+
builder.push_metric("channel_1", channel.new_receiver(), nones_are_zeros=False)
791811
engine = builder.build()
792812

793813
engine_from_receiver = FormulaEngine.from_receiver(

0 commit comments

Comments
 (0)