12
12
from abc import ABC
13
13
from collections import deque
14
14
from collections .abc import Callable
15
- from typing import Generic , TypeVar , Union , overload
15
+ from typing import Generic , SupportsFloat , TypeVar , Union , overload
16
16
17
17
from frequenz .channels import Broadcast , Receiver
18
18
40
40
41
41
_logger = logging .Logger (__name__ )
42
42
43
+ SupportsFloatInputT = TypeVar ("SupportsFloatInputT" , bound = SupportsFloat )
44
+ """Type variable for inputs that support conversion to float."""
45
+
46
+ SupportsFloatOutputT = TypeVar ("SupportsFloatOutputT" , bound = SupportsFloat )
47
+ """Type variable for outputs that support conversion to float."""
48
+
43
49
_operator_precedence = {
44
50
"max" : 0 ,
45
51
"min" : 1 ,
95
101
# but mypy doesn't support that, so we need to use `# type: ignore` in several places in
96
102
# this, and subsequent classes, to avoid mypy errors.
97
103
class _ComposableFormulaEngine (
98
- ABC , Generic [_GenericEngine , _GenericHigherOrderBuilder , SupportsFloatT ]
104
+ ABC ,
105
+ Generic [
106
+ _GenericEngine ,
107
+ _GenericHigherOrderBuilder ,
108
+ SupportsFloatInputT ,
109
+ SupportsFloatOutputT ,
110
+ ],
99
111
):
100
112
"""A base class for formula engines."""
101
113
102
- _create_method : Callable [[float ], SupportsFloatT ]
114
+ _create_method : Callable [[float ], SupportsFloatOutputT ]
103
115
_higher_order_builder : type [_GenericHigherOrderBuilder ]
104
116
_task : asyncio .Task [None ] | None = None
105
117
@@ -110,8 +122,7 @@ async def _stop(self) -> None:
110
122
await cancel_and_await (self ._task )
111
123
112
124
def __add__ (
113
- self ,
114
- other : _GenericEngine | _GenericHigherOrderBuilder | SupportsFloatT ,
125
+ self , other : _GenericEngine | _GenericHigherOrderBuilder | SupportsFloatInputT
115
126
) -> _GenericHigherOrderBuilder :
116
127
"""Return a formula builder that adds (data in) `other` to `self`.
117
128
@@ -126,7 +137,7 @@ def __add__(
126
137
return self ._higher_order_builder (self , self ._create_method ) + other # type: ignore
127
138
128
139
def __sub__ (
129
- self , other : _GenericEngine | _GenericHigherOrderBuilder | SupportsFloatT
140
+ self , other : _GenericEngine | _GenericHigherOrderBuilder | SupportsFloatInputT
130
141
) -> _GenericHigherOrderBuilder :
131
142
"""Return a formula builder that subtracts (data in) `other` from `self`.
132
143
@@ -171,7 +182,7 @@ def __truediv__(
171
182
return self ._higher_order_builder (self , self ._create_method ) / other # type: ignore
172
183
173
184
def max (
174
- self , other : _GenericEngine | _GenericHigherOrderBuilder | SupportsFloatT
185
+ self , other : _GenericEngine | _GenericHigherOrderBuilder | SupportsFloatInputT
175
186
) -> _GenericHigherOrderBuilder :
176
187
"""Return a formula engine that outputs the maximum of `self` and `other`.
177
188
@@ -186,7 +197,7 @@ def max(
186
197
return self ._higher_order_builder (self , self ._create_method ).max (other ) # type: ignore
187
198
188
199
def min (
189
- self , other : _GenericEngine | _GenericHigherOrderBuilder | SupportsFloatT
200
+ self , other : _GenericEngine | _GenericHigherOrderBuilder | SupportsFloatInputT
190
201
) -> _GenericHigherOrderBuilder :
191
202
"""Return a formula engine that outputs the minimum of `self` and `other`.
192
203
@@ -221,11 +232,11 @@ def production(self) -> _GenericHigherOrderBuilder:
221
232
222
233
223
234
class FormulaEngine (
224
- Generic [SupportsFloatT ],
235
+ Generic [SupportsFloatInputT , SupportsFloatOutputT ],
225
236
_ComposableFormulaEngine [
226
237
"FormulaEngine" , # type: ignore[type-arg]
227
238
"HigherOrderFormulaBuilder" , # type: ignore[type-arg]
228
- SupportsFloatT ,
239
+ SupportsFloatOutputT ,
229
240
],
230
241
):
231
242
"""[`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine]s are a
@@ -294,8 +305,8 @@ class FormulaEngine(
294
305
295
306
def __init__ (
296
307
self ,
297
- builder : FormulaBuilder [SupportsFloatT ],
298
- create_method : Callable [[float ], SupportsFloatT ],
308
+ builder : FormulaBuilder [SupportsFloatInputT , SupportsFloatOutputT ],
309
+ create_method : Callable [[float ], SupportsFloatOutputT ],
299
310
) -> None :
300
311
"""Create a `FormulaEngine` instance.
301
312
@@ -308,19 +319,21 @@ def __init__(
308
319
"""
309
320
self ._higher_order_builder = HigherOrderFormulaBuilder
310
321
self ._name : str = builder .name
311
- self ._builder : FormulaBuilder [SupportsFloatT ] = builder
322
+ self ._builder : FormulaBuilder [SupportsFloatInputT , SupportsFloatOutputT ] = (
323
+ builder
324
+ )
312
325
self ._create_method = create_method
313
- self ._channel : Broadcast [Sample [SupportsFloatT ]] = Broadcast (self ._name )
326
+ self ._channel : Broadcast [Sample [SupportsFloatInputT ]] = Broadcast (self ._name )
314
327
315
328
@classmethod
316
329
def from_receiver (
317
330
cls ,
318
331
name : str ,
319
- receiver : Receiver [Sample [SupportsFloatT ]],
320
- create_method : Callable [[float ], SupportsFloatT ],
332
+ receiver : Receiver [Sample [SupportsFloatInputT ]],
333
+ create_method : Callable [[float ], SupportsFloatOutputT ],
321
334
* ,
322
335
nones_are_zeros : bool = False ,
323
- ) -> FormulaEngine [SupportsFloatT ]:
336
+ ) -> FormulaEngine [SupportsFloatInputT , SupportsFloatOutputT ]:
324
337
"""
325
338
Create a formula engine from a receiver.
326
339
@@ -370,7 +383,7 @@ async def run() -> None:
370
383
async def _run (self ) -> None :
371
384
await self ._builder .subscribe ()
372
385
steps , metric_fetchers = self ._builder .finalize ()
373
- evaluator = FormulaEvaluator [SupportsFloatT ](
386
+ evaluator = FormulaEvaluator [SupportsFloatInputT , SupportsFloatOutputT ](
374
387
self ._name , steps , metric_fetchers , self ._create_method
375
388
)
376
389
sender = self ._channel .new_sender ()
@@ -556,7 +569,7 @@ def new_receiver(
556
569
return self ._channel .new_receiver (name , max_size )
557
570
558
571
559
- class FormulaBuilder (Generic [SupportsFloatT ]):
572
+ class FormulaBuilder (Generic [SupportsFloatInputT , SupportsFloatOutputT ]):
560
573
"""Builds a post-fix formula engine that operates on `Sample` receivers.
561
574
562
575
Operators and metrics need to be pushed in in-fix order, and they get rearranged
@@ -585,7 +598,7 @@ class FormulaBuilder(Generic[SupportsFloatT]):
585
598
"""
586
599
587
600
def __init__ (
588
- self , name : str , create_method : Callable [[float ], SupportsFloatT ]
601
+ self , name : str , create_method : Callable [[float ], SupportsFloatOutputT ]
589
602
) -> None :
590
603
"""Create a `FormulaBuilder` instance.
591
604
@@ -596,10 +609,10 @@ def __init__(
596
609
`Power.from_watts`, for example.
597
610
"""
598
611
self ._name = name
599
- self ._create_method : Callable [[float ], SupportsFloatT ] = create_method
612
+ self ._create_method : Callable [[float ], SupportsFloatOutputT ] = create_method
600
613
self ._build_stack : list [FormulaStep ] = []
601
614
self ._steps : list [FormulaStep ] = []
602
- self ._metric_fetchers : dict [str , MetricFetcher [SupportsFloatT ]] = {}
615
+ self ._metric_fetchers : dict [str , MetricFetcher [SupportsFloatInputT ]] = {}
603
616
604
617
def push_oper (self , oper : str ) -> None : # pylint: disable=too-many-branches
605
618
"""Push an operator into the engine.
@@ -643,7 +656,7 @@ def push_oper(self, oper: str) -> None: # pylint: disable=too-many-branches
643
656
def push_metric (
644
657
self ,
645
658
name : str ,
646
- data_stream : Receiver [Sample [SupportsFloatT ]],
659
+ data_stream : Receiver [Sample [SupportsFloatInputT ]],
647
660
* ,
648
661
nones_are_zeros : bool ,
649
662
) -> None :
@@ -735,7 +748,7 @@ async def subscribe(self) -> None:
735
748
736
749
def finalize (
737
750
self ,
738
- ) -> tuple [list [FormulaStep ], dict [str , MetricFetcher [SupportsFloatT ]]]:
751
+ ) -> tuple [list [FormulaStep ], dict [str , MetricFetcher [SupportsFloatInputT ]]]:
739
752
"""Finalize and return the steps and fetchers for the formula.
740
753
741
754
Returns:
@@ -755,7 +768,7 @@ def __str__(self) -> str:
755
768
steps = self ._steps if len (self ._steps ) > 0 else self ._build_stack
756
769
return format_formula (steps )
757
770
758
- def build (self ) -> FormulaEngine [SupportsFloatT ]:
771
+ def build (self ) -> FormulaEngine [SupportsFloatInputT , SupportsFloatOutputT ]:
759
772
"""Create a formula engine with the steps and fetchers that have been pushed.
760
773
761
774
Returns:
@@ -765,13 +778,16 @@ def build(self) -> FormulaEngine[SupportsFloatT]:
765
778
return FormulaEngine (self , create_method = self ._create_method )
766
779
767
780
768
- class _BaseHOFormulaBuilder (ABC , Generic [SupportsFloatT ]):
781
+ class _BaseHOFormulaBuilder (ABC , Generic [SupportsFloatInputT , SupportsFloatOutputT ]):
769
782
"""Provides a way to build formulas from the outputs of other formulas."""
770
783
771
784
def __init__ (
772
785
self ,
773
- engine : FormulaEngine [SupportsFloatT ] | FormulaEngine3Phase [SupportsFloatT ],
774
- create_method : Callable [[float ], SupportsFloatT ],
786
+ engine : (
787
+ FormulaEngine [SupportsFloatInputT , SupportsFloatOutputT ]
788
+ | FormulaEngine3Phase [SupportsFloatT ]
789
+ ),
790
+ create_method : Callable [[float ], SupportsFloatOutputT ],
775
791
) -> None :
776
792
"""Create a `GenericHigherOrderFormulaBuilder` instance.
777
793
@@ -785,20 +801,20 @@ def __init__(
785
801
self ._steps : deque [
786
802
tuple [
787
803
TokenType ,
788
- FormulaEngine [SupportsFloatT ]
804
+ FormulaEngine [SupportsFloatInputT , SupportsFloatOutputT ]
789
805
| FormulaEngine3Phase [SupportsFloatT ]
790
806
| Quantity
791
807
| float
792
808
| str ,
793
809
]
794
810
] = deque ()
795
811
self ._steps .append ((TokenType .COMPONENT_METRIC , engine ))
796
- self ._create_method : Callable [[float ], SupportsFloatT ] = create_method
812
+ self ._create_method : Callable [[float ], SupportsFloatOutputT ] = create_method
797
813
798
814
@overload
799
815
def _push (
800
816
self , oper : str , other : _CompositionType1Phase
801
- ) -> HigherOrderFormulaBuilder [SupportsFloatT ]: ...
817
+ ) -> HigherOrderFormulaBuilder [SupportsFloatInputT , SupportsFloatOutputT ]: ...
802
818
803
819
@overload
804
820
def _push (
@@ -1061,13 +1077,13 @@ def production(
1061
1077
1062
1078
1063
1079
class HigherOrderFormulaBuilder (
1064
- Generic [ SupportsFloatT ], _BaseHOFormulaBuilder [ SupportsFloatT ]
1080
+ _BaseHOFormulaBuilder [ SupportsFloatInputT , SupportsFloatT ]
1065
1081
):
1066
1082
"""A specialization of the _BaseHOFormulaBuilder for `FormulaReceiver`."""
1067
1083
1068
1084
def build (
1069
1085
self , name : str , * , nones_are_zeros : bool = False
1070
- ) -> FormulaEngine [SupportsFloatT ]:
1086
+ ) -> FormulaEngine [SupportsFloatInputT , SupportsFloatT ]:
1071
1087
"""Build a `FormulaEngine` instance from the builder.
1072
1088
1073
1089
Args:
@@ -1099,7 +1115,7 @@ def build(
1099
1115
1100
1116
1101
1117
class HigherOrderFormulaBuilder3Phase (
1102
- Generic [ SupportsFloatT ], _BaseHOFormulaBuilder [ SupportsFloatT ]
1118
+ _BaseHOFormulaBuilder [ SupportsFloatInputT , SupportsFloatT ]
1103
1119
):
1104
1120
"""A specialization of the _BaseHOFormulaBuilder for `FormulaReceiver3Phase`."""
1105
1121
0 commit comments