From 80bdd5e2e70ec66b4555b0f415c3605cf596d858 Mon Sep 17 00:00:00 2001 From: Chris Barnes Date: Thu, 9 Dec 2021 12:58:54 +0000 Subject: [PATCH] Parse inf and nan in Quantity Resolves #1432 Case-insensitive; also accepts "infinity". --- CHANGES | 4 ++-- docs/defining-quantities.rst | 14 ++++++++++++++ pint/registry.py | 4 ++++ pint/testsuite/test_quantity.py | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 74051242c..44f56e2f3 100644 --- a/CHANGES +++ b/CHANGES @@ -7,7 +7,7 @@ Pint Changelog - Upgrade min version of uncertainties to 3.1.4 - Fix setting options of the application registry (Issue #1403). - Fix Quantity & Unit `is_compatible_with` with registry active contexts (Issue #1424). - +- Allow Quantity to parse 'NaN' and 'inf(inity)', case insensitive 0.18 (2021-10-26) ----------------- @@ -70,7 +70,7 @@ Pint Changelog - Fix tolist function with scalar ndarray. (Issue #1195, thanks jules-ch) - Corrected typos and dacstrings -- Implements a first benchmark suite in airspeed velocity (asv). +- Implements a first benchmark suite in airspeed velocity (asv). - Power for pseudo-dimensionless units. (Issue #1185, thanks Kevin Fuhr) diff --git a/docs/defining-quantities.rst b/docs/defining-quantities.rst index 7ab7157ac..845c81c0d 100644 --- a/docs/defining-quantities.rst +++ b/docs/defining-quantities.rst @@ -144,6 +144,20 @@ brackets to get the expected result: >>> Q_('3 l / (100 km)') +Special strings for NaN (Not a Number) and inf(inity) are also handled in a case-insensitive fashion. +Note that, as usual, NaN != NaN. + +.. doctest:: + + >>> Q_('inf m') + + >>> Q_('-INFINITY m') + + >>> Q_('nan m') + + >>> Q_('NaN m') + + .. note:: Since version 0.7, Pint **does not** use eval_ under the hood. This change removes the `serious security problems`_ that the system is exposed to when parsing information from untrusted sources. diff --git a/pint/registry.py b/pint/registry.py index 6bf4bb027..e3b5c7762 100644 --- a/pint/registry.py +++ b/pint/registry.py @@ -1218,6 +1218,10 @@ def _eval_token(self, token, case_sensitive=None, use_decimal=False, **values): if token_type == NAME: if token_text == "dimensionless": return 1 * self.dimensionless + elif token_text.lower() in ("inf", "infinity"): + return float("inf") + elif token_text.lower() == "nan": + return float("nan") elif token_text in values: return self.Quantity(values[token_text]) else: diff --git a/pint/testsuite/test_quantity.py b/pint/testsuite/test_quantity.py index ddb242d9c..8f68262b4 100644 --- a/pint/testsuite/test_quantity.py +++ b/pint/testsuite/test_quantity.py @@ -617,6 +617,24 @@ def test_is_compatible_with_with_context(self): with self.ureg.context("sp"): assert a.is_compatible_with(b) + @pytest.mark.parametrize(["inf_str"], [("inf",), ("-infinity",), ("INFINITY",)]) + @pytest.mark.parametrize(["has_unit"], [(True,), (False,)]) + def test_infinity(self, inf_str, has_unit): + inf = float(inf_str) + ref = self.Q_(inf, "meter" if has_unit else None) + test = self.Q_(inf_str + (" meter" if has_unit else "")) + assert ref == test + + @pytest.mark.parametrize(["nan_str"], [("nan",), ("NAN",)]) + @pytest.mark.parametrize(["has_unit"], [(True,), (False,)]) + def test_nan(self, nan_str, has_unit): + nan = float(nan_str) + ref = self.Q_(nan, " meter" if has_unit else None) + test = self.Q_(nan_str + (" meter" if has_unit else "")) + assert ref.units == test.units + assert math.isnan(test.magnitude) + assert ref != test + class TestQuantityToCompact(QuantityTestCase): def assertQuantityAlmostIdentical(self, q1, q2):