Skip to main content
`Ctrl`+`K`

#

# Source code for pint.formatting

    """
        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 numbers import Number
    from typing import Iterable

    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)