Skip to content

Commit 645158a

Browse files
authored
Merge pull request #2135 from hgrecco/_testing_improve
Fix small annoyances across different test to clean up the log.
2 parents 6e91d5c + d7c69f7 commit 645158a

9 files changed

+94
-61
lines changed

pint/delegates/formatter/_spec_helpers.py

+39-23
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"""
2-
pint.delegates.formatter._spec_helpers
3-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2+
pint.delegates.formatter._spec_helpers
3+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44
5-
Convenient functions to deal with format specifications.
5+
Convenient functions to deal with format specifications.
66
7-
:copyright: 2022 by Pint Authors, see AUTHORS for more details.
8-
:license: BSD, see LICENSE for more details.
7+
:copyright: 2022 by Pint Authors, see AUTHORS for more details.
8+
:license: BSD, see LICENSE for more details.
99
"""
1010

1111
from __future__ import annotations
@@ -87,45 +87,61 @@ def remove_custom_flags(spec: str) -> str:
8787
return spec
8888

8989

90+
##########
91+
# This weird way of defining split format
92+
# is the only reasonable way I foudn to use
93+
# lru_cache in a function that might emit warning
94+
# and do it every time.
95+
# TODO: simplify it when there are no warnings.
96+
97+
9098
@functools.lru_cache
91-
def split_format(
99+
def _split_format(
92100
spec: str, default: str, separate_format_defaults: bool = True
93-
) -> tuple[str, str]:
101+
) -> tuple[str, str, list[str]]:
94102
"""Split format specification into magnitude and unit format."""
95103
mspec = remove_custom_flags(spec)
96104
uspec = extract_custom_flags(spec)
97105

98106
default_mspec = remove_custom_flags(default)
99107
default_uspec = extract_custom_flags(default)
100108

