summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES1
-rw-r--r--docs/formatting.rst39
-rw-r--r--pint/formatting.py10
-rw-r--r--pint/quantity.py56
-rw-r--r--pint/testsuite/test_quantity.py18
-rw-r--r--pint/unit.py2
6 files changed, 104 insertions, 22 deletions
diff --git a/CHANGES b/CHANGES
index 142c95a..7a2f80b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,7 @@ Pint Changelog
0.19 (unreleased)
-----------------
+- Deprecate the old format defaulting behavior and prepare for the new one (Issue #1407)
- Fix a bug for offset units of higher dimension, e.g. gauge pressure.
(Issue #1066, thanks dalito)
- Fix type hints of function wrapper (Issue #1431)
diff --git a/docs/formatting.rst b/docs/formatting.rst
index 74fb8bd..5b4c91f 100644
--- a/docs/formatting.rst
+++ b/docs/formatting.rst
@@ -20,16 +20,45 @@ specifications <formatspec>`. The basic format is:
where each part is optional and the order of these is arbitrary.
-In case any part (except the modifier) is omitted, the corresponding value in
-:py:attr:`Quantity.default_format` or :py:attr:`Unit.default_format` is filled in. If
-that is not set (it evaluates to ``False``), :py:attr:`UnitRegistry.default_format` is
-used. If both are not set, the global default of ``"D"`` and the magnitude's default
+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.UnitRegistry()
+ ...: ureg.default_format = "~P"
+
+ In [2]: u = ureg.Unit("m ** 2 / s ** 2")
+ ...: f"{u}"
+
+ In [3]: u.default_format = "~C"
+ ...: f"{u}"
+
+ In [4]: u.default_format, ureg.default_format
+
+ In [5]: q = ureg.Quantity(1.25, "m ** 2 / s ** 2")
+ ...: f"{q}"
+
+ 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"``.
+
+If both are not set, the global default of ``"D"`` and the magnitude's default
format are used instead.
.. note::
Modifiers may be used without specifying any format: ``"~"`` is a valid format
- specification.
+ specification and is equal to ``"~D"``.
Unit Format Specifications
diff --git a/pint/formatting.py b/pint/formatting.py
index a04205d..5a458db 100644
--- a/pint/formatting.py
+++ b/pint/formatting.py
@@ -455,14 +455,20 @@ def siunitx_format_unit(units, registry):
def extract_custom_flags(spec):
import re
- flag_re = re.compile("(" + "|".join(list(_FORMATTERS.keys()) + ["~"]) + ")")
+ if not spec:
+ return ""
+
+ # sort by length, with longer items first
+ known_flags = sorted(_FORMATTERS.keys(), key=len, reverse=True)
+
+ flag_re = re.compile("(" + "|".join(known_flags + ["~"]) + ")")
custom_flags = flag_re.findall(spec)
return "".join(custom_flags)
def remove_custom_flags(spec):
- for flag in list(_FORMATTERS.keys()) + ["~"]:
+ for flag in sorted(_FORMATTERS.keys(), key=len, reverse=True) + ["~"]:
if flag:
spec = spec.replace(flag, "")
return spec
diff --git a/pint/quantity.py b/pint/quantity.py
index 2b89df3..e742874 100644
--- a/pint/quantity.py
+++ b/pint/quantity.py
@@ -345,18 +345,47 @@ class Quantity(PrettyIPython, SharedRegistryObject, Generic[_MagnitudeType]):
if self._REGISTRY.fmt_locale is not None:
return self.format_babel(spec)
- spec = spec or self.default_format
+ mspec = remove_custom_flags(spec)
+ uspec = extract_custom_flags(spec)
+
+ default_mspec = remove_custom_flags(self.default_format)
+ default_uspec = extract_custom_flags(self.default_format)
+ if spec:
+ if not uspec and default_uspec:
+ warnings.warn(
+ (
+ "The given format spec does not contain a unit formatter."
+ " Falling back to the builtin defaults, but in the future"
+ " the unit formatter specified in the `default_format`"
+ " attribute will be used instead."
+ ),
+ DeprecationWarning,
+ )
+ if not mspec and default_mspec:
+ warnings.warn(
+ (
+ "The given format spec does not contain a magnitude formatter."
+ " Falling back to the builtin defaults, but in the future"
+ " the magnitude formatter specified in the `default_format`"
+ " attribute will be used instead."
+ ),
+ DeprecationWarning,
+ )
+ else:
+ mspec, uspec = default_mspec, default_uspec
# If Compact is selected, do it at the beginning
if "#" in spec:
- spec = spec.replace("#", "")
+ # TODO: don't replace '#'
+ mspec = mspec.replace("#", "")
+ uspec = uspec.replace("#", "")
obj = self.to_compact()
else:
obj = self
- if "L" in spec:
+ if "L" in uspec:
allf = plain_allf = r"{}\ {}"
- elif "H" in spec:
+ elif "H" in uspec:
allf = plain_allf = "{} {}"
if iterable(obj.magnitude):
# Use HTML table instead of plain text template for array-likes
@@ -370,20 +399,19 @@ class Quantity(PrettyIPython, SharedRegistryObject, Generic[_MagnitudeType]):
else:
allf = plain_allf = "{} {}"
- if "Lx" in spec:
+ if "Lx" in uspec:
# the LaTeX siunitx code
- spec = spec.replace("Lx", "")
# TODO: add support for extracting options
opts = ""
ustr = siunitx_format_unit(obj.units._units, obj._REGISTRY)
allf = r"\SI[%s]{{{}}}{{{}}}" % opts
else:
# Hand off to unit formatting
- uspec = extract_custom_flags(spec)
- ustr = format(obj.units, uspec)
+ # TODO: only use `uspec` after completing the deprecation cycle
+ ustr = format(obj.units, mspec + uspec)
- mspec = remove_custom_flags(spec)
- if "H" in spec:
+ # mspec = remove_custom_flags(spec)
+ if "H" in uspec:
# HTML formatting
if hasattr(obj.magnitude, "_repr_html_"):
# If magnitude has an HTML repr, nest it within Pint's
@@ -417,7 +445,7 @@ class Quantity(PrettyIPython, SharedRegistryObject, Generic[_MagnitudeType]):
+ "</pre>"
)
elif isinstance(self.magnitude, ndarray):
- if "L" in spec:
+ if "L" in uspec:
# Use ndarray LaTeX special formatting
mstr = ndarray_to_latex(obj.magnitude, mspec)
else:
@@ -432,12 +460,12 @@ class Quantity(PrettyIPython, SharedRegistryObject, Generic[_MagnitudeType]):
else:
mstr = format(obj.magnitude, mspec).replace("\n", "")
- if "L" in spec:
+ if "L" in uspec:
mstr = self._exp_pattern.sub(r"\1\\times 10^{\2\3}", mstr)
- elif "H" in spec or "P" in spec:
+ elif "H" in uspec or "P" in uspec:
m = self._exp_pattern.match(mstr)
_exp_formatter = (
- _pretty_fmt_exponent if "P" in spec else lambda s: f"<sup>{s}</sup>"
+ _pretty_fmt_exponent if "P" in uspec else lambda s: f"<sup>{s}</sup>"
)
if m:
exp = int(m.group(2) + m.group(3))
diff --git a/pint/testsuite/test_quantity.py b/pint/testsuite/test_quantity.py
index 5030074..c29fd19 100644
--- a/pint/testsuite/test_quantity.py
+++ b/pint/testsuite/test_quantity.py
@@ -262,6 +262,24 @@ class TestQuantity(QuantityTestCase):
ureg.default_format = spec
assert f"{x}" == result
+ def test_formatting_override_default_units(self):
+ ureg = UnitRegistry()
+ ureg.default_format = "~"
+ x = ureg.Quantity(4, "m ** 2")
+
+ assert f"{x:dP}" == "4 meterĀ²"
+ with pytest.warns(DeprecationWarning):
+ assert f"{x:d}" == "4 meter ** 2"
+
+ def test_formatting_override_default_magnitude(self):
+ ureg = UnitRegistry()
+ ureg.default_format = ".2f"
+ x = ureg.Quantity(4, "m ** 2")
+
+ assert f"{x:dP}" == "4 meterĀ²"
+ with pytest.warns(DeprecationWarning):
+ assert f"{x:D}" == "4 meter ** 2"
+
def test_exponent_formatting(self):
ureg = UnitRegistry()
x = ureg.Quantity(1e20, "meter")
diff --git a/pint/unit.py b/pint/unit.py
index 8221d19..18a22de 100644
--- a/pint/unit.py
+++ b/pint/unit.py
@@ -80,7 +80,7 @@ class Unit(PrettyIPython, SharedRegistryObject):
return "<Unit('{}')>".format(self._units)
def __format__(self, spec) -> str:
- spec = spec or extract_custom_flags(self.default_format)
+ spec = extract_custom_flags(spec or self.default_format)
if "~" in spec:
if not self._units:
return ""