Skip to content

Commit 5eb5aa6

Browse files
committed
Make Quantity more directly not default constructible
We are going to make `Quantity` a abstract base class, so we don't need to do the trick to only allow to call the default constructor for the `Quantity` class but not sub-classes. We just make `Quantity.__init__()` always raise a `TypeError`, as subclasses can use `_new()` to create instances. Signed-off-by: Leandro Lucarella <luca-frequenz@llucax.com>
1 parent cb458ea commit 5eb5aa6

File tree

1 file changed

+20
-34
lines changed

1 file changed

+20
-34
lines changed

src/frequenz/sdk/timeseries/_quantities.py

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import math
1111
from datetime import timedelta
12-
from typing import Any, NoReturn, Self, TypeVar, overload
12+
from typing import Self, TypeVar, overload
1313

1414
QuantityT = TypeVar(
1515
"QuantityT",
@@ -41,14 +41,27 @@ class Quantity:
4141
class. Sub-classes must define this.
4242
"""
4343

44-
def __init__(self, value: float, exponent: int = 0) -> None:
45-
"""Initialize a new quantity.
44+
def __init__(self) -> None:
45+
"""Initialize this instance.
4646
47-
Args:
48-
value: The value of this quantity in a given exponent of the base unit.
49-
exponent: The exponent of the base unit the given value is in.
47+
This constructor is not meant to be used directly. Use one of the `from_*()`
48+
methods instead.
49+
50+
Raises:
51+
TypeError: Every time this constructor is called.
5052
"""
51-
self._base_value = value * 10.0**exponent
53+
# This is for documentation purposes and to hint to mypy that this
54+
# attribute exists.
55+
self._base_value: float = 0.0
56+
"""The value of this quantity in the base unit."""
57+
58+
cls = type(self)
59+
raise TypeError(
60+
"Use of default constructor is not allowed for "
61+
f"{cls.__module__}.{cls.__qualname__}, "
62+
f"use {cls.__name__}.zero() or one of the "
63+
f"`{cls.__name__}.from_*()` constructors instead."
64+
)
5265

5366
@classmethod
5467
def _new(cls, value: float, *, exponent: int = 0) -> Self:
@@ -451,29 +464,8 @@ def __abs__(self) -> Self:
451464
return absolute
452465

453466

454-
class _NoDefaultConstructible(type):
455-
"""A metaclass that disables the default constructor."""
456-
457-
def __call__(cls, *_args: Any, **_kwargs: Any) -> NoReturn:
458-
"""Raise a TypeError when the default constructor is called.
459-
460-
Args:
461-
*_args: ignored positional arguments.
462-
**_kwargs: ignored keyword arguments.
463-
464-
Raises:
465-
TypeError: Always.
466-
"""
467-
raise TypeError(
468-
"Use of default constructor NOT allowed for "
469-
f"{cls.__module__}.{cls.__qualname__}, "
470-
f"use one of the `{cls.__name__}.from_*()` methods instead."
471-
)
472-
473-
474467
class Temperature(
475468
Quantity,
476-
metaclass=_NoDefaultConstructible,
477469
exponent_unit_map={
478470
0: "°C",
479471
},
@@ -503,7 +495,6 @@ def as_celsius(self) -> float:
503495

504496
class Power(
505497
Quantity,
506-
metaclass=_NoDefaultConstructible,
507498
exponent_unit_map={
508499
-3: "mW",
509500
0: "W",
@@ -679,7 +670,6 @@ def __truediv__(self, other: Current | Voltage) -> Voltage | Current:
679670

680671
class Current(
681672
Quantity,
682-
metaclass=_NoDefaultConstructible,
683673
exponent_unit_map={
684674
-3: "mA",
685675
0: "A",
@@ -777,7 +767,6 @@ def __mul__(self, other: Percentage | Voltage) -> Self | Power:
777767

778768
class Voltage(
779769
Quantity,
780-
metaclass=_NoDefaultConstructible,
781770
exponent_unit_map={0: "V", -3: "mV", 3: "kV"},
782771
):
783772
"""A voltage quantity.
@@ -892,7 +881,6 @@ def __mul__(self, other: Percentage | Current) -> Self | Power:
892881

893882
class Energy(
894883
Quantity,
895-
metaclass=_NoDefaultConstructible,
896884
exponent_unit_map={
897885
0: "Wh",
898886
3: "kWh",
@@ -1032,7 +1020,6 @@ def __truediv__(self, other: timedelta | Power) -> Power | timedelta:
10321020

10331021
class Frequency(
10341022
Quantity,
1035-
metaclass=_NoDefaultConstructible,
10361023
exponent_unit_map={0: "Hz", 3: "kHz", 6: "MHz", 9: "GHz"},
10371024
):
10381025
"""A frequency quantity.
@@ -1137,7 +1124,6 @@ def period(self) -> timedelta:
11371124

11381125
class Percentage(
11391126
Quantity,
1140-
metaclass=_NoDefaultConstructible,
11411127
exponent_unit_map={0: "%"},
11421128
):
11431129
"""A percentage quantity.

0 commit comments

Comments
 (0)