1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
"""
pint.formatter
~~~~~~~~~~~~~~
Format units for pint.
:copyright: 2016 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
from __future__ import annotations
from collections.abc import Iterable
from numbers import Number
from .delegates.formatter._format_helpers import (
_PRETTY_EXPONENTS, # noqa: F401
)
from .delegates.formatter._format_helpers import (
join_u as _join, # noqa: F401
)
from .delegates.formatter._format_helpers import (
pretty_fmt_exponent as _pretty_fmt_exponent, # noqa: F401
)
from .delegates.formatter._spec_helpers import (
_BASIC_TYPES, # noqa: F401
FORMATTER, # noqa: F401
REGISTERED_FORMATTERS,
extract_custom_flags, # noqa: F401
remove_custom_flags, # noqa: F401
)
from .delegates.formatter._spec_helpers import (
parse_spec as _parse_spec, # noqa: F401
)
from .delegates.formatter._spec_helpers import (
split_format as split_format, # noqa: F401
)
# noqa
from .delegates.formatter._to_register import register_unit_format # noqa: F401
# Backwards compatiblity stuff
from .delegates.formatter.latex import (
_EXP_PATTERN, # noqa: F401
latex_escape, # noqa: F401
matrix_to_latex, # noqa: F401
ndarray_to_latex, # noqa: F401
ndarray_to_latex_parts, # noqa: F401
siunitx_format_unit, # noqa: F401
vector_to_latex, # noqa: F401
)
def formatter(
items: Iterable[tuple[str, Number]],
as_ratio: bool = True,
single_denominator: bool = False,
product_fmt: str = " * ",
division_fmt: str = " / ",
power_fmt: str = "{} ** {}",
parentheses_fmt: str = "({0})",
exp_call: FORMATTER = "{:n}".format,
sort: bool = True,
) -> str:
"""Format a list of (name, exponent) pairs.
Parameters
----------
items : list
a list of (name, exponent) pairs.
as_ratio : bool, optional
True to display as ratio, False as negative powers. (Default value = True)
single_denominator : bool, optional
all with terms with negative exponents are
collected together. (Default value = False)
product_fmt : str
the format used for multiplication. (Default value = " * ")
division_fmt : str
the format used for division. (Default value = " / ")
power_fmt : str
the format used for exponentiation. (Default value = "{} ** {}")
parentheses_fmt : str
the format used for parenthesis. (Default value = "({0})")
exp_call : callable
(Default value = lambda x: f"{x:n}")
sort : bool, optional
True to sort the formatted units alphabetically (Default value = True)
Returns
-------
str
the formula as a string.
"""
join_u = _join
if sort is False:
items = tuple(items)
else:
items = sorted(items)
if not items:
return ""
if as_ratio:
fun = lambda x: exp_call(abs(x))
else:
fun = exp_call
pos_terms, neg_terms = [], []
for key, value in items:
if value == 1:
pos_terms.append(key)
elif value > 0:
pos_terms.append(power_fmt.format(key, fun(value)))
elif value == -1 and as_ratio:
neg_terms.append(key)
else:
neg_terms.append(power_fmt.format(key, fun(value)))
if not as_ratio:
# Show as Product: positive * negative terms ** -1
return _join(product_fmt, pos_terms + neg_terms)
# Show as Ratio: positive terms / negative terms
pos_ret = _join(product_fmt, pos_terms) or "1"
if not neg_terms:
return pos_ret
if single_denominator:
neg_ret = join_u(product_fmt, neg_terms)
if len(neg_terms) > 1:
neg_ret = parentheses_fmt.format(neg_ret)
else:
neg_ret = join_u(division_fmt, neg_terms)
# TODO: first or last pos_ret should be pluralized
return _join(division_fmt, [pos_ret, neg_ret])
def format_unit(unit, spec: str, registry=None, **options):
# registry may be None to allow formatting `UnitsContainer` objects
# in that case, the spec may not be "Lx"
if not unit:
if spec.endswith("%"):
return ""
else:
return "dimensionless"
if not spec:
spec = "D"
if registry is None:
_formatter = REGISTERED_FORMATTERS.get(spec, None)
else:
try:
_formatter = registry.formatter._formatters[spec]
except Exception:
_formatter = registry.formatter._formatters.get(spec, None)
if _formatter is None:
raise ValueError(f"Unknown conversion specified: {spec}")
return _formatter.format_unit(unit)
|