Skip to content

Commit

Permalink
Merge branch 'master' into SPKorhonen-liter-alt
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewgsavage committed Jul 8, 2024
2 parents c86b6a1 + 6483353 commit 1cb8ba4
Show file tree
Hide file tree
Showing 18 changed files with 188 additions and 30 deletions.
12 changes: 8 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.9", "3.10", "3.11", "3.12"]
numpy: [null, "numpy>=1.23,<2.0.0", "numpy>=2.0.0rc1"]
uncertainties: [null, "uncertainties==3.1.6", "uncertainties>=3.1.6,<4.0.0"]
extras: [null]
include:
- python-version: "3.10" # Minimal versions
numpy: "numpy"
extras: matplotlib==2.2.5
numpy: "numpy>=1.23,<2.0.0"
extras: matplotlib==3.5.3
- python-version: "3.10"
numpy: "numpy"
uncertainties: "uncertainties"
extras: "sparse xarray netCDF4 dask[complete]==2023.4.0 graphviz babel==2.8 mip>=1.13"
extras: "sparse xarray netCDF4 dask[complete]==2024.5.1 graphviz babel==2.8 mip>=1.13"
- python-version: "3.10"
numpy: "numpy==1.26.1"
uncertainties: null
extras: "babel==2.15 matplotlib==3.9.0"
runs-on: ubuntu-latest

env:
Expand Down
13 changes: 12 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ Pint Changelog
0.25 (unreleased)
-----------------

Added ℓ as alternative for liter
- Added ℓ as alternative for liter
- Support permille units and `‰` symbol (PR #2033, Issue #1963)


0.24.1 (2024-06-24)
-----------------

- Fix custom formatter needing the registry object. (PR #2011)
- Support python 3.9 following difficulties installing with NumPy 2. (PR #2019)
- Fix default formatting of dimensionless unit issue. (PR #2012)
- Fix bug preventing custom formatters with modifiers working. (PR #2021)


0.24 (2024-06-07)
Expand Down Expand Up @@ -65,6 +75,7 @@ Added ℓ as alternative for liter
- Add numpy.linalg.norm implementation.
(PR #1251)


0.22 (2023-05-25)
-----------------

Expand Down
8 changes: 7 additions & 1 deletion docs/_static/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,14 @@ pre, code {

.sd-card .sd-card-header {
border: none;
color: #150458 !important;
color: #150458;
font-size: var(--pst-font-size-h5);
font-weight: bold;
padding: 2.5rem 0rem 0.5rem 0rem;
}

html[data-theme=dark] {
.sd-card .sd-card-header {
color: #FFF;
}
}
13 changes: 13 additions & 0 deletions docs/advanced/currencies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,16 @@ currency on its own dimension, and then implement transformations::
More sophisticated formulas, e.g. dealing with flat fees and thresholds, can be
implemented with arbitrary python code by programmatically defining a context (see
:ref:`contexts`).

Currency Symbols
----------------

Many common currency symbols are not supported by the pint parser. A preprocessor can be used as a workaround:

.. doctest::

>>> import pint
>>> ureg = pint.UnitRegistry(preprocessors = [lambda s: s.replace("", "EUR")])
>>> ureg.define("euro = [currency] = € = EUR")
>>> print(ureg.Quantity("1 €"))
1 euro
3 changes: 2 additions & 1 deletion docs/user/formatting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ formats:
'2.3e-06 kilogram ** -1 * meter ** 3 * second ** -2'

where ``unit`` is a :py:class:`dict` subclass containing the unit names and
their exponents.
their exponents, ``registry`` is the current instance of :py:class:``UnitRegistry`` and
``options`` is not yet implemented.

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`,
Expand Down
6 changes: 3 additions & 3 deletions docs/user/log_units.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,16 @@ will not work:
.. doctest::

>>> -161.0 * ureg('dBm/Hz') == (-161.0 * ureg.dBm / ureg.Hz)
False
np.False_

But this will:

.. doctest::

>>> ureg('-161.0 dBm/Hz') == (-161.0 * ureg.dBm / ureg.Hz)
True
np.True_
>>> Q_(-161.0, 'dBm') / ureg.Hz == (-161.0 * ureg.dBm / ureg.Hz)
True
np.True_

To begin using this feature while avoiding problems, define logarithmic units
as single-unit quantities and convert them to their base units as quickly as
Expand Down
11 changes: 9 additions & 2 deletions pint/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@
from typing import (
Any,
NoReturn,
TypeAlias, # noqa
)

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

if sys.version_info >= (3, 11):
from typing import Self # noqa
else:
Expand Down Expand Up @@ -71,7 +75,8 @@ class BehaviorChangeWarning(UserWarning):

try:
from uncertainties import UFloat, ufloat
from uncertainties import unumpy as unp

unp = None

HAS_UNCERTAINTIES = True
except ImportError:
Expand All @@ -88,6 +93,8 @@ class BehaviorChangeWarning(UserWarning):
HAS_NUMPY = True
NUMPY_VER = np.__version__
if HAS_UNCERTAINTIES:
from uncertainties import unumpy as unp

NUMERIC_TYPES = (Number, Decimal, ndarray, np.number, UFloat)
else:
NUMERIC_TYPES = (Number, Decimal, ndarray, np.number)
Expand Down
1 change: 1 addition & 0 deletions pint/default_en.txt
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ byte = 8 * bit = B = octet

# Ratios
percent = 0.01 = %
permille = 0.001 = ‰
ppm = 1e-6

# Length
Expand Down
19 changes: 16 additions & 3 deletions pint/delegates/formatter/_compound_unit_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@
TYPE_CHECKING,
Any,
Literal,
TypeAlias,
TypedDict,
TypeVar,
)

from ...compat import babel_parse
from ...compat import TypeAlias, babel_parse
from ...util import UnitsContainer

T = TypeVar("T")
Expand Down Expand Up @@ -83,11 +82,19 @@ def localize_per(
locale = babel_parse(locale)

patterns = locale._data["compound_unit_patterns"].get("per", None)
if patterns is None:
return default or "{}/{}"

patterns = patterns.get(length, None)
if patterns is None:
return default or "{}/{}"

return patterns.get(length, default or "{}/{}")
# babel 2.8
if isinstance(patterns, str):
return patterns

# babe; 2.15
return patterns.get("compound", default or "{}/{}")


@functools.lru_cache
Expand Down Expand Up @@ -257,6 +264,12 @@ def prepare_compount_unit(

# out: unit_name, unit_exponent

if len(out) == 0:
if "~" in spec:
return ([], [])
else:
return ([("dimensionless", 1)], [])

if "~" in spec:
if registry is None:
raise ValueError(
Expand Down
2 changes: 2 additions & 0 deletions pint/delegates/formatter/_format_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ def join_mu(joint_fstring: str, mstr: str, ustr: str) -> str:
This avoids that `3 and `1 / m` becomes `3 1 / m`
"""
if ustr == "":
return mstr
if ustr.startswith("1 / "):
return joint_fstring.format(mstr, ustr[2:])
return joint_fstring.format(mstr, ustr)
Expand Down
2 changes: 2 additions & 0 deletions pint/delegates/formatter/_to_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ def wrapper(func: Callable[[PlainUnit, UnitRegistry], str]):
raise ValueError(f"format {name!r} already exists") # or warn instead

class NewFormatter(BaseFormatter):
spec = name

def format_magnitude(
self,
magnitude: Magnitude,
Expand Down
18 changes: 13 additions & 5 deletions pint/delegates/formatter/full.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,20 @@ def get_formatter(self, spec: str):
if k in spec:
return v

try:
return REGISTERED_FORMATTERS[spec]
except KeyError:
pass
for k, v in REGISTERED_FORMATTERS.items():
if k in spec:
orphan_fmt = REGISTERED_FORMATTERS[k]
break
else:
return self._formatters["D"]

return self._formatters["D"]
try:
fmt = orphan_fmt.__class__(self._registry)
spec = getattr(fmt, "spec", spec)
self._formatters[spec] = fmt
return fmt
except Exception:
return orphan_fmt

def format_magnitude(
self, magnitude: Magnitude, mspec: str = "", **babel_kwds: Unpack[BabelKwds]
Expand Down
3 changes: 3 additions & 0 deletions pint/facets/plain/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ def __init__(
# use a default preprocessor to support "%"
self.preprocessors.insert(0, lambda string: string.replace("%", " percent "))

# use a default preprocessor to support permille "‰"
self.preprocessors.insert(0, lambda string: string.replace("‰", " permille "))

#: mode used to fill in the format defaults
self.separate_format_defaults = separate_format_defaults

Expand Down
23 changes: 20 additions & 3 deletions pint/testsuite/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,26 @@ def requires_numpy_at_least(version):
)


requires_babel = pytest.mark.skipif(
not HAS_BABEL, reason="Requires Babel with units support"
)
def requires_babel(tested_locales=[]):
if not HAS_BABEL:
return pytest.mark.skip("Requires Babel with units support")

import locale

default_locale = locale.getlocale(locale.LC_NUMERIC)
locales_unavailable = False
try:
for loc in tested_locales:
locale.setlocale(locale.LC_NUMERIC, loc)
except locale.Error:
locales_unavailable = True
locale.setlocale(locale.LC_NUMERIC, default_locale)

return pytest.mark.skipif(
locales_unavailable, reason="Tested locales not available."
)


requires_not_babel = pytest.mark.skipif(
HAS_BABEL, reason="Requires Babel not to be installed"
)
Expand Down
8 changes: 4 additions & 4 deletions pint/testsuite/test_babel.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def test_no_babel(func_registry):
distance.format_babel(locale="fr_FR", length="long")


@helpers.requires_babel()
@helpers.requires_babel(["fr_FR", "ro_RO"])
def test_format(func_registry):
ureg = func_registry
dirname = os.path.dirname(__file__)
Expand All @@ -36,7 +36,7 @@ def test_format(func_registry):
assert mks.format_babel(locale="fr_FR") == "métrique"


@helpers.requires_babel()
@helpers.requires_babel(["fr_FR", "ro_RO"])
def test_registry_locale():
ureg = UnitRegistry(fmt_locale="fr_FR")
dirname = os.path.dirname(__file__)
Expand All @@ -60,7 +60,7 @@ def test_registry_locale():
assert mks.format_babel(locale="fr_FR") == "métrique"


@helpers.requires_babel()
@helpers.requires_babel(["fr_FR"])
def test_unit_format_babel():
ureg = UnitRegistry(fmt_locale="fr_FR")
volume = ureg.Unit("ml")
Expand All @@ -85,7 +85,7 @@ def test_no_registry_locale(func_registry):
distance.format_babel()


@helpers.requires_babel()
@helpers.requires_babel(["fr_FR"])
def test_str(func_registry):
ureg = func_registry
d = 24.1 * ureg.meter
Expand Down
2 changes: 2 additions & 0 deletions pint/testsuite/test_formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ def test_split_format(format, default, flag, expected):
def test_register_unit_format(func_registry):
@fmt.register_unit_format("custom")
def format_custom(unit, registry, **options):
# Ensure the registry is correct..
registry.Unit(unit)
return "<formatted unit>"

quantity = 1.0 * func_registry.meter
Expand Down
Loading

0 comments on commit 1cb8ba4

Please sign in to comment.