summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHernan <hernan.grecco@gmail.com>2022-05-01 11:23:30 -0300
committerHernan <hernan.grecco@gmail.com>2022-05-01 11:24:27 -0300
commit6f5ff5a34f8f5fa8db1c7d87d8e8e80ac53a56ef (patch)
tree8e14f5737cab1f32193a23ab58f9d039a1b11bde
parent5282bc09a905c7d223eea46cf1f136c30f45c688 (diff)
downloadpint-6f5ff5a34f8f5fa8db1c7d87d8e8e80ac53a56ef.tar.gz
Typing and documentation changes
-rw-r--r--pint/__init__.py4
-rw-r--r--pint/_typing.py5
-rw-r--r--pint/facets/context/__init__.py3
-rw-r--r--pint/facets/context/definitions.py2
-rw-r--r--pint/facets/context/objects.py2
-rw-r--r--pint/facets/context/registry.py2
-rw-r--r--pint/facets/formatting/__init__.py225
-rw-r--r--pint/facets/formatting/objects.py227
-rw-r--r--pint/facets/formatting/registry.py17
-rw-r--r--pint/facets/measurement/__init__.py219
-rw-r--r--pint/facets/measurement/objects.py192
-rw-r--r--pint/facets/measurement/registry.py42
-rw-r--r--pint/facets/nonmultiplicative/__init__.py8
-rw-r--r--pint/facets/nonmultiplicative/definitions.py8
-rw-r--r--pint/facets/nonmultiplicative/objects.py8
-rw-r--r--pint/facets/nonmultiplicative/registry.py8
-rw-r--r--pint/facets/numpy/numpy_func.py6
-rw-r--r--pint/facets/numpy/quantity.py8
-rw-r--r--pint/facets/numpy/unit.py8
-rw-r--r--pint/facets/plain/definitions.py2
-rw-r--r--pint/facets/plain/registry.py8
-rw-r--r--pint/facets/plain/unit.py2
-rwxr-xr-xpint/pint-convert2
-rw-r--r--pint/util.py11
24 files changed, 566 insertions, 453 deletions
diff --git a/pint/__init__.py b/pint/__init__.py
index a39c281..a450323 100644
--- a/pint/__init__.py
+++ b/pint/__init__.py
@@ -11,6 +11,8 @@
:license: BSD, see LICENSE for more details.
"""
+from __future__ import annotations
+
from importlib.metadata import version
from .errors import ( # noqa: F401
@@ -63,7 +65,7 @@ def _unpickle(cls, *args):
object of type cls
"""
- from pint.facets.plain.unit import UnitsContainer
+ from pint.facets.plain import UnitsContainer
for arg in args:
# Prefixed units are defined within the registry
diff --git a/pint/_typing.py b/pint/_typing.py
index b8c5675..cfb803b 100644
--- a/pint/_typing.py
+++ b/pint/_typing.py
@@ -1,8 +1,9 @@
+from __future__ import annotations
+
from typing import TYPE_CHECKING, Any, Callable, Tuple, TypeVar, Union
if TYPE_CHECKING:
- from .facets.plain import Quantity, Unit
- from .facets.plain.unit import UnitsContainer
+ from .facets.plain import Quantity, Unit, UnitsContainer
UnitLike = Union[str, "UnitsContainer", "Unit"]
diff --git a/pint/facets/context/__init__.py b/pint/facets/context/__init__.py
index 7058e19..61685a2 100644
--- a/pint/facets/context/__init__.py
+++ b/pint/facets/context/__init__.py
@@ -2,7 +2,8 @@
pint.facets.context
~~~~~~~~~~~~~~~~~~~
- Adds pint the capability to contexts.
+ Adds pint the capability to contexts: predefined conversions
+ between incompatible dimensions.
:copyright: 2022 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
diff --git a/pint/facets/context/definitions.py b/pint/facets/context/definitions.py
index f1ac7f7..6e07ba5 100644
--- a/pint/facets/context/definitions.py
+++ b/pint/facets/context/definitions.py
@@ -1,5 +1,5 @@
"""
- pint.facets.systems.definitions
+ pint.facets.context.definitions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: 2022 by Pint Authors, see AUTHORS for more details.
diff --git a/pint/facets/context/objects.py b/pint/facets/context/objects.py
index cc408d3..0aca430 100644
--- a/pint/facets/context/objects.py
+++ b/pint/facets/context/objects.py
@@ -1,5 +1,5 @@
"""
- pint.facets.systems.objects
+ pint.facets.context.objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: 2022 by Pint Authors, see AUTHORS for more details.
diff --git a/pint/facets/context/registry.py b/pint/facets/context/registry.py
index a663326..27f19bb 100644
--- a/pint/facets/context/registry.py
+++ b/pint/facets/context/registry.py
@@ -1,5 +1,5 @@
"""
- pint.facets.systems.registry
+ pint.facets.context.registry
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: 2022 by Pint Authors, see AUTHORS for more details.
diff --git a/pint/facets/formatting/__init__.py b/pint/facets/formatting/__init__.py
index 7d423a1..f9c8c82 100644
--- a/pint/facets/formatting/__init__.py
+++ b/pint/facets/formatting/__init__.py
@@ -10,226 +10,7 @@
from __future__ import annotations
-import re
-from typing import Any
+from .objects import FormattingQuantity, FormattingUnit
+from .registry import FormattingRegistry
-from ...compat import babel_parse, ndarray, np
-from ...formatting import (
- _pretty_fmt_exponent,
- extract_custom_flags,
- format_unit,
- ndarray_to_latex,
- remove_custom_flags,
- siunitx_format_unit,
- split_format,
-)
-from ...util import iterable
-from ..plain import UnitsContainer
-
-
-class FormattingQuantity:
-
- _exp_pattern = re.compile(r"([0-9]\.?[0-9]*)e(-?)\+?0*([0-9]+)")
-
- def __format__(self, spec: str) -> str:
- if self._REGISTRY.fmt_locale is not None:
- return self.format_babel(spec)
-
- mspec, uspec = split_format(
- spec, self.default_format, self._REGISTRY.separate_format_defaults
- )
-
- # If Compact is selected, do it at the beginning
- if "#" in spec:
- # TODO: don't replace '#'
- mspec = mspec.replace("#", "")
- uspec = uspec.replace("#", "")
- obj = self.to_compact()
- else:
- obj = self
-
- if "L" in uspec:
- allf = plain_allf = r"{}\ {}"
- elif "H" in uspec:
- allf = plain_allf = "{} {}"
- if iterable(obj.magnitude):
- # Use HTML table instead of plain text template for array-likes
- allf = (
- "<table><tbody>"
- "<tr><th>Magnitude</th>"
- "<td style='text-align:left;'>{}</td></tr>"
- "<tr><th>Units</th><td style='text-align:left;'>{}</td></tr>"
- "</tbody></table>"
- )
- else:
- allf = plain_allf = "{} {}"
-
- if "Lx" in uspec:
- # the LaTeX siunitx code
- # 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
- # TODO: only use `uspec` after completing the deprecation cycle
- ustr = format(obj.units, mspec + uspec)
-
- # 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
- mstr = obj.magnitude._repr_html_()
- else:
- if isinstance(self.magnitude, ndarray):
- # Use custom ndarray text formatting with monospace font
- formatter = "{{:{}}}".format(mspec)
- # Need to override for scalars, which are detected as iterable,
- # and don't respond to printoptions.
- if self.magnitude.ndim == 0:
- allf = plain_allf = "{} {}"
- mstr = formatter.format(obj.magnitude)
- else:
- with np.printoptions(
- formatter={"float_kind": formatter.format}
- ):
- mstr = (
- "<pre>"
- + format(obj.magnitude).replace("\n", "<br>")
- + "</pre>"
- )
- elif not iterable(obj.magnitude):
- # Use plain text for scalars
- mstr = format(obj.magnitude, mspec)
- else:
- # Use monospace font for other array-likes
- mstr = (
- "<pre>"
- + format(obj.magnitude, mspec).replace("\n", "<br>")
- + "</pre>"
- )
- elif isinstance(self.magnitude, ndarray):
- if "L" in uspec:
- # Use ndarray LaTeX special formatting
- mstr = ndarray_to_latex(obj.magnitude, mspec)
- else:
- # Use custom ndarray text formatting--need to handle scalars differently
- # since they don't respond to printoptions
- formatter = "{{:{}}}".format(mspec)
- if obj.magnitude.ndim == 0:
- mstr = formatter.format(obj.magnitude)
- else:
- with np.printoptions(formatter={"float_kind": formatter.format}):
- mstr = format(obj.magnitude).replace("\n", "")
- else:
- mstr = format(obj.magnitude, mspec).replace("\n", "")
-
- if "L" in uspec:
- mstr = self._exp_pattern.sub(r"\1\\times 10^{\2\3}", mstr)
- elif "H" in uspec or "P" in uspec:
- m = self._exp_pattern.match(mstr)
- _exp_formatter = (
- _pretty_fmt_exponent if "P" in uspec else lambda s: f"<sup>{s}</sup>"
- )
- if m:
- exp = int(m.group(2) + m.group(3))
- mstr = self._exp_pattern.sub(r"\1×10" + _exp_formatter(exp), mstr)
-
- if allf == plain_allf and ustr.startswith("1 /"):
- # Write e.g. "3 / s" instead of "3 1 / s"
- ustr = ustr[2:]
- return allf.format(mstr, ustr).strip()
-
- def _repr_pretty_(self, p, cycle):
- if cycle:
- super()._repr_pretty_(p, cycle)
- else:
- p.pretty(self.magnitude)
- p.text(" ")
- p.pretty(self.units)
-
- def format_babel(self, spec: str = "", **kwspec: Any) -> str:
- spec = spec or self.default_format
-
- # standard cases
- if "#" in spec:
- spec = spec.replace("#", "")
- obj = self.to_compact()
- else:
- obj = self
- kwspec = dict(kwspec)
- if "length" in kwspec:
- kwspec["babel_length"] = kwspec.pop("length")
-
- loc = kwspec.get("locale", self._REGISTRY.fmt_locale)
- if loc is None:
- raise ValueError("Provide a `locale` value to localize translation.")
-
- kwspec["locale"] = babel_parse(loc)
- kwspec["babel_plural_form"] = kwspec["locale"].plural_form(obj.magnitude)
- return "{} {}".format(
- format(obj.magnitude, remove_custom_flags(spec)),
- obj.units.format_babel(spec, **kwspec),
- ).replace("\n", "")
-
- def __str__(self) -> str:
- if self._REGISTRY.fmt_locale is not None:
- return self.format_babel()
-
- return format(self)
-
-
-class FormattingUnit:
- def __str__(self):
- return format(self)
-
- def __format__(self, spec) -> str:
- _, uspec = split_format(
- spec, self.default_format, self._REGISTRY.separate_format_defaults
- )
- if "~" in uspec:
- if not self._units:
- return ""
- units = UnitsContainer(
- dict(
- (self._REGISTRY._get_symbol(key), value)
- for key, value in self._units.items()
- )
- )
- uspec = uspec.replace("~", "")
- else:
- units = self._units
-
- return format_unit(units, uspec, registry=self._REGISTRY)
-
- def format_babel(self, spec="", locale=None, **kwspec: Any) -> str:
- spec = spec or extract_custom_flags(self.default_format)
-
- if "~" in spec:
- if self.dimensionless:
- return ""
- units = UnitsContainer(
- dict(
- (self._REGISTRY._get_symbol(key), value)
- for key, value in self._units.items()
- )
- )
- spec = spec.replace("~", "")
- else:
- units = self._units
-
- locale = self._REGISTRY.fmt_locale if locale is None else locale
-
- if locale is None:
- raise ValueError("Provide a `locale` value to localize translation.")
- else:
- kwspec["locale"] = babel_parse(locale)
-
- return units.format_babel(spec, registry=self._REGISTRY, **kwspec)
-
-
-class FormattingRegistry:
-
- _quantity_class = FormattingQuantity
- _unit_class = FormattingUnit
+__all__ = [FormattingQuantity, FormattingUnit, FormattingRegistry]
diff --git a/pint/facets/formatting/objects.py b/pint/facets/formatting/objects.py
new file mode 100644
index 0000000..fb701f1
--- /dev/null
+++ b/pint/facets/formatting/objects.py
@@ -0,0 +1,227 @@
+"""
+ pint.facets.formatting.objects
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :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 Any
+
+from ...compat import babel_parse, ndarray, np
+from ...formatting import (
+ _pretty_fmt_exponent,
+ extract_custom_flags,
+ format_unit,
+ ndarray_to_latex,
+ remove_custom_flags,
+ siunitx_format_unit,
+ split_format,
+)
+from ...util import iterable
+from ..plain import UnitsContainer
+
+
+class FormattingQuantity:
+
+ _exp_pattern = re.compile(r"([0-9]\.?[0-9]*)e(-?)\+?0*([0-9]+)")
+
+ def __format__(self, spec: str) -> str:
+ if self._REGISTRY.fmt_locale is not None:
+ return self.format_babel(spec)
+
+ mspec, uspec = split_format(
+ spec, self.default_format, self._REGISTRY.separate_format_defaults
+ )
+
+ # If Compact is selected, do it at the beginning
+ if "#" in spec:
+ # TODO: don't replace '#'
+ mspec = mspec.replace("#", "")
+ uspec = uspec.replace("#", "")
+ obj = self.to_compact()
+ else:
+ obj = self
+
+ if "L" in uspec:
+ allf = plain_allf = r"{}\ {}"
+ elif "H" in uspec:
+ allf = plain_allf = "{} {}"
+ if iterable(obj.magnitude):
+ # Use HTML table instead of plain text template for array-likes
+ allf = (
+ "<table><tbody>"
+ "<tr><th>Magnitude</th>"
+ "<td style='text-align:left;'>{}</td></tr>"
+ "<tr><th>Units</th><td style='text-align:left;'>{}</td></tr>"
+ "</tbody></table>"
+ )
+ else:
+ allf = plain_allf = "{} {}"
+
+ if "Lx" in uspec:
+ # the LaTeX siunitx code
+ # 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
+ # TODO: only use `uspec` after completing the deprecation cycle
+ ustr = format(obj.units, mspec + uspec)
+
+ # 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
+ mstr = obj.magnitude._repr_html_()
+ else:
+ if isinstance(self.magnitude, ndarray):
+ # Use custom ndarray text formatting with monospace font
+ formatter = "{{:{}}}".format(mspec)
+ # Need to override for scalars, which are detected as iterable,
+ # and don't respond to printoptions.
+ if self.magnitude.ndim == 0:
+ allf = plain_allf = "{} {}"
+ mstr = formatter.format(obj.magnitude)
+ else:
+ with np.printoptions(
+ formatter={"float_kind": formatter.format}
+ ):
+ mstr = (
+ "<pre>"
+ + format(obj.magnitude).replace("\n", "<br>")
+ + "</pre>"
+ )
+ elif not iterable(obj.magnitude):
+ # Use plain text for scalars
+ mstr = format(obj.magnitude, mspec)
+ else:
+ # Use monospace font for other array-likes
+ mstr = (
+ "<pre>"
+ + format(obj.magnitude, mspec).replace("\n", "<br>")
+ + "</pre>"
+ )
+ elif isinstance(self.magnitude, ndarray):
+ if "L" in uspec:
+ # Use ndarray LaTeX special formatting
+ mstr = ndarray_to_latex(obj.magnitude, mspec)
+ else:
+ # Use custom ndarray text formatting--need to handle scalars differently
+ # since they don't respond to printoptions
+ formatter = "{{:{}}}".format(mspec)
+ if obj.magnitude.ndim == 0:
+ mstr = formatter.format(obj.magnitude)
+ else:
+ with np.printoptions(formatter={"float_kind": formatter.format}):
+ mstr = format(obj.magnitude).replace("\n", "")
+ else:
+ mstr = format(obj.magnitude, mspec).replace("\n", "")
+
+ if "L" in uspec:
+ mstr = self._exp_pattern.sub(r"\1\\times 10^{\2\3}", mstr)
+ elif "H" in uspec or "P" in uspec:
+ m = self._exp_pattern.match(mstr)
+ _exp_formatter = (
+ _pretty_fmt_exponent if "P" in uspec else lambda s: f"<sup>{s}</sup>"
+ )
+ if m:
+ exp = int(m.group(2) + m.group(3))
+ mstr = self._exp_pattern.sub(r"\1×10" + _exp_formatter(exp), mstr)
+
+ if allf == plain_allf and ustr.startswith("1 /"):
+ # Write e.g. "3 / s" instead of "3 1 / s"
+ ustr = ustr[2:]
+ return allf.format(mstr, ustr).strip()
+
+ def _repr_pretty_(self, p, cycle):
+ if cycle:
+ super()._repr_pretty_(p, cycle)
+ else:
+ p.pretty(self.magnitude)
+ p.text(" ")
+ p.pretty(self.units)
+
+ def format_babel(self, spec: str = "", **kwspec: Any) -> str:
+ spec = spec or self.default_format
+
+ # standard cases
+ if "#" in spec:
+ spec = spec.replace("#", "")
+ obj = self.to_compact()
+ else:
+ obj = self
+ kwspec = dict(kwspec)
+ if "length" in kwspec:
+ kwspec["babel_length"] = kwspec.pop("length")
+
+ loc = kwspec.get("locale", self._REGISTRY.fmt_locale)
+ if loc is None:
+ raise ValueError("Provide a `locale` value to localize translation.")
+
+ kwspec["locale"] = babel_parse(loc)
+ kwspec["babel_plural_form"] = kwspec["locale"].plural_form(obj.magnitude)
+ return "{} {}".format(
+ format(obj.magnitude, remove_custom_flags(spec)),
+ obj.units.format_babel(spec, **kwspec),
+ ).replace("\n", "")
+
+ def __str__(self) -> str:
+ if self._REGISTRY.fmt_locale is not None:
+ return self.format_babel()
+
+ return format(self)
+
+
+class FormattingUnit:
+ def __str__(self):
+ return format(self)
+
+ def __format__(self, spec) -> str:
+ _, uspec = split_format(
+ spec, self.default_format, self._REGISTRY.separate_format_defaults
+ )
+ if "~" in uspec:
+ if not self._units:
+ return ""
+ units = UnitsContainer(
+ dict(
+ (self._REGISTRY._get_symbol(key), value)
+ for key, value in self._units.items()
+ )
+ )
+ uspec = uspec.replace("~", "")
+ else:
+ units = self._units
+
+ return format_unit(units, uspec, registry=self._REGISTRY)
+
+ def format_babel(self, spec="", locale=None, **kwspec: Any) -> str:
+ spec = spec or extract_custom_flags(self.default_format)
+
+ if "~" in spec:
+ if self.dimensionless:
+ return ""
+ units = UnitsContainer(
+ dict(
+ (self._REGISTRY._get_symbol(key), value)
+ for key, value in self._units.items()
+ )
+ )
+ spec = spec.replace("~", "")
+ else:
+ units = self._units
+
+ locale = self._REGISTRY.fmt_locale if locale is None else locale
+
+ if locale is None:
+ raise ValueError("Provide a `locale` value to localize translation.")
+ else:
+ kwspec["locale"] = babel_parse(locale)
+
+ return units.format_babel(spec, registry=self._REGISTRY, **kwspec)
diff --git a/pint/facets/formatting/registry.py b/pint/facets/formatting/registry.py
new file mode 100644
index 0000000..f3bd9c7
--- /dev/null
+++ b/pint/facets/formatting/registry.py
@@ -0,0 +1,17 @@
+"""
+ pint.facets.formatting.registry
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import annotations
+
+from .objects import FormattingQuantity, FormattingUnit
+
+
+class FormattingRegistry:
+
+ _quantity_class = FormattingQuantity
+ _unit_class = FormattingUnit
diff --git a/pint/facets/measurement/__init__.py b/pint/facets/measurement/__init__.py
index f43598b..83454dc 100644
--- a/pint/facets/measurement/__init__.py
+++ b/pint/facets/measurement/__init__.py
@@ -1,213 +1,16 @@
-from __future__ import annotations
-
-import copy
-import re
-
-from ...compat import ufloat
-from ...formatting import _FORMATS, extract_custom_flags, siunitx_format_unit
-from ...util import build_dependent_class, create_class_with_registry
-from ..plain import PlainQuantity
-
-MISSING = object()
-
-
-class MeasurementQuantity:
-
- # Measurement support
- def plus_minus(self, error, relative=False):
- if isinstance(error, self.__class__):
- if relative:
- raise ValueError("{} is not a valid relative error.".format(error))
- error = error.to(self._units).magnitude
- else:
- if relative:
- error = error * abs(self.magnitude)
-
- return self._REGISTRY.Measurement(copy.copy(self.magnitude), error, self._units)
-
-
-class Measurement(PlainQuantity):
- """Implements a class to describe a quantity with uncertainty.
-
- Parameters
- ----------
- value : pint.Quantity or any numeric type
- The expected value of the measurement
- error : pint.Quantity or any numeric type
- The error or uncertainty of the measurement
-
- Returns
- -------
-
- """
-
- def __new__(cls, value, error, units=MISSING):
- if units is MISSING:
- try:
- value, units = value.magnitude, value.units
- except AttributeError:
- # if called with two arguments and the first looks like a ufloat
- # then assume the second argument is the units, keep value intact
- if hasattr(value, "nominal_value"):
- units = error
- error = MISSING # used for check below
- else:
- units = ""
- try:
- error = error.to(units).magnitude
- except AttributeError:
- pass
-
- if error is MISSING:
- mag = value
- elif error < 0:
- raise ValueError("The magnitude of the error cannot be negative")
- else:
- mag = ufloat(value, error)
-
- inst = super().__new__(cls, mag, units)
- return inst
-
- @property
- def value(self):
- return self._REGISTRY.Quantity(self.magnitude.nominal_value, self.units)
-
- @property
- def error(self):
- return self._REGISTRY.Quantity(self.magnitude.std_dev, self.units)
-
- @property
- def rel(self):
- return abs(self.magnitude.std_dev / self.magnitude.nominal_value)
-
- def __reduce__(self):
- # See notes in Quantity.__reduce__
- from pint import _unpickle_measurement
-
- return _unpickle_measurement, (Measurement, self.magnitude, self._units)
+"""
+ pint.facets.measurement
+ ~~~~~~~~~~~~~~~~~~~~~~~
- def __repr__(self):
- return "<Measurement({}, {}, {})>".format(
- self.magnitude.nominal_value, self.magnitude.std_dev, self.units
- )
+ Adds pint the capability to handle measurements (quantities with uncertainties).
- def __str__(self):
- return "{}".format(self)
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
- def __format__(self, spec):
-
- spec = spec or self.default_format
-
- # special cases
- if "Lx" in spec: # the LaTeX siunitx code
- # the uncertainties module supports formatting
- # numbers in value(unc) notation (i.e. 1.23(45) instead of 1.23 +/- 0.45),
- # using type code 'S', which siunitx actually accepts as input.
- # However, the implementation is incompatible with siunitx.
- # Uncertainties will do 9.1(1.1), which is invalid, should be 9.1(11).
- # TODO: add support for extracting options
- #
- # Get rid of this code, we'll deal with it here
- spec = spec.replace("Lx", "")
- # The most compatible format from uncertainties is the default format,
- # but even this requires fixups.
- # For one, SIUnitx does not except some formats that unc does, like 'P',
- # and 'S' is broken as stated, so...
- spec = spec.replace("S", "").replace("P", "")
- # get SIunitx options
- # TODO: allow user to set this value, somehow
- opts = _FORMATS["Lx"]["siopts"]
- if opts != "":
- opts = r"[" + opts + r"]"
- # SI requires space between "+-" (or "\pm") and the nominal value
- # and uncertainty, and doesn't accept "+/-", so this setting
- # selects the desired replacement.
- pm_fmt = _FORMATS["Lx"]["pm_fmt"]
- mstr = format(self.magnitude, spec).replace(r"+/-", pm_fmt)
- # Also, SIunitx doesn't accept parentheses, which uncs uses with
- # scientific notation ('e' or 'E' and sometimes 'g' or 'G').
- mstr = mstr.replace("(", "").replace(")", " ")
- ustr = siunitx_format_unit(self.units._units, self._REGISTRY)
- return r"\SI%s{%s}{%s}" % (opts, mstr, ustr)
-
- # standard cases
- if "L" in spec:
- newpm = pm = r" \pm "
- pars = _FORMATS["L"]["parentheses_fmt"]
- elif "P" in spec:
- newpm = pm = "±"
- pars = _FORMATS["P"]["parentheses_fmt"]
- else:
- newpm = pm = "+/-"
- pars = _FORMATS[""]["parentheses_fmt"]
-
- if "C" in spec:
- sp = ""
- newspec = spec.replace("C", "")
- pars = _FORMATS["C"]["parentheses_fmt"]
- else:
- sp = " "
- newspec = spec
-
- if "H" in spec:
- newpm = "&plusmn;"
- newspec = spec.replace("H", "")
- pars = _FORMATS["H"]["parentheses_fmt"]
-
- mag = format(self.magnitude, newspec).replace(pm, sp + newpm + sp)
- if "(" in mag:
- # Exponential format has its own parentheses
- pars = "{}"
-
- if "L" in newspec and "S" in newspec:
- mag = mag.replace("(", r"\left(").replace(")", r"\right)")
-
- if "L" in newspec:
- space = r"\ "
- else:
- space = " "
-
- uspec = extract_custom_flags(spec)
- ustr = format(self.units, uspec)
- if not ("uS" in newspec or "ue" in newspec or "u%" in newspec):
- mag = pars.format(mag)
-
- if "H" in spec:
- # Fix exponential format
- mag = re.sub(r"\)e\+0?(\d+)", r")×10<sup>\1</sup>", mag)
- mag = re.sub(r"\)e-0?(\d+)", r")×10<sup>-\1</sup>", mag)
-
- return mag + space + ustr
-
-
-# TODO: Remove in the near future
-# This is kept for easy backward compatibility during refactoring.
-_Measurement = Measurement
-
-
-class MeasurementRegistry:
-
- _quantity_class = MeasurementQuantity
- _measurement_class = Measurement
-
- def __init_subclass__(cls, **kwargs):
- super().__init_subclass__()
-
- cls.Measurement = build_dependent_class(
- cls, "Measurement", "_measurement_class"
- )
-
- def _init_dynamic_classes(self) -> None:
- """Generate subclasses on the fly and attach them to self"""
- super()._init_dynamic_classes()
-
- if ufloat is not None:
- self.Measurement = create_class_with_registry(self, self.Measurement)
- else:
+from __future__ import annotations
- def no_uncertainties(*args, **kwargs):
- raise RuntimeError(
- "Pint requires the 'uncertainties' package to create a Measurement object."
- )
+from .objects import Measurement, MeasurementQuantity
+from .registry import MeasurementRegistry
- self.Measurement = no_uncertainties
+__all__ = [Measurement, MeasurementQuantity, MeasurementRegistry]
diff --git a/pint/facets/measurement/objects.py b/pint/facets/measurement/objects.py
new file mode 100644
index 0000000..7817bdf
--- /dev/null
+++ b/pint/facets/measurement/objects.py
@@ -0,0 +1,192 @@
+"""
+ pint.facets.measurement.objects
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import annotations
+
+import copy
+import re
+
+from ...compat import ufloat
+from ...formatting import _FORMATS, extract_custom_flags, siunitx_format_unit
+from ..plain import PlainQuantity
+
+MISSING = object()
+
+
+class MeasurementQuantity:
+
+ # Measurement support
+ def plus_minus(self, error, relative=False):
+ if isinstance(error, self.__class__):
+ if relative:
+ raise ValueError("{} is not a valid relative error.".format(error))
+ error = error.to(self._units).magnitude
+ else:
+ if relative:
+ error = error * abs(self.magnitude)
+
+ return self._REGISTRY.Measurement(copy.copy(self.magnitude), error, self._units)
+
+
+class Measurement(PlainQuantity):
+ """Implements a class to describe a quantity with uncertainty.
+
+ Parameters
+ ----------
+ value : pint.Quantity or any numeric type
+ The expected value of the measurement
+ error : pint.Quantity or any numeric type
+ The error or uncertainty of the measurement
+
+ Returns
+ -------
+
+ """
+
+ def __new__(cls, value, error, units=MISSING):
+ if units is MISSING:
+ try:
+ value, units = value.magnitude, value.units
+ except AttributeError:
+ # if called with two arguments and the first looks like a ufloat
+ # then assume the second argument is the units, keep value intact
+ if hasattr(value, "nominal_value"):
+ units = error
+ error = MISSING # used for check below
+ else:
+ units = ""
+ try:
+ error = error.to(units).magnitude
+ except AttributeError:
+ pass
+
+ if error is MISSING:
+ mag = value
+ elif error < 0:
+ raise ValueError("The magnitude of the error cannot be negative")
+ else:
+ mag = ufloat(value, error)
+
+ inst = super().__new__(cls, mag, units)
+ return inst
+
+ @property
+ def value(self):
+ return self._REGISTRY.Quantity(self.magnitude.nominal_value, self.units)
+
+ @property
+ def error(self):
+ return self._REGISTRY.Quantity(self.magnitude.std_dev, self.units)
+
+ @property
+ def rel(self):
+ return abs(self.magnitude.std_dev / self.magnitude.nominal_value)
+
+ def __reduce__(self):
+ # See notes in Quantity.__reduce__
+ from pint import _unpickle_measurement
+
+ return _unpickle_measurement, (Measurement, self.magnitude, self._units)
+
+ def __repr__(self):
+ return "<Measurement({}, {}, {})>".format(
+ self.magnitude.nominal_value, self.magnitude.std_dev, self.units
+ )
+
+ def __str__(self):
+ return "{}".format(self)
+
+ def __format__(self, spec):
+
+ spec = spec or self.default_format
+
+ # special cases
+ if "Lx" in spec: # the LaTeX siunitx code
+ # the uncertainties module supports formatting
+ # numbers in value(unc) notation (i.e. 1.23(45) instead of 1.23 +/- 0.45),
+ # using type code 'S', which siunitx actually accepts as input.
+ # However, the implementation is incompatible with siunitx.
+ # Uncertainties will do 9.1(1.1), which is invalid, should be 9.1(11).
+ # TODO: add support for extracting options
+ #
+ # Get rid of this code, we'll deal with it here
+ spec = spec.replace("Lx", "")
+ # The most compatible format from uncertainties is the default format,
+ # but even this requires fixups.
+ # For one, SIUnitx does not except some formats that unc does, like 'P',
+ # and 'S' is broken as stated, so...
+ spec = spec.replace("S", "").replace("P", "")
+ # get SIunitx options
+ # TODO: allow user to set this value, somehow
+ opts = _FORMATS["Lx"]["siopts"]
+ if opts != "":
+ opts = r"[" + opts + r"]"
+ # SI requires space between "+-" (or "\pm") and the nominal value
+ # and uncertainty, and doesn't accept "+/-", so this setting
+ # selects the desired replacement.
+ pm_fmt = _FORMATS["Lx"]["pm_fmt"]
+ mstr = format(self.magnitude, spec).replace(r"+/-", pm_fmt)
+ # Also, SIunitx doesn't accept parentheses, which uncs uses with
+ # scientific notation ('e' or 'E' and sometimes 'g' or 'G').
+ mstr = mstr.replace("(", "").replace(")", " ")
+ ustr = siunitx_format_unit(self.units._units, self._REGISTRY)
+ return r"\SI%s{%s}{%s}" % (opts, mstr, ustr)
+
+ # standard cases
+ if "L" in spec:
+ newpm = pm = r" \pm "
+ pars = _FORMATS["L"]["parentheses_fmt"]
+ elif "P" in spec:
+ newpm = pm = "±"
+ pars = _FORMATS["P"]["parentheses_fmt"]
+ else:
+ newpm = pm = "+/-"
+ pars = _FORMATS[""]["parentheses_fmt"]
+
+ if "C" in spec:
+ sp = ""
+ newspec = spec.replace("C", "")
+ pars = _FORMATS["C"]["parentheses_fmt"]
+ else:
+ sp = " "
+ newspec = spec
+
+ if "H" in spec:
+ newpm = "&plusmn;"
+ newspec = spec.replace("H", "")
+ pars = _FORMATS["H"]["parentheses_fmt"]
+
+ mag = format(self.magnitude, newspec).replace(pm, sp + newpm + sp)
+ if "(" in mag:
+ # Exponential format has its own parentheses
+ pars = "{}"
+
+ if "L" in newspec and "S" in newspec:
+ mag = mag.replace("(", r"\left(").replace(")", r"\right)")
+
+ if "L" in newspec:
+ space = r"\ "
+ else:
+ space = " "
+
+ uspec = extract_custom_flags(spec)
+ ustr = format(self.units, uspec)
+ if not ("uS" in newspec or "ue" in newspec or "u%" in newspec):
+ mag = pars.format(mag)
+
+ if "H" in spec:
+ # Fix exponential format
+ mag = re.sub(r"\)e\+0?(\d+)", r")×10<sup>\1</sup>", mag)
+ mag = re.sub(r"\)e-0?(\d+)", r")×10<sup>-\1</sup>", mag)
+
+ return mag + space + ustr
+
+
+# TODO: Remove in the near future
+# This is kept for easy backward compatibility during refactoring.
+_Measurement = Measurement
diff --git a/pint/facets/measurement/registry.py b/pint/facets/measurement/registry.py
new file mode 100644
index 0000000..77bde3d
--- /dev/null
+++ b/pint/facets/measurement/registry.py
@@ -0,0 +1,42 @@
+"""
+ pint.facets.measurement.objects
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
+
+from __future__ import annotations
+
+from ...compat import ufloat
+from ...util import build_dependent_class, create_class_with_registry
+from .objects import Measurement, MeasurementQuantity
+
+
+class MeasurementRegistry:
+
+ _quantity_class = MeasurementQuantity
+ _measurement_class = Measurement
+
+ def __init_subclass__(cls, **kwargs):
+ super().__init_subclass__()
+
+ cls.Measurement = build_dependent_class(
+ cls, "Measurement", "_measurement_class"
+ )
+
+ def _init_dynamic_classes(self) -> None:
+ """Generate subclasses on the fly and attach them to self"""
+ super()._init_dynamic_classes()
+
+ if ufloat is not None:
+ self.Measurement = create_class_with_registry(self, self.Measurement)
+ else:
+
+ def no_uncertainties(*args, **kwargs):
+ raise RuntimeError(
+ "Pint requires the 'uncertainties' package to create a Measurement object."
+ )
+
+ self.Measurement = no_uncertainties
diff --git a/pint/facets/nonmultiplicative/__init__.py b/pint/facets/nonmultiplicative/__init__.py
index 3aca18c..56b8710 100644
--- a/pint/facets/nonmultiplicative/__init__.py
+++ b/pint/facets/nonmultiplicative/__init__.py
@@ -1,8 +1,10 @@
"""
- pint.facets.group
- ~~~~~~~~~~~~~~~~~
+ pint.facets.nonmultiplicative
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Adds pint the capability to group units.
+ Adds pint the capability to handle nonmultiplicative units:
+ - offset
+ - logarithmic
:copyright: 2022 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
diff --git a/pint/facets/nonmultiplicative/definitions.py b/pint/facets/nonmultiplicative/definitions.py
index 370501f..024fedb 100644
--- a/pint/facets/nonmultiplicative/definitions.py
+++ b/pint/facets/nonmultiplicative/definitions.py
@@ -1,3 +1,11 @@
+"""
+ pint.facets.nonmultiplicative.definitions
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
from __future__ import annotations
from dataclasses import dataclass
diff --git a/pint/facets/nonmultiplicative/objects.py b/pint/facets/nonmultiplicative/objects.py
index d11d0f6..1708e32 100644
--- a/pint/facets/nonmultiplicative/objects.py
+++ b/pint/facets/nonmultiplicative/objects.py
@@ -1,3 +1,11 @@
+"""
+ pint.facets.nonmultiplicative.objects
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
from __future__ import annotations
from typing import List
diff --git a/pint/facets/nonmultiplicative/registry.py b/pint/facets/nonmultiplicative/registry.py
index 0cf2e14..0f2e30c 100644
--- a/pint/facets/nonmultiplicative/registry.py
+++ b/pint/facets/nonmultiplicative/registry.py
@@ -1,3 +1,11 @@
+"""
+ pint.facets.nonmultiplicative.registry
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
from __future__ import annotations
from typing import Any, Optional, Union
diff --git a/pint/facets/numpy/numpy_func.py b/pint/facets/numpy/numpy_func.py
index fc71f9b..4662761 100644
--- a/pint/facets/numpy/numpy_func.py
+++ b/pint/facets/numpy/numpy_func.py
@@ -1,8 +1,8 @@
"""
- pint.numpy_func
- ~~~~~~~~~~~~~~~
+ pint.facets.numpy.numpy_func
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :copyright: 2019 by Pint Authors, see AUTHORS for more details.
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
diff --git a/pint/facets/numpy/quantity.py b/pint/facets/numpy/quantity.py
index b064f2d..2436100 100644
--- a/pint/facets/numpy/quantity.py
+++ b/pint/facets/numpy/quantity.py
@@ -1,3 +1,11 @@
+"""
+ pint.facets.numpy.quantity
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
from __future__ import annotations
import functools
diff --git a/pint/facets/numpy/unit.py b/pint/facets/numpy/unit.py
index c4d81a8..fc94853 100644
--- a/pint/facets/numpy/unit.py
+++ b/pint/facets/numpy/unit.py
@@ -1,3 +1,11 @@
+"""
+ pint.facets.numpy.unit
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
from __future__ import annotations
from ...compat import is_upcast_type
diff --git a/pint/facets/plain/definitions.py b/pint/facets/plain/definitions.py
index f17b0d5..c1d1d9a 100644
--- a/pint/facets/plain/definitions.py
+++ b/pint/facets/plain/definitions.py
@@ -2,8 +2,6 @@
pint.facets.plain.definitions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Base unit converting capabilites.
-
:copyright: 2022 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
diff --git a/pint/facets/plain/registry.py b/pint/facets/plain/registry.py
index 0f7c569..e235a60 100644
--- a/pint/facets/plain/registry.py
+++ b/pint/facets/plain/registry.py
@@ -1,3 +1,11 @@
+"""
+ pint.facets.plain.registry
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
from __future__ import annotations
import copy
diff --git a/pint/facets/plain/unit.py b/pint/facets/plain/unit.py
index c8af857..5fb050b 100644
--- a/pint/facets/plain/unit.py
+++ b/pint/facets/plain/unit.py
@@ -2,8 +2,6 @@
pint.facets.plain.unit
~~~~~~~~~~~~~~~~~~~~~
- Functions and classes related to unit definitions and conversions.
-
:copyright: 2016 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
diff --git a/pint/pint-convert b/pint/pint-convert
index 2d8eae0..600016b 100755
--- a/pint/pint-convert
+++ b/pint/pint-convert
@@ -8,6 +8,8 @@
:license: BSD, see LICENSE for more details.
"""
+from __future__ import annotations
+
import argparse
import re
diff --git a/pint/util.py b/pint/util.py
index 99c03f9..2b95765 100644
--- a/pint/util.py
+++ b/pint/util.py
@@ -29,10 +29,9 @@ from .formatting import format_unit
from .pint_eval import build_eval_tree
if TYPE_CHECKING:
- from pint.facets.plain.quantity import Quantity
+ from pint import Quantity, UnitRegistry
from ._typing import UnitLike
- from .facets.plain import PlainRegistry
logger = logging.getLogger(__name__)
logger.addHandler(NullHandler())
@@ -810,7 +809,7 @@ class SharedRegistryObject:
"""
- _REGISTRY: ClassVar[PlainRegistry]
+ _REGISTRY: ClassVar[UnitRegistry]
_units: UnitsContainer
def __new__(cls, *args, **kwargs):
@@ -876,7 +875,7 @@ class PrettyIPython:
def to_units_container(
- unit_like: Union[UnitLike, Quantity], registry: Optional[PlainRegistry] = None
+ unit_like: Union[UnitLike, Quantity], registry: Optional[UnitRegistry] = None
) -> UnitsContainer:
"""Convert a unit compatible type to a UnitsContainer.
@@ -909,7 +908,7 @@ def to_units_container(
def infer_base_unit(
- unit_like: Union[UnitLike, Quantity], registry: Optional[PlainRegistry] = None
+ unit_like: Union[UnitLike, Quantity], registry: Optional[UnitRegistry] = None
) -> UnitsContainer:
"""
Given a Quantity or UnitLike, give the UnitsContainer for it's plain units.
@@ -919,7 +918,7 @@ def infer_base_unit(
unit_like : Union[UnitLike, Quantity]
Quantity or Unit to infer the plain units from.
- registry: Optional[PlainRegistry]
+ registry: Optional[UnitRegistry]
If provided, uses the registry's UnitsContainer and parse_unit_name. If None,
uses the registry attached to unit_like.