Skip to content

Commit

Permalink
Merge branch 'master' into app_reg
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewgsavage authored Feb 2, 2024
2 parents 8671676 + 3cc2d36 commit be6e247
Show file tree
Hide file tree
Showing 38 changed files with 2,302 additions and 1,186 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ jobs:
if: ${{ matrix.extras != null }}
run: pip install ${{matrix.extras}}

- name: Install locales
if: ${{ matrix.extras != null }}
run: |
sudo apt-get install language-pack-es language-pack-fr language-pack-ro
sudo localedef -i es_ES -f UTF-8 es_ES
sudo localedef -i fr_FR -f UTF-8 fr_FR
sudo localedef -i ro_RO -f UTF-8 ro_RO
- name: Install dependencies
run: |
sudo apt install -y graphviz
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ jobs:
key: pip-docs
restore-keys: pip-docs

- name: Install locales
run: |
sudo apt-get install language-pack-fr
sudo localedef -i fr_FR -f UTF-8 fr_FR
- name: Install dependencies
run: |
sudo apt install -y pandoc
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2
build:
os: ubuntu-22.04
tools:
python: "3.9"
python: "3.11"
sphinx:
configuration: docs/conf.py
fail_on_warning: false
Expand Down
3 changes: 1 addition & 2 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ Pint Changelog
- Better support for uncertainties (See #1611, #1614)
- Implement `numpy.broadcast_arrays` (#1607)
- An ndim attribute has been added to Quantity and DataFrame has been added to upcast
types for pint-pandas compatibility. (#1596)
types for pint-pandas compatibility. (#1596)
- Fix a recursion error that would be raised when passing quantities to `cond` and `x`.
(Issue #1510, #1530)
- Update test_non_int tests for pytest.
Expand All @@ -145,7 +145,6 @@ types for pint-pandas compatibility. (#1596)
- Better support for pandas and dask.
- Fix masked arrays (with multiple values) incorrectly being passed through
setitem (Issue #1584)

- Add Quantity.to_preferred

0.19.2 (2022-04-23)
Expand Down
14 changes: 8 additions & 6 deletions docs/getting/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,8 @@ If Babel_ is installed you can translate unit names to any language

.. doctest::

>>> accel.format_babel(locale='fr_FR')
'1.3 mètre par seconde²'
>>> ureg.formatter.format_quantity(accel, locale='fr_FR')
'1,3 mètres/secondes²'

You can also specify the format locale at the registry level either at creation:

Expand All @@ -440,20 +440,22 @@ or later:

.. doctest::

>>> ureg.set_fmt_locale('fr_FR')
>>> ureg.formatter.set_locale('fr_FR')

and by doing that, string formatting is now localized:

.. doctest::

>>> ureg.default_format = 'P'
>>> accel = 1.3 * ureg.parse_units('meter/second**2')
>>> str(accel)
'1.3 mètre par seconde²'
'1,3 mètres/secondes²'
>>> "%s" % accel
'1.3 mètre par seconde²'
'1,3 mètres/secondes²'
>>> "{}".format(accel)
'1.3 mètre par seconde²'
'1,3 mètres/secondes²'

If you want to customize string formatting, take a look at :ref:`formatting`.


.. _`default list of units`: https://github.com/hgrecco/pint/blob/master/pint/default_en.txt
Expand Down
199 changes: 81 additions & 118 deletions docs/user/formatting.rst
Original file line number Diff line number Diff line change
@@ -1,159 +1,122 @@
.. currentmodule:: pint


.. ipython:: python
:suppress:
import pint
String formatting specification
===============================

The conversion of :py:class:`Unit` and :py:class:`Quantity` objects to strings (e.g.
through the :py:class:`str` builtin or f-strings) can be customized using :ref:`format
specifications <formatspec>`. The basic format is:
The conversion of :py:class:`Unit`, :py:class:`Quantity` and :py:class:`Measurement`
objects to strings (e.g. through the :py:class:`str` builtin or f-strings) can be
customized using :ref:`format specifications <formatspec>`. The basic format is:

.. code-block:: none
[magnitude format][modifier][unit format]
[magnitude format][modifier][pint format]
where each part is optional and the order of these is arbitrary.

In case the format is omitted, the corresponding value in the object's
``.default_format`` attribute (:py:attr:`Quantity.default_format` or
:py:attr:`Unit.default_format`) is filled in. For example:

.. ipython::

In [1]: ureg = pint.get_application_registry()
...: ureg.default_format = "~P"
.. doctest::

In [2]: u = ureg.Unit("m ** 2 / s ** 2")
...: f"{u}"
>>> import pint
>>> ureg = pint.get_application_registry()
>>> q = 2.3e-6 * ureg.m ** 3 / (ureg.s ** 2 * ureg.kg)
>>> f"{q:~P}" # short pretty
'2.3×10⁻⁶ m³/kg/s²'
>>> f"{q:~#P}" # compact short pretty
'2.3 mm³/g/s²'
>>> f"{q:P#~}" # also compact short pretty
'2.3 mm³/g/s²'
>>> f"{q:.2f~#P}" # short compact pretty with 2 float digits
'2.30 mm³/g/s²'
>>> f"{q:#~}" # short compact default
'2.3 mm ** 3 / g / s ** 2'

In [3]: u.default_format = "~C"
...: f"{u}"
In case the format is omitted, the corresponding value in the formatter
``.default_format`` attribute is filled in. For example:

In [4]: u.default_format, ureg.default_format
.. doctest::

In [5]: q = ureg.Quantity(1.25, "m ** 2 / s ** 2")
...: f"{q}"
>>> ureg.formatter.default_format = "P"
>>> f"{q}"
'2.3×10⁻⁶ meter³/kilogram/second²'

In [6]: q.default_format = ".3fP"
...: f"{q}"

In [7]: q.default_format, ureg.default_format

.. note::

In the future, the magnitude and unit format spec will be evaluated
independently, such that with a global default of
``ureg.default_format = ".3f"`` and ``f"{q:P}`` the format that
will be used is ``".3fP"``. This behavior can be opted into by
setting :py:attr:`UnitRegistry.separate_format_defaults` to :py:obj:`True`.

If both are not set, the global default of ``"D"`` and the magnitude's default
format are used instead.
Pint Format Types
-----------------
``pint`` comes with a variety of unit formats. These impact the complete representation:

.. note::
======= =============== ======================================================================
Spec Name Examples
======= =============== ======================================================================
``D`` default ``3.4e+09 kilogram * meter / second ** 2``
``P`` pretty ``3.4×10⁹ kilogram·meter/second²``
``H`` HTML ``3.4×10<sup>9</sup> kilogram meter/second<sup>2</sup>``
``L`` latex ``3.4\\times 10^{9}\\ \\frac{\\mathrm{kilogram} \\cdot \\mathrm{meter}}{\\mathrm{second}^{2}}``
``Lx`` latex siunitx ``\\SI[]{3.4e+09}{\\kilo\\gram\\meter\\per\\second\\squared}``
``C`` compact ``3.4e+09 kilogram*meter/second**2``
======= =============== ======================================================================

Modifiers may be used without specifying any format: ``"~"`` is a valid format
specification and is equal to ``"~D"``.
These examples are using `g`` as numeric modifier. :py:class:`Measurement` are also affected
by these modifiers.


Unit Format Specifications
--------------------------
The :py:class:`Unit` class ignores the magnitude format part, and the unit format
consists of just the format type.
Quantity modifiers
------------------

Let's look at some examples:
======== =================================================== ================================
Modifier Meaning Example
======== =================================================== ================================
``#`` Call :py:meth:`Quantity.to_compact` first ``1.0 m·mg/s²`` (``f"{q:#~P}"``)
======== =================================================== ================================

.. ipython:: python

ureg = pint.get_application_registry()
u = ureg.kg * ureg.m / ureg.s ** 2
Unit modifiers

f"{u:P}" # using the pretty format
f"{u:~P}" # short pretty
f"{u:P~}" # also short pretty
======== =================================================== ================================
Modifier Meaning Example
======== =================================================== ================================
``~`` Use the unit's symbol instead of its canonical name ``kg·m/s²`` (``f"{u:~P}"``)
======== =================================================== ================================

# default format
u.default_format
ureg.default_format
str(u) # default: default
f"{u:~}" # default: short default
ureg.default_format = "C" # registry default to compact
str(u) # default: compact
f"{u}" # default: compact
u.default_format = "P"
f"{u}" # default: pretty
u.default_format = "" # TODO: switch to None
ureg.default_format = "" # TODO: switch to None
f"{u}" # default: default
Magnitude modifiers
-------------------

Unit Format Types
-----------------
``pint`` comes with a variety of unit formats:
Pint uses the :ref:`format specifications <formatspec>`. However, it is important to remember
that only the type honors the locale. Using any other numeric format (e.g. `g`, `e`, `f`)
will result in a non-localized representation of the number.

======= =============== ======================================================================
Spec Name Example
======= =============== ======================================================================
``D`` default ``kilogram * meter / second ** 2``
``P`` pretty ``kilogram·meter/second²``
``H`` HTML ``kilogram meter/second<sup>2</sup>``
``L`` latex ``\frac{\mathrm{kilogram} \cdot \mathrm{meter}}{\mathrm{second}^{2}}``
``Lx`` latex siunitx ``\si[]{\kilo\gram\meter\per\second\squared}``
``C`` compact ``kilogram*meter/second**2``
======= =============== ======================================================================

Custom Unit Format Types
------------------------
Custom formats
--------------
Using :py:func:`pint.register_unit_format`, it is possible to add custom
formats:

.. ipython::

In [1]: u = ureg.Unit("m ** 3 / (s ** 2 * kg)")

In [2]: @pint.register_unit_format("simple")
...: def format_unit_simple(unit, registry, **options):
...: return " * ".join(f"{u} ** {p}" for u, p in unit.items())
.. doctest::

In [3]: f"{u:~simple}"
>>> @pint.register_unit_format("Z")
... def format_unit_simple(unit, registry, **options):
... return " * ".join(f"{u} ** {p}" for u, p in unit.items())
>>> f"{q:Z}"
'2.3e-06 meter ** 3 * second ** -2 * kilogram ** -1'

where ``unit`` is a :py:class:`dict` subclass containing the unit names and
their exponents.

Quantity Format Specifications
------------------------------
The magnitude format is forwarded to the magnitude (for a unit-spec of ``H`` the
magnitude's ``_repr_html_`` is called).
You can choose to replace the complete formatter. Briefly, the formatter if an object with the
following methods: `format_magnitude`, `format_unit`, `format_quantity`, `format_uncertainty`,
`format_measurement`. The easiest way to create your own formatter is to subclass one that you like.

Let's look at some more examples:
.. doctest::

.. ipython:: python
>>> from pint.delegates.formatter.plain import DefaultFormatter
>>> class MyFormatter(DefaultFormatter):
...
... default_format = ""
...
... def format_unit(self, unit, uspec: str = "", **babel_kwds) -> str:
... return "ups!"
...
>>> ureg.formatter = MyFormatter()
>>> str(q)
'2.3e-06 ups!'

q = 1e-6 * u

# modifiers
f"{q:~P}" # short pretty
f"{q:~#P}" # compact short pretty
f"{q:P#~}" # also compact short pretty
# additional magnitude format
f"{q:.2f~#P}" # short compact pretty with 2 float digits
f"{q:#~}" # short compact default
Quantity Format Types
---------------------
There are no special quantity formats yet.

Modifiers
---------
======== =================================================== ================================
Modifier Meaning Example
======== =================================================== ================================
``~`` Use the unit's symbol instead of its canonical name ``kg·m/s²`` (``f"{u:~P}"``)
``#`` Call :py:meth:`Quantity.to_compact` first ``1.0 m·mg/s²`` (``f"{q:#~P}"``)
======== =================================================== ================================
By replacing other methods, you can customize the output as much as you need.
4 changes: 3 additions & 1 deletion pint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

from importlib.metadata import version

from .delegates.formatter._format_helpers import formatter

from .errors import ( # noqa: F401
DefinitionSyntaxError,
DimensionalityError,
Expand All @@ -25,7 +27,7 @@
UndefinedUnitError,
UnitStrippedWarning,
)
from .formatting import formatter, register_unit_format
from .formatting import register_unit_format
from .registry import ApplicationRegistry, LazyRegistry, UnitRegistry
from .util import logger, pi_theorem # noqa: F401

Expand Down
12 changes: 12 additions & 0 deletions pint/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@
from typing_extensions import Never # noqa


if sys.version_info >= (3, 11):
from typing import Unpack # noqa
else:
from typing_extensions import Unpack # noqa


if sys.version_info >= (3, 13):
from warnings import deprecated # noqa
else:
from typing_extensions import deprecated # noqa


def missing_dependency(
package: str, display_name: Optional[str] = None
) -> Callable[..., NoReturn]:
Expand Down
3 changes: 2 additions & 1 deletion pint/delegates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@

from . import txt_defparser
from .base_defparser import ParserConfig, build_disk_cache_class
from .formatter import Formatter

__all__ = ["txt_defparser", "ParserConfig", "build_disk_cache_class"]
__all__ = ["txt_defparser", "ParserConfig", "build_disk_cache_class", "Formatter"]
Loading

0 comments on commit be6e247

Please sign in to comment.