Skip to content

Commit c1a4668

Browse files
committed
Use keyword-only arguments for AST node dataclasses
Also use `None` as a default value for span, to avoid having to type it when constructing higher level formulas. Signed-off-by: Sahas Subramanian <[email protected]>
1 parent c953071 commit c1a4668

File tree

3 files changed

+47
-52
lines changed

3 files changed

+47
-52
lines changed

src/frequenz/sdk/timeseries/formulas/_ast.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
_logger = logging.getLogger(__name__)
2222

2323

24-
@dataclass
24+
@dataclass(kw_only=True)
2525
class Node(abc.ABC):
2626
"""An abstract syntax tree node representing a formula expression."""
2727

28-
span: tuple[int, int] | None
28+
span: tuple[int, int] | None = None
2929

3030
@abc.abstractmethod
3131
def evaluate(self) -> float | None:
@@ -41,7 +41,7 @@ def __str__(self) -> str:
4141
return self.format()
4242

4343

44-
@dataclass
44+
@dataclass(kw_only=True)
4545
class TelemetryStream(Node, Generic[QuantityT]):
4646
"""A AST node that retrieves values from a component's telemetry stream."""
4747

@@ -73,7 +73,7 @@ async def fetch_next(self) -> None:
7373
self._latest_sample = await anext(self.stream)
7474

7575

76-
@dataclass
76+
@dataclass(kw_only=True)
7777
class FunCall(Node):
7878
"""A function call in the formula."""
7979

@@ -92,7 +92,7 @@ def format(self, wrap: bool = False) -> str:
9292
return f"{self.function.name}({args_str})"
9393

9494

95-
@dataclass
95+
@dataclass(kw_only=True)
9696
class Constant(Node):
9797
"""A constant numerical value in the formula."""
9898

@@ -109,7 +109,7 @@ def format(self, wrap: bool = False) -> str:
109109
return str(self.value)
110110

111111

112-
@dataclass
112+
@dataclass(kw_only=True)
113113
class Add(Node):
114114
"""Addition operation node."""
115115

@@ -134,7 +134,7 @@ def format(self, wrap: bool = False) -> str:
134134
return expr
135135

136136

137-
@dataclass
137+
@dataclass(kw_only=True)
138138
class Sub(Node):
139139
"""Subtraction operation node."""
140140

@@ -159,7 +159,7 @@ def format(self, wrap: bool = False) -> str:
159159
return expr
160160

161161

162-
@dataclass
162+
@dataclass(kw_only=True)
163163
class Mul(Node):
164164
"""Multiplication operation node."""
165165

@@ -181,7 +181,7 @@ def format(self, wrap: bool = False) -> str:
181181
return f"{self.left.format(True)} * {self.right.format(True)}"
182182

183183

184-
@dataclass
184+
@dataclass(kw_only=True)
185185
class Div(Node):
186186
"""Division operation node."""
187187

