|
4 | 4 | import re
|
5 | 5 |
|
6 | 6 | import pint
|
7 |
| -from pint import ( # noqa: F401 |
8 |
| - DimensionalityError, |
9 |
| - UndefinedUnitError, |
10 |
| - UnitStrippedWarning, |
11 |
| -) |
| 7 | +from packaging.version import Version |
12 | 8 |
|
13 | 9 | from .utils import emit_user_level_warning
|
14 | 10 |
|
15 |
| -# from `xclim`'s unit support module with permission of the maintainers |
16 |
| -try: |
17 | 11 |
|
18 |
| - @pint.register_unit_format("cf") |
19 |
| - def short_formatter(unit, registry, **options): |
20 |
| - """Return a CF-compliant unit string from a `pint` unit. |
21 |
| -
|
22 |
| - Parameters |
23 |
| - ---------- |
24 |
| - unit : pint.UnitContainer |
25 |
| - Input unit. |
26 |
| - registry : pint.UnitRegistry |
27 |
| - the associated registry |
28 |
| - **options |
29 |
| - Additional options (may be ignored) |
30 |
| -
|
31 |
| - Returns |
32 |
| - ------- |
33 |
| - out : str |
34 |
| - Units following CF-Convention, using symbols. |
35 |
| - """ |
36 |
| - import re |
37 |
| - |
38 |
| - # convert UnitContainer back to Unit |
39 |
| - unit = registry.Unit(unit) |
40 |
| - # Print units using abbreviations (millimeter -> mm) |
41 |
| - s = f"{unit:~D}" |
42 |
| - |
43 |
| - # Search and replace patterns |
44 |
| - pat = r"(?P<inverse>(?:1 )?/ )?(?P<unit>\w+)(?: \*\* (?P<pow>\d))?" |
45 |
| - |
46 |
| - def repl(m): |
47 |
| - i, u, p = m.groups() |
48 |
| - p = p or (1 if i else "") |
49 |
| - neg = "-" if i else "" |
50 |
| - |
51 |
| - return f"{u}{neg}{p}" |
52 |
| - |
53 |
| - out, n = re.subn(pat, repl, s) |
54 |
| - |
55 |
| - # Remove multiplications |
56 |
| - out = out.replace(" * ", " ") |
57 |
| - # Delta degrees: |
58 |
| - out = out.replace("Δ°", "delta_deg") |
59 |
| - return out.replace("percent", "%") |
| 12 | +@pint.register_unit_format("cf") |
| 13 | +def short_formatter(unit, registry, **options): |
| 14 | + """Return a CF-compliant unit string from a `pint` unit. |
| 15 | +
|
| 16 | + Parameters |
| 17 | + ---------- |
| 18 | + unit : pint.UnitContainer |
| 19 | + Input unit. |
| 20 | + registry : pint.UnitRegistry |
| 21 | + The associated registry |
| 22 | + **options |
| 23 | + Additional options (may be ignored) |
| 24 | +
|
| 25 | + Returns |
| 26 | + ------- |
| 27 | + out : str |
| 28 | + Units following CF-Convention, using symbols. |
| 29 | + """ |
| 30 | + # pint 0.24.1 gives {"dimensionless": 1} for non-shortened dimensionless units |
| 31 | + # CF uses "1" to denote fractions and dimensionless quantities |
| 32 | + if unit == {"dimensionless": 1} or not unit: |
| 33 | + return "1" |
| 34 | + |
| 35 | + # If u is a name, get its symbol (same as pint's "~" pre-formatter) |
| 36 | + # otherwise, assume a symbol (pint should have already raised on invalid units before this) |
| 37 | + unit = pint.util.UnitsContainer( |
| 38 | + { |
| 39 | + registry._get_symbol(u) if u in registry._units else u: exp |
| 40 | + for u, exp in unit.items() |
| 41 | + } |
| 42 | + ) |
| 43 | + |
| 44 | + # Change in formatter signature in pint 0.24 |
| 45 | + if Version(pint.__version__) < Version("0.24"): |
| 46 | + args = (unit.items(),) |
| 47 | + else: |
| 48 | + # Numerators splitted from denominators |
| 49 | + args = ( |
| 50 | + ((u, e) for u, e in unit.items() if e >= 0), |
| 51 | + ((u, e) for u, e in unit.items() if e < 0), |
| 52 | + ) |
| 53 | + |
| 54 | + out = pint.formatter(*args, as_ratio=False, product_fmt=" ", power_fmt="{}{}") |
| 55 | + # To avoid potentiel unicode problems in netCDF. In both cases, this unit is not recognized by udunits |
| 56 | + return out.replace("Δ°", "delta_deg") |
60 | 57 |
|
61 |
| -except ImportError: |
62 |
| - pass |
63 | 58 |
|
64 | 59 | # ------
|
65 | 60 | # Reused with modification from MetPy under the terms of the BSD 3-Clause License.
|
|
0 commit comments