pyconvertx 1.0.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.
- pyconvert/__init__.py +110 -0
- pyconvert/bases.py +199 -0
- pyconvert/exceptions.py +139 -0
- pyconvert/py.typed +0 -0
- pyconvert/registry.py +759 -0
- pyconvert/units.py +221 -0
- pyconvertx-1.0.0.dist-info/METADATA +315 -0
- pyconvertx-1.0.0.dist-info/RECORD +11 -0
- pyconvertx-1.0.0.dist-info/WHEEL +5 -0
- pyconvertx-1.0.0.dist-info/licenses/LICENSE +21 -0
- pyconvertx-1.0.0.dist-info/top_level.txt +1 -0
pyconvert/registry.py
ADDED
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
"""Central conversion registry: the data PyConvert converts with.
|
|
2
|
+
|
|
3
|
+
Every unit is stored as its size **relative to its category's base unit**, so a
|
|
4
|
+
conversion is a single ratio::
|
|
5
|
+
|
|
6
|
+
y = x * f_source / f_target
|
|
7
|
+
|
|
8
|
+
A registry entry is either
|
|
9
|
+
|
|
10
|
+
* a plain ``float`` -- a purely multiplicative unit (14 of the 15 categories), or
|
|
11
|
+
* a ``(scale, offset)`` tuple -- an affine unit, where
|
|
12
|
+
``base_value = x * scale + offset``.
|
|
13
|
+
|
|
14
|
+
Only temperature needs offsets. Its scale and offset are written as
|
|
15
|
+
:class:`fractions.Fraction` so that ``5/9`` stays an exact ratio and
|
|
16
|
+
``100 celsius -> fahrenheit`` returns exactly ``212.0`` rather than
|
|
17
|
+
``211.99999999999997``. See :mod:`pyconvert.units` for the two code paths.
|
|
18
|
+
|
|
19
|
+
**On accuracy.** Many of these factors are exact by international definition
|
|
20
|
+
(one inch *is* 25.4 mm; it is not a measurement). Those definitions are written
|
|
21
|
+
below as exact rationals and rounded to ``float`` exactly once, at the end. The
|
|
22
|
+
difference is small but real: computing the US gallon as ``231 * 0.0254 ** 3``
|
|
23
|
+
in floating point yields ``0.0037854117839999997``, three steps of rounding
|
|
24
|
+
away from the correctly rounded ``0.003785411784``. Factors that genuinely are
|
|
25
|
+
measurements (particle masses, the solar mass) are marked as such.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from fractions import Fraction
|
|
31
|
+
from math import pi
|
|
32
|
+
|
|
33
|
+
from .exceptions import RegistryError, UnknownCategoryError, UnknownUnitError
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"ALIASES",
|
|
37
|
+
"AMBIGUOUS",
|
|
38
|
+
"BASE_UNITS",
|
|
39
|
+
"CATEGORIES",
|
|
40
|
+
"all_units",
|
|
41
|
+
"build_index",
|
|
42
|
+
"category_of",
|
|
43
|
+
"resolve",
|
|
44
|
+
"units_in",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
# --------------------------------------------------------------------------
|
|
48
|
+
# Exact defined constants, as rationals, shared so they cannot disagree.
|
|
49
|
+
# The ``_Q`` suffix marks an exact quantity; ``float()`` is applied once, where
|
|
50
|
+
# the value enters a table.
|
|
51
|
+
# --------------------------------------------------------------------------
|
|
52
|
+
_IN_Q = Fraction(254, 10_000) # inch -> metre
|
|
53
|
+
_FT_Q = 12 * _IN_Q # foot -> metre
|
|
54
|
+
_YD_Q = 3 * _FT_Q # yard -> metre
|
|
55
|
+
_MI_Q = 1760 * _YD_Q # statute mile -> metre
|
|
56
|
+
_LB_Q = Fraction(45_359_237, 100_000_000) # pound -> kilogram
|
|
57
|
+
_LBF_Q = Fraction(44_482_216_152_605, 10 ** 13) # pound-force -> newton
|
|
58
|
+
_GAL_US_Q = 231 * _IN_Q ** 3 # US liquid gallon -> m3
|
|
59
|
+
_GAL_IMP_Q = Fraction(454_609, 100_000_000) # imperial gallon -> m3
|
|
60
|
+
_BTU_Q = Fraction(105_505_585_262, 100_000_000) # BTU_IT -> joule
|
|
61
|
+
_MMHG_Q = Fraction(133_322_387_415, 10 ** 9) # mmHg -> pascal (conventional)
|
|
62
|
+
_HP_Q = 550 * _FT_Q * _LBF_Q # mechanical horsepower -> watt
|
|
63
|
+
|
|
64
|
+
# --------------------------------------------------------------------------
|
|
65
|
+
# 1. Length -- base: metre
|
|
66
|
+
# --------------------------------------------------------------------------
|
|
67
|
+
LENGTH = {
|
|
68
|
+
"m": 1.0,
|
|
69
|
+
"km": 1000.0,
|
|
70
|
+
"hm": 100.0,
|
|
71
|
+
"dam": 10.0,
|
|
72
|
+
"dm": 0.1,
|
|
73
|
+
"cm": 0.01,
|
|
74
|
+
"mm": 1e-3,
|
|
75
|
+
"um": 1e-6,
|
|
76
|
+
"nm": 1e-9,
|
|
77
|
+
"pm": 1e-12,
|
|
78
|
+
"angstrom": 1e-10, # exact
|
|
79
|
+
"in": float(_IN_Q), # exact
|
|
80
|
+
"ft": float(_FT_Q), # exact
|
|
81
|
+
"yd": float(_YD_Q), # exact
|
|
82
|
+
"mi": float(_MI_Q), # exact
|
|
83
|
+
"nmi": 1852.0, # exact (nautical mile)
|
|
84
|
+
"thou": float(_IN_Q / 1000), # exact
|
|
85
|
+
"fathom": float(6 * _FT_Q), # exact
|
|
86
|
+
"rod": float(Fraction(33, 2) * _FT_Q), # exact (16.5 ft)
|
|
87
|
+
"chain": float(66 * _FT_Q), # exact
|
|
88
|
+
"furlong": float(660 * _FT_Q), # exact
|
|
89
|
+
"ly": float(299_792_458 * Fraction(36_525, 100) * 86_400), # exact: c * Julian year
|
|
90
|
+
"au": 149_597_870_700.0, # exact (IAU 2012)
|
|
91
|
+
"pc": 648_000 / pi * 149_597_870_700.0, # exact definition, irrational value
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# --------------------------------------------------------------------------
|
|
95
|
+
# 2. Mass -- base: kilogram
|
|
96
|
+
# --------------------------------------------------------------------------
|
|
97
|
+
MASS = {
|
|
98
|
+
"kg": 1.0,
|
|
99
|
+
"g": 1e-3,
|
|
100
|
+
"mg": 1e-6,
|
|
101
|
+
"ug": 1e-9,
|
|
102
|
+
"t": 1000.0, # tonne (metric ton), exact
|
|
103
|
+
"quintal": 100.0, # exact -- v0.1.0 had 1000, wrong by 10x
|
|
104
|
+
"lb": float(_LB_Q), # exact
|
|
105
|
+
"oz": float(_LB_Q / 16), # exact -- avoirdupois ounce, NOT fluid
|
|
106
|
+
"st": float(14 * _LB_Q), # exact (stone)
|
|
107
|
+
"ton_us": float(2000 * _LB_Q), # exact (short ton)
|
|
108
|
+
"ton_uk": float(2240 * _LB_Q), # exact (long ton)
|
|
109
|
+
"grain": float(Fraction(6_479_891, 10 ** 11)), # exact
|
|
110
|
+
"carat": 2e-4, # exact (metric carat)
|
|
111
|
+
"u": 1.66053906892e-27, # measured, CODATA 2022
|
|
112
|
+
"m_e": 9.1093837139e-31, # measured, CODATA 2022 (electron)
|
|
113
|
+
"m_earth": 5.9722e24, # measured, IAU nominal
|
|
114
|
+
"m_sun": 1.98841e30, # measured, IAU nominal
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# --------------------------------------------------------------------------
|
|
118
|
+
# 3. Speed -- base: metre per second
|
|
119
|
+
# --------------------------------------------------------------------------
|
|
120
|
+
SPEED = {
|
|
121
|
+
"m/s": 1.0,
|
|
122
|
+
"km/h": float(Fraction(1000, 3600)), # exact
|
|
123
|
+
"cm/s": 0.01,
|
|
124
|
+
"mm/s": 1e-3,
|
|
125
|
+
"ft/s": float(_FT_Q), # exact
|
|
126
|
+
"mph": float(_MI_Q / 3600), # exact
|
|
127
|
+
"kn": float(Fraction(1852, 3600)), # exact (knot)
|
|
128
|
+
"c": 299_792_458.0, # exact (speed of light in vacuum)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# --------------------------------------------------------------------------
|
|
132
|
+
# 4. Area -- base: square metre
|
|
133
|
+
# --------------------------------------------------------------------------
|
|
134
|
+
AREA = {
|
|
135
|
+
"m2": 1.0,
|
|
136
|
+
"km2": 1e6,
|
|
137
|
+
"cm2": 1e-4,
|
|
138
|
+
"mm2": 1e-6,
|
|
139
|
+
"ha": 1e4, # hectare, exact
|
|
140
|
+
"are": 100.0, # exact
|
|
141
|
+
"in2": float(_IN_Q ** 2), # exact
|
|
142
|
+
"ft2": float(_FT_Q ** 2), # exact
|
|
143
|
+
"yd2": float(_YD_Q ** 2), # exact
|
|
144
|
+
"mi2": float(_MI_Q ** 2), # exact
|
|
145
|
+
"ac": float(4840 * _YD_Q ** 2), # exact (acre = 4840 square yards)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# --------------------------------------------------------------------------
|
|
149
|
+
# 5. Volume -- base: cubic metre
|
|
150
|
+
# --------------------------------------------------------------------------
|
|
151
|
+
VOLUME = {
|
|
152
|
+
"m3": 1.0,
|
|
153
|
+
"cm3": 1e-6,
|
|
154
|
+
"mm3": 1e-9,
|
|
155
|
+
"km3": 1e9,
|
|
156
|
+
"L": 1e-3, # exact
|
|
157
|
+
"mL": 1e-6,
|
|
158
|
+
"uL": 1e-9,
|
|
159
|
+
"cL": 1e-5,
|
|
160
|
+
"dL": 1e-4,
|
|
161
|
+
"kL": 1.0,
|
|
162
|
+
"in3": float(_IN_Q ** 3), # exact
|
|
163
|
+
"ft3": float(_FT_Q ** 3), # exact
|
|
164
|
+
"yd3": float(_YD_Q ** 3), # exact
|
|
165
|
+
"gal_us": float(_GAL_US_Q), # exact (US liquid gallon)
|
|
166
|
+
"qt_us": float(_GAL_US_Q / 4),
|
|
167
|
+
"pt_us": float(_GAL_US_Q / 8),
|
|
168
|
+
"cup_us": float(_GAL_US_Q / 16),
|
|
169
|
+
"floz_us": float(_GAL_US_Q / 128),
|
|
170
|
+
"tbsp_us": float(_GAL_US_Q / 256),
|
|
171
|
+
"tsp_us": float(_GAL_US_Q / 768),
|
|
172
|
+
"gal_imp": float(_GAL_IMP_Q), # exact (imperial gallon)
|
|
173
|
+
"qt_imp": float(_GAL_IMP_Q / 4),
|
|
174
|
+
"pt_imp": float(_GAL_IMP_Q / 8),
|
|
175
|
+
"floz_imp": float(_GAL_IMP_Q / 160),
|
|
176
|
+
"bbl": float(42 * _GAL_US_Q), # exact (oil barrel)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# --------------------------------------------------------------------------
|
|
180
|
+
# 6. Energy -- base: joule
|
|
181
|
+
# --------------------------------------------------------------------------
|
|
182
|
+
ENERGY = {
|
|
183
|
+
"J": 1.0,
|
|
184
|
+
"mJ": 1e-3,
|
|
185
|
+
"kJ": 1e3,
|
|
186
|
+
"MJ": 1e6,
|
|
187
|
+
"GJ": 1e9,
|
|
188
|
+
"cal": 4.184, # exact (thermochemical calorie)
|
|
189
|
+
"kcal": 4184.0, # exact
|
|
190
|
+
"cal_it": 4.1868, # exact (International Table calorie)
|
|
191
|
+
"Wh": 3600.0, # exact
|
|
192
|
+
"kWh": 3.6e6, # exact
|
|
193
|
+
"MWh": 3.6e9, # exact
|
|
194
|
+
"eV": 1.602176634e-19, # exact since the 2019 SI redefinition
|
|
195
|
+
"keV": 1.602176634e-16,
|
|
196
|
+
"MeV": 1.602176634e-13,
|
|
197
|
+
"GeV": 1.602176634e-10,
|
|
198
|
+
"erg": 1e-7, # exact
|
|
199
|
+
"BTU": float(_BTU_Q), # exact (BTU_IT)
|
|
200
|
+
"therm": 1.054804e8, # exact (US therm)
|
|
201
|
+
"ft_lbf": float(_FT_Q * _LBF_Q), # exact
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# --------------------------------------------------------------------------
|
|
205
|
+
# 7. Power -- base: watt
|
|
206
|
+
# --------------------------------------------------------------------------
|
|
207
|
+
POWER = {
|
|
208
|
+
"W": 1.0,
|
|
209
|
+
"uW": 1e-6,
|
|
210
|
+
"mW": 1e-3, # milliwatt -- lowercase m
|
|
211
|
+
"kW": 1e3,
|
|
212
|
+
"MW": 1e6, # megawatt -- uppercase M
|
|
213
|
+
"GW": 1e9,
|
|
214
|
+
"TW": 1e12,
|
|
215
|
+
"hp": float(_HP_Q), # exact (mechanical/imperial horsepower)
|
|
216
|
+
"hp_metric": 735.49875, # exact (metric horsepower, a.k.a. PS)
|
|
217
|
+
"BTU/h": float(_BTU_Q / 3600), # exact
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# --------------------------------------------------------------------------
|
|
221
|
+
# 8. Temperature -- base: kelvin. The only affine category.
|
|
222
|
+
#
|
|
223
|
+
# Entries are ``(scale, offset)`` with ``kelvin = x * scale + offset``, written
|
|
224
|
+
# as exact rationals. Fahrenheit's offset is 273.15 - 32 * 5/9, i.e. the kelvin
|
|
225
|
+
# value of 0 degrees Fahrenheit, kept as the arithmetic that defines it.
|
|
226
|
+
# --------------------------------------------------------------------------
|
|
227
|
+
TEMPERATURE = {
|
|
228
|
+
"kelvin": (Fraction(1), Fraction(0)),
|
|
229
|
+
"celsius": (Fraction(1), Fraction(27315, 100)),
|
|
230
|
+
"fahrenheit": (Fraction(5, 9), Fraction(27315, 100) - 32 * Fraction(5, 9)),
|
|
231
|
+
"rankine": (Fraction(5, 9), Fraction(0)),
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
# --------------------------------------------------------------------------
|
|
235
|
+
# 9. Time -- base: second
|
|
236
|
+
# --------------------------------------------------------------------------
|
|
237
|
+
TIME = {
|
|
238
|
+
"s": 1.0,
|
|
239
|
+
"ms": 1e-3,
|
|
240
|
+
"us": 1e-6,
|
|
241
|
+
"ns": 1e-9,
|
|
242
|
+
"ps": 1e-12, # picosecond -- lowercase; see "PS" alias
|
|
243
|
+
"min": 60.0,
|
|
244
|
+
"h": 3600.0,
|
|
245
|
+
"d": 86_400.0,
|
|
246
|
+
"week": 604_800.0,
|
|
247
|
+
"fortnight": 1_209_600.0,
|
|
248
|
+
"yr": 31_557_600.0, # Julian year, exactly 365.25 days
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
# --------------------------------------------------------------------------
|
|
252
|
+
# 10. Pressure -- base: pascal
|
|
253
|
+
# --------------------------------------------------------------------------
|
|
254
|
+
PRESSURE = {
|
|
255
|
+
"Pa": 1.0,
|
|
256
|
+
"hPa": 100.0,
|
|
257
|
+
"kPa": 1e3,
|
|
258
|
+
"MPa": 1e6,
|
|
259
|
+
"GPa": 1e9,
|
|
260
|
+
"bar": 1e5, # exact
|
|
261
|
+
"mbar": 100.0, # exact
|
|
262
|
+
"atm": 101_325.0, # exact
|
|
263
|
+
"torr": float(Fraction(101_325, 760)), # exact (1/760 atm)
|
|
264
|
+
"mmHg": float(_MMHG_Q), # exact by convention
|
|
265
|
+
"inHg": float(_MMHG_Q * _IN_Q / Fraction(1, 1000)), # 25.4 mmHg, exact
|
|
266
|
+
"psi": float(_LBF_Q / _IN_Q ** 2), # exact
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
# --------------------------------------------------------------------------
|
|
270
|
+
# 11. Frequency -- base: hertz
|
|
271
|
+
# --------------------------------------------------------------------------
|
|
272
|
+
FREQUENCY = {
|
|
273
|
+
"Hz": 1.0,
|
|
274
|
+
"mHz": 1e-3, # millihertz -- lowercase m
|
|
275
|
+
"kHz": 1e3,
|
|
276
|
+
"MHz": 1e6, # megahertz -- uppercase M
|
|
277
|
+
"GHz": 1e9,
|
|
278
|
+
"THz": 1e12,
|
|
279
|
+
"rpm": float(Fraction(1, 60)), # exact (revolutions per minute)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
# --------------------------------------------------------------------------
|
|
283
|
+
# 12. Digital storage -- base: byte
|
|
284
|
+
#
|
|
285
|
+
# Decimal prefixes are powers of 1000 (kB, MB, ...); binary prefixes are powers
|
|
286
|
+
# of 1024 (KiB, MiB, ...). Both are present and they are NOT interchangeable.
|
|
287
|
+
# Bit units are spelled out ("kbit", not "kb") so they can never be confused
|
|
288
|
+
# with byte units by case alone.
|
|
289
|
+
# --------------------------------------------------------------------------
|
|
290
|
+
STORAGE = {
|
|
291
|
+
"B": 1.0,
|
|
292
|
+
"bit": 0.125, # exact
|
|
293
|
+
"kB": 1e3,
|
|
294
|
+
"MB": 1e6,
|
|
295
|
+
"GB": 1e9,
|
|
296
|
+
"TB": 1e12,
|
|
297
|
+
"PB": 1e15,
|
|
298
|
+
"KiB": 1024.0,
|
|
299
|
+
"MiB": 1024.0 ** 2,
|
|
300
|
+
"GiB": 1024.0 ** 3,
|
|
301
|
+
"TiB": 1024.0 ** 4,
|
|
302
|
+
"PiB": 1024.0 ** 5,
|
|
303
|
+
"kbit": 125.0, # 1000 bits
|
|
304
|
+
"Mbit": 1.25e5,
|
|
305
|
+
"Gbit": 1.25e8,
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
# --------------------------------------------------------------------------
|
|
309
|
+
# 13. Angle -- base: radian
|
|
310
|
+
# --------------------------------------------------------------------------
|
|
311
|
+
ANGLE = {
|
|
312
|
+
"rad": 1.0,
|
|
313
|
+
"mrad": 1e-3,
|
|
314
|
+
"deg": pi / 180,
|
|
315
|
+
"grad": pi / 200, # gradian / gon
|
|
316
|
+
"arcmin": pi / 10_800,
|
|
317
|
+
"arcsec": pi / 648_000,
|
|
318
|
+
"turn": 2 * pi, # full revolution
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
# --------------------------------------------------------------------------
|
|
322
|
+
# 14. Electric current -- base: ampere
|
|
323
|
+
# --------------------------------------------------------------------------
|
|
324
|
+
CURRENT = {
|
|
325
|
+
"A": 1.0,
|
|
326
|
+
"kA": 1e3,
|
|
327
|
+
"mA": 1e-3,
|
|
328
|
+
"uA": 1e-6,
|
|
329
|
+
"nA": 1e-9,
|
|
330
|
+
"pA": 1e-12,
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
# --------------------------------------------------------------------------
|
|
334
|
+
# 15. Electric potential -- base: volt
|
|
335
|
+
# --------------------------------------------------------------------------
|
|
336
|
+
POTENTIAL = {
|
|
337
|
+
"V": 1.0,
|
|
338
|
+
"MV": 1e6,
|
|
339
|
+
"kV": 1e3,
|
|
340
|
+
"mV": 1e-3,
|
|
341
|
+
"uV": 1e-6,
|
|
342
|
+
"nV": 1e-9,
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
# --------------------------------------------------------------------------
|
|
346
|
+
# Category table and base units
|
|
347
|
+
# --------------------------------------------------------------------------
|
|
348
|
+
CATEGORIES = {
|
|
349
|
+
"length": LENGTH,
|
|
350
|
+
"mass": MASS,
|
|
351
|
+
"speed": SPEED,
|
|
352
|
+
"area": AREA,
|
|
353
|
+
"volume": VOLUME,
|
|
354
|
+
"energy": ENERGY,
|
|
355
|
+
"power": POWER,
|
|
356
|
+
"temperature": TEMPERATURE,
|
|
357
|
+
"time": TIME,
|
|
358
|
+
"pressure": PRESSURE,
|
|
359
|
+
"frequency": FREQUENCY,
|
|
360
|
+
"storage": STORAGE,
|
|
361
|
+
"angle": ANGLE,
|
|
362
|
+
"current": CURRENT,
|
|
363
|
+
"potential": POTENTIAL,
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
BASE_UNITS = {
|
|
367
|
+
"length": "m",
|
|
368
|
+
"mass": "kg",
|
|
369
|
+
"speed": "m/s",
|
|
370
|
+
"area": "m2",
|
|
371
|
+
"volume": "m3",
|
|
372
|
+
"energy": "J",
|
|
373
|
+
"power": "W",
|
|
374
|
+
"temperature": "kelvin",
|
|
375
|
+
"time": "s",
|
|
376
|
+
"pressure": "Pa",
|
|
377
|
+
"frequency": "Hz",
|
|
378
|
+
"storage": "B",
|
|
379
|
+
"angle": "rad",
|
|
380
|
+
"current": "A",
|
|
381
|
+
"potential": "V",
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
# --------------------------------------------------------------------------
|
|
385
|
+
# Aliases: alternative spellings that resolve to a canonical symbol.
|
|
386
|
+
#
|
|
387
|
+
# Lookup is case-sensitive, because it has to be: mW is a milliwatt and MW a
|
|
388
|
+
# megawatt, mHz is a millihertz and MHz a megahertz, and KB and KiB differ by
|
|
389
|
+
# 2.4%. Folding case would make those indistinguishable. Aliases below cover
|
|
390
|
+
# the spellings people actually type instead.
|
|
391
|
+
#
|
|
392
|
+
# Deliberately absent: bare "C", "F" and "R" (they are the SI symbols for
|
|
393
|
+
# coulomb, farad and roentgen, so claiming them for temperature would block
|
|
394
|
+
# those categories later); bare "pt" (point or pint); bare "ton" (three
|
|
395
|
+
# different tons differing by up to 12%). Those must be spelled out.
|
|
396
|
+
# --------------------------------------------------------------------------
|
|
397
|
+
ALIASES = {
|
|
398
|
+
# -- length ----------------------------------------------------------
|
|
399
|
+
"metre": "m", "meter": "m", "metres": "m", "meters": "m",
|
|
400
|
+
"kilometre": "km", "kilometer": "km", "kilometres": "km", "kilometers": "km",
|
|
401
|
+
"centimetre": "cm", "centimeter": "cm", "centimetres": "cm", "centimeters": "cm",
|
|
402
|
+
"millimetre": "mm", "millimeter": "mm",
|
|
403
|
+
"micrometre": "um", "micrometer": "um", "micron": "um",
|
|
404
|
+
"nanometre": "nm", "nanometer": "nm", "picometre": "pm", "picometer": "pm",
|
|
405
|
+
"decimetre": "dm", "decimeter": "dm", "hectometre": "hm", "hectometer": "hm",
|
|
406
|
+
"decametre": "dam", "decameter": "dam",
|
|
407
|
+
"inch": "in", "inches": "in",
|
|
408
|
+
"foot": "ft", "feet": "ft",
|
|
409
|
+
"yard": "yd", "yards": "yd",
|
|
410
|
+
"mile": "mi", "miles": "mi",
|
|
411
|
+
"nautical_mile": "nmi", "nautical_miles": "nmi",
|
|
412
|
+
"mil": "thou",
|
|
413
|
+
"fathoms": "fathom", "rods": "rod", "chains": "chain", "furlongs": "furlong",
|
|
414
|
+
"lightyear": "ly", "light_year": "ly", "lightyears": "ly", "light_years": "ly",
|
|
415
|
+
"astronomical_unit": "au", "parsec": "pc", "parsecs": "pc",
|
|
416
|
+
"Å": "angstrom", "angstroms": "angstrom",
|
|
417
|
+
|
|
418
|
+
# -- mass ------------------------------------------------------------
|
|
419
|
+
"kilogram": "kg", "kilogramme": "kg", "kilograms": "kg", "kgs": "kg",
|
|
420
|
+
"gram": "g", "gramme": "g", "grams": "g",
|
|
421
|
+
"milligram": "mg", "milligrams": "mg", "microgram": "ug", "micrograms": "ug",
|
|
422
|
+
"tonne": "t", "tonnes": "t", "metric_ton": "t", "metric_tonne": "t",
|
|
423
|
+
"quintals": "quintal",
|
|
424
|
+
"pound": "lb", "pounds": "lb", "lbs": "lb",
|
|
425
|
+
"ounce": "oz", "ounces": "oz",
|
|
426
|
+
"stone": "st", "stones": "st",
|
|
427
|
+
"short_ton": "ton_us", "us_ton": "ton_us",
|
|
428
|
+
"long_ton": "ton_uk", "imperial_ton": "ton_uk", "uk_ton": "ton_uk",
|
|
429
|
+
"grains": "grain", "carats": "carat",
|
|
430
|
+
"amu": "u", "dalton": "u", "Da": "u", "atomic_mass_unit": "u",
|
|
431
|
+
"electron_mass": "m_e",
|
|
432
|
+
"earth_mass": "m_earth", "earthmass": "m_earth",
|
|
433
|
+
"solar_mass": "m_sun", "solarmass": "m_sun", "solar_masses": "m_sun",
|
|
434
|
+
|
|
435
|
+
# -- speed -----------------------------------------------------------
|
|
436
|
+
"mps": "m/s", "m_s": "m/s", "meters_per_second": "m/s", "metres_per_second": "m/s",
|
|
437
|
+
"kmh": "km/h", "kph": "km/h", "km_h": "km/h",
|
|
438
|
+
"kilometers_per_hour": "km/h", "kilometres_per_hour": "km/h",
|
|
439
|
+
"mi/h": "mph", "miles_per_hour": "mph",
|
|
440
|
+
"fps": "ft/s", "ft_s": "ft/s", "feet_per_second": "ft/s",
|
|
441
|
+
"knot": "kn", "knots": "kn",
|
|
442
|
+
"speed_of_light": "c",
|
|
443
|
+
|
|
444
|
+
# -- area ------------------------------------------------------------
|
|
445
|
+
"m^2": "m2", "m²": "m2", "sqm": "m2", "square_meter": "m2", "square_metre": "m2",
|
|
446
|
+
"km^2": "km2", "km²": "km2", "square_kilometer": "km2", "square_kilometre": "km2",
|
|
447
|
+
"cm^2": "cm2", "cm²": "cm2", "square_centimeter": "cm2", "square_centimetre": "cm2",
|
|
448
|
+
"mm^2": "mm2", "mm²": "mm2",
|
|
449
|
+
"in^2": "in2", "in²": "in2", "sqin": "in2", "square_inch": "in2",
|
|
450
|
+
"ft^2": "ft2", "ft²": "ft2", "sqft": "ft2",
|
|
451
|
+
"square_foot": "ft2", "square_feet": "ft2",
|
|
452
|
+
"yd^2": "yd2", "yd²": "yd2", "square_yard": "yd2",
|
|
453
|
+
"mi^2": "mi2", "mi²": "mi2", "square_mile": "mi2",
|
|
454
|
+
"hectare": "ha", "hectares": "ha",
|
|
455
|
+
"acre": "ac", "acres": "ac",
|
|
456
|
+
"ares": "are",
|
|
457
|
+
|
|
458
|
+
# -- volume ----------------------------------------------------------
|
|
459
|
+
"m^3": "m3", "m³": "m3", "cubic_meter": "m3", "cubic_metre": "m3",
|
|
460
|
+
"cm^3": "cm3", "cm³": "cm3", "cc": "cm3", "cubic_centimeter": "cm3",
|
|
461
|
+
"mm^3": "mm3", "mm³": "mm3", "km^3": "km3", "km³": "km3",
|
|
462
|
+
"in^3": "in3", "in³": "in3", "cubic_inch": "in3",
|
|
463
|
+
"ft^3": "ft3", "ft³": "ft3", "cuft": "ft3",
|
|
464
|
+
"cubic_foot": "ft3", "cubic_feet": "ft3",
|
|
465
|
+
"yd^3": "yd3", "yd³": "yd3", "cubic_yard": "yd3",
|
|
466
|
+
"l": "L", "liter": "L", "litre": "L", "liters": "L", "litres": "L",
|
|
467
|
+
"ml": "mL", "milliliter": "mL", "millilitre": "mL",
|
|
468
|
+
"cl": "cL", "centiliter": "cL", "dl": "dL", "deciliter": "dL",
|
|
469
|
+
"kl": "kL", "kiloliter": "kL", "ul": "uL", "microliter": "uL", "microlitre": "uL",
|
|
470
|
+
"gal": "gal_us", "gallon": "gal_us", "gallons": "gal_us", "gallon_us": "gal_us",
|
|
471
|
+
"quart": "qt_us", "quarts": "qt_us",
|
|
472
|
+
"pint": "pt_us", "pints": "pt_us",
|
|
473
|
+
"cup": "cup_us", "cups": "cup_us",
|
|
474
|
+
"floz": "floz_us", "fluid_ounce": "floz_us", "fl_oz": "floz_us",
|
|
475
|
+
"tbsp": "tbsp_us", "tablespoon": "tbsp_us",
|
|
476
|
+
"tsp": "tsp_us", "teaspoon": "tsp_us",
|
|
477
|
+
"gallon_imp": "gal_imp", "imperial_gallon": "gal_imp",
|
|
478
|
+
"imperial_pint": "pt_imp", "imperial_quart": "qt_imp",
|
|
479
|
+
"imperial_fluid_ounce": "floz_imp",
|
|
480
|
+
"barrel": "bbl", "barrels": "bbl", "oil_barrel": "bbl",
|
|
481
|
+
|
|
482
|
+
# -- energy ----------------------------------------------------------
|
|
483
|
+
"joule": "J", "joules": "J",
|
|
484
|
+
"kilojoule": "kJ", "megajoule": "MJ", "gigajoule": "GJ", "millijoule": "mJ",
|
|
485
|
+
"calorie": "cal", "calories": "cal",
|
|
486
|
+
"kilocalorie": "kcal", "kilocalories": "kcal", "Cal": "kcal",
|
|
487
|
+
"watt_hour": "Wh", "watthour": "Wh",
|
|
488
|
+
"kilowatt_hour": "kWh", "kilowatthour": "kWh", "kwh": "kWh",
|
|
489
|
+
"megawatt_hour": "MWh",
|
|
490
|
+
"electronvolt": "eV", "electron_volt": "eV",
|
|
491
|
+
"ergs": "erg",
|
|
492
|
+
"btu": "BTU", "Btu": "BTU", "british_thermal_unit": "BTU",
|
|
493
|
+
"therms": "therm",
|
|
494
|
+
"foot_pound": "ft_lbf", "ft_lb": "ft_lbf", "ftlb": "ft_lbf",
|
|
495
|
+
|
|
496
|
+
# -- power -----------------------------------------------------------
|
|
497
|
+
"watt": "W", "watts": "W",
|
|
498
|
+
"kilowatt": "kW", "megawatt": "MW", "gigawatt": "GW", "terawatt": "TW",
|
|
499
|
+
"milliwatt": "mW", "microwatt": "uW",
|
|
500
|
+
"horsepower": "hp", "HP": "hp", "mechanical_horsepower": "hp",
|
|
501
|
+
"PS": "hp_metric", "cv": "hp_metric", "metric_horsepower": "hp_metric",
|
|
502
|
+
"btu/h": "BTU/h", "BTU/hr": "BTU/h", "btu_per_hour": "BTU/h",
|
|
503
|
+
|
|
504
|
+
# -- temperature -----------------------------------------------------
|
|
505
|
+
"°C": "celsius", "degC": "celsius", "deg_c": "celsius", "centigrade": "celsius",
|
|
506
|
+
"°F": "fahrenheit", "degF": "fahrenheit", "deg_f": "fahrenheit",
|
|
507
|
+
"K": "kelvin", "°K": "kelvin", "kelvins": "kelvin",
|
|
508
|
+
"°R": "rankine", "degR": "rankine", "deg_r": "rankine",
|
|
509
|
+
|
|
510
|
+
# -- time ------------------------------------------------------------
|
|
511
|
+
"sec": "s", "secs": "s", "second": "s", "seconds": "s",
|
|
512
|
+
"msec": "ms", "millisecond": "ms", "milliseconds": "ms",
|
|
513
|
+
"usec": "us", "microsecond": "us", "microseconds": "us",
|
|
514
|
+
"nsec": "ns", "nanosecond": "ns", "picosecond": "ps",
|
|
515
|
+
"minute": "min", "minutes": "min", "mins": "min",
|
|
516
|
+
"hour": "h", "hours": "h", "hr": "h", "hrs": "h",
|
|
517
|
+
"day": "d", "days": "d",
|
|
518
|
+
"weeks": "week", "fortnights": "fortnight",
|
|
519
|
+
"year": "yr", "years": "yr", "julian_year": "yr",
|
|
520
|
+
|
|
521
|
+
# -- pressure --------------------------------------------------------
|
|
522
|
+
"pascal": "Pa", "pascals": "Pa",
|
|
523
|
+
"kilopascal": "kPa", "megapascal": "MPa", "gigapascal": "GPa",
|
|
524
|
+
"hectopascal": "hPa",
|
|
525
|
+
"bars": "bar", "millibar": "mbar", "millibars": "mbar",
|
|
526
|
+
"atmosphere": "atm", "atmospheres": "atm",
|
|
527
|
+
"Torr": "torr",
|
|
528
|
+
"mmhg": "mmHg", "mm_hg": "mmHg",
|
|
529
|
+
"inhg": "inHg", "in_hg": "inHg",
|
|
530
|
+
"PSI": "psi", "lbf/in2": "psi", "pounds_per_square_inch": "psi",
|
|
531
|
+
|
|
532
|
+
# -- frequency -------------------------------------------------------
|
|
533
|
+
"hertz": "Hz", "hz": "Hz",
|
|
534
|
+
"kilohertz": "kHz", "megahertz": "MHz", "gigahertz": "GHz",
|
|
535
|
+
"terahertz": "THz", "millihertz": "mHz",
|
|
536
|
+
"RPM": "rpm", "rev/min": "rpm", "revolutions_per_minute": "rpm",
|
|
537
|
+
|
|
538
|
+
# -- storage ---------------------------------------------------------
|
|
539
|
+
"byte": "B", "bytes": "B", "bits": "bit",
|
|
540
|
+
"KB": "kB", "kilobyte": "kB", "kilobytes": "kB",
|
|
541
|
+
"megabyte": "MB", "megabytes": "MB",
|
|
542
|
+
"gigabyte": "GB", "gigabytes": "GB",
|
|
543
|
+
"terabyte": "TB", "terabytes": "TB", "petabyte": "PB", "petabytes": "PB",
|
|
544
|
+
"kibibyte": "KiB", "mebibyte": "MiB", "gibibyte": "GiB",
|
|
545
|
+
"tebibyte": "TiB", "pebibyte": "PiB",
|
|
546
|
+
"kilobit": "kbit", "megabit": "Mbit", "gigabit": "Gbit",
|
|
547
|
+
|
|
548
|
+
# -- angle -----------------------------------------------------------
|
|
549
|
+
"°": "deg", "degree": "deg", "degrees": "deg",
|
|
550
|
+
"radian": "rad", "radians": "rad", "milliradian": "mrad",
|
|
551
|
+
"gradian": "grad", "gradians": "grad", "gon": "grad",
|
|
552
|
+
"arcminute": "arcmin", "arcminutes": "arcmin",
|
|
553
|
+
"arcsecond": "arcsec", "arcseconds": "arcsec",
|
|
554
|
+
"revolution": "turn", "revolutions": "turn", "rev": "turn", "turns": "turn",
|
|
555
|
+
|
|
556
|
+
# -- current ---------------------------------------------------------
|
|
557
|
+
"ampere": "A", "amperes": "A", "amp": "A", "amps": "A",
|
|
558
|
+
"kiloampere": "kA", "milliampere": "mA", "microampere": "uA",
|
|
559
|
+
"nanoampere": "nA", "picoampere": "pA",
|
|
560
|
+
|
|
561
|
+
# -- potential -------------------------------------------------------
|
|
562
|
+
"volt": "V", "volts": "V",
|
|
563
|
+
"megavolt": "MV", "kilovolt": "kV",
|
|
564
|
+
"millivolt": "mV", "microvolt": "uV", "nanovolt": "nV",
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
# --------------------------------------------------------------------------
|
|
568
|
+
# Micro-sign aliases, generated rather than typed.
|
|
569
|
+
#
|
|
570
|
+
# "u" is written two ways that look identical on screen: MICRO SIGN (U+00B5),
|
|
571
|
+
# which is what a Mac's Option-M produces, and GREEK SMALL LETTER MU (U+03BC).
|
|
572
|
+
# A dict treats them as different keys, so "microseconds" typed by one user
|
|
573
|
+
# would silently fail for another. Both map to the ASCII spelling.
|
|
574
|
+
# Generated, so a new micro-prefixed unit picks this up automatically.
|
|
575
|
+
# --------------------------------------------------------------------------
|
|
576
|
+
_MICRO_SIGNS = ("\u00b5", "\u03bc")
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def add_micro_aliases(categories: dict, aliases: dict) -> None:
|
|
580
|
+
"""Map both micro signs onto the ASCII ``u`` spelling of each unit.
|
|
581
|
+
|
|
582
|
+
``len > 1`` excludes ``"u"`` itself, the atomic mass unit, which is a unit
|
|
583
|
+
and not a prefix.
|
|
584
|
+
"""
|
|
585
|
+
for table in categories.values():
|
|
586
|
+
for symbol in table:
|
|
587
|
+
if len(symbol) > 1 and symbol.startswith("u"):
|
|
588
|
+
for sign in _MICRO_SIGNS:
|
|
589
|
+
aliases.setdefault(sign + symbol[1:], symbol)
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def build_index(
|
|
593
|
+
categories: dict, aliases: dict, base_units: dict
|
|
594
|
+
) -> tuple[dict, list[str]]:
|
|
595
|
+
"""Build the symbol index and collect every inconsistency found.
|
|
596
|
+
|
|
597
|
+
Returns ``(index, problems)``, where ``index`` maps a canonical symbol to
|
|
598
|
+
``(category, entry)`` and ``problems`` is empty when the registry is sound.
|
|
599
|
+
Problems are collected rather than raised one at a time, so editing the
|
|
600
|
+
registry surfaces every mistake in a single run.
|
|
601
|
+
|
|
602
|
+
The inputs are arguments rather than module globals on purpose: that is what
|
|
603
|
+
lets a test hand it a deliberately broken registry and assert on the report.
|
|
604
|
+
|
|
605
|
+
>>> build_index({"a": {"x": 1.0}, "b": {"x": 1.0}},
|
|
606
|
+
... {}, {"a": "x", "b": "x"})[1]
|
|
607
|
+
["symbol 'x' is defined in both 'a' and 'b'"]
|
|
608
|
+
>>> build_index({"a": {"x": 1.0}}, {"q": "nope"}, {"a": "x"})[1]
|
|
609
|
+
["alias 'q' points at unknown unit 'nope'"]
|
|
610
|
+
"""
|
|
611
|
+
index: dict[str, tuple[str, object]] = {}
|
|
612
|
+
problems: list[str] = []
|
|
613
|
+
|
|
614
|
+
for category, table in categories.items():
|
|
615
|
+
for symbol, entry in table.items():
|
|
616
|
+
if symbol in index:
|
|
617
|
+
problems.append(
|
|
618
|
+
f"symbol {symbol!r} is defined in both "
|
|
619
|
+
f"{index[symbol][0]!r} and {category!r}"
|
|
620
|
+
)
|
|
621
|
+
index[symbol] = (category, entry)
|
|
622
|
+
|
|
623
|
+
for alias, target in aliases.items():
|
|
624
|
+
if alias in index:
|
|
625
|
+
problems.append(
|
|
626
|
+
f"alias {alias!r} shadows the canonical symbol {alias!r} "
|
|
627
|
+
f"in {index[alias][0]!r}"
|
|
628
|
+
)
|
|
629
|
+
if target not in index:
|
|
630
|
+
problems.append(f"alias {alias!r} points at unknown unit {target!r}")
|
|
631
|
+
|
|
632
|
+
for category, base in base_units.items():
|
|
633
|
+
if category not in categories:
|
|
634
|
+
problems.append(f"BASE_UNITS has category {category!r} with no table")
|
|
635
|
+
elif base not in categories[category]:
|
|
636
|
+
problems.append(f"base unit {base!r} is not present in {category!r}")
|
|
637
|
+
|
|
638
|
+
for category in categories:
|
|
639
|
+
if category not in base_units:
|
|
640
|
+
problems.append(f"category {category!r} has no declared base unit")
|
|
641
|
+
continue
|
|
642
|
+
base = base_units[category]
|
|
643
|
+
if base not in categories[category]:
|
|
644
|
+
continue # already reported by the loop above; don't crash on it
|
|
645
|
+
entry = categories[category][base]
|
|
646
|
+
scale, offset = entry if isinstance(entry, tuple) else (entry, 0)
|
|
647
|
+
if scale != 1 or offset != 0:
|
|
648
|
+
problems.append(
|
|
649
|
+
f"base unit {base!r} of {category!r} must have "
|
|
650
|
+
f"scale 1 and offset 0, has {entry!r}"
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
return index, problems
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
add_micro_aliases(CATEGORIES, ALIASES)
|
|
657
|
+
_INDEX, _problems = build_index(CATEGORIES, ALIASES, BASE_UNITS)
|
|
658
|
+
|
|
659
|
+
if _problems:
|
|
660
|
+
raise RegistryError(
|
|
661
|
+
"the unit registry is inconsistent:\n - " + "\n - ".join(_problems)
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
del _problems
|
|
665
|
+
|
|
666
|
+
#: Every spelling the registry accepts: canonical symbols plus aliases.
|
|
667
|
+
_SPELLINGS = sorted(set(_INDEX) | set(ALIASES))
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
# --------------------------------------------------------------------------
|
|
671
|
+
# Lookup API
|
|
672
|
+
# --------------------------------------------------------------------------
|
|
673
|
+
#: Spellings the registry refuses on purpose, each with the fix.
|
|
674
|
+
#:
|
|
675
|
+
#: These are not typos, they are genuinely ambiguous: resolving them to one
|
|
676
|
+
#: arbitrary meaning would return a confidently wrong number. Fuzzy matching is
|
|
677
|
+
#: no help here either -- it answers "C" with "c", the speed of light -- so each
|
|
678
|
+
#: one carries an explicit message instead.
|
|
679
|
+
AMBIGUOUS = {
|
|
680
|
+
"C": "use 'celsius' or 'degC' (bare 'C' is the SI symbol for coulomb)",
|
|
681
|
+
"F": "use 'fahrenheit' or 'degF' (bare 'F' is the SI symbol for farad)",
|
|
682
|
+
"R": "use 'rankine' or 'degR' (bare 'R' is the symbol for roentgen)",
|
|
683
|
+
"ton": (
|
|
684
|
+
"'ton' means three different masses: use 't' (tonne, 1000 kg), "
|
|
685
|
+
"'ton_us' (short, 907.18474 kg) or 'ton_uk' (long, 1016.0469088 kg)"
|
|
686
|
+
),
|
|
687
|
+
"tons": "see 'ton': use 't', 'ton_us' or 'ton_uk'",
|
|
688
|
+
"pt": "'pt' is both point and pint: use 'pt_us' or 'pt_imp' for pints",
|
|
689
|
+
"b": "use 'bit' or 'B' (byte) -- 'b' and 'B' differ only by case",
|
|
690
|
+
"kb": "use 'kbit' (1000 bits) or 'kB' (1000 bytes)",
|
|
691
|
+
"mb": "use 'Mbit' (10**6 bits) or 'MB' (10**6 bytes)",
|
|
692
|
+
"gb": "use 'Gbit' (10**9 bits) or 'GB' (10**9 bytes)",
|
|
693
|
+
"tb": "use 'TB' (10**12 bytes) or 'TiB' (2**40 bytes)",
|
|
694
|
+
"cal_cap": "use 'kcal' for the food Calorie, or 'cal' for the gram calorie",
|
|
695
|
+
"fl_ounce": "use 'floz_us' or 'floz_imp'",
|
|
696
|
+
"hp_imp": "use 'hp' (mechanical) or 'hp_metric' (PS)",
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def as_affine(entry: object) -> tuple[object, object]:
|
|
701
|
+
"""Return ``(scale, offset)`` for a registry entry.
|
|
702
|
+
|
|
703
|
+
Plain numeric entries are purely multiplicative, so their offset is zero.
|
|
704
|
+
"""
|
|
705
|
+
if isinstance(entry, tuple):
|
|
706
|
+
return entry
|
|
707
|
+
return entry, 0
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
def resolve(unit: object) -> tuple[str, str, object]:
|
|
711
|
+
"""Resolve any accepted spelling to ``(symbol, category, entry)``.
|
|
712
|
+
|
|
713
|
+
>>> resolve("kilometre")[:2]
|
|
714
|
+
('km', 'length')
|
|
715
|
+
>>> resolve("MiB")[:2]
|
|
716
|
+
('MiB', 'storage')
|
|
717
|
+
|
|
718
|
+
Raises :class:`~pyconvert.exceptions.UnknownUnitError` otherwise, with a
|
|
719
|
+
suggestion where one is close enough to be useful.
|
|
720
|
+
"""
|
|
721
|
+
if not isinstance(unit, str):
|
|
722
|
+
raise UnknownUnitError(unit, _SPELLINGS)
|
|
723
|
+
symbol = ALIASES.get(unit, unit)
|
|
724
|
+
try:
|
|
725
|
+
category, entry = _INDEX[symbol]
|
|
726
|
+
except KeyError:
|
|
727
|
+
if unit in AMBIGUOUS:
|
|
728
|
+
raise UnknownUnitError(unit, hint=AMBIGUOUS[unit]) from None
|
|
729
|
+
raise UnknownUnitError(unit, _SPELLINGS) from None
|
|
730
|
+
return symbol, category, entry
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def category_of(unit: object) -> str:
|
|
734
|
+
"""Return the category a unit belongs to.
|
|
735
|
+
|
|
736
|
+
>>> category_of("psi")
|
|
737
|
+
'pressure'
|
|
738
|
+
"""
|
|
739
|
+
return resolve(unit)[1]
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def units_in(category: str) -> list[str]:
|
|
743
|
+
"""Return the canonical symbols of one category, sorted.
|
|
744
|
+
|
|
745
|
+
>>> units_in("temperature")
|
|
746
|
+
['celsius', 'fahrenheit', 'kelvin', 'rankine']
|
|
747
|
+
"""
|
|
748
|
+
try:
|
|
749
|
+
table = CATEGORIES[category]
|
|
750
|
+
except KeyError:
|
|
751
|
+
raise UnknownCategoryError(category, list(CATEGORIES)) from None
|
|
752
|
+
return sorted(table)
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
def all_units(include_aliases: bool = False) -> list[str]:
|
|
756
|
+
"""Return every canonical symbol, sorted; with aliases if asked."""
|
|
757
|
+
if include_aliases:
|
|
758
|
+
return list(_SPELLINGS)
|
|
759
|
+
return sorted(_INDEX)
|