""" pint.delegates.formatter.html ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implements: - HTML: suitable for web/jupyter notebook outputs. :copyright: 2022 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ from __future__ import annotations import re from typing import TYPE_CHECKING, Any, Iterable from ..._typing import Magnitude from ...compat import Unpack, ndarray, np from ...util import iterable from ._compound_unit_helpers import ( BabelKwds, SortFunc, localize_per, prepare_compount_unit, ) from ._format_helpers import ( formatter, join_mu, join_unc, override_locale, ) from ._spec_helpers import ( remove_custom_flags, split_format, ) from .plain import BaseFormatter if TYPE_CHECKING: from ...facets.measurement import Measurement from ...facets.plain import MagnitudeT, PlainQuantity, PlainUnit _EXP_PATTERN = re.compile(r"([0-9]\.?[0-9]*)e(-?)\+?0*([0-9]+)") class HTMLFormatter(BaseFormatter): """HTML localizable text formatter.""" def format_magnitude( self, magnitude: Magnitude, mspec: str = "", **babel_kwds: Unpack[BabelKwds] ) -> str: with override_locale(mspec, babel_kwds.get("locale", None)) as format_number: if hasattr(magnitude, "_repr_html_"): # If magnitude has an HTML repr, nest it within Pint's mstr = magnitude._repr_html_() # type: ignore assert isinstance(mstr, str) else: if isinstance(magnitude, ndarray): # Need to override for scalars, which are detected as iterable, # and don't respond to printoptions. if magnitude.ndim == 0: mstr = format_number(magnitude) else: with np.printoptions(formatter={"float_kind": format_number}): mstr = ( "
" + format(magnitude).replace("\n", "") + ""
)
elif not iterable(magnitude):
# Use plain text for scalars
mstr = format_number(magnitude)
else:
# Use monospace font for other array-likes
mstr = (
""
+ format_number(magnitude).replace("\n", "
")
+ ""
)
m = _EXP_PATTERN.match(mstr)
_exp_formatter = lambda s: f"{s}"
if m:
exp = int(m.group(2) + m.group(3))
mstr = _EXP_PATTERN.sub(r"\1×10" + _exp_formatter(exp), mstr)
return mstr
def format_unit(
self,
unit: PlainUnit | Iterable[tuple[str, Any]],
uspec: str = "",
sort_func: SortFunc | None = None,
**babel_kwds: Unpack[BabelKwds],
) -> str:
numerator, denominator = prepare_compount_unit(
unit,
uspec,
sort_func=sort_func,
**babel_kwds,
registry=self._registry,
)
if babel_kwds.get("locale", None):
length = babel_kwds.get("length") or ("short" if "~" in uspec else "long")
division_fmt = localize_per(length, babel_kwds.get("locale"), "{}/{}")
else:
division_fmt = "{}/{}"
return formatter(
numerator,
denominator,
as_ratio=True,
single_denominator=True,
product_fmt=r" ",
division_fmt=division_fmt,
power_fmt=r"{}{}",
parentheses_fmt=r"({})",
)
def format_quantity(
self,
quantity: PlainQuantity[MagnitudeT],
qspec: str = "",
sort_func: SortFunc | None = None,
**babel_kwds: Unpack[BabelKwds],
) -> str:
registry = self._registry
mspec, uspec = split_format(
qspec, registry.formatter.default_format, registry.separate_format_defaults
)
if iterable(quantity.magnitude):
# Use HTML table instead of plain text template for array-likes
joint_fstring = (
"| Magnitude | " "{} |
|---|---|
| Units | {} |