Skip to content

Commit

Permalink
Merge #957
Browse files Browse the repository at this point in the history
957: Add parameterized test for type immutability r=hgrecco a=jthielen

As discussed in #925, this adds a parameterized test to verify that the internal type is not mutated under common operations (as encountered in #399, #481, #509, #622, #678).

- [x] Closes #925, Closes #481
- [x] Executed ``black -t py36 . && isort -rc . && flake8`` with no errors
- [x] The change is fully covered by automated unit tests
- ~~Documented in docs/ as appropriate~~
- [x] Added an entry to the CHANGES file


Co-authored-by: Jon Thielen <github@jont.cc>
  • Loading branch information
bors[bot] and jthielen authored Dec 27, 2019
2 parents 5cd3331 + ca3d9c7 commit 8d8491f
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 38 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Pint Changelog
0.10 (unreleased)
-----------------

- Added tests for immutability of the magnitude's type under common operations
(Issue #956, Thanks Jon Thielen)
- Switched test configuration to pytest and added tests of Pint's matplotlib support.
(Issue #954, Thanks Jon Thielen)
- Deprecate array protocol fallback except where explicitly defined (`__array__`,
Expand Down
83 changes: 45 additions & 38 deletions pint/testsuite/test_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
import pprint
import unittest

import pytest

from pint import Context, DimensionalityError, UnitRegistry
from pint.compat import np
from pint.testsuite import QuantityTestCase, helpers
from pint.unit import UnitsContainer
from pint.util import ParserHelper

ureg = UnitRegistry()


class TestIssues(QuantityTestCase):

Expand All @@ -23,7 +27,6 @@ def test_issue25(self):
self.assertEqual(x, ParserHelper(10, {"%": 1}))
x = ParserHelper.from_string("10 ‰")
self.assertEqual(x, ParserHelper(10, {"‰": 1}))
ureg = UnitRegistry()
ureg.define("percent = [fraction]; offset: 0 = %")
ureg.define("permille = percent / 10 = ‰")
x = ureg.parse_expression("10 %")
Expand All @@ -33,7 +36,6 @@ def test_issue25(self):
self.assertEqual(x.to("‰"), ureg.Quantity(1, {"‰": 1}))

def test_issue29(self):
ureg = UnitRegistry()
t = 4 * ureg("mW")
self.assertEqual(t.magnitude, 4)
self.assertEqual(t._units, UnitsContainer(milliwatt=1))
Expand All @@ -43,7 +45,6 @@ def test_issue29(self):
@helpers.requires_numpy()
def test_issue37(self):
x = np.ma.masked_array([1, 2, 3], mask=[True, True, False])
ureg = UnitRegistry()
q = ureg.meter * x
self.assertIsInstance(q, ureg.Quantity)
np.testing.assert_array_equal(q.magnitude, x)
Expand All @@ -67,7 +68,6 @@ def test_issue37(self):
@helpers.requires_numpy()
def test_issue39(self):
x = np.matrix([[1, 2, 3], [1, 2, 3], [1, 2, 3]])
ureg = UnitRegistry()
q = ureg.meter * x
self.assertIsInstance(q, ureg.Quantity)
np.testing.assert_array_equal(q.magnitude, x)
Expand All @@ -89,7 +89,6 @@ def test_issue39(self):

@helpers.requires_numpy()
def test_issue44(self):
ureg = UnitRegistry()
x = 4.0 * ureg.dimensionless
np.sqrt(x)
self.assertQuantityAlmostEqual(
Expand All @@ -102,13 +101,11 @@ def test_issue44(self):
def test_issue45(self):
import math

ureg = UnitRegistry()
self.assertAlmostEqual(math.sqrt(4 * ureg.m / ureg.cm), math.sqrt(4 * 100))
self.assertAlmostEqual(float(ureg.V / ureg.mV), 1000.0)

@helpers.requires_numpy()
def test_issue45b(self):
ureg = UnitRegistry()
self.assertAlmostEqual(
np.sin([np.pi / 2] * ureg.m / ureg.m),
np.sin([np.pi / 2] * ureg.dimensionless),
Expand All @@ -119,7 +116,6 @@ def test_issue45b(self):
)

def test_issue50(self):
ureg = UnitRegistry()
Q_ = ureg.Quantity
self.assertEqual(Q_(100), 100 * ureg.dimensionless)
self.assertEqual(Q_("100"), 100 * ureg.dimensionless)
Expand All @@ -146,18 +142,15 @@ def test_issue52(self):
self.assertRaises(ValueError, fun, q1, q2)

def test_issue54(self):
ureg = UnitRegistry()
self.assertEqual((1 * ureg.km / ureg.m + 1).magnitude, 1001)

def test_issue54_related(self):
ureg = UnitRegistry()
self.assertEqual(ureg.km / ureg.m, 1000)
self.assertEqual(1000, ureg.km / ureg.m)
self.assertLess(900, ureg.km / ureg.m)
self.assertGreater(1100, ureg.km / ureg.m)

def test_issue61(self):
ureg = UnitRegistry()
Q_ = ureg.Quantity
for value in ({}, {"a": 3}, None):
self.assertRaises(TypeError, Q_, value)
Expand All @@ -167,19 +160,16 @@ def test_issue61(self):

@helpers.requires_not_numpy()
def test_issue61_notNP(self):
ureg = UnitRegistry()
Q_ = ureg.Quantity
for value in ([1, 2, 3], (1, 2, 3)):
self.assertRaises(TypeError, Q_, value)
self.assertRaises(TypeError, Q_, value, "meter")

def test_issue62(self):
ureg = UnitRegistry()
m = ureg("m**0.5")
self.assertEqual(str(m.units), "meter ** 0.5")

def test_issue66(self):
ureg = UnitRegistry()
self.assertEqual(
ureg.get_dimensionality(UnitsContainer({"[temperature]": 1})),
UnitsContainer({"[temperature]": 1}),
Expand All @@ -192,7 +182,6 @@ def test_issue66(self):
)

def test_issue66b(self):
ureg = UnitRegistry()
self.assertEqual(
ureg.get_base_units(ureg.kelvin),
(1.0, ureg.Unit(UnitsContainer({"kelvin": 1}))),
Expand All @@ -203,13 +192,11 @@ def test_issue66b(self):
)

def test_issue69(self):
ureg = UnitRegistry()
q = ureg("m").to(ureg("in"))
self.assertEqual(q, ureg("m").to("in"))

@helpers.requires_numpy()
def test_issue74(self):
ureg = UnitRegistry()
v1 = np.asarray([1.0, 2.0, 3.0])
v2 = np.asarray([3.0, 2.0, 1.0])
q1 = v1 * ureg.ms
Expand All @@ -232,7 +219,6 @@ def test_issue74(self):

@helpers.requires_numpy()
def test_issue75(self):
ureg = UnitRegistry()
v1 = np.asarray([1.0, 2.0, 3.0])
v2 = np.asarray([3.0, 2.0, 1.0])
q1 = v1 * ureg.ms
Expand All @@ -249,14 +235,12 @@ def test_issue75(self):

@helpers.requires_uncertainties()
def test_issue77(self):
ureg = UnitRegistry()
acc = (5.0 * ureg("m/s/s")).plus_minus(0.25)
tim = (37.0 * ureg("s")).plus_minus(0.16)
dis = acc * tim ** 2 / 2
self.assertEqual(dis.value, acc.value * tim.value ** 2 / 2)

def test_issue85(self):
ureg = UnitRegistry()

T = 4.0 * ureg.kelvin
m = 1.0 * ureg.amu
Expand Down Expand Up @@ -329,7 +313,6 @@ def test_issue86c(self):
self.assertQuantityAlmostEqual(ureg.k * 2 * T, ureg.k * (2 * T))

def test_issue93(self):
ureg = UnitRegistry()
x = 5 * ureg.meter
self.assertIsInstance(x.magnitude, int)
y = 0.1 * ureg.meter
Expand All @@ -344,7 +327,6 @@ def test_issue93(self):

@helpers.requires_numpy_previous_than("1.10")
def test_issue94(self):
ureg = UnitRegistry()
v1 = np.array([5, 5]) * ureg.meter
v2 = 0.1 * ureg.meter
v3 = np.array([5, 5]) * ureg.meter
Expand All @@ -354,7 +336,6 @@ def test_issue94(self):
np.testing.assert_array_equal(v3.magnitude, np.array([5, 5]))

def test_issue104(self):
ureg = UnitRegistry()

x = [ureg("1 meter"), ureg("1 meter"), ureg("1 meter")]
y = [ureg("1 meter")] * 3
Expand All @@ -374,7 +355,6 @@ def summer(values):
self.assertQuantityAlmostEqual(y[0], ureg.Quantity(1, "meter"))

def test_issue105(self):
ureg = UnitRegistry()

func = ureg.parse_unit_name
val = list(func("meter"))
Expand All @@ -388,7 +368,6 @@ def test_issue105(self):
self.assertEqual(val, func("METER", False))

def test_issue121(self):
ureg = UnitRegistry()
z, v = 0, 2.0
self.assertEqual(z + v * ureg.meter, v * ureg.meter)
self.assertEqual(z - v * ureg.meter, -v * ureg.meter)
Expand All @@ -400,7 +379,6 @@ def test_issue121(self):
@helpers.requires_numpy18()
def test_issue121b(self):
sh = (2, 1)
ureg = UnitRegistry()

z, v = 0, 2.0
self.assertEqual(z + v * ureg.meter, v * ureg.meter)
Expand Down Expand Up @@ -441,15 +419,12 @@ def test_issue170(self):
self.assertIsInstance(iq, int)

def test_angstrom_creation(self):
ureg = UnitRegistry()
ureg.Quantity(2, "Å")

def test_alternative_angstrom_definition(self):
ureg = UnitRegistry()
ureg.Quantity(2, "\u212B")

def test_micro_creation(self):
ureg = UnitRegistry()
ureg.Quantity(2, "µm")

@helpers.requires_numpy()
Expand Down Expand Up @@ -505,8 +480,6 @@ def test_issue354_356_370(self):
self.assertEqual("{:~}".format(1 * self.ureg("MiB")), "1 MiB")

def test_issue468(self):
ureg = UnitRegistry()

@ureg.wraps(("kg"), "meter")
def f(x):
return x
Expand Down Expand Up @@ -548,7 +521,6 @@ def f(x):
self.assertRaises(DimensionalityError, f, ureg.Quantity(1, "m"))

def test_issue625a(self):
ureg = UnitRegistry()
Q_ = ureg.Quantity
from math import sqrt

Expand All @@ -574,7 +546,6 @@ def calculate_time_to_fall(height, gravity=Q_(9.8, "m/s^2")):
self.assertAlmostEqual(t2, Q_(3.508232077228117, "s"))

def test_issue625b(self):
ureg = UnitRegistry()
Q_ = ureg.Quantity

@ureg.wraps("=A*B", ("=A", "=B"))
Expand All @@ -601,7 +572,6 @@ def get_product(a=2 * u.m, b=3 * u.m, c=5 * u.m):
self.assertEqual(get_product(c=1 * u.dimensionless), 6 * u.m ** 2)

def test_issue655a(self):
ureg = UnitRegistry()
distance = 1 * ureg.m
time = 1 * ureg.s
velocity = distance / time
Expand All @@ -611,7 +581,6 @@ def test_issue655a(self):
self.assertEqual(velocity.check("1 / [time] * [length]"), True)

def test_issue655b(self):
ureg = UnitRegistry()
Q_ = ureg.Quantity

@ureg.check("[length]", "[length]/[time]^2")
Expand All @@ -629,7 +598,6 @@ def pendulum_period(length, G=Q_(1, "standard_gravity")):
self.assertAlmostEqual(t, Q_("4.928936075204336 second"))

def test_issue783(self):
ureg = UnitRegistry()
assert not ureg("g") == []

def test_issue856(self):
Expand Down Expand Up @@ -682,13 +650,11 @@ def test_issue912(self):
handles TypeError, but not generic Exceptions. This test will fail if
pint.DimensionalityError stops being a subclass of TypeError.
"""
ureg = UnitRegistry()
meter_units = ureg.get_compatible_units(ureg.meter)
hertz_units = ureg.get_compatible_units(ureg.hertz)
pprint.pformat(meter_units | hertz_units)

def test_issue932(self):
ureg = UnitRegistry()
q = ureg.Quantity("1 kg")
with self.assertRaises(DimensionalityError):
q.to("joule")
Expand All @@ -697,3 +663,44 @@ def test_issue932(self):
ureg.disable_contexts()
with self.assertRaises(DimensionalityError):
q.to("joule")


try:

@pytest.mark.skipif(np is None, reason="NumPy is not available")
@pytest.mark.parametrize(
"callable",
[
lambda x: np.sin(x / x.units), # Issue 399
lambda x: np.cos(x / x.units), # Issue 399
np.isfinite, # Issue 481
np.shape, # Issue 509
np.size, # Issue 509
np.sqrt, # Issue 622
lambda x: x.mean(), # Issue 678
lambda x: x.copy(), # Issue 678
np.array,
lambda x: x.conjugate,
],
)
@pytest.mark.parametrize(
"q",
[
pytest.param(ureg.Quantity(1, "m"), id="python scalar int"),
pytest.param(ureg.Quantity([1, 2, 3, 4], "m"), id="array int"),
pytest.param(ureg.Quantity([1], "m")[0], id="numpy scalar int"),
pytest.param(ureg.Quantity(1.0, "m"), id="python scalar float"),
pytest.param(ureg.Quantity([1.0, 2.0, 3.0, 4.0], "m"), id="array float"),
pytest.param(ureg.Quantity([1.0], "m")[0], id="numpy scalar float"),
],
)
def test_issue925(callable, q):
# Test for immutability of type
type_before = type(q._magnitude)
callable(q)
assert isinstance(q._magnitude, type_before)


except AttributeError:
# Calling attributes on np will fail if NumPy is not available
pass

0 comments on commit 8d8491f

Please sign in to comment.