src/frequenz/sdk/timeseries/formulas/_formula.py

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,8 @@ def __init__(
177177

178178
if isinstance(formula, Formula):
179179
self.root: _ast.Node = _ast.TelemetryStream(
180-
None,
181-
str(formula),
182-
formula.new_receiver(),
180+
source=str(formula),
181+
stream=formula.new_receiver(),
183182
)
184183
self._streams.append(self.root)
185184
self._sub_formulas.append(formula)
@@ -195,13 +194,16 @@ def __add__(
195194
right_node = other.root
196195
self._streams.extend(other._streams)
197196
elif isinstance(other, Formula):
198-
right_node = _ast.TelemetryStream(None, str(other), other.new_receiver())
197+
right_node = _ast.TelemetryStream(
198+
source=str(other),
199+
stream=other.new_receiver(),
200+
)
199201
self._streams.append(right_node)
200202
self._sub_formulas.append(other)
201203
else:
202-
right_node = _ast.Constant(None, other.base_value)
204+
right_node = _ast.Constant(value=other.base_value)
203205

204-
new_root = _ast.Add(None, self.root, right_node)
206+
new_root = _ast.Add(left=self.root, right=right_node)
205207
return FormulaBuilder(
206208
new_root,
207209
self._create_method,
@@ -218,13 +220,15 @@ def __sub__(
218220
right_node = other.root
219221
self._streams.extend(other._streams)
220222
elif isinstance(other, Formula):
221-
right_node = _ast.TelemetryStream(None, str(other), other.new_receiver())
223+
right_node = _ast.TelemetryStream(
224+
source=str(other), stream=other.new_receiver()
225+
)
222226
self._streams.append(right_node)
223227
self._sub_formulas.append(other)
224228
else:
225-
right_node = _ast.Constant(None, other.base_value)
229+
right_node = _ast.Constant(value=other.base_value)
226230

227-
new_root = _ast.Sub(None, self.root, right_node)
231+
new_root = _ast.Sub(left=self.root, right=right_node)
228232
return FormulaBuilder(
229233
new_root,
230234
self._create_method,
@@ -234,8 +238,8 @@ def __sub__(
234238

235239
def __mul__(self, other: float) -> FormulaBuilder[QuantityT]:
236240
"""Create a multiplication operation node."""
237-
right_node = _ast.Constant(None, other)
238-
new_root = _ast.Mul(None, self.root, right_node)
241+
right_node = _ast.Constant(value=other)
242+
new_root = _ast.Mul(left=self.root, right=right_node)
239243
return FormulaBuilder(
240244
new_root,
241245
self._create_method,
@@ -248,8 +252,8 @@ def __truediv__(
248252
other: float,
249253
) -> FormulaBuilder[QuantityT]:
250254
"""Create a division operation node."""
251-
right_node = _ast.Constant(None, other)
252-
new_root = _ast.Div(None, self.root, right_node)
255+
right_node = _ast.Constant(value=other)
256+
new_root = _ast.Div(left=self.root, right=right_node)
253257
return FormulaBuilder(
254258
new_root,
255259
self._create_method,
@@ -269,20 +273,18 @@ def coalesce(
269273
self._streams.extend(item._streams) # pylint: disable=protected-access
270274
elif isinstance(item, Formula):
271275
right_node = _ast.TelemetryStream(
272-
None,
273-
str(item),
274-
item.new_receiver(),
276+
source=str(item),
277+
stream=item.new_receiver(),
275278
)
276279
right_nodes.append(right_node)
277280
self._streams.append(right_node)
278281
self._sub_formulas.append(item)
279282
else:
280-
right_nodes.append(_ast.Constant(None, item.base_value))
283+
right_nodes.append(_ast.Constant(value=item.base_value))
281284

282285
new_root = _ast.FunCall(
283-
None,
284-
Coalesce(),
285-
[self.root] + right_nodes,
286+
function=Coalesce(),
287+
args=[self.root] + right_nodes,
286288
)
287289

288290
return FormulaBuilder(
@@ -304,20 +306,18 @@ def min(
304306
self._streams.extend(item._streams) # pylint: disable=protected-access
305307
elif isinstance(item, Formula):
306308
right_node = _ast.TelemetryStream(
307-
None,
308-
str(item),
309-
item.new_receiver(),
309+
source=str(item),
310+
stream=item.new_receiver(),
310311
)
311312
right_nodes.append(right_node)
312313
self._streams.append(right_node)
313314
self._sub_formulas.append(item)
314315
else:
315-
right_nodes.append(_ast.Constant(None, item.base_value))
316+
right_nodes.append(_ast.Constant(value=item.base_value))
316317

317318
new_root = _ast.FunCall(
318-
None,
319-
Min(),
320-
[self.root] + right_nodes,
319+
function=Min(),
320+
args=[self.root] + right_nodes,
321321
)
322322

323323
return FormulaBuilder(
@@ -339,20 +339,18 @@ def max(
339339
self._streams.extend(item._streams) # pylint: disable=protected-access
340340
elif isinstance(item, Formula):
341341
right_node = _ast.TelemetryStream(
342-
None,
343-
str(item),
344-
item.new_receiver(),
342+
source=str(item),
343+
stream=item.new_receiver(),
345344
)
346345
right_nodes.append(right_node)
347346
self._streams.append(right_node)
348347
self._sub_formulas.append(item)
349348
else:
350-
right_nodes.append(_ast.Constant(None, item.base_value))
349+
right_nodes.append(_ast.Constant(value=item.base_value))
351350

352351
new_root = _ast.FunCall(
353-
None,
354-
Max(),
355-
[self.root] + right_nodes,
352+
function=Max(),
353+
args=[self.root] + right_nodes,
356354
)
357355

358356
return FormulaBuilder(

src/frequenz/sdk/timeseries/formulas/_formula_3_phase_evaluator.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,16 @@ def __init__(
4444
self._phase_3_formula: Formula[QuantityT] = phase_3
4545
self._components: list[_ast.TelemetryStream[QuantityT]] = [
4646
_ast.TelemetryStream(
47-
None,
48-
"phase_1",
49-
phase_1.new_receiver(),
47+
source="phase_1",
48+
stream=phase_1.new_receiver(),
5049
),
5150
_ast.TelemetryStream(
52-
None,
53-
"phase_2",
54-
phase_2.new_receiver(),
51+
source="phase_2",
52+
stream=phase_2.new_receiver(),
5553
),
5654
_ast.TelemetryStream(
57-
None,
58-
"phase_3",
59-
phase_3.new_receiver(),
55+
source="phase_3",
56+
stream=phase_3.new_receiver(),
6057
),
6158
]
6259
self._output_channel: Broadcast[Sample3Phase[QuantityT]] = output_channel

0 commit comments

Comments
 (0)