1import copy
2import functools
3import logging
4import math
5import re
6
7import pytest
8
9from pint import (
10    DefinitionSyntaxError,
11    DimensionalityError,
12    RedefinitionError,
13    UndefinedUnitError,
14)
15from pint.compat import np
16from pint.registry import LazyRegistry, UnitRegistry
17from pint.testsuite import QuantityTestCase, helpers
18from pint.util import ParserHelper, UnitsContainer
19
20
21class TestUnit(QuantityTestCase):
22    def test_creation(self):
23        for arg in ("meter", UnitsContainer(meter=1), self.U_("m")):
24            assert self.U_(arg)._units == UnitsContainer(meter=1)
25        with pytest.raises(TypeError):
26            self.U_(1)
27
28    def test_deepcopy(self):
29        x = self.U_(UnitsContainer(meter=1))
30        assert x == copy.deepcopy(x)
31
32    def test_unit_repr(self):
33        x = self.U_(UnitsContainer(meter=1))
34        assert str(x) == "meter"
35        assert repr(x) == "<Unit('meter')>"
36
37    def test_unit_formatting(self, subtests):
38        x = self.U_(UnitsContainer(meter=2, kilogram=1, second=-1))
39        for spec, result in (
40            ("{}", str(x)),
41            ("{!s}", str(x)),
42            ("{!r}", repr(x)),
43            (
44                "{:L}",
45                r"\frac{\mathrm{kilogram} \cdot \mathrm{meter}^{2}}{\mathrm{second}}",
46            ),
47            ("{:P}", "kilogram·meter²/second"),
48            ("{:H}", "kilogram meter<sup>2</sup>/second"),
49            ("{:C}", "kilogram*meter**2/second"),
50            ("{:Lx}", r"\si[]{\kilo\gram\meter\squared\per\second}"),
51            ("{:~}", "kg * m ** 2 / s"),
52            ("{:L~}", r"\frac{\mathrm{kg} \cdot \mathrm{m}^{2}}{\mathrm{s}}"),
53            ("{:P~}", "kg·m²/s"),
54            ("{:H~}", "kg m<sup>2</sup>/s"),
55            ("{:C~}", "kg*m**2/s"),
56        ):
57            with subtests.test(spec):
58                assert spec.format(x) == result
59
60    def test_unit_default_formatting(self, subtests):
61        ureg = UnitRegistry()
62        x = ureg.Unit(UnitsContainer(meter=2, kilogram=1, second=-1))
63        for spec, result in (
64            (
65                "L",
66                r"\frac{\mathrm{kilogram} \cdot \mathrm{meter}^{2}}{\mathrm{second}}",
67            ),
68            ("P", "kilogram·meter²/second"),
69            ("H", "kilogram meter<sup>2</sup>/second"),
70            ("C", "kilogram*meter**2/second"),
71            ("~", "kg * m ** 2 / s"),
72            ("L~", r"\frac{\mathrm{kg} \cdot \mathrm{m}^{2}}{\mathrm{s}}"),
73            ("P~", "kg·m²/s"),
74            ("H~", "kg m<sup>2</sup>/s"),
75            ("C~", "kg*m**2/s"),
76        ):
77            with subtests.test(spec):
78                ureg.default_format = spec
79                assert f"{x}" == result, f"Failed for {spec}, {result}"
80
81    def test_unit_formatting_snake_case(self, subtests):
82        # Test that snake_case units are escaped where appropriate
83        ureg = UnitRegistry()
84        x = ureg.Unit(UnitsContainer(oil_barrel=1))
85        for spec, result in (
86            ("L", r"\mathrm{oil\_barrel}"),
87            ("P", "oil_barrel"),
88            ("H", "oil_barrel"),
89            ("C", "oil_barrel"),
90            ("~", "oil_bbl"),
91            ("L~", r"\mathrm{oil\_bbl}"),
92            ("P~", "oil_bbl"),
93            ("H~", "oil_bbl"),
94            ("C~", "oil_bbl"),
95        ):
96            with subtests.test(spec):
97                ureg.default_format = spec
98                assert f"{x}" == result, f"Failed for {spec}, {result}"
99
100    def test_unit_formatting_custom(self, monkeypatch):
101        from pint import formatting, register_unit_format
102
103        monkeypatch.setattr(formatting, "_FORMATTERS", formatting._FORMATTERS.copy())
104
105        @register_unit_format("new")
106        def format_new(unit, **options):
107            return "new format"
108
109        ureg = UnitRegistry()
110
111        assert "{:new}".format(ureg.m) == "new format"
112
113    def test_ipython(self):
114        alltext = []
115
116        class Pretty:
117            @staticmethod
118            def text(text):
119                alltext.append(text)
120
121        ureg = UnitRegistry()
122        x = ureg.Unit(UnitsContainer(meter=2, kilogram=1, second=-1))
123        assert x._repr_html_() == "kilogram meter<sup>2</sup>/second"
124        assert (
125            x._repr_latex_() == r"$\frac{\mathrm{kilogram} \cdot "
126            r"\mathrm{meter}^{2}}{\mathrm{second}}$"
127        )
128        x._repr_pretty_(Pretty, False)
129        assert "".join(alltext) == "kilogram·meter²/second"
130        ureg.default_format = "~"
131        assert x._repr_html_() == "kg m<sup>2</sup>/s"
132        assert (
133            x._repr_latex_() == r"$\frac{\mathrm{kg} \cdot \mathrm{m}^{2}}{\mathrm{s}}$"
134        )
135        alltext = []
136        x._repr_pretty_(Pretty, False)
137        assert "".join(alltext) == "kg·m²/s"
138
139    def test_unit_mul(self):
140        x = self.U_("m")
141        assert x * 1 == self.Q_(1, "m")
142        assert x * 0.5 == self.Q_(0.5, "m")
143        assert x * self.Q_(1, "m") == self.Q_(1, "m**2")
144        assert 1 * x == self.Q_(1, "m")
145
146    def test_unit_div(self):
147        x = self.U_("m")
148        assert x / 1 == self.Q_(1, "m")
149        assert x / 0.5 == self.Q_(2.0, "m")
150        assert x / self.Q_(1, "m") == self.Q_(1)
151
152    def test_unit_rdiv(self):
153        x = self.U_("m")
154        assert 1 / x == self.Q_(1, "1/m")
155
156    def test_unit_pow(self):
157        x = self.U_("m")
158        assert x ** 2 == self.U_("m**2")
159
160    def test_unit_hash(self):
161        x = self.U_("m")
162        assert hash(x) == hash(x._units)
163
164    def test_unit_eqs(self):
165        x = self.U_("m")
166        assert x == self.U_("m")
167        assert x != self.U_("cm")
168
169        assert x == self.Q_(1, "m")
170        assert x != self.Q_(2, "m")
171
172        assert x == UnitsContainer({"meter": 1})
173
174        y = self.U_("cm/m")
175        assert y == 0.01
176
177        assert self.U_("byte") == self.U_("byte")
178        assert not (self.U_("byte") != self.U_("byte"))
179
180    def test_unit_cmp(self):
181
182        x = self.U_("m")
183        assert x < self.U_("km")
184        assert x > self.U_("mm")
185
186        y = self.U_("m/mm")
187        assert y > 1
188        assert y < 1e6
189
190    def test_dimensionality(self):
191
192        x = self.U_("m")
193        assert x.dimensionality == UnitsContainer({"[length]": 1})
194
195    def test_dimensionless(self):
196
197        assert self.U_("m/mm").dimensionless
198        assert not self.U_("m").dimensionless
199
200    def test_unit_casting(self):
201
202        assert int(self.U_("m/mm")) == 1000
203        assert float(self.U_("mm/m")) == 1e-3
204        assert complex(self.U_("mm/mm")) == 1 + 0j
205
206    @helpers.requires_numpy
207    def test_array_interface(self):
208        import numpy as np
209
210        x = self.U_("m")
211        arr = np.ones(10)
212        helpers.assert_quantity_equal(arr * x, self.Q_(arr, "m"))
213        helpers.assert_quantity_equal(arr / x, self.Q_(arr, "1/m"))
214        helpers.assert_quantity_equal(x / arr, self.Q_(arr, "m"))
215
216
217class TestRegistry(QuantityTestCase):
218    @classmethod
219    def setup_class(cls):
220        super().setup_class()
221        cls.ureg.autoconvert_offset_to_baseunit = False
222
223    def test_base(self):
224        ureg = UnitRegistry(None)
225        ureg.define("meter = [length]")
226        with pytest.raises(DefinitionSyntaxError):
227            ureg.define("meter = [length]")
228        with pytest.raises(TypeError):
229            ureg.define(list())
230        ureg.define("degC = kelvin; offset: 273.15")
231
232    def test_define(self):
233        ureg = UnitRegistry(None)
234        assert isinstance(dir(ureg), list)
235        assert len(dir(ureg)) > 0
236
237    def test_load(self):
238        import pkg_resources
239
240        from pint import unit
241
242        data = pkg_resources.resource_filename(unit.__name__, "default_en.txt")
243        ureg1 = UnitRegistry()
244        ureg2 = UnitRegistry(data)
245        assert dir(ureg1) == dir(ureg2)
246        with pytest.raises(ValueError):
247            UnitRegistry(None).load_definitions("notexisting")
248
249    def test_default_format(self):
250        ureg = UnitRegistry()
251        q = ureg.meter
252        s1 = f"{q}"
253        s2 = f"{q:~}"
254        ureg.default_format = "~"
255        s3 = f"{q}"
256        assert s2 == s3
257        assert s1 != s3
258        assert ureg.default_format == "~"
259
260    def test_iterate(self):
261        ureg = UnitRegistry()
262        assert "meter" in list(ureg)
263
264    def test_parse_number(self):
265        assert self.ureg.parse_expression("pi") == math.pi
266        assert self.ureg.parse_expression("x", x=2) == 2
267        assert self.ureg.parse_expression("x", x=2.3) == 2.3
268        assert self.ureg.parse_expression("x * y", x=2.3, y=3) == 2.3 * 3
269        assert self.ureg.parse_expression("x", x=(1 + 1j)) == (1 + 1j)
270
271    def test_parse_single(self):
272        assert self.ureg.parse_expression("meter") == self.Q_(
273            1, UnitsContainer(meter=1.0)
274        )
275        assert self.ureg.parse_expression("second") == self.Q_(
276            1, UnitsContainer(second=1.0)
277        )
278
279    def test_parse_alias(self):
280        assert self.ureg.parse_expression("metre") == self.Q_(
281            1, UnitsContainer(meter=1.0)
282        )
283
284    def test_parse_plural(self):
285        assert self.ureg.parse_expression("meters") == self.Q_(
286            1, UnitsContainer(meter=1.0)
287        )
288
289    def test_parse_prefix(self):
290        assert self.ureg.parse_expression("kilometer") == self.Q_(
291            1, UnitsContainer(kilometer=1.0)
292        )
293
294    def test_parse_complex(self):
295        assert self.ureg.parse_expression("kilometre") == self.Q_(
296            1, UnitsContainer(kilometer=1.0)
297        )
298        assert self.ureg.parse_expression("kilometres") == self.Q_(
299            1, UnitsContainer(kilometer=1.0)
300        )
301
302    def test_parse_mul_div(self):
303        assert self.ureg.parse_expression("meter*meter") == self.Q_(
304            1, UnitsContainer(meter=2.0)
305        )
306        assert self.ureg.parse_expression("meter**2") == self.Q_(
307            1, UnitsContainer(meter=2.0)
308        )
309        assert self.ureg.parse_expression("meter*second") == self.Q_(
310            1, UnitsContainer(meter=1.0, second=1)
311        )
312        assert self.ureg.parse_expression("meter/second") == self.Q_(
313            1, UnitsContainer(meter=1.0, second=-1)
314        )
315        assert self.ureg.parse_expression("meter/second**2") == self.Q_(
316            1, UnitsContainer(meter=1.0, second=-2)
317        )
318
319    def test_parse_pretty(self):
320        assert self.ureg.parse_expression("meter/second²") == self.Q_(
321            1, UnitsContainer(meter=1.0, second=-2)
322        )
323        assert self.ureg.parse_expression("m³/s³") == self.Q_(
324            1, UnitsContainer(meter=3.0, second=-3)
325        )
326        assert self.ureg.parse_expression("meter² · second") == self.Q_(
327            1, UnitsContainer(meter=2.0, second=1)
328        )
329        assert self.ureg.parse_expression("m²·s⁻²") == self.Q_(
330            1, UnitsContainer(meter=2, second=-2)
331        )
332        assert self.ureg.parse_expression("meter⁰.⁵·second") == self.Q_(
333            1, UnitsContainer(meter=0.5, second=1)
334        )
335        assert self.ureg.parse_expression("meter³⁷/second⁴.³²¹") == self.Q_(
336            1, UnitsContainer(meter=37, second=-4.321)
337        )
338
339    def test_parse_factor(self):
340        assert self.ureg.parse_expression("42*meter") == self.Q_(
341            42, UnitsContainer(meter=1.0)
342        )
343        assert self.ureg.parse_expression("meter*42") == self.Q_(
344            42, UnitsContainer(meter=1.0)
345        )
346
347    def test_rep_and_parse(self):
348        q = self.Q_(1, "g/(m**2*s)")
349        assert self.Q_(q.magnitude, str(q.units)) == q
350
351    def test_as_delta(self):
352        parse = self.ureg.parse_units
353        assert parse("kelvin", as_delta=True) == UnitsContainer(kelvin=1)
354        assert parse("kelvin", as_delta=False) == UnitsContainer(kelvin=1)
355        assert parse("kelvin**(-1)", as_delta=True) == UnitsContainer(kelvin=-1)
356        assert parse("kelvin**(-1)", as_delta=False) == UnitsContainer(kelvin=-1)
357        assert parse("kelvin**2", as_delta=True) == UnitsContainer(kelvin=2)
358        assert parse("kelvin**2", as_delta=False) == UnitsContainer(kelvin=2)
359        assert parse("kelvin*meter", as_delta=True) == UnitsContainer(kelvin=1, meter=1)
360        assert parse("kelvin*meter", as_delta=False) == UnitsContainer(
361            kelvin=1, meter=1
362        )
363
364    @helpers.requires_numpy
365    def test_parse_with_force_ndarray(self):
366        ureg = UnitRegistry(force_ndarray=True)
367
368        assert ureg.parse_expression("m * s ** -2").units == ureg.m / ureg.s ** 2
369
370    def test_parse_expression_with_preprocessor(self):
371        # Add parsing of UDUNITS-style power
372        self.ureg.preprocessors.append(
373            functools.partial(
374                re.sub,
375                r"(?<=[A-Za-z])(?![A-Za-z])(?<![0-9\-][eE])(?<![0-9\-])(?=[0-9\-])",
376                "**",
377            )
378        )
379        # Test equality
380        assert self.ureg.parse_expression("42 m2") == self.Q_(
381            42, UnitsContainer(meter=2.0)
382        )
383        assert self.ureg.parse_expression("1e6 Hz s-2") == self.Q_(
384            1e6, UnitsContainer(second=-3.0)
385        )
386        assert self.ureg.parse_expression("3 metre3") == self.Q_(
387            3, UnitsContainer(meter=3.0)
388        )
389        # Clean up and test previously expected value
390        self.ureg.preprocessors.pop()
391        assert self.ureg.parse_expression("1e6 Hz s-2") == self.Q_(
392            999998.0, UnitsContainer()
393        )
394
395    def test_parse_unit_with_preprocessor(self):
396        # Add parsing of UDUNITS-style power
397        self.ureg.preprocessors.append(
398            functools.partial(
399                re.sub,
400                r"(?<=[A-Za-z])(?![A-Za-z])(?<![0-9\-][eE])(?<![0-9\-])(?=[0-9\-])",
401                "**",
402            )
403        )
404        # Test equality
405        assert self.ureg.parse_units("m2") == UnitsContainer(meter=2.0)
406        assert self.ureg.parse_units("m-2") == UnitsContainer(meter=-2.0)
407        # Clean up
408        self.ureg.preprocessors.pop()
409
410    def test_name(self):
411        with pytest.raises(UndefinedUnitError):
412            self.ureg.get_name("asdf")
413
414    def test_symbol(self):
415        with pytest.raises(UndefinedUnitError):
416            self.ureg.get_symbol("asdf")
417
418        assert self.ureg.get_symbol("meter") == "m"
419        assert self.ureg.get_symbol("second") == "s"
420        assert self.ureg.get_symbol("hertz") == "Hz"
421
422        assert self.ureg.get_symbol("kilometer") == "km"
423        assert self.ureg.get_symbol("megahertz") == "MHz"
424        assert self.ureg.get_symbol("millisecond") == "ms"
425
426    def test_imperial_symbol(self):
427        assert self.ureg.get_symbol("inch") == "in"
428        assert self.ureg.get_symbol("foot") == "ft"
429        assert self.ureg.get_symbol("inches") == "in"
430        assert self.ureg.get_symbol("feet") == "ft"
431        assert self.ureg.get_symbol("international_foot") == "ft"
432        assert self.ureg.get_symbol("international_inch") == "in"
433
434    def test_pint(self):
435        assert self.ureg.pint < self.ureg.liter
436        assert self.ureg.pint < self.ureg.imperial_pint
437
438    def test_wraps(self):
439        def func(x):
440            return x
441
442        ureg = self.ureg
443
444        with pytest.raises(TypeError):
445            ureg.wraps((3 * ureg.meter, [None]))
446        with pytest.raises(TypeError):
447            ureg.wraps((None, [3 * ureg.meter]))
448
449        f0 = ureg.wraps(None, [None])(func)
450        assert f0(3.0) == 3.0
451
452        f0 = ureg.wraps(None, None)(func)
453        assert f0(3.0) == 3.0
454
455        f1 = ureg.wraps(None, ["meter"])(func)
456        with pytest.raises(ValueError):
457            f1(3.0)
458        assert f1(3.0 * ureg.centimeter) == 0.03
459        assert f1(3.0 * ureg.meter) == 3.0
460        with pytest.raises(DimensionalityError):
461            f1(3 * ureg.second)
462
463        f1b = ureg.wraps(None, [ureg.meter])(func)
464        with pytest.raises(ValueError):
465            f1b(3.0)
466        assert f1b(3.0 * ureg.centimeter) == 0.03
467        assert f1b(3.0 * ureg.meter) == 3.0
468        with pytest.raises(DimensionalityError):
469            f1b(3 * ureg.second)
470
471        f1c = ureg.wraps("meter", [ureg.meter])(func)
472        assert f1c(3.0 * ureg.centimeter) == 0.03 * ureg.meter
473        assert f1c(3.0 * ureg.meter) == 3.0 * ureg.meter
474        with pytest.raises(DimensionalityError):
475            f1c(3 * ureg.second)
476
477        f1d = ureg.wraps(ureg.meter, [ureg.meter])(func)
478        assert f1d(3.0 * ureg.centimeter) == 0.03 * ureg.meter
479        assert f1d(3.0 * ureg.meter) == 3.0 * ureg.meter
480        with pytest.raises(DimensionalityError):
481            f1d(3 * ureg.second)
482
483        f1 = ureg.wraps(None, "meter")(func)
484        with pytest.raises(ValueError):
485            f1(3.0)
486        assert f1(3.0 * ureg.centimeter) == 0.03
487        assert f1(3.0 * ureg.meter) == 3.0
488        with pytest.raises(DimensionalityError):
489            f1(3 * ureg.second)
490
491        f2 = ureg.wraps("centimeter", ["meter"])(func)
492        with pytest.raises(ValueError):
493            f2(3.0)
494        assert f2(3.0 * ureg.centimeter) == 0.03 * ureg.centimeter
495        assert f2(3.0 * ureg.meter) == 3 * ureg.centimeter
496
497        f3 = ureg.wraps("centimeter", ["meter"], strict=False)(func)
498        assert f3(3) == 3 * ureg.centimeter
499        assert f3(3.0 * ureg.centimeter) == 0.03 * ureg.centimeter
500        assert f3(3.0 * ureg.meter) == 3.0 * ureg.centimeter
501
502        def gfunc(x, y):
503            return x + y
504
505        g0 = ureg.wraps(None, [None, None])(gfunc)
506        assert g0(3, 1) == 4
507
508        g1 = ureg.wraps(None, ["meter", "centimeter"])(gfunc)
509        with pytest.raises(ValueError):
510            g1(3 * ureg.meter, 1)
511        assert g1(3 * ureg.meter, 1 * ureg.centimeter) == 4
512        assert g1(3 * ureg.meter, 1 * ureg.meter) == 3 + 100
513
514        def hfunc(x, y):
515            return x, y
516
517        h0 = ureg.wraps(None, [None, None])(hfunc)
518        assert h0(3, 1) == (3, 1)
519
520        h1 = ureg.wraps(["meter", "centimeter"], [None, None])(hfunc)
521        assert h1(3, 1) == [3 * ureg.meter, 1 * ureg.cm]
522
523        h2 = ureg.wraps(("meter", "centimeter"), [None, None])(hfunc)
524        assert h2(3, 1) == (3 * ureg.meter, 1 * ureg.cm)
525
526        h3 = ureg.wraps((None,), (None, None))(hfunc)
527        assert h3(3, 1) == (3, 1)
528
529    def test_wrap_referencing(self):
530
531        ureg = self.ureg
532
533        def gfunc(x, y):
534            return x + y
535
536        def gfunc2(x, y):
537            return x ** 2 + y
538
539        def gfunc3(x, y):
540            return x ** 2 * y
541
542        rst = 3.0 * ureg.meter + 1.0 * ureg.centimeter
543
544        g0 = ureg.wraps("=A", ["=A", "=A"])(gfunc)
545        assert g0(3.0 * ureg.meter, 1.0 * ureg.centimeter) == rst.to("meter")
546        assert g0(3, 1) == 4
547
548        g1 = ureg.wraps("=A", ["=A", "=A"])(gfunc)
549        assert g1(3.0 * ureg.meter, 1.0 * ureg.centimeter) == rst.to("centimeter")
550
551        g2 = ureg.wraps("=A", ["=A", "=A"])(gfunc)
552        assert g2(3.0 * ureg.meter, 1.0 * ureg.centimeter) == rst.to("meter")
553
554        g3 = ureg.wraps("=A**2", ["=A", "=A**2"])(gfunc2)
555        a = 3.0 * ureg.meter
556        b = (2.0 * ureg.centimeter) ** 2
557        assert g3(a, b) == gfunc2(a, b)
558        assert g3(3, 2) == gfunc2(3, 2)
559
560        g4 = ureg.wraps("=A**2 * B", ["=A", "=B"])(gfunc3)
561        assert g4(3.0 * ureg.meter, 2.0 * ureg.second) == ureg(
562            "(3*meter)**2 * 2 *second"
563        )
564        assert g4(3.0 * ureg.meter, 2.0) == ureg("(3*meter)**2 * 2")
565        assert g4(3.0, 2.0 * ureg.second) == ureg("3**2 * 2 * second")
566
567    def test_check(self):
568        def func(x):
569            return x
570
571        ureg = self.ureg
572
573        f0 = ureg.check("[length]")(func)
574        with pytest.raises(DimensionalityError):
575            f0(3.0)
576        assert f0(3.0 * ureg.centimeter) == 0.03 * ureg.meter
577        with pytest.raises(DimensionalityError):
578            f0(3.0 * ureg.kilogram)
579
580        f0b = ureg.check(ureg.meter)(func)
581        with pytest.raises(DimensionalityError):
582            f0b(3.0)
583        assert f0b(3.0 * ureg.centimeter) == 0.03 * ureg.meter
584        with pytest.raises(DimensionalityError):
585            f0b(3.0 * ureg.kilogram)
586
587        def gfunc(x, y):
588            return x / y
589
590        g0 = ureg.check(None, None)(gfunc)
591        assert g0(6, 2) == 3
592        assert g0(6 * ureg.parsec, 2) == 3 * ureg.parsec
593
594        g1 = ureg.check("[speed]", "[time]")(gfunc)
595        with pytest.raises(DimensionalityError):
596            g1(3.0, 1)
597        with pytest.raises(DimensionalityError):
598            g1(1 * ureg.parsec, 1 * ureg.angstrom)
599        with pytest.raises(TypeError):
600            g1(1 * ureg.km / ureg.hour, 1 * ureg.hour, 3.0)
601        assert (
602            g1(3.6 * ureg.km / ureg.hour, 1 * ureg.second)
603            == 1 * ureg.meter / ureg.second ** 2
604        )
605
606        with pytest.raises(TypeError):
607            ureg.check("[speed]")(gfunc)
608        with pytest.raises(TypeError):
609            ureg.check("[speed]", "[time]", "[mass]")(gfunc)
610
611    def test_to_ref_vs_to(self):
612        self.ureg.autoconvert_offset_to_baseunit = True
613        q = 8.0 * self.ureg.inch
614        t = 8.0 * self.ureg.degF
615        dt = 8.0 * self.ureg.delta_degF
616        assert q.to("yard").magnitude == self.ureg._units[
617            "inch"
618        ].converter.to_reference(8.0)
619        assert t.to("kelvin").magnitude == self.ureg._units[
620            "degF"
621        ].converter.to_reference(8.0)
622        assert dt.to("kelvin").magnitude == self.ureg._units[
623            "delta_degF"
624        ].converter.to_reference(8.0)
625
626    def test_redefinition(self, caplog):
627        d = UnitRegistry().define
628
629        with caplog.at_level(logging.DEBUG):
630            d("meter = [fruits]")
631            d("kilo- = 1000")
632            d("[speed] = [vegetables]")
633
634            # aliases
635            d("bla = 3.2 meter = inch")
636            d("myk- = 1000 = kilo-")
637
638        assert len(caplog.records) == 5
639
640    def test_convert_parse_str(self):
641        ureg = self.ureg
642        assert ureg.convert(1, "meter", "inch") == ureg.convert(
643            1, UnitsContainer(meter=1), UnitsContainer(inch=1)
644        )
645
646    @helpers.requires_numpy
647    def test_convert_inplace(self):
648        ureg = self.ureg
649
650        # Conversions with single units take a different codepath than
651        # Conversions with more than one unit.
652        src_dst1 = UnitsContainer(meter=1), UnitsContainer(inch=1)
653        src_dst2 = UnitsContainer(meter=1, second=-1), UnitsContainer(inch=1, minute=-1)
654        for src, dst in (src_dst1, src_dst2):
655            v = (ureg.convert(1, src, dst),)
656
657            a = np.ones((3, 1))
658            ac = np.ones((3, 1))
659
660            r1 = ureg.convert(a, src, dst)
661            np.testing.assert_allclose(r1, v * ac)
662            assert r1 is not a
663
664            r2 = ureg.convert(a, src, dst, inplace=True)
665            np.testing.assert_allclose(r2, v * ac)
666            assert r2 is a
667
668    def test_repeated_convert(self):
669        # Because of caching, repeated conversions were failing.
670        self.ureg.convert(1, "m", "ft")
671        self.ureg.convert(1, "m", "ft")
672
673    def test_singular_SI_prefix_convert(self):
674        # Fix for issue 156
675        self.ureg.convert(1, "mm", "m")
676        self.ureg.convert(1, "ms", "s")
677        self.ureg.convert(1, "m", "mm")
678        self.ureg.convert(1, "s", "ms")
679
680    def test_parse_units(self):
681        ureg = self.ureg
682        assert ureg.parse_units("") == ureg.Unit("")
683        with pytest.raises(ValueError):
684            ureg.parse_units("2 * meter")
685
686    def test_parse_string_pattern(self):
687        ureg = self.ureg
688        assert ureg.parse_pattern("10'11", r"{foot}'{inch}") == [
689            ureg.Quantity(10.0, "foot"),
690            ureg.Quantity(11.0, "inch"),
691        ]
692
693    def test_parse_string_pattern_no_preprocess(self):
694        """Were preprocessors enabled, this would be interpreted as 10*11, not
695        two separate units, and thus cause the parsing to fail"""
696        ureg = self.ureg
697        assert ureg.parse_pattern("10 11", r"{kg} {lb}") == [
698            ureg.Quantity(10.0, "kilogram"),
699            ureg.Quantity(11.0, "pound"),
700        ]
701
702    def test_parse_pattern_many_results(self):
703        ureg = self.ureg
704        assert ureg.parse_pattern(
705            "1.5kg or 2kg will be fine, if you do not have 3kg",
706            r"{kg}kg",
707            many=True,
708        ) == [
709            [ureg.Quantity(1.5, "kilogram")],
710            [ureg.Quantity(2.0, "kilogram")],
711            [ureg.Quantity(3.0, "kilogram")],
712        ]
713
714    def test_parse_pattern_many_results_two_units(self):
715        ureg = self.ureg
716        assert ureg.parse_pattern("10'10 or 10'11", "{foot}'{inch}", many=True) == [
717            [ureg.Quantity(10.0, "foot"), ureg.Quantity(10.0, "inch")],
718            [ureg.Quantity(10.0, "foot"), ureg.Quantity(11.0, "inch")],
719        ]
720
721    def test_case_sensitivity(self):
722        ureg = self.ureg
723        # Default
724        with pytest.raises(UndefinedUnitError):
725            ureg.parse_units("Meter")
726        with pytest.raises(UndefinedUnitError):
727            ureg.parse_units("j")
728        # Force True
729        with pytest.raises(UndefinedUnitError):
730            ureg.parse_units("Meter", case_sensitive=True)
731        with pytest.raises(UndefinedUnitError):
732            ureg.parse_units("j", case_sensitive=True)
733        # Force False
734        assert ureg.parse_units("Meter", case_sensitive=False) == UnitsContainer(
735            meter=1
736        )
737        assert ureg.parse_units("j", case_sensitive=False) == UnitsContainer(joule=1)
738
739
740class TestCaseInsensitiveRegistry(QuantityTestCase):
741
742    kwargs = dict(case_sensitive=False)
743
744    def test_case_sensitivity(self):
745        ureg = self.ureg
746        # Default
747        assert ureg.parse_units("Meter") == UnitsContainer(meter=1)
748        assert ureg.parse_units("j") == UnitsContainer(joule=1)
749        # Force True
750        with pytest.raises(UndefinedUnitError):
751            ureg.parse_units("Meter", case_sensitive=True)
752        with pytest.raises(UndefinedUnitError):
753            ureg.parse_units("j", case_sensitive=True)
754        # Force False
755        assert ureg.parse_units("Meter", case_sensitive=False) == UnitsContainer(
756            meter=1
757        )
758        assert ureg.parse_units("j", case_sensitive=False) == UnitsContainer(joule=1)
759
760
761class TestCompatibleUnits(QuantityTestCase):
762    def _test(self, input_units):
763        gd = self.ureg.get_dimensionality
764        dim = gd(input_units)
765        equiv = self.ureg.get_compatible_units(input_units)
766        for eq in equiv:
767            assert gd(eq) == dim
768        assert equiv == self.ureg.get_compatible_units(dim)
769
770    def _test2(self, units1, units2):
771        equiv1 = self.ureg.get_compatible_units(units1)
772        equiv2 = self.ureg.get_compatible_units(units2)
773        assert equiv1 == equiv2
774
775    def test_many(self):
776        self._test(self.ureg.meter)
777        self._test(self.ureg.seconds)
778        self._test(self.ureg.newton)
779        self._test(self.ureg.kelvin)
780
781    def test_context_sp(self):
782
783        gd = self.ureg.get_dimensionality
784
785        # length, frequency, energy
786        valid = [
787            gd(self.ureg.meter),
788            gd(self.ureg.hertz),
789            gd(self.ureg.joule),
790            1 / gd(self.ureg.meter),
791        ]
792
793        with self.ureg.context("sp"):
794            equiv = self.ureg.get_compatible_units(self.ureg.meter)
795            result = set()
796            for eq in equiv:
797                dim = gd(eq)
798                result.add(dim)
799                assert dim in valid
800
801            assert len(result) == len(valid)
802
803    def test_get_base_units(self):
804        ureg = UnitRegistry()
805        assert ureg.get_base_units("") == (1, ureg.Unit(""))
806        assert ureg.get_base_units("pi") == (math.pi, ureg.Unit(""))
807        assert ureg.get_base_units("ln10") == (math.log(10), ureg.Unit(""))
808        assert ureg.get_base_units("meter") == ureg.get_base_units(
809            ParserHelper(meter=1)
810        )
811
812    def test_get_compatible_units(self):
813        ureg = UnitRegistry()
814        assert ureg.get_compatible_units("") == frozenset()
815        assert ureg.get_compatible_units("meter") == ureg.get_compatible_units(
816            ParserHelper(meter=1)
817        )
818
819
820class TestRegistryWithDefaultRegistry(TestRegistry):
821    @classmethod
822    def setup_class(cls):
823        from pint import _DEFAULT_REGISTRY
824
825        cls.ureg = _DEFAULT_REGISTRY
826        cls.Q_ = cls.ureg.Quantity
827
828    def test_lazy(self):
829        x = LazyRegistry()
830        x.test = "test"
831        assert isinstance(x, UnitRegistry)
832        y = LazyRegistry()
833        y("meter")
834        assert isinstance(y, UnitRegistry)
835
836    def test_redefinition(self):
837        d = self.ureg.define
838        with pytest.raises(DefinitionSyntaxError):
839            d("meter = [time]")
840        with pytest.raises(RedefinitionError):
841            d("meter = [newdim]")
842        with pytest.raises(RedefinitionError):
843            d("kilo- = 1000")
844        with pytest.raises(RedefinitionError):
845            d("[speed] = [length]")
846
847        # aliases
848        assert "inch" in self.ureg._units
849        with pytest.raises(RedefinitionError):
850            d("bla = 3.2 meter = inch")
851        with pytest.raises(RedefinitionError):
852            d("myk- = 1000 = kilo-")
853
854
855class TestConvertWithOffset(QuantityTestCase):
856
857    # The dicts in convert_with_offset are used to create a UnitsContainer.
858    # We create UnitsContainer to avoid any auto-conversion of units.
859    convert_with_offset = [
860        (({"degC": 1}, {"degC": 1}), 10),
861        (({"degC": 1}, {"kelvin": 1}), 283.15),
862        (({"degC": 1}, {"degC": 1, "millimeter": 1, "meter": -1}), "error"),
863        (({"degC": 1}, {"kelvin": 1, "millimeter": 1, "meter": -1}), 283150),
864        (({"kelvin": 1}, {"degC": 1}), -263.15),
865        (({"kelvin": 1}, {"kelvin": 1}), 10),
866        (({"kelvin": 1}, {"degC": 1, "millimeter": 1, "meter": -1}), "error"),
867        (({"kelvin": 1}, {"kelvin": 1, "millimeter": 1, "meter": -1}), 10000),
868        (({"degC": 1, "millimeter": 1, "meter": -1}, {"degC": 1}), "error"),
869        (({"degC": 1, "millimeter": 1, "meter": -1}, {"kelvin": 1}), "error"),
870        (
871            (
872                {"degC": 1, "millimeter": 1, "meter": -1},
873                {"degC": 1, "millimeter": 1, "meter": -1},
874            ),
875            10,
876        ),
877        (
878            (
879                {"degC": 1, "millimeter": 1, "meter": -1},
880                {"kelvin": 1, "millimeter": 1, "meter": -1},
881            ),
882            "error",
883        ),
884        (({"kelvin": 1, "millimeter": 1, "meter": -1}, {"degC": 1}), -273.14),
885        (({"kelvin": 1, "millimeter": 1, "meter": -1}, {"kelvin": 1}), 0.01),
886        (
887            (
888                {"kelvin": 1, "millimeter": 1, "meter": -1},
889                {"degC": 1, "millimeter": 1, "meter": -1},
890            ),
891            "error",
892        ),
893        (
894            (
895                {"kelvin": 1, "millimeter": 1, "meter": -1},
896                {"kelvin": 1, "millimeter": 1, "meter": -1},
897            ),
898            10,
899        ),
900        (({"degC": 2}, {"kelvin": 2}), "error"),
901        (({"degC": 1, "degF": 1}, {"kelvin": 2}), "error"),
902        (({"degC": 1, "kelvin": 1}, {"kelvin": 2}), "error"),
903    ]
904
905    @pytest.mark.parametrize(("input_tuple", "expected"), convert_with_offset)
906    def test_to_and_from_offset_units(self, input_tuple, expected):
907        src, dst = input_tuple
908        src, dst = UnitsContainer(src), UnitsContainer(dst)
909        value = 10.0
910        convert = self.ureg.convert
911        if isinstance(expected, str):
912            with pytest.raises(DimensionalityError):
913                convert(value, src, dst)
914            if src != dst:
915                with pytest.raises(DimensionalityError):
916                    convert(value, dst, src)
917        else:
918            helpers.assert_quantity_almost_equal(
919                convert(value, src, dst), expected, atol=0.001
920            )
921            if src != dst:
922                helpers.assert_quantity_almost_equal(
923                    convert(expected, dst, src), value, atol=0.001
924                )
925
926    def test_alias(self):
927        # Use load_definitions
928        ureg = UnitRegistry(
929            [
930                "canonical = [] = can = alias1 = alias2\n",
931                # overlapping aliases
932                "@alias canonical = alias2 = alias3\n",
933                # Against another alias
934                "@alias alias3 = alias4\n",
935            ]
936        )
937
938        # Use define
939        ureg.define("@alias canonical = alias5")
940
941        # Test that new aliases work
942        # Test that pre-existing aliases and symbol are not eliminated
943        for a in ("can", "alias1", "alias2", "alias3", "alias4", "alias5"):
944            assert ureg.Unit(a) == ureg.Unit("canonical")
945
946        # Test that aliases defined multiple times are not duplicated
947        assert ureg._units["canonical"].aliases == (
948            "alias1",
949            "alias2",
950            "alias3",
951            "alias4",
952            "alias5",
953        )
954
955        # Define against unknown name
956        with pytest.raises(KeyError):
957            ureg.define("@alias notexist = something")
958