From 6be06c6175c53040f34b492a137ae52c41955dd0 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 12 May 2024 21:41:21 +0900 Subject: [PATCH 1/9] Skip failing benchmark test (#1981) --- pint/testsuite/benchmarks/test_10_registry.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pint/testsuite/benchmarks/test_10_registry.py b/pint/testsuite/benchmarks/test_10_registry.py index 09264fa44..3a1d42da5 100644 --- a/pint/testsuite/benchmarks/test_10_registry.py +++ b/pint/testsuite/benchmarks/test_10_registry.py @@ -164,6 +164,9 @@ def test_load_definitions_stage_1(benchmark, cache_folder, use_cache_folder): benchmark(pint.UnitRegistry, None, cache_folder=use_cache_folder) +@pytest.mark.skip( + "Test failing ValueError: Group USCSLengthInternational already present in registry" +) @pytest.mark.parametrize("use_cache_folder", (None, True)) def test_load_definitions_stage_2(benchmark, cache_folder, use_cache_folder): """empty registry creation + parsing default files + definition object loading""" From d28efac6a0a029c21cd15e8fec2c8cd6ddbc1779 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Sun, 12 May 2024 21:46:10 +0900 Subject: [PATCH 2/9] avoid calling str on array (#1959) --- pint/facets/plain/quantity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pint/facets/plain/quantity.py b/pint/facets/plain/quantity.py index 2727a7da3..a18919273 100644 --- a/pint/facets/plain/quantity.py +++ b/pint/facets/plain/quantity.py @@ -140,7 +140,7 @@ class PlainQuantity(Generic[MagnitudeT], PrettyIPython, SharedRegistryObject): def ndim(self) -> int: if isinstance(self.magnitude, numbers.Number): return 0 - if str(self.magnitude) == "": + if str(type(self.magnitude)) == "NAType": return 0 return self.magnitude.ndim From e5b04b4d7dd3a399a4d57dbeb7d10e2454eedcbf Mon Sep 17 00:00:00 2001 From: David Linke Date: Sun, 12 May 2024 14:47:21 +0200 Subject: [PATCH 3/9] Document defaults of pint.UnitRegistry (#1919) This updates the doc-string to include the defaults for all parameters. --- pint/registry.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/pint/registry.py b/pint/registry.py index 210ea9112..ceb9b62d1 100644 --- a/pint/registry.py +++ b/pint/registry.py @@ -69,31 +69,38 @@ class UnitRegistry(GenericUnitRegistry[Quantity, Unit]): ---------- filename : path of the units definition file to load or line-iterable object. - Empty to load the default definition file. + Empty string to load the default definition file. (default) None to leave the UnitRegistry empty. force_ndarray : bool convert any input, scalar or not to a numpy.ndarray. + (Default: False) force_ndarray_like : bool convert all inputs other than duck arrays to a numpy.ndarray. + (Default: False) default_as_delta : In the context of a multiplication of units, interpret non-multiplicative units as their *delta* counterparts. + (Default: False) autoconvert_offset_to_baseunit : If True converts offset units in quantities are converted to their plain units in multiplicative - context. If False no conversion happens. + context. If False no conversion happens. (Default: False) on_redefinition : str action to take in case a unit is redefined. - 'warn', 'raise', 'ignore' + 'warn', 'raise', 'ignore' (Default: 'raise') auto_reduce_dimensions : If True, reduce dimensionality on appropriate operations. + (Default: False) autoconvert_to_preferred : If True, converts preferred units on appropriate operations. + (Default: False) preprocessors : list of callables which are iteratively ran on any input expression - or unit string + or unit string or None for no preprocessor. + (Default=None) fmt_locale : - locale identifier string, used in `format_babel`. Default to None + locale identifier string, used in `format_babel` or None. + (Default=None) case_sensitive : bool, optional Control default case sensitivity of unit parsing. (Default: True) cache_folder : str or pathlib.Path or None, optional From be4e15b05a024766c50fbf8289d4c54b9f21ab1b Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Mon, 13 May 2024 00:12:43 +0900 Subject: [PATCH 4/9] Fix doctests (#1982) --- docs/advanced/pitheorem.rst | 6 ++++-- docs/api/facets.rst | 2 +- docs/getting/tutorial.rst | 8 ++++---- docs/user/angular_frequency.rst | 2 +- docs/user/defining-quantities.rst | 2 +- docs/user/formatting.rst | 5 +++-- pint/facets/plain/qto.py | 4 ++-- 7 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/advanced/pitheorem.rst b/docs/advanced/pitheorem.rst index cd3716528..06409d8b5 100644 --- a/docs/advanced/pitheorem.rst +++ b/docs/advanced/pitheorem.rst @@ -33,8 +33,10 @@ Which can be pretty printed using the `Pint` formatter: >>> from pint import formatter >>> result = pi_theorem({'V': '[length]/[time]', 'T': '[time]', 'L': '[length]'}) - >>> print(formatter(result[0].items())) - T * V / L + >>> numerator = [item for item in result[0].items() if item[1]>0] + >>> denominator = [item for item in result[0].items() if item[1]<0] + >>> print(formatter(numerator, denominator)) + V * T / L You can also apply the Buckingham π theorem associated to a Registry. In this case, you can use derived dimensions such as speed: diff --git a/docs/api/facets.rst b/docs/api/facets.rst index f4b6a54e8..d835f5cea 100644 --- a/docs/api/facets.rst +++ b/docs/api/facets.rst @@ -16,7 +16,7 @@ The default UnitRegistry inherits from all of them. :members: :exclude-members: Quantity, Unit, Measurement, Group, Context, System -.. automodule:: pint.facets.formatting +.. automodule:: pint.delegates.formatter :members: :exclude-members: Quantity, Unit, Measurement, Group, Context, System diff --git a/docs/getting/tutorial.rst b/docs/getting/tutorial.rst index bb3505b51..d675860f2 100644 --- a/docs/getting/tutorial.rst +++ b/docs/getting/tutorial.rst @@ -428,7 +428,7 @@ If Babel_ is installed you can translate unit names to any language .. doctest:: >>> ureg.formatter.format_quantity(accel, locale='fr_FR') - '1,3 mètres/secondes²' + '1,3 mètres par seconde²' You can also specify the format locale at the registry level either at creation: @@ -449,11 +449,11 @@ and by doing that, string formatting is now localized: >>> ureg.default_format = 'P' >>> accel = 1.3 * ureg.parse_units('meter/second**2') >>> str(accel) - '1,3 mètres/secondes²' + '1,3 mètres par seconde²' >>> "%s" % accel - '1,3 mètres/secondes²' + '1,3 mètres par seconde²' >>> "{}".format(accel) - '1,3 mètres/secondes²' + '1,3 mètres par seconde²' If you want to customize string formatting, take a look at :ref:`formatting`. diff --git a/docs/user/angular_frequency.rst b/docs/user/angular_frequency.rst index 58e126a9c..61bdf1614 100644 --- a/docs/user/angular_frequency.rst +++ b/docs/user/angular_frequency.rst @@ -2,7 +2,7 @@ Angles and Angular Frequency -================= +============================= Angles ------ diff --git a/docs/user/defining-quantities.rst b/docs/user/defining-quantities.rst index e40b08cf9..a7405151a 100644 --- a/docs/user/defining-quantities.rst +++ b/docs/user/defining-quantities.rst @@ -134,7 +134,7 @@ For example, the units of .. doctest:: >>> Q_('3 l / 100 km') - + may be unexpected at first but, are a consequence of applying this rule. Use brackets to get the expected result: diff --git a/docs/user/formatting.rst b/docs/user/formatting.rst index f17939a86..d45fc1e13 100644 --- a/docs/user/formatting.rst +++ b/docs/user/formatting.rst @@ -95,7 +95,7 @@ formats: ... 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' + '2.3e-06 kilogram ** -1 * meter ** 3 * second ** -2' where ``unit`` is a :py:class:`dict` subclass containing the unit names and their exponents. @@ -111,10 +111,11 @@ following methods: `format_magnitude`, `format_unit`, `format_quantity`, `format ... ... default_format = "" ... - ... def format_unit(self, unit, uspec: str = "", **babel_kwds) -> str: + ... def format_unit(self, unit, uspec, sort_func, **babel_kwds) -> str: ... return "ups!" ... >>> ureg.formatter = MyFormatter() + >>> ureg.formatter._registry = ureg >>> str(q) '2.3e-06 ups!' diff --git a/pint/facets/plain/qto.py b/pint/facets/plain/qto.py index 9de541584..22176491d 100644 --- a/pint/facets/plain/qto.py +++ b/pint/facets/plain/qto.py @@ -184,7 +184,7 @@ def to_preferred( >>> (1*ureg.acre).to_preferred([ureg.meters]) >>> (1*(ureg.force_pound*ureg.m)).to_preferred([ureg.W]) - + """ units = _get_preferred(quantity, preferred_units) @@ -204,7 +204,7 @@ def ito_preferred( >>> (1*ureg.acre).to_preferred([ureg.meters]) >>> (1*(ureg.force_pound*ureg.m)).to_preferred([ureg.W]) - + """ units = _get_preferred(quantity, preferred_units) From 449697e505d35b103e5db874747eca65d37c2526 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Sun, 12 May 2024 17:20:14 +0200 Subject: [PATCH 5/9] Fix siunitx format of integer powers with non_int_type=decimal.Decimal (#1977) --- .gitignore | 2 ++ CHANGES | 1 + pint/delegates/formatter/latex.py | 4 ++-- pint/testsuite/test_issues.py | 17 +++++++++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index ae702bac3..69fd3338d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ MANIFEST .mypy_cache pip-wheel-metadata pint/testsuite/dask-worker-space +venv +.envrc # WebDAV file system cache files .DAV/ diff --git a/CHANGES b/CHANGES index 048765ec0..c27473af5 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,7 @@ Pint Changelog - Add `dim_sort` function to _formatter_helpers. - Add `dim_order` and `default_sort_func` properties to FullFormatter. (PR #1926, fixes Issue #1841) +- Fix LaTeX siuntix formatting when using non_int_type=decimal.Decimal. 0.23 (2023-12-08) diff --git a/pint/delegates/formatter/latex.py b/pint/delegates/formatter/latex.py index 476997b84..468a65fa4 100644 --- a/pint/delegates/formatter/latex.py +++ b/pint/delegates/formatter/latex.py @@ -124,8 +124,8 @@ def siunitx_format_unit( ) -> str: """Returns LaTeX code for the unit that can be put into an siunitx command.""" - def _tothe(power: int | float) -> str: - if isinstance(power, int) or (isinstance(power, float) and power.is_integer()): + def _tothe(power) -> str: + if power == int(power): if power == 1: return "" elif power == 2: diff --git a/pint/testsuite/test_issues.py b/pint/testsuite/test_issues.py index dc63ececd..06ca4c322 100644 --- a/pint/testsuite/test_issues.py +++ b/pint/testsuite/test_issues.py @@ -1201,3 +1201,20 @@ def test_issues_1841_xfail(): # this prints "2*pi hour * radian", not "2*pi radian * hour" unless sort_dims is True # print(q) + + +@pytest.mark.parametrize( + "given,expected", + [ + ( + "8.989e9 newton * meter^2 / coulomb^2", + r"\SI[]{8.989E+9}{\meter\squared\newton\per\coulomb\squared}", + ), + ("5 * meter / second", r"\SI[]{5}{\meter\per\second}"), + ("2.2 * meter^4", r"\SI[]{2.2}{\meter\tothe{4}}"), + ("2.2 * meter^-4", r"\SI[]{2.2}{\per\meter\tothe{4}}"), + ], +) +def test_issue1772(given, expected): + ureg = UnitRegistry(non_int_type=decimal.Decimal) + assert f"{ureg(given):Lx}" == expected From 2b4a8b7d322a40eb1f9a9d656bac0a72f51ca47c Mon Sep 17 00:00:00 2001 From: Bhavin Patel <15210802+bpatel2107@users.noreply.github.com> Date: Sun, 12 May 2024 16:23:50 +0100 Subject: [PATCH 6/9] Implement numpy roll (#1968) --- CHANGES | 1 + pint/facets/numpy/numpy_func.py | 2 ++ pint/testsuite/test_numpy.py | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/CHANGES b/CHANGES index c27473af5..e45cd50e6 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,7 @@ Pint Changelog 0.24 (unreleased) ----------------- +- Implement numpy roll (Related to issue #981) - Add `dim_sort` function to _formatter_helpers. - Add `dim_order` and `default_sort_func` properties to FullFormatter. (PR #1926, fixes Issue #1841) diff --git a/pint/facets/numpy/numpy_func.py b/pint/facets/numpy/numpy_func.py index 29724837f..fe80727d4 100644 --- a/pint/facets/numpy/numpy_func.py +++ b/pint/facets/numpy/numpy_func.py @@ -413,6 +413,7 @@ def implementation(*args, **kwargs): "take", "trace", "transpose", + "roll", "ceil", "floor", "hypot", @@ -850,6 +851,7 @@ def implementation(*args, **kwargs): ("median", "a", True), ("nanmedian", "a", True), ("transpose", "a", True), + ("roll", "a", True), ("copy", "a", True), ("average", "a", True), ("nanmean", "a", True), diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index 69c8128c0..b58be1791 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -288,6 +288,11 @@ def test_broadcast_arrays(self): result = np.broadcast_arrays(x, y, subok=True) helpers.assert_quantity_equal(result, expected) + def test_roll(self): + helpers.assert_quantity_equal( + np.roll(self.q, 1), [[4, 1], [2, 3]] * self.ureg.m + ) + class TestNumpyMathematicalFunctions(TestNumpyMethods): # https://www.numpy.org/devdocs/reference/routines.math.html From feaa945ab346971a101ca670d1a96fe3112ed8e0 Mon Sep 17 00:00:00 2001 From: Ryan May Date: Sun, 12 May 2024 11:29:41 -0600 Subject: [PATCH 7/9] MNT: Handle trapz for numpy>=2 (#1971) trapz has been deprecated in favor of the newly available trapezoid function. This wraps the new function and avoids a DeprecationWarning on numpy>=2. --- pint/facets/numpy/numpy_func.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pint/facets/numpy/numpy_func.py b/pint/facets/numpy/numpy_func.py index fe80727d4..ac702014c 100644 --- a/pint/facets/numpy/numpy_func.py +++ b/pint/facets/numpy/numpy_func.py @@ -741,8 +741,11 @@ def _base_unit_if_needed(a): raise OffsetUnitCalculusError(a.units) +# Can remove trapz wrapping when we only support numpy>=2 @implements("trapz", "function") +@implements("trapezoid", "function") def _trapz(y, x=None, dx=1.0, **kwargs): + trapezoid = np.trapezoid if hasattr(np, "trapezoid") else np.trapz y = _base_unit_if_needed(y) units = y.units if x is not None: @@ -750,13 +753,13 @@ def _trapz(y, x=None, dx=1.0, **kwargs): x = _base_unit_if_needed(x) units *= x.units x = x._magnitude - ret = np.trapz(y._magnitude, x, **kwargs) + ret = trapezoid(y._magnitude, x, **kwargs) else: if hasattr(dx, "units"): dx = _base_unit_if_needed(dx) units *= dx.units dx = dx._magnitude - ret = np.trapz(y._magnitude, dx=dx, **kwargs) + ret = trapezoid(y._magnitude, dx=dx, **kwargs) return y.units._REGISTRY.Quantity(ret, units) From cbdd79e3e1ec8d179c365db3b9c4dcf0383930d0 Mon Sep 17 00:00:00 2001 From: David Linke Date: Sun, 12 May 2024 20:25:58 +0200 Subject: [PATCH 8/9] Fix converting to offset units of higher dimension e.g. gauge pressure (#1952) --- CHANGES | 2 ++ pint/facets/nonmultiplicative/registry.py | 7 ++++++- pint/testsuite/test_issues.py | 12 +++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index e45cd50e6..fe3f0b48b 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,8 @@ Pint Changelog (PR #1926, fixes Issue #1841) - Fix LaTeX siuntix formatting when using non_int_type=decimal.Decimal. +- Fix converting to offset units of higher dimension e.g. gauge pressure (#1949). +- 0.23 (2023-12-08) ----------------- diff --git a/pint/facets/nonmultiplicative/registry.py b/pint/facets/nonmultiplicative/registry.py index 4985ba51b..d476cc676 100644 --- a/pint/facets/nonmultiplicative/registry.py +++ b/pint/facets/nonmultiplicative/registry.py @@ -192,7 +192,7 @@ def _add_ref_of_log_or_offset_unit( self, offset_unit: str, all_units: UnitsContainer ) -> UnitsContainer: slct_unit = self._units[offset_unit] - if slct_unit.is_logarithmic or (not slct_unit.is_multiplicative): + if slct_unit.is_logarithmic: # Extract reference unit slct_ref = slct_unit.reference @@ -204,6 +204,11 @@ def _add_ref_of_log_or_offset_unit( (u, e) = [(u, e) for u, e in slct_ref.items()].pop() # Add it back to the unit list return all_units.add(u, e) + + if not slct_unit.is_multiplicative: # is offset unit + # Extract reference unit + return slct_unit.reference + # Otherwise, return the units unmodified return all_units diff --git a/pint/testsuite/test_issues.py b/pint/testsuite/test_issues.py index 06ca4c322..69909c1a4 100644 --- a/pint/testsuite/test_issues.py +++ b/pint/testsuite/test_issues.py @@ -1147,7 +1147,7 @@ def test_issue1725(registry_empty): assert registry_empty.get_compatible_units("dollar") == set() -def test_issues_1505(): +def test_issue1505(): ur = UnitRegistry(non_int_type=decimal.Decimal) assert isinstance(ur.Quantity("1m/s").magnitude, decimal.Decimal) @@ -1203,6 +1203,16 @@ def test_issues_1841_xfail(): # print(q) +def test_issue1949(registry_empty): + ureg = UnitRegistry() + ureg.define( + "in_Hg_gauge = 3386389 * gram / metre / second ** 2; offset:101325000 = inHg_g = in_Hg_g = inHg_gauge" + ) + q = ureg.Quantity("1 atm").to("inHg_gauge") + assert q.units == ureg.in_Hg_gauge + assert_equal(q.magnitude, 0.0) + + @pytest.mark.parametrize( "given,expected", [ From cb0ec94a34b88028bb000e1c0b061c16295a3324 Mon Sep 17 00:00:00 2001 From: andrewgsavage Date: Mon, 13 May 2024 03:32:04 +0900 Subject: [PATCH 9/9] numpy2 support (#1985) --- .github/workflows/ci.yml | 2 +- CHANGES | 7 ++++--- pint/facets/numpy/numpy_func.py | 2 +- pint/testsuite/test_numpy.py | 15 ++++++++++++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d445a2970..0797decb5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: fail-fast: false matrix: python-version: ["3.10", "3.11", "3.12"] - numpy: [null, "numpy>=1.23,<2.0.0"] + 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: diff --git a/CHANGES b/CHANGES index fe3f0b48b..50b524949 100644 --- a/CHANGES +++ b/CHANGES @@ -4,14 +4,15 @@ Pint Changelog 0.24 (unreleased) ----------------- +- NumPy 2.0 support + (PR #1985, #1971) - Implement numpy roll (Related to issue #981) - Add `dim_sort` function to _formatter_helpers. - Add `dim_order` and `default_sort_func` properties to FullFormatter. (PR #1926, fixes Issue #1841) - Fix LaTeX siuntix formatting when using non_int_type=decimal.Decimal. - -- Fix converting to offset units of higher dimension e.g. gauge pressure (#1949). -- +- Fix converting to offset units of higher dimension e.g. gauge pressure + (PR #1949) 0.23 (2023-12-08) ----------------- diff --git a/pint/facets/numpy/numpy_func.py b/pint/facets/numpy/numpy_func.py index ac702014c..0d03af74e 100644 --- a/pint/facets/numpy/numpy_func.py +++ b/pint/facets/numpy/numpy_func.py @@ -741,7 +741,7 @@ def _base_unit_if_needed(a): raise OffsetUnitCalculusError(a.units) -# Can remove trapz wrapping when we only support numpy>=2 +# NP2 Can remove trapz wrapping when we only support numpy>=2 @implements("trapz", "function") @implements("trapezoid", "function") def _trapz(y, x=None, dx=1.0, **kwargs): diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py index b58be1791..486102124 100644 --- a/pint/testsuite/test_numpy.py +++ b/pint/testsuite/test_numpy.py @@ -438,6 +438,7 @@ def test_cross(self): np.cross(a, b), [[-15, -2, 39]] * self.ureg.kPa * self.ureg.m**2 ) + # NP2: Remove this when we only support np>=2.0 @helpers.requires_array_function_protocol() def test_trapz(self): helpers.assert_quantity_equal( @@ -445,6 +446,15 @@ def test_trapz(self): 7.5 * self.ureg.J * self.ureg.m, ) + @helpers.requires_array_function_protocol() + def test_trapezoid(self): + # NP2: Remove this when we only support np>=2.0 + if np.lib.NumpyVersion(np.__version__) >= "2.0.0b1": + helpers.assert_quantity_equal( + np.trapezoid([1.0, 2.0, 3.0, 4.0] * self.ureg.J, dx=1 * self.ureg.m), + 7.5 * self.ureg.J * self.ureg.m, + ) + @helpers.requires_array_function_protocol() def test_dot(self): helpers.assert_quantity_equal( @@ -758,9 +768,12 @@ def test_minimum(self): np.minimum(self.q, self.Q_([0, 5], "m")), self.Q_([[0, 2], [0, 4]], "m") ) + # NP2: Can remove Q_(arr).ptp test when we only support numpy>=2 def test_ptp(self): - assert self.q.ptp() == 3 * self.ureg.m + if not np.lib.NumpyVersion(np.__version__) >= "2.0.0b1": + assert self.q.ptp() == 3 * self.ureg.m + # NP2: Keep this test for numpy>=2, it's only arr.ptp() that is deprecated @helpers.requires_array_function_protocol() def test_ptp_numpy_func(self): helpers.assert_quantity_equal(np.ptp(self.q, axis=0), [2, 2] * self.ureg.m)