python-units 0.2.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- api/public.py +51 -0
- api/si.py +51 -1
- core/__init__.py +8 -0
- core/deprecations.py +62 -0
- core/quantity.py +271 -18
- core/unit_definitions.py +267 -22
- models/dimension.py +27 -1
- python_units-0.4.0.dist-info/METADATA +470 -0
- {python_units-0.2.0.dist-info → python_units-0.4.0.dist-info}/RECORD +13 -12
- units/unit.py +16 -0
- python_units-0.2.0.dist-info/METADATA +0 -279
- {python_units-0.2.0.dist-info → python_units-0.4.0.dist-info}/WHEEL +0 -0
- {python_units-0.2.0.dist-info → python_units-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {python_units-0.2.0.dist-info → python_units-0.4.0.dist-info}/top_level.txt +0 -0
api/public.py
CHANGED
|
@@ -5,20 +5,40 @@ from api.si import (
|
|
|
5
5
|
ampere,
|
|
6
6
|
becquerel,
|
|
7
7
|
candela,
|
|
8
|
+
centimetre,
|
|
8
9
|
coulomb,
|
|
9
10
|
degree_celcius,
|
|
10
11
|
farad,
|
|
12
|
+
gram,
|
|
11
13
|
gray,
|
|
12
14
|
henry,
|
|
13
15
|
hertz,
|
|
16
|
+
hour,
|
|
14
17
|
joule,
|
|
15
18
|
katal,
|
|
16
19
|
kelvin,
|
|
17
20
|
kilogram,
|
|
21
|
+
kiloampere,
|
|
22
|
+
kilometre,
|
|
23
|
+
kilovolt,
|
|
24
|
+
kilowatt,
|
|
18
25
|
lumen,
|
|
19
26
|
lux,
|
|
27
|
+
megawatt,
|
|
20
28
|
metre,
|
|
29
|
+
microgram,
|
|
30
|
+
micrometre,
|
|
31
|
+
microsecond,
|
|
32
|
+
milliampere,
|
|
33
|
+
milligram,
|
|
34
|
+
millimetre,
|
|
35
|
+
millisecond,
|
|
36
|
+
millivolt,
|
|
37
|
+
milliwatt,
|
|
38
|
+
minute,
|
|
21
39
|
mole,
|
|
40
|
+
nanometre,
|
|
41
|
+
nanosecond,
|
|
22
42
|
newton,
|
|
23
43
|
ohm,
|
|
24
44
|
pascal,
|
|
@@ -28,6 +48,7 @@ from api.si import (
|
|
|
28
48
|
sievert,
|
|
29
49
|
steradian,
|
|
30
50
|
tesla,
|
|
51
|
+
tonne,
|
|
31
52
|
volt,
|
|
32
53
|
watt,
|
|
33
54
|
weber,
|
|
@@ -43,16 +64,21 @@ from core.quantity import (
|
|
|
43
64
|
Quantity,
|
|
44
65
|
complex_quantity,
|
|
45
66
|
complex_unit,
|
|
67
|
+
convert,
|
|
46
68
|
float_quantity,
|
|
47
69
|
float_unit,
|
|
48
70
|
int_quantity,
|
|
49
71
|
int_unit,
|
|
50
72
|
long_quantity,
|
|
51
73
|
long_unit,
|
|
74
|
+
multiplier,
|
|
75
|
+
unit,
|
|
76
|
+
value,
|
|
52
77
|
)
|
|
53
78
|
from core.unit_definitions import BaseUnit, CustomUnitBase, DerivedUnit, SIUnit
|
|
54
79
|
from models.dimension import Dimension, DimensionSystem
|
|
55
80
|
|
|
81
|
+
|
|
56
82
|
Unit = Quantity
|
|
57
83
|
|
|
58
84
|
__all__ = [
|
|
@@ -72,28 +98,50 @@ __all__ = [
|
|
|
72
98
|
"ampere",
|
|
73
99
|
"becquerel",
|
|
74
100
|
"candela",
|
|
101
|
+
"centimetre",
|
|
75
102
|
"complex_quantity",
|
|
76
103
|
"complex_unit",
|
|
104
|
+
"convert",
|
|
77
105
|
"coulomb",
|
|
78
106
|
"degree_celcius",
|
|
79
107
|
"farad",
|
|
80
108
|
"float_quantity",
|
|
81
109
|
"float_unit",
|
|
110
|
+
"gram",
|
|
82
111
|
"gray",
|
|
83
112
|
"henry",
|
|
84
113
|
"hertz",
|
|
114
|
+
"hour",
|
|
85
115
|
"int_quantity",
|
|
86
116
|
"int_unit",
|
|
87
117
|
"joule",
|
|
88
118
|
"katal",
|
|
89
119
|
"kelvin",
|
|
90
120
|
"kilogram",
|
|
121
|
+
"kiloampere",
|
|
122
|
+
"kilometre",
|
|
123
|
+
"kilovolt",
|
|
124
|
+
"kilowatt",
|
|
91
125
|
"long_quantity",
|
|
92
126
|
"long_unit",
|
|
93
127
|
"lumen",
|
|
94
128
|
"lux",
|
|
129
|
+
"megawatt",
|
|
95
130
|
"metre",
|
|
131
|
+
"microgram",
|
|
132
|
+
"micrometre",
|
|
133
|
+
"microsecond",
|
|
134
|
+
"milliampere",
|
|
135
|
+
"milligram",
|
|
136
|
+
"millimetre",
|
|
137
|
+
"millisecond",
|
|
138
|
+
"millivolt",
|
|
139
|
+
"milliwatt",
|
|
140
|
+
"minute",
|
|
141
|
+
"multiplier",
|
|
96
142
|
"mole",
|
|
143
|
+
"nanometre",
|
|
144
|
+
"nanosecond",
|
|
97
145
|
"newton",
|
|
98
146
|
"ohm",
|
|
99
147
|
"pascal",
|
|
@@ -103,6 +151,9 @@ __all__ = [
|
|
|
103
151
|
"sievert",
|
|
104
152
|
"steradian",
|
|
105
153
|
"tesla",
|
|
154
|
+
"tonne",
|
|
155
|
+
"unit",
|
|
156
|
+
"value",
|
|
106
157
|
"volt",
|
|
107
158
|
"watt",
|
|
108
159
|
"weber",
|
api/si.py
CHANGED
|
@@ -29,7 +29,11 @@ siemens = DerivedUnit.define("S", ampere / volt)
|
|
|
29
29
|
weber = DerivedUnit.define("Wb", volt * second)
|
|
30
30
|
tesla = DerivedUnit.define("T", weber / metre / metre)
|
|
31
31
|
henry = DerivedUnit.define("H", weber / ampere)
|
|
32
|
-
degree_celcius = DerivedUnit.define(
|
|
32
|
+
degree_celcius = DerivedUnit.define(
|
|
33
|
+
"°C",
|
|
34
|
+
kelvin,
|
|
35
|
+
supports_multiplicative_conversion=False,
|
|
36
|
+
)
|
|
33
37
|
lumen = DerivedUnit.define("lm", candela * steradian)
|
|
34
38
|
lux = DerivedUnit.define("lx", lumen / metre / metre)
|
|
35
39
|
becquerel = DerivedUnit.define("Bq", SIUnit() / second)
|
|
@@ -37,6 +41,31 @@ gray = DerivedUnit.define("Gy", joule / kilogram)
|
|
|
37
41
|
sievert = DerivedUnit.define("Sv", joule / kilogram)
|
|
38
42
|
katal = DerivedUnit.define("kat", mole / second)
|
|
39
43
|
|
|
44
|
+
kilometre = DerivedUnit.define("km", metre, conversion_factor=1000.0)
|
|
45
|
+
centimetre = DerivedUnit.define("cm", metre, conversion_factor=0.01)
|
|
46
|
+
millimetre = DerivedUnit.define("mm", metre, conversion_factor=0.001)
|
|
47
|
+
micrometre = DerivedUnit.define("µm", metre, conversion_factor=0.000001)
|
|
48
|
+
nanometre = DerivedUnit.define("nm", metre, conversion_factor=0.000000001)
|
|
49
|
+
|
|
50
|
+
gram = DerivedUnit.define("g", kilogram, conversion_factor=0.001)
|
|
51
|
+
milligram = DerivedUnit.define("mg", kilogram, conversion_factor=0.000001)
|
|
52
|
+
microgram = DerivedUnit.define("µg", kilogram, conversion_factor=0.000000001)
|
|
53
|
+
tonne = DerivedUnit.define("t", kilogram, conversion_factor=1000.0)
|
|
54
|
+
|
|
55
|
+
minute = DerivedUnit.define("min", second, conversion_factor=60.0)
|
|
56
|
+
hour = DerivedUnit.define("h", second, conversion_factor=3600.0)
|
|
57
|
+
millisecond = DerivedUnit.define("ms", second, conversion_factor=0.001)
|
|
58
|
+
microsecond = DerivedUnit.define("µs", second, conversion_factor=0.000001)
|
|
59
|
+
nanosecond = DerivedUnit.define("ns", second, conversion_factor=0.000000001)
|
|
60
|
+
|
|
61
|
+
milliampere = DerivedUnit.define("mA", ampere, conversion_factor=0.001)
|
|
62
|
+
kiloampere = DerivedUnit.define("kA", ampere, conversion_factor=1000.0)
|
|
63
|
+
millivolt = DerivedUnit.define("mV", volt, conversion_factor=0.001)
|
|
64
|
+
kilovolt = DerivedUnit.define("kV", volt, conversion_factor=1000.0)
|
|
65
|
+
milliwatt = DerivedUnit.define("mW", watt, conversion_factor=0.001)
|
|
66
|
+
kilowatt = DerivedUnit.define("kW", watt, conversion_factor=1000.0)
|
|
67
|
+
megawatt = DerivedUnit.define("MW", watt, conversion_factor=1000000.0)
|
|
68
|
+
|
|
40
69
|
for unit in (
|
|
41
70
|
newton,
|
|
42
71
|
pascal,
|
|
@@ -59,20 +88,40 @@ __all__ = [
|
|
|
59
88
|
"ampere",
|
|
60
89
|
"becquerel",
|
|
61
90
|
"candela",
|
|
91
|
+
"centimetre",
|
|
62
92
|
"coulomb",
|
|
63
93
|
"degree_celcius",
|
|
64
94
|
"farad",
|
|
95
|
+
"gram",
|
|
65
96
|
"gray",
|
|
66
97
|
"henry",
|
|
67
98
|
"hertz",
|
|
99
|
+
"hour",
|
|
68
100
|
"joule",
|
|
69
101
|
"katal",
|
|
70
102
|
"kelvin",
|
|
71
103
|
"kilogram",
|
|
104
|
+
"kiloampere",
|
|
105
|
+
"kilometre",
|
|
106
|
+
"kilovolt",
|
|
107
|
+
"kilowatt",
|
|
72
108
|
"lumen",
|
|
73
109
|
"lux",
|
|
110
|
+
"megawatt",
|
|
74
111
|
"metre",
|
|
112
|
+
"microgram",
|
|
113
|
+
"micrometre",
|
|
114
|
+
"microsecond",
|
|
115
|
+
"milliampere",
|
|
116
|
+
"milligram",
|
|
117
|
+
"millimetre",
|
|
118
|
+
"millisecond",
|
|
119
|
+
"millivolt",
|
|
120
|
+
"milliwatt",
|
|
121
|
+
"minute",
|
|
75
122
|
"mole",
|
|
123
|
+
"nanometre",
|
|
124
|
+
"nanosecond",
|
|
76
125
|
"newton",
|
|
77
126
|
"ohm",
|
|
78
127
|
"pascal",
|
|
@@ -82,6 +131,7 @@ __all__ = [
|
|
|
82
131
|
"sievert",
|
|
83
132
|
"steradian",
|
|
84
133
|
"tesla",
|
|
134
|
+
"tonne",
|
|
85
135
|
"volt",
|
|
86
136
|
"watt",
|
|
87
137
|
"weber",
|
core/__init__.py
CHANGED
|
@@ -11,12 +11,16 @@ from .quantity import (
|
|
|
11
11
|
Quantity,
|
|
12
12
|
complex_quantity,
|
|
13
13
|
complex_unit,
|
|
14
|
+
convert,
|
|
14
15
|
float_quantity,
|
|
15
16
|
float_unit,
|
|
16
17
|
int_quantity,
|
|
17
18
|
int_unit,
|
|
18
19
|
long_quantity,
|
|
19
20
|
long_unit,
|
|
21
|
+
multiplier,
|
|
22
|
+
unit,
|
|
23
|
+
value,
|
|
20
24
|
)
|
|
21
25
|
from .unit_definitions import BaseUnit, CustomUnitBase, DerivedUnit, SIUnit
|
|
22
26
|
|
|
@@ -33,10 +37,14 @@ __all__ = [
|
|
|
33
37
|
"UnitsError",
|
|
34
38
|
"complex_quantity",
|
|
35
39
|
"complex_unit",
|
|
40
|
+
"convert",
|
|
36
41
|
"float_quantity",
|
|
37
42
|
"float_unit",
|
|
38
43
|
"int_quantity",
|
|
39
44
|
"int_unit",
|
|
40
45
|
"long_quantity",
|
|
41
46
|
"long_unit",
|
|
47
|
+
"multiplier",
|
|
48
|
+
"unit",
|
|
49
|
+
"value",
|
|
42
50
|
]
|
core/deprecations.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Deprecation helpers for compatibility APIs."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import warnings
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import TypeVar
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
_T = TypeVar("_T")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def warn_legacy_api(name: str, replacement: str, removal_version: str) -> None:
|
|
15
|
+
"""
|
|
16
|
+
Emit a deprecation warning for a legacy public API.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
name: Deprecated API name.
|
|
20
|
+
replacement: Preferred API name or usage pattern.
|
|
21
|
+
removal_version: Planned removal release.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
None.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
None.
|
|
28
|
+
"""
|
|
29
|
+
warnings.warn(
|
|
30
|
+
"{} is deprecated; use {} instead. It is scheduled for removal in {}.".format(
|
|
31
|
+
name,
|
|
32
|
+
replacement,
|
|
33
|
+
removal_version,
|
|
34
|
+
),
|
|
35
|
+
DeprecationWarning,
|
|
36
|
+
stacklevel=4,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def deprecated_call(
|
|
41
|
+
name: str,
|
|
42
|
+
replacement: str,
|
|
43
|
+
removal_version: str,
|
|
44
|
+
callback: Callable[[], _T],
|
|
45
|
+
) -> _T:
|
|
46
|
+
"""
|
|
47
|
+
Warn for a deprecated API call and return the callback result.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
name: Deprecated API name.
|
|
51
|
+
replacement: Preferred API name or usage pattern.
|
|
52
|
+
removal_version: Planned removal release.
|
|
53
|
+
callback: Zero-argument callable that performs the actual work.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
The callback result.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
Any exception raised by ``callback``.
|
|
60
|
+
"""
|
|
61
|
+
warn_legacy_api(name, replacement, removal_version)
|
|
62
|
+
return callback()
|
core/quantity.py
CHANGED
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
from core.deprecations import deprecated_call
|
|
6
7
|
from core.errors import InvalidValueError, UnitCompatibilityError, UnitOperandError
|
|
7
|
-
from core.unit_definitions import
|
|
8
|
+
from core.unit_definitions import (
|
|
9
|
+
BaseUnit,
|
|
10
|
+
DerivedUnit,
|
|
11
|
+
SIUnit,
|
|
12
|
+
clone_unit,
|
|
13
|
+
require_unit_instance,
|
|
14
|
+
)
|
|
8
15
|
from models.dimension import SI_DIMENSION_SYSTEM
|
|
9
16
|
from utils.numbers import Scalar, is_number, is_real_number, validate_numeric_value
|
|
10
17
|
|
|
@@ -17,6 +24,49 @@ def require_quantity_operand(operand: object, operation: str) -> None:
|
|
|
17
24
|
)
|
|
18
25
|
|
|
19
26
|
|
|
27
|
+
def normalize_scalar(value: Scalar) -> Scalar:
|
|
28
|
+
"""Return ``int`` for exact integer floats, otherwise return ``value``."""
|
|
29
|
+
if isinstance(value, float) and value.is_integer():
|
|
30
|
+
return int(value)
|
|
31
|
+
return value
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def normalize_product(value: Scalar, left: Scalar, right: Scalar) -> Scalar:
|
|
35
|
+
"""Normalize exact integer products only when both inputs were integers."""
|
|
36
|
+
if isinstance(left, int) and isinstance(right, int):
|
|
37
|
+
return normalize_scalar(value)
|
|
38
|
+
return value
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def normalize_reverse_floor(value: Scalar, denominator: "Quantity") -> Scalar:
|
|
42
|
+
"""Preserve legacy int display for unscaled integer reverse floor division."""
|
|
43
|
+
if (
|
|
44
|
+
denominator.unit.conversion_factor == 1.0
|
|
45
|
+
and isinstance(denominator.value, int)
|
|
46
|
+
):
|
|
47
|
+
return normalize_scalar(value)
|
|
48
|
+
return value
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def normalize_result_unit(result_unit: BaseUnit) -> BaseUnit:
|
|
52
|
+
"""Return a renderable result unit with no hidden anonymous scale."""
|
|
53
|
+
if isinstance(result_unit, DerivedUnit) or result_unit.conversion_factor == 1.0:
|
|
54
|
+
return result_unit
|
|
55
|
+
try:
|
|
56
|
+
return result_unit.__class__(
|
|
57
|
+
dimension=result_unit.dimension,
|
|
58
|
+
supports_multiplicative_conversion=(
|
|
59
|
+
result_unit.supports_multiplicative_conversion
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
except TypeError:
|
|
63
|
+
unit = result_unit.__class__(dimension=result_unit.dimension)
|
|
64
|
+
unit._supports_multiplicative_conversion = (
|
|
65
|
+
result_unit.supports_multiplicative_conversion
|
|
66
|
+
)
|
|
67
|
+
return unit
|
|
68
|
+
|
|
69
|
+
|
|
20
70
|
class Quantity:
|
|
21
71
|
"""A numeric value coupled to a unit definition."""
|
|
22
72
|
|
|
@@ -80,6 +130,44 @@ class Quantity:
|
|
|
80
130
|
"unsupported scalar for {}: {}".format(operation, type(value).__name__)
|
|
81
131
|
)
|
|
82
132
|
|
|
133
|
+
def _base_value(self) -> Scalar:
|
|
134
|
+
return self.value * self.unit.conversion_factor
|
|
135
|
+
|
|
136
|
+
def to(self, target_unit: BaseUnit) -> "Quantity":
|
|
137
|
+
"""
|
|
138
|
+
Convert this quantity to a compatible scale-only target unit.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
target_unit: Unit definition with the same dimension.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Quantity expressed in ``target_unit``.
|
|
145
|
+
|
|
146
|
+
Raises:
|
|
147
|
+
InvalidUnitError: If ``target_unit`` is not a unit definition.
|
|
148
|
+
UnitCompatibilityError: If dimensions differ or either unit cannot
|
|
149
|
+
be converted with a multiplicative scale factor.
|
|
150
|
+
"""
|
|
151
|
+
require_unit_instance(target_unit)
|
|
152
|
+
if self.unit.dimension != target_unit.dimension:
|
|
153
|
+
raise UnitCompatibilityError(
|
|
154
|
+
"cannot convert {} to {}".format(self.unit, target_unit)
|
|
155
|
+
)
|
|
156
|
+
if (
|
|
157
|
+
not self.unit.supports_multiplicative_conversion
|
|
158
|
+
or not target_unit.supports_multiplicative_conversion
|
|
159
|
+
):
|
|
160
|
+
raise UnitCompatibilityError(
|
|
161
|
+
"units require a non-multiplicative conversion: {} and {}".format(
|
|
162
|
+
self.unit,
|
|
163
|
+
target_unit,
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
return self.__class__(
|
|
167
|
+
normalize_scalar(self._base_value() / target_unit.conversion_factor),
|
|
168
|
+
target_unit,
|
|
169
|
+
)
|
|
170
|
+
|
|
83
171
|
def __add__(self, quantity2: object) -> "Quantity":
|
|
84
172
|
self._require_compatible_quantity(quantity2, "addition")
|
|
85
173
|
return self.__class__(self.value + quantity2.value, self.unit)
|
|
@@ -97,9 +185,22 @@ class Quantity:
|
|
|
97
185
|
|
|
98
186
|
def __mul__(self, quantity2: object) -> "Quantity":
|
|
99
187
|
if isinstance(quantity2, Quantity):
|
|
100
|
-
|
|
188
|
+
result_unit = self.unit * quantity2.unit
|
|
189
|
+
result_value = self._base_value() * quantity2._base_value()
|
|
190
|
+
return self.__class__(
|
|
191
|
+
normalize_product(result_value, self.value, quantity2.value),
|
|
192
|
+
normalize_result_unit(result_unit),
|
|
193
|
+
)
|
|
101
194
|
if isinstance(quantity2, BaseUnit):
|
|
102
|
-
|
|
195
|
+
result_unit = self.unit * quantity2
|
|
196
|
+
result_value = (
|
|
197
|
+
self._base_value()
|
|
198
|
+
* quantity2.conversion_factor
|
|
199
|
+
)
|
|
200
|
+
return self.__class__(
|
|
201
|
+
normalize_scalar(result_value),
|
|
202
|
+
normalize_result_unit(result_unit),
|
|
203
|
+
)
|
|
103
204
|
self._require_numeric_scalar(quantity2, "multiplication")
|
|
104
205
|
return self.__class__(self.value * quantity2, self.unit)
|
|
105
206
|
|
|
@@ -108,29 +209,55 @@ class Quantity:
|
|
|
108
209
|
|
|
109
210
|
def __truediv__(self, quantity2: object) -> "Quantity":
|
|
110
211
|
if isinstance(quantity2, Quantity):
|
|
111
|
-
|
|
212
|
+
result_unit = self.unit / quantity2.unit
|
|
213
|
+
result_value = self._base_value() / quantity2._base_value()
|
|
214
|
+
return self.__class__(result_value, normalize_result_unit(result_unit))
|
|
112
215
|
if isinstance(quantity2, BaseUnit):
|
|
113
|
-
|
|
216
|
+
result_unit = self.unit / quantity2
|
|
217
|
+
result_value = (
|
|
218
|
+
self._base_value()
|
|
219
|
+
/ quantity2.conversion_factor
|
|
220
|
+
)
|
|
221
|
+
return self.__class__(
|
|
222
|
+
normalize_scalar(result_value),
|
|
223
|
+
normalize_result_unit(result_unit),
|
|
224
|
+
)
|
|
114
225
|
self._require_numeric_scalar(quantity2, "division")
|
|
115
226
|
return self.__class__(self.value / quantity2, self.unit)
|
|
116
227
|
|
|
117
228
|
def __rtruediv__(self, quantity2: object) -> "Quantity":
|
|
118
229
|
if isinstance(quantity2, Quantity):
|
|
119
|
-
|
|
230
|
+
result_unit = quantity2.unit / self.unit
|
|
231
|
+
result_value = quantity2._base_value() / self._base_value()
|
|
232
|
+
return self.__class__(result_value, normalize_result_unit(result_unit))
|
|
120
233
|
self._require_numeric_scalar(quantity2, "division")
|
|
121
|
-
|
|
234
|
+
result_unit = self._dimensionless_unit() / self.unit
|
|
235
|
+
result_value = quantity2 / self._base_value()
|
|
236
|
+
return self.__class__(result_value, normalize_result_unit(result_unit))
|
|
122
237
|
|
|
123
238
|
def __floordiv__(self, quantity2: object) -> "Quantity":
|
|
124
239
|
if isinstance(quantity2, Quantity):
|
|
125
|
-
|
|
240
|
+
result_unit = self.unit / quantity2.unit
|
|
241
|
+
result_value = self._base_value() // quantity2._base_value()
|
|
242
|
+
return self.__class__(result_value, normalize_result_unit(result_unit))
|
|
126
243
|
self._require_real_scalar(quantity2, "floor division")
|
|
127
244
|
return self.__class__(self.value // quantity2, self.unit)
|
|
128
245
|
|
|
129
246
|
def __rfloordiv__(self, quantity2: object) -> "Quantity":
|
|
130
247
|
if isinstance(quantity2, Quantity):
|
|
131
|
-
|
|
248
|
+
result_unit = quantity2.unit / self.unit
|
|
249
|
+
result_value = quantity2._base_value() // self._base_value()
|
|
250
|
+
return self.__class__(
|
|
251
|
+
normalize_scalar(result_value),
|
|
252
|
+
normalize_result_unit(result_unit),
|
|
253
|
+
)
|
|
132
254
|
self._require_real_scalar(quantity2, "floor division")
|
|
133
|
-
|
|
255
|
+
result_unit = self._dimensionless_unit() / self.unit
|
|
256
|
+
result_value = quantity2 // self._base_value()
|
|
257
|
+
return self.__class__(
|
|
258
|
+
normalize_reverse_floor(result_value, self),
|
|
259
|
+
normalize_result_unit(result_unit),
|
|
260
|
+
)
|
|
134
261
|
|
|
135
262
|
def __mod__(self, quantity2: object) -> "Quantity":
|
|
136
263
|
if isinstance(quantity2, Quantity):
|
|
@@ -144,7 +271,12 @@ class Quantity:
|
|
|
144
271
|
self._require_compatible_quantity(quantity2, "modulo")
|
|
145
272
|
return self.__class__(quantity2.value % self.value, self.unit)
|
|
146
273
|
self._require_real_scalar(quantity2, "modulo")
|
|
147
|
-
|
|
274
|
+
result_unit = self._dimensionless_unit() / self.unit
|
|
275
|
+
result_value = quantity2 % self._base_value()
|
|
276
|
+
return self.__class__(
|
|
277
|
+
normalize_scalar(result_value),
|
|
278
|
+
normalize_result_unit(result_unit),
|
|
279
|
+
)
|
|
148
280
|
|
|
149
281
|
def __divmod__(self, quantity2: object) -> tuple["Quantity", "Quantity"]:
|
|
150
282
|
return self.__floordiv__(quantity2), self.__mod__(quantity2)
|
|
@@ -162,7 +294,12 @@ class Quantity:
|
|
|
162
294
|
)
|
|
163
295
|
if self.is_unitless:
|
|
164
296
|
return self.__class__(self.value ** exponent, self.unit)
|
|
165
|
-
|
|
297
|
+
result_unit = self.unit ** exponent
|
|
298
|
+
result_value = self._base_value() ** exponent
|
|
299
|
+
return self.__class__(
|
|
300
|
+
normalize_scalar(result_value),
|
|
301
|
+
normalize_result_unit(result_unit),
|
|
302
|
+
)
|
|
166
303
|
|
|
167
304
|
def __neg__(self) -> "Quantity":
|
|
168
305
|
return self.__class__(-self.value, self.unit)
|
|
@@ -200,8 +337,12 @@ def float_quantity(quantity: Quantity) -> Quantity:
|
|
|
200
337
|
|
|
201
338
|
def long_quantity(quantity: Quantity) -> Quantity:
|
|
202
339
|
"""Legacy compatibility helper equivalent to ``int_quantity``."""
|
|
203
|
-
|
|
204
|
-
|
|
340
|
+
return deprecated_call(
|
|
341
|
+
"long_quantity",
|
|
342
|
+
"int_quantity",
|
|
343
|
+
"1.0.0",
|
|
344
|
+
lambda: int_quantity(quantity),
|
|
345
|
+
)
|
|
205
346
|
|
|
206
347
|
|
|
207
348
|
def complex_quantity(quantity: Quantity) -> Quantity:
|
|
@@ -210,7 +351,119 @@ def complex_quantity(quantity: Quantity) -> Quantity:
|
|
|
210
351
|
return Quantity(complex(quantity.value), quantity.unit)
|
|
211
352
|
|
|
212
353
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
354
|
+
def convert(quantity: Quantity, target_unit: BaseUnit) -> Quantity:
|
|
355
|
+
"""
|
|
356
|
+
Convert a quantity to a compatible scale-only target unit.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
quantity: Quantity to convert.
|
|
360
|
+
target_unit: Unit definition with the same dimension.
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
Quantity expressed in ``target_unit``.
|
|
364
|
+
|
|
365
|
+
Raises:
|
|
366
|
+
UnitOperandError: If ``quantity`` is not a Quantity.
|
|
367
|
+
InvalidUnitError: If ``target_unit`` is not a unit definition.
|
|
368
|
+
UnitCompatibilityError: If conversion is not scale-only compatible.
|
|
369
|
+
"""
|
|
370
|
+
require_quantity_operand(quantity, "conversion")
|
|
371
|
+
return quantity.to(target_unit)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def value(quantity: Quantity) -> Scalar:
|
|
375
|
+
"""
|
|
376
|
+
Return the numeric value of a quantity without converting it.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
quantity: Quantity to inspect.
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
Numeric value stored on the quantity.
|
|
383
|
+
|
|
384
|
+
Raises:
|
|
385
|
+
UnitOperandError: If ``quantity`` is not a Quantity.
|
|
386
|
+
"""
|
|
387
|
+
require_quantity_operand(quantity, "value extraction")
|
|
388
|
+
return quantity.value
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def unit(quantity: Quantity) -> BaseUnit:
|
|
392
|
+
"""
|
|
393
|
+
Return the unit definition of a quantity.
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
quantity: Quantity to inspect.
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
Unit definition stored on the quantity.
|
|
400
|
+
|
|
401
|
+
Raises:
|
|
402
|
+
UnitOperandError: If ``quantity`` is not a Quantity.
|
|
403
|
+
"""
|
|
404
|
+
require_quantity_operand(quantity, "unit extraction")
|
|
405
|
+
return quantity.unit
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def multiplier(quantity_or_unit: Quantity | BaseUnit) -> float:
|
|
409
|
+
"""
|
|
410
|
+
Return the multiplicative factor to the canonical base dimension.
|
|
411
|
+
|
|
412
|
+
Args:
|
|
413
|
+
quantity_or_unit: Quantity or unit definition to inspect.
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
Multiplicative factor for the unit carried by the input.
|
|
417
|
+
|
|
418
|
+
Raises:
|
|
419
|
+
UnitOperandError: If the input is neither Quantity nor BaseUnit.
|
|
420
|
+
"""
|
|
421
|
+
if isinstance(quantity_or_unit, Quantity):
|
|
422
|
+
return quantity_or_unit.unit.conversion_factor
|
|
423
|
+
if isinstance(quantity_or_unit, BaseUnit):
|
|
424
|
+
return quantity_or_unit.conversion_factor
|
|
425
|
+
raise UnitOperandError(
|
|
426
|
+
"unsupported operand for multiplier extraction: {}".format(
|
|
427
|
+
type(quantity_or_unit).__name__
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def int_unit(quantity: Quantity) -> Quantity:
|
|
433
|
+
"""Legacy compatibility helper equivalent to ``int_quantity``."""
|
|
434
|
+
return deprecated_call(
|
|
435
|
+
"int_unit",
|
|
436
|
+
"int_quantity",
|
|
437
|
+
"1.0.0",
|
|
438
|
+
lambda: int_quantity(quantity),
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def float_unit(quantity: Quantity) -> Quantity:
|
|
443
|
+
"""Legacy compatibility helper equivalent to ``float_quantity``."""
|
|
444
|
+
return deprecated_call(
|
|
445
|
+
"float_unit",
|
|
446
|
+
"float_quantity",
|
|
447
|
+
"1.0.0",
|
|
448
|
+
lambda: float_quantity(quantity),
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def long_unit(quantity: Quantity) -> Quantity:
|
|
453
|
+
"""Legacy compatibility helper equivalent to ``int_quantity``."""
|
|
454
|
+
return deprecated_call(
|
|
455
|
+
"long_unit",
|
|
456
|
+
"int_quantity",
|
|
457
|
+
"1.0.0",
|
|
458
|
+
lambda: int_quantity(quantity),
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def complex_unit(quantity: Quantity) -> Quantity:
|
|
463
|
+
"""Legacy compatibility helper equivalent to ``complex_quantity``."""
|
|
464
|
+
return deprecated_call(
|
|
465
|
+
"complex_unit",
|
|
466
|
+
"complex_quantity",
|
|
467
|
+
"1.0.0",
|
|
468
|
+
lambda: complex_quantity(quantity),
|
|
469
|
+
)
|