frequenz-quantities 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. frequenz-quantities-1.0.0/LICENSE +21 -0
  2. frequenz-quantities-1.0.0/MANIFEST.in +13 -0
  3. frequenz-quantities-1.0.0/PKG-INFO +68 -0
  4. frequenz-quantities-1.0.0/README.md +36 -0
  5. frequenz-quantities-1.0.0/RELEASE_NOTES.md +14 -0
  6. frequenz-quantities-1.0.0/pyproject.toml +180 -0
  7. frequenz-quantities-1.0.0/setup.cfg +4 -0
  8. frequenz-quantities-1.0.0/src/frequenz/quantities/__init__.py +112 -0
  9. frequenz-quantities-1.0.0/src/frequenz/quantities/_apparent_power.py +234 -0
  10. frequenz-quantities-1.0.0/src/frequenz/quantities/_current.py +131 -0
  11. frequenz-quantities-1.0.0/src/frequenz/quantities/_energy.py +188 -0
  12. frequenz-quantities-1.0.0/src/frequenz/quantities/_frequency.py +115 -0
  13. frequenz-quantities-1.0.0/src/frequenz/quantities/_percentage.py +66 -0
  14. frequenz-quantities-1.0.0/src/frequenz/quantities/_power.py +244 -0
  15. frequenz-quantities-1.0.0/src/frequenz/quantities/_quantity.py +541 -0
  16. frequenz-quantities-1.0.0/src/frequenz/quantities/_reactive_power.py +235 -0
  17. frequenz-quantities-1.0.0/src/frequenz/quantities/_temperature.py +39 -0
  18. frequenz-quantities-1.0.0/src/frequenz/quantities/_voltage.py +148 -0
  19. frequenz-quantities-1.0.0/src/frequenz/quantities/conftest.py +13 -0
  20. frequenz-quantities-1.0.0/src/frequenz/quantities/experimental/__init__.py +11 -0
  21. frequenz-quantities-1.0.0/src/frequenz/quantities/experimental/marshmallow.py +271 -0
  22. frequenz-quantities-1.0.0/src/frequenz/quantities/py.typed +0 -0
  23. frequenz-quantities-1.0.0/src/frequenz_quantities.egg-info/PKG-INFO +68 -0
  24. frequenz-quantities-1.0.0/src/frequenz_quantities.egg-info/SOURCES.txt +25 -0
  25. frequenz-quantities-1.0.0/src/frequenz_quantities.egg-info/dependency_links.txt +1 -0
  26. frequenz-quantities-1.0.0/src/frequenz_quantities.egg-info/requires.txt +53 -0
  27. frequenz-quantities-1.0.0/src/frequenz_quantities.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright © 2024 Frequenz Energy-as-a-Service GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,13 @@
1
+ exclude .cookiecutter-replay.json
2
+ exclude .editorconfig
3
+ exclude .gitignore
4
+ exclude CODEOWNERS
5
+ exclude CONTRIBUTING.md
6
+ exclude mkdocs.yml
7
+ exclude noxfile.py
8
+ exclude src/conftest.py
9
+ recursive-exclude .github *
10
+ recursive-exclude benchmarks *
11
+ recursive-exclude docs *
12
+ recursive-exclude tests *
13
+ recursive-include py *.pyi
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.1
2
+ Name: frequenz-quantities
3
+ Version: 1.0.0
4
+ Summary: Types for holding quantities with units
5
+ Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
6
+ License: MIT
7
+ Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-quantities-python/
8
+ Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-quantities-python/releases
9
+ Project-URL: Issues, https://github.com/frequenz-floss/frequenz-quantities-python/issues
10
+ Project-URL: Repository, https://github.com/frequenz-floss/frequenz-quantities-python
11
+ Project-URL: Support, https://github.com/frequenz-floss/frequenz-quantities-python/discussions/categories/support
12
+ Keywords: frequenz,python,lib,library,quantities,unit,conversion
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: <4,>=3.11
21
+ Description-Content-Type: text/markdown
22
+ Provides-Extra: dev-flake8
23
+ Provides-Extra: dev-formatting
24
+ Provides-Extra: dev-mkdocs
25
+ Provides-Extra: dev-mypy
26
+ Provides-Extra: dev-noxfile
27
+ Provides-Extra: dev-pylint
28
+ Provides-Extra: dev-pytest
29
+ Provides-Extra: marshmallow
30
+ Provides-Extra: dev
31
+ License-File: LICENSE
32
+
33
+ # Frequenz Quantities Library
34
+
35
+ [![Build Status](https://github.com/frequenz-floss/frequenz-quantities-python/actions/workflows/ci.yaml/badge.svg)](https://github.com/frequenz-floss/frequenz-quantities-python/actions/workflows/ci.yaml)
36
+ [![PyPI Package](https://img.shields.io/pypi/v/frequenz-quantities)](https://pypi.org/project/frequenz-quantities/)
37
+ [![Docs](https://img.shields.io/badge/docs-latest-informational)](https://frequenz-floss.github.io/frequenz-quantities-python/)
38
+
39
+ ## Introduction
40
+
41
+ This library provide types for holding quantities with units. The main goal is
42
+ to avoid mistakes while working with different types of quantities, for example
43
+ avoiding adding a length to a time.
44
+
45
+ It also prevents mistakes when operating between the same quantity but in
46
+ different units, like adding a power in Joules to a power in Watts without
47
+ converting one of them.
48
+
49
+ Quantities store the value in a base unit, and then provide methods to get that
50
+ quantity as a particular unit.
51
+
52
+ ## Documentation
53
+
54
+ For more information on how to use this library and examples, please check the
55
+ [Documentation website](https://frequenz-floss.github.io/frequenz-quantities-python/).
56
+
57
+ ## Supported Platforms
58
+
59
+ The following platforms are officially supported (tested):
60
+
61
+ - **Python:** 3.11
62
+ - **Operating System:** Ubuntu Linux 20.04
63
+ - **Architectures:** amd64, arm64
64
+
65
+ ## Contributing
66
+
67
+ If you want to know how to build this project and contribute to it, please
68
+ check out the [Contributing Guide](CONTRIBUTING.md).
@@ -0,0 +1,36 @@
1
+ # Frequenz Quantities Library
2
+
3
+ [![Build Status](https://github.com/frequenz-floss/frequenz-quantities-python/actions/workflows/ci.yaml/badge.svg)](https://github.com/frequenz-floss/frequenz-quantities-python/actions/workflows/ci.yaml)
4
+ [![PyPI Package](https://img.shields.io/pypi/v/frequenz-quantities)](https://pypi.org/project/frequenz-quantities/)
5
+ [![Docs](https://img.shields.io/badge/docs-latest-informational)](https://frequenz-floss.github.io/frequenz-quantities-python/)
6
+
7
+ ## Introduction
8
+
9
+ This library provide types for holding quantities with units. The main goal is
10
+ to avoid mistakes while working with different types of quantities, for example
11
+ avoiding adding a length to a time.
12
+
13
+ It also prevents mistakes when operating between the same quantity but in
14
+ different units, like adding a power in Joules to a power in Watts without
15
+ converting one of them.
16
+
17
+ Quantities store the value in a base unit, and then provide methods to get that
18
+ quantity as a particular unit.
19
+
20
+ ## Documentation
21
+
22
+ For more information on how to use this library and examples, please check the
23
+ [Documentation website](https://frequenz-floss.github.io/frequenz-quantities-python/).
24
+
25
+ ## Supported Platforms
26
+
27
+ The following platforms are officially supported (tested):
28
+
29
+ - **Python:** 3.11
30
+ - **Operating System:** Ubuntu Linux 20.04
31
+ - **Architectures:** amd64, arm64
32
+
33
+ ## Contributing
34
+
35
+ If you want to know how to build this project and contribute to it, please
36
+ check out the [Contributing Guide](CONTRIBUTING.md).
@@ -0,0 +1,14 @@
1
+ # Frequenz Quantities Library Release Notes
2
+
3
+ ## Summary
4
+
5
+ This first stable release of `frequenz-quantities`!
6
+
7
+ The code is based on the quantities code in the [`frequenz-sdk` v1.0.0rc601](https://github.com/frequenz-floss/frequenz-sdk-python/releases/tag/v1.0.0-rc601) but with some new features and improvements.
8
+
9
+ ## New Features
10
+
11
+ - Added support for `__round__` (`round(quantity)`), `__pos__` (`+quantity`) and `__mod__` (`quantity % quantity`) operators.
12
+ - Add `ReactivePower` quantity.
13
+ - Add `ApparentPower` quantity.
14
+ - Add an **experimental** marshmallow module available when adding `[marshmallow]` to the requirements. This module provides `marshmallow` fields for quantities and a `QuantitySchema` to use as a base schema that supports loading and dumping quantities.
@@ -0,0 +1,180 @@
1
+ # License: MIT
2
+ # Copyright © 2024 Frequenz Energy-as-a-Service GmbH
3
+
4
+ [build-system]
5
+ requires = [
6
+ "setuptools == 68.1.0",
7
+ "setuptools_scm[toml] == 7.1.0",
8
+ "frequenz-repo-config[lib] == 0.10.0",
9
+ ]
10
+ build-backend = "setuptools.build_meta"
11
+
12
+ [project]
13
+ name = "frequenz-quantities"
14
+ description = "Types for holding quantities with units"
15
+ readme = "README.md"
16
+ license = { text = "MIT" }
17
+ keywords = [
18
+ "frequenz",
19
+ "python",
20
+ "lib",
21
+ "library",
22
+ "quantities",
23
+ "unit",
24
+ "conversion",
25
+ ]
26
+ classifiers = [
27
+ "Development Status :: 3 - Alpha",
28
+ "Intended Audience :: Developers",
29
+ "License :: OSI Approved :: MIT License",
30
+ "Programming Language :: Python :: 3",
31
+ "Programming Language :: Python :: 3 :: Only",
32
+ "Topic :: Software Development :: Libraries",
33
+ "Typing :: Typed",
34
+ ]
35
+ requires-python = ">= 3.11, < 4"
36
+ dependencies = ["typing-extensions >= 4.5.0, < 5"]
37
+ dynamic = ["version"]
38
+
39
+ [[project.authors]]
40
+ name = "Frequenz Energy-as-a-Service GmbH"
41
+ email = "floss@frequenz.com"
42
+
43
+ [project.optional-dependencies]
44
+ dev-flake8 = [
45
+ "flake8 == 7.1.1",
46
+ "flake8-docstrings == 1.7.0",
47
+ "flake8-pyproject == 1.2.3", # For reading the flake8 config from pyproject.toml
48
+ "pydoclint == 0.5.9",
49
+ "pydocstyle == 6.3.0",
50
+ ]
51
+ dev-formatting = ["black == 24.10.0", "isort == 5.13.2"]
52
+ dev-mkdocs = [
53
+ "Markdown == 3.7",
54
+ "black == 24.10.0",
55
+ "mike == 2.1.3",
56
+ "mkdocs-gen-files == 0.5.0",
57
+ "mkdocs-literate-nav == 0.6.1",
58
+ "mkdocs-macros-plugin == 1.3.7",
59
+ "mkdocs-material == 9.5.43",
60
+ "mkdocstrings[python] == 0.26.2",
61
+ "mkdocstrings-python == 1.12.2",
62
+ "frequenz-repo-config[lib] == 0.10.0",
63
+ ]
64
+ dev-mypy = [
65
+ "mypy == 1.13.0",
66
+ "types-Markdown == 3.7.0.20240822",
67
+ # For checking the noxfile, docs/ script, and tests
68
+ "frequenz-quantities[dev-mkdocs,dev-noxfile,dev-pytest,marshmallow]",
69
+ ]
70
+ dev-noxfile = ["nox == 2024.10.9", "frequenz-repo-config[lib] == 0.10.0"]
71
+ dev-pylint = [
72
+ # dev-pytest already defines a dependency to pylint because of the examples
73
+ # For checking the noxfile, docs/ script, and tests
74
+ "frequenz-quantities[dev-mkdocs,dev-noxfile,dev-pytest,marshmallow]",
75
+ ]
76
+ dev-pytest = [
77
+ "pytest == 8.3.3",
78
+ "pylint == 3.3.1", # We need this to check for the examples
79
+ "frequenz-repo-config[extra-lint-examples] == 0.10.0",
80
+ "pytest-mock == 3.14.0",
81
+ "pytest-asyncio == 0.24.0",
82
+ "async-solipsism == 0.7",
83
+ "hypothesis == 6.116.0",
84
+ "frequenz-quantities[marshmallow]",
85
+ ]
86
+
87
+ marshmallow = [
88
+ "marshmallow >= 3.0.0, < 4",
89
+ "marshmallow-dataclass >= 8.0.0, < 9",
90
+ ]
91
+
92
+ dev = [
93
+ "frequenz-quantities[dev-mkdocs,dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest,marshmallow]",
94
+ ]
95
+
96
+ [project.urls]
97
+ Documentation = "https://frequenz-floss.github.io/frequenz-quantities-python/"
98
+ Changelog = "https://github.com/frequenz-floss/frequenz-quantities-python/releases"
99
+ Issues = "https://github.com/frequenz-floss/frequenz-quantities-python/issues"
100
+ Repository = "https://github.com/frequenz-floss/frequenz-quantities-python"
101
+ Support = "https://github.com/frequenz-floss/frequenz-quantities-python/discussions/categories/support"
102
+
103
+ [tool.black]
104
+ line-length = 88
105
+ target-version = ['py311']
106
+ include = '\.pyi?$'
107
+
108
+ [tool.isort]
109
+ profile = "black"
110
+ line_length = 88
111
+ src_paths = ["benchmarks", "examples", "src", "tests"]
112
+
113
+ [tool.flake8]
114
+ # We give some flexibility to go over 88, there are cases like long URLs or
115
+ # code in documenation that have extra indentation. Black will still take care
116
+ # of making everything that can be 88 wide, 88 wide.
117
+ max-line-length = 100
118
+ extend-ignore = [
119
+ "E203", # Whitespace before ':' (conflicts with black)
120
+ "W503", # Line break before binary operator (conflicts with black)
121
+ ]
122
+ # pydoclint options
123
+ style = "google"
124
+ check-return-types = false
125
+ check-yield-types = false
126
+ arg-type-hints-in-docstring = false
127
+ arg-type-hints-in-signature = true
128
+ allow-init-docstring = true
129
+
130
+ [tool.pylint.similarities]
131
+ ignore-comments = ['yes']
132
+ ignore-docstrings = ['yes']
133
+ ignore-imports = ['no']
134
+ min-similarity-lines = 40
135
+
136
+ [tool.pylint.messages_control]
137
+ disable = [
138
+ "too-few-public-methods",
139
+ "too-many-return-statements",
140
+ # disabled because it conflicts with isort
141
+ "wrong-import-order",
142
+ "ungrouped-imports",
143
+ # pylint's unsubscriptable check is buggy and is not needed because
144
+ # it is a type-check, for which we already have mypy.
145
+ "unsubscriptable-object",
146
+ # Checked by mypy
147
+ "no-member",
148
+ # Checked by flake8
149
+ "f-string-without-interpolation",
150
+ "line-too-long",
151
+ "missing-function-docstring",
152
+ "redefined-outer-name",
153
+ "unnecessary-lambda-assignment",
154
+ "unused-import",
155
+ "unused-variable",
156
+ ]
157
+
158
+ [tool.pytest.ini_options]
159
+ testpaths = ["tests", "src"]
160
+ asyncio_mode = "auto"
161
+ required_plugins = ["pytest-asyncio", "pytest-mock"]
162
+
163
+ [tool.mypy]
164
+ explicit_package_bases = true
165
+ namespace_packages = true
166
+ # This option disables mypy cache, and it is sometimes useful to enable it if
167
+ # you are getting weird intermittent error, or error in the CI but not locally
168
+ # (or vice versa). In particular errors saying that type: ignore is not
169
+ # used but getting the original ignored error when removing the type: ignore.
170
+ # See for example: https://github.com/python/mypy/issues/2960
171
+ #no_incremental = true
172
+ packages = ["frequenz.quantities"]
173
+ strict = true
174
+
175
+ [[tool.mypy.overrides]]
176
+ module = ["mkdocs_macros.*", "sybil", "sybil.*"]
177
+ ignore_missing_imports = true
178
+
179
+ [tool.setuptools_scm]
180
+ version_scheme = "post-release"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,112 @@
1
+ # License: MIT
2
+ # Copyright © 2024 Frequenz Energy-as-a-Service GmbH
3
+
4
+ """Types for holding quantities with units.
5
+
6
+ This library provide types for holding quantities with units. The main goal is to avoid
7
+ mistakes while working with different types of quantities, for example avoiding adding
8
+ a length to a time.
9
+
10
+ It also prevents mistakes when operating between the same quantity but in different
11
+ units, like adding a power in Joules to a power in Watts without converting one of them.
12
+
13
+ Quantities store the value in a base unit, and then provide methods to get that quantity
14
+ as a particular unit. They can only be constructed using special constructors with the
15
+ form `Quantity.from_<unit>`, for example
16
+ [`Power.from_watts(10.0)`][frequenz.quantities.Power.from_watts].
17
+
18
+ Internally quantities store values as `float`s, so regular [float issues and limitations
19
+ apply](https://docs.python.org/3/tutorial/floatingpoint.html), although some of them are
20
+ tried to be mitigated.
21
+
22
+ Quantities are also immutable, so operations between quantities return a new instance of
23
+ the quantity.
24
+
25
+ This library provides the following types:
26
+
27
+ - [ApparentPower][frequenz.quantities.ApparentPower]: A quantity representing apparent
28
+ power.
29
+ - [Current][frequenz.quantities.Current]: A quantity representing an electric current.
30
+ - [Energy][frequenz.quantities.Energy]: A quantity representing energy.
31
+ - [Frequency][frequenz.quantities.Frequency]: A quantity representing frequency.
32
+ - [Percentage][frequenz.quantities.Percentage]: A quantity representing a percentage.
33
+ - [Power][frequenz.quantities.Power]: A quantity representing power.
34
+ - [ReactivePower][frequenz.quantities.ReactivePower]: A quantity representing reactive
35
+ power.
36
+ - [Temperature][frequenz.quantities.Temperature]: A quantity representing temperature.
37
+ - [Voltage][frequenz.quantities.Voltage]: A quantity representing electric voltage.
38
+
39
+ There is also the unitless [Quantity][frequenz.quantities.Quantity] class. All
40
+ quantities are subclasses of this class and it can be used as a base to create new
41
+ quantities. Using the `Quantity` class directly is discouraged, as it doesn't provide
42
+ any unit conversion methods.
43
+
44
+ Example:
45
+ ```python
46
+ from datetime import timedelta
47
+ from frequenz.quantities import Power, Voltage, Current, Energy
48
+
49
+ # Create a power quantity
50
+ power = Power.from_watts(230.0)
51
+
52
+ # Printing uses a unit to make the string as short as possible
53
+ print(f"Power: {power}") # Power: 230.0 W
54
+ # The precision can be changed
55
+ print(f"Power: {power:0.3}") # Power: 230.000 W
56
+ # The conversion methods can be used to get the value in a particular unit
57
+ print(f"Power in MW: {power.as_megawatt()}") # Power in MW: 0.00023 MW
58
+
59
+ # Create a voltage quantity
60
+ voltage = Voltage.from_volts(230.0)
61
+
62
+ # Calculate the current
63
+ current = power / voltage
64
+ assert isinstance(current, Current)
65
+ print(f"Current: {current}") # Current: 1.0 A
66
+ assert current.isclose(Current.from_amperes(1.0))
67
+
68
+ # Calculate the energy
69
+ energy = power * timedelta(hours=1)
70
+ assert isinstance(energy, Energy)
71
+ print(f"Energy: {energy}") # Energy: 230.0 Wh
72
+ print(f"Energy in kWh: {energy.as_kilowatt_hours()}") # Energy in kWh: 0.23
73
+
74
+ # Invalid operations are not permitted
75
+ # (when using a type hinting linter like mypy, this will be caught at linting time)
76
+ try:
77
+ power + voltage
78
+ except TypeError as e:
79
+ print(f"Error: {e}") # Error: unsupported operand type(s) for +: 'Power' and 'Voltage'
80
+ ```
81
+
82
+ This library also provides an [**experimental** module with marshmallow fields and
83
+ a base schema][frequenz.quantities.experimental.marshmallow] to serialize and
84
+ deserialize quantities using the marshmallow library. To use it, you need to make sure
85
+ to install this package with the `marshmallow` optional dependencies (e.g.
86
+ `pip install frequenz-quantities[marshmallow]`).
87
+ """
88
+
89
+
90
+ from ._apparent_power import ApparentPower
91
+ from ._current import Current
92
+ from ._energy import Energy
93
+ from ._frequency import Frequency
94
+ from ._percentage import Percentage
95
+ from ._power import Power
96
+ from ._quantity import Quantity
97
+ from ._reactive_power import ReactivePower
98
+ from ._temperature import Temperature
99
+ from ._voltage import Voltage
100
+
101
+ __all__ = [
102
+ "ApparentPower",
103
+ "Current",
104
+ "Energy",
105
+ "Frequency",
106
+ "Percentage",
107
+ "Power",
108
+ "Quantity",
109
+ "ReactivePower",
110
+ "Temperature",
111
+ "Voltage",
112
+ ]
@@ -0,0 +1,234 @@
1
+ # License: MIT
2
+ # Copyright © 2024 Frequenz Energy-as-a-Service GmbH
3
+
4
+ """Types for holding apparent power quantities with units."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import TYPE_CHECKING, Self, overload
9
+
10
+ from ._quantity import NoDefaultConstructible, Quantity
11
+
12
+ if TYPE_CHECKING:
13
+ from ._current import Current
14
+ from ._percentage import Percentage
15
+ from ._voltage import Voltage
16
+
17
+
18
+ class ApparentPower(
19
+ Quantity,
20
+ metaclass=NoDefaultConstructible,
21
+ exponent_unit_map={
22
+ -3: "mVA",
23
+ 0: "VA",
24
+ 3: "kVA",
25
+ 6: "MVA",
26
+ },
27
+ ):
28
+ """A apparent power quantity.
29
+
30
+ Objects of this type are wrappers around `float` values and are immutable.
31
+
32
+ The constructors accept a single `float` value, the `as_*()` methods return a
33
+ `float` value, and each of the arithmetic operators supported by this type are
34
+ actually implemented using floating-point arithmetic.
35
+
36
+ So all considerations about floating-point arithmetic apply to this type as well.
37
+ """
38
+
39
+ @classmethod
40
+ def from_volt_amperes(cls, value: float) -> Self:
41
+ """Initialize a new apparent power quantity.
42
+
43
+ Args:
44
+ value: The apparent power in volt-amperes (VA).
45
+
46
+ Returns:
47
+ A new apparent power quantity.
48
+ """
49
+ return cls._new(value)
50
+
51
+ @classmethod
52
+ def from_milli_volt_amperes(cls, mva: float) -> Self:
53
+ """Initialize a new apparent power quantity.
54
+
55
+ Args:
56
+ mva: The apparent power in millivolt-amperes (mVA).
57
+
58
+ Returns:
59
+ A new apparent power quantity.
60
+ """
61
+ return cls._new(mva, exponent=-3)
62
+
63
+ @classmethod
64
+ def from_kilo_volt_amperes(cls, kva: float) -> Self:
65
+ """Initialize a new apparent power quantity.
66
+
67
+ Args:
68
+ kva: The apparent power in kilovolt-amperes (kVA).
69
+
70
+ Returns:
71
+ A new apparent power quantity.
72
+ """
73
+ return cls._new(kva, exponent=3)
74
+
75
+ @classmethod
76
+ def from_mega_volt_amperes(cls, mva: float) -> Self:
77
+ """Initialize a new apparent power quantity.
78
+
79
+ Args:
80
+ mva: The apparent power in megavolt-amperes (MVA).
81
+
82
+ Returns:
83
+ A new apparent power quantity.
84
+ """
85
+ return cls._new(mva, exponent=6)
86
+
87
+ def as_volt_amperes(self) -> float:
88
+ """Return the apparent power in volt-amperes (VA).
89
+
90
+ Returns:
91
+ The apparent power in volt-amperes (VA).
92
+ """
93
+ return self._base_value
94
+
95
+ def as_milli_volt_amperes(self) -> float:
96
+ """Return the apparent power in millivolt-amperes (mVA).
97
+
98
+ Returns:
99
+ The apparent power in millivolt-amperes (mVA).
100
+ """
101
+ return self._base_value * 1e3
102
+
103
+ def as_kilo_volt_amperes(self) -> float:
104
+ """Return the apparent power in kilovolt-amperes (kVA).
105
+
106
+ Returns:
107
+ The apparent power in kilovolt-amperes (kVA).
108
+ """
109
+ return self._base_value / 1e3
110
+
111
+ def as_mega_volt_amperes(self) -> float:
112
+ """Return the apparent power in megavolt-amperes (MVA).
113
+
114
+ Returns:
115
+ The apparent power in megavolt-amperes (MVA).
116
+ """
117
+ return self._base_value / 1e6
118
+
119
+ @overload
120
+ def __mul__(self, scalar: float, /) -> Self:
121
+ """Scale this power by a scalar.
122
+
123
+ Args:
124
+ scalar: The scalar by which to scale this power.
125
+
126
+ Returns:
127
+ The scaled power.
128
+ """
129
+
130
+ @overload
131
+ def __mul__(self, percent: Percentage, /) -> Self:
132
+ """Scale this power by a percentage.
133
+
134
+ Args:
135
+ percent: The percentage by which to scale this power.
136
+
137
+ Returns:
138
+ The scaled power.
139
+ """
140
+
141
+ def __mul__(self, other: float | Percentage, /) -> Self:
142
+ """Return a power or energy from multiplying this power by the given value.
143
+
144
+ Args:
145
+ other: The scalar, percentage or duration to multiply by.
146
+
147
+ Returns:
148
+ A power or energy.
149
+ """
150
+ from ._percentage import Percentage # pylint: disable=import-outside-toplevel
151
+
152
+ match other:
153
+ case float() | Percentage():
154
+ return super().__mul__(other)
155
+ case _:
156
+ return NotImplemented
157
+
158
+ # We need the ignore here because otherwise mypy will give this error:
159
+ # > Overloaded operator methods can't have wider argument types in overrides
160
+ # The problem seems to be when the other type implements an **incompatible**
161
+ # __rmul__ method, which is not the case here, so we should be safe.
162
+ # Please see this example:
163
+ # https://github.com/python/mypy/blob/c26f1297d4f19d2d1124a30efc97caebb8c28616/test-data/unit/check-overloading.test#L4738C1-L4769C55
164
+ # And a discussion in a mypy issue here:
165
+ # https://github.com/python/mypy/issues/4985#issuecomment-389692396
166
+ @overload # type: ignore[override]
167
+ def __truediv__(self, other: float, /) -> Self:
168
+ """Divide this power by a scalar.
169
+
170
+ Args:
171
+ other: The scalar to divide this power by.
172
+
173
+ Returns:
174
+ The divided power.
175
+ """
176
+
177
+ @overload
178
+ def __truediv__(self, other: Self, /) -> float:
179
+ """Return the ratio of this power to another.
180
+
181
+ Args:
182
+ other: The other power.
183
+
184
+ Returns:
185
+ The ratio of this power to another.
186
+ """
187
+
188
+ @overload
189
+ def __truediv__(self, current: Current, /) -> Voltage:
190
+ """Return a voltage from dividing this power by the given current.
191
+
192
+ Args:
193
+ current: The current to divide by.
194
+
195
+ Returns:
196
+ A voltage from dividing this power by the a current.
197
+ """
198
+
199
+ @overload
200
+ def __truediv__(self, voltage: Voltage, /) -> Current:
201
+ """Return a current from dividing this power by the given voltage.
202
+
203
+ Args:
204
+ voltage: The voltage to divide by.
205
+
206
+ Returns:
207
+ A current from dividing this power by a voltage.
208
+ """
209
+
210
+ def __truediv__(
211
+ self, other: float | Self | Current | Voltage, /
212
+ ) -> Self | float | Voltage | Current:
213
+ """Return a current or voltage from dividing this power by the given value.
214
+
215
+ Args:
216
+ other: The scalar, power, current or voltage to divide by.
217
+
218
+ Returns:
219
+ A current or voltage from dividing this power by the given value.
220
+ """
221
+ from ._current import Current # pylint: disable=import-outside-toplevel
222
+ from ._voltage import Voltage # pylint: disable=import-outside-toplevel
223
+
224
+ match other:
225
+ case float():
226
+ return super().__truediv__(other)
227
+ case ApparentPower():
228
+ return self._base_value / other._base_value
229
+ case Current():
230
+ return Voltage._new(self._base_value / other._base_value)
231
+ case Voltage():
232
+ return Current._new(self._base_value / other._base_value)
233
+ case _:
234
+ return NotImplemented