109+
warns = []
101110
if separate_format_defaults in (False, None):
102111
# should we warn always or only if there was no explicit choice?
103112
# Given that we want to eventually remove the flag again, I'd say yes?
104113
if spec and separate_format_defaults is None:
105114
if not uspec and default_uspec:
106-
warnings.warn(
107-
(
108-
"The given format spec does not contain a unit formatter."
109-
" Falling back to the builtin defaults, but in the future"
110-
" the unit formatter specified in the `default_format`"
111-
" attribute will be used instead."
112-
),
113-
DeprecationWarning,
115+
warns.append(
116+
"The given format spec does not contain a unit formatter."
117+
" Falling back to the builtin defaults, but in the future"
118+
" the unit formatter specified in the `default_format`"
119+
" attribute will be used instead."
114120
)
115121
if not mspec and default_mspec:
116-
warnings.warn(
117-
(
118-
"The given format spec does not contain a magnitude formatter."
119-
" Falling back to the builtin defaults, but in the future"
120-
" the magnitude formatter specified in the `default_format`"
121-
" attribute will be used instead."
122-
),
123-
DeprecationWarning,
122+
warns.append(
123+
"The given format spec does not contain a magnitude formatter."
124+
" Falling back to the builtin defaults, but in the future"
125+
" the magnitude formatter specified in the `default_format`"
126+
" attribute will be used instead."
124127
)
125128
elif not spec:
126129
mspec, uspec = default_mspec, default_uspec
127130
else:
128131
mspec = mspec or default_mspec
129132
uspec = uspec or default_uspec
130133

134+
return mspec, uspec, warns
135+
136+
137+
def split_format(
138+
spec: str, default: str, separate_format_defaults: bool = True
139+
) -> tuple[str, str]:
140+
"""Split format specification into magnitude and unit format."""
141+
142+
mspec, uspec, warns = _split_format(spec, default, separate_format_defaults)
143+
144+
for warn_msg in warns:
145+
warnings.warn(warn_msg, DeprecationWarning)
146+
131147
return mspec, uspec

pint/testing.py

+16-4
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,16 @@ def assert_equal(first, second, msg: str | None = None) -> None:
7373
if isinstance(m1, ndarray) or isinstance(m2, ndarray):
7474
np.testing.assert_array_equal(m1, m2, err_msg=msg)
7575
elif not isinstance(m1, Number):
76-
warnings.warn("In assert_equal, m1 is not a number ", UserWarning)
76+
warnings.warn(
77+
f"In assert_equal, m1 is not a number {first} ({m1}) vs. {second} ({m2}) ",
78+
UserWarning,
79+
)
7780
return
7881
elif not isinstance(m2, Number):
79-
warnings.warn("In assert_equal, m2 is not a number ", UserWarning)
82+
warnings.warn(
83+
f"In assert_equal, m2 is not a number {first} ({m1}) vs. {second} ({m2}) ",
84+
UserWarning,
85+
)
8086
return
8187
elif math.isnan(m1):
8288
assert math.isnan(m2), msg
@@ -131,10 +137,16 @@ def assert_allclose(
131137
if isinstance(m1, ndarray) or isinstance(m2, ndarray):
132138
np.testing.assert_allclose(m1, m2, rtol=rtol, atol=atol, err_msg=msg)
133139
elif not isinstance(m1, Number):
134-
warnings.warn("In assert_equal, m1 is not a number ", UserWarning)
140+
warnings.warn(
141+
f"In assert_equal, m1 is not a number {first} ({m1}) vs. {second} ({m2}) ",
142+
UserWarning,
143+
)
135144
return
136145
elif not isinstance(m2, Number):
137-
warnings.warn("In assert_equal, m2 is not a number ", UserWarning)
146+
warnings.warn(
147+
f"In assert_equal, m1 is not a number {first} ({m1}) vs. {second} ({m2}) ",
148+
UserWarning,
149+
)
138150
return
139151
elif math.isnan(m1):
140152
assert math.isnan(m2), msg

pint/testsuite/test_babel.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def test_no_babel(func_registry):
1313
ureg = func_registry
1414
distance = 24.0 * ureg.meter
1515
with pytest.raises(Exception):
16-
distance.format_babel(locale="fr_FR", length="long")
16+
ureg.formatter.format_unit_babel(distance, locale="fr_FR", length="long")
1717

1818

1919
@helpers.requires_babel(["fr_FR", "ro_RO"])

pint/testsuite/test_issues.py

+3-27
Original file line numberDiff line numberDiff line change
@@ -70,29 +70,6 @@ def test_issue37(self, module_registry):
7070
np.testing.assert_array_equal(qq.magnitude, x * m)
7171
assert qq.units == module_registry.meter.units
7272

73-
@pytest.mark.xfail
74-
@helpers.requires_numpy
75-
def test_issue39(self, module_registry):
76-
x = np.matrix([[1, 2, 3], [1, 2, 3], [1, 2, 3]])
77-
q = module_registry.meter * x
78-
assert isinstance(q, module_registry.Quantity)
79-
np.testing.assert_array_equal(q.magnitude, x)
80-
assert q.units == module_registry.meter.units
81-
q = x * module_registry.meter
82-
assert isinstance(q, module_registry.Quantity)
83-
np.testing.assert_array_equal(q.magnitude, x)
84-
assert q.units == module_registry.meter.units
85-
86-
m = np.matrix(2 * np.ones(3, 3))
87-
qq = q * m
88-
assert isinstance(qq, module_registry.Quantity)
89-
np.testing.assert_array_equal(qq.magnitude, x * m)
90-
assert qq.units == module_registry.meter.units
91-
qq = m * q
92-
assert isinstance(qq, module_registry.Quantity)
93-
np.testing.assert_array_equal(qq.magnitude, x * m)
94-
assert qq.units == module_registry.meter.units
95-
9673
@helpers.requires_numpy
9774
def test_issue44(self, module_registry):
9875
x = 4.0 * module_registry.dimensionless
@@ -909,7 +886,7 @@ def test_issue1963(self, module_registry):
909886
assert_equal(1e2 * b, a)
910887
assert_equal(c, 50 * a)
911888

912-
assert_equal((1 * ureg.milligram) / (1 * ureg.gram), ureg.permille)
889+
assert_equal((1 * ureg.milligram) / (1 * ureg.gram), 1 * ureg.permille)
913890

914891
@pytest.mark.xfail
915892
@helpers.requires_uncertainties()
@@ -1230,7 +1207,7 @@ def test_issue_1845():
12301207
def test_issues_1841(func_registry, units, spec, expected):
12311208
ur = func_registry
12321209
ur.formatter.default_sort_func = sort_by_dimensionality
1233-
ur.default_format = spec
1210+
ur.formatter.default_format = spec
12341211
value = ur.Unit(UnitsContainer(**units))
12351212
assert f"{value}" == expected
12361213

@@ -1242,7 +1219,7 @@ def test_issues_1841_xfail():
12421219

12431220
# sets compact display mode by default
12441221
ur = UnitRegistry()
1245-
ur.default_format = "~P"
1222+
ur.formatter.default_format = "~P"
12461223
ur.formatter.default_sort_func = sort_by_dimensionality
12471224

12481225
q = ur.Quantity("2*pi radian * hour")
@@ -1297,7 +1274,6 @@ def test_issue2017():
12971274

12981275
@fmt.register_unit_format("test")
12991276
def _test_format(unit, registry, **options):
1300-
print("format called")
13011277
proc = {u.replace("µ", "u"): e for u, e in unit.items()}
13021278
return fmt.formatter(
13031279
proc.items(),

pint/testsuite/test_measurement.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -296,5 +296,5 @@ def test_tokenization(self):
296296

297297
pint_eval.tokenizer = pint_eval.uncertainty_tokenizer
298298
for p in pint_eval.tokenizer("8 + / - 4"):
299-
print(p)
299+
str(p)
300300
assert True

pint/testsuite/test_numpy.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ def test_cross(self):
440440

441441
# NP2: Remove this when we only support np>=2.0
442442
@helpers.requires_array_function_protocol()
443+
@helpers.requires_numpy_previous_than("2.0")
443444
def test_trapz(self):
444445
helpers.assert_quantity_equal(
445446
np.trapz([1.0, 2.0, 3.0, 4.0] * self.ureg.J, dx=1 * self.ureg.m),
@@ -1227,8 +1228,10 @@ def test_copyto(self):
12271228
helpers.assert_quantity_equal(q, self.Q_([[2, 2], [6, 4]], "m"))
12281229
np.copyto(q, 0, where=[[False, False], [True, False]])
12291230
helpers.assert_quantity_equal(q, self.Q_([[2, 2], [0, 4]], "m"))
1230-
np.copyto(a, q)
1231-
self.assertNDArrayEqual(a, np.array([[2, 2], [0, 4]]))
1231+
with pytest.warns(UnitStrippedWarning):
1232+
# as a is not quantity, the unit is stripped.
1233+
np.copyto(a, q)
1234+
self.assertNDArrayEqual(a, np.array([[2, 2], [0, 4]]))
12321235

12331236
@helpers.requires_array_function_protocol()
12341237
def test_tile(self):

pint/testsuite/test_numpy_func.py

+25
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ def test_numpy_wrap(self):
195195
# TODO (#905 follow-up): test that NotImplemented is returned when upcast types
196196
# present
197197

198+
@helpers.requires_numpy_previous_than("2.0")
198199
def test_trapz(self):
199200
with ExitStack() as stack:
200201
stack.callback(
@@ -210,12 +211,36 @@ def test_trapz(self):
210211
np.trapz(t, x=z), self.Q_(1108.6, "kelvin meter")
211212
)
212213

214+
@helpers.requires_numpy_at_least("2.0")
215+
def test_trapezoid(self):
216+
with ExitStack() as stack:
217+
stack.callback(
218+
setattr,
219+
self.ureg,
220+
"autoconvert_offset_to_baseunit",
221+
self.ureg.autoconvert_offset_to_baseunit,
222+
)
223+
self.ureg.autoconvert_offset_to_baseunit = True
224+
t = self.Q_(np.array([0.0, 4.0, 8.0]), "degC")
225+
z = self.Q_(np.array([0.0, 2.0, 4.0]), "m")
226+
helpers.assert_quantity_equal(
227+
np.trapezoid(t, x=z), self.Q_(1108.6, "kelvin meter")
228+
)
229+
230+
@helpers.requires_numpy_previous_than("2.0")
213231
def test_trapz_no_autoconvert(self):
214232
t = self.Q_(np.array([0.0, 4.0, 8.0]), "degC")
215233
z = self.Q_(np.array([0.0, 2.0, 4.0]), "m")
216234
with pytest.raises(OffsetUnitCalculusError):
217235
np.trapz(t, x=z)
218236

237+
@helpers.requires_numpy_at_least("2.0")
238+
def test_trapezoid_no_autoconvert(self):
239+
t = self.Q_(np.array([0.0, 4.0, 8.0]), "degC")
240+
z = self.Q_(np.array([0.0, 2.0, 4.0]), "m")
241+
with pytest.raises(OffsetUnitCalculusError):
242+
np.trapezoid(t, x=z)
243+
219244
def test_correlate(self):
220245
a = self.Q_(np.array([1, 2, 3]), "m")
221246
v = self.Q_(np.array([0, 1, 0.5]), "s")

pint/testsuite/test_quantity.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -273,27 +273,27 @@ def test_default_formatting(self, subtests):
273273
ureg.formatter.default_format = spec
274274
assert f"{x}" == result
275275

276-
@pytest.mark.xfail(reason="Still not clear how default formatting will work.")
277276
def test_formatting_override_default_units(self):
278277
ureg = UnitRegistry()
279278
ureg.formatter.default_format = "~"
280279
x = ureg.Quantity(4, "m ** 2")
281280

282281
assert f"{x:dP}" == "4 meter²"
282+
ureg.separate_format_defaults = None
283283
with pytest.warns(DeprecationWarning):
284284
assert f"{x:d}" == "4 meter ** 2"
285285

286286
ureg.separate_format_defaults = True
287287
with assert_no_warnings():
288288
assert f"{x:d}" == "4 m ** 2"
289289

290-
@pytest.mark.xfail(reason="Still not clear how default formatting will work.")
291290
def test_formatting_override_default_magnitude(self):
292291
ureg = UnitRegistry()
293292
ureg.formatter.default_format = ".2f"
294293
x = ureg.Quantity(4, "m ** 2")
295294

296295
assert f"{x:dP}" == "4 meter²"
296+
ureg.separate_format_defaults = None
297297
with pytest.warns(DeprecationWarning):
298298
assert f"{x:D}" == "4 meter ** 2"
299299

@@ -842,7 +842,7 @@ def test_nonnumeric_magnitudes(self):
842842
ureg = self.ureg
843843
x = "some string" * ureg.m
844844
with pytest.warns(UndefinedBehavior):
845-
self.compare_quantity_compact(x, x)
845+
x.to_compact()
846846

847847
def test_very_large_to_compact(self):
848848
# This should not raise an IndexError

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ cache-keys = [{ file = "pyproject.toml" }, { git = true }]
8989

9090
[tool.pytest.ini_options]
9191
addopts = "--import-mode=importlib"
92+
xfail_strict = true
9293
pythonpath = "."
9394

9495
[tool.ruff.format]

0 commit comments

Comments
 (0)