1import itertools 2import logging 3import math 4import re 5from collections import defaultdict 6 7import pytest 8 9from pint import ( 10 DefinitionSyntaxError, 11 DimensionalityError, 12 UndefinedUnitError, 13 UnitRegistry, 14) 15from pint.context import Context 16from pint.testsuite import helpers 17from pint.util import UnitsContainer 18 19 20def add_ctxs(ureg): 21 a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1}) 22 d = Context("lc") 23 d.add_transformation(a, b, lambda ureg, x: ureg.speed_of_light / x) 24 d.add_transformation(b, a, lambda ureg, x: ureg.speed_of_light / x) 25 26 ureg.add_context(d) 27 28 a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[current]": 1}) 29 d = Context("ab") 30 d.add_transformation(a, b, lambda ureg, x: ureg.ampere * ureg.meter / x) 31 d.add_transformation(b, a, lambda ureg, x: ureg.ampere * ureg.meter / x) 32 33 ureg.add_context(d) 34 35 36def add_arg_ctxs(ureg): 37 a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1}) 38 d = Context("lc") 39 d.add_transformation(a, b, lambda ureg, x, n: ureg.speed_of_light / x / n) 40 d.add_transformation(b, a, lambda ureg, x, n: ureg.speed_of_light / x / n) 41 42 ureg.add_context(d) 43 44 a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[current]": 1}) 45 d = Context("ab") 46 d.add_transformation(a, b, lambda ureg, x: ureg.ampere * ureg.meter / x) 47 d.add_transformation(b, a, lambda ureg, x: ureg.ampere * ureg.meter / x) 48 49 ureg.add_context(d) 50 51 52def add_argdef_ctxs(ureg): 53 a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1}) 54 d = Context("lc", defaults=dict(n=1)) 55 assert d.defaults == dict(n=1) 56 57 d.add_transformation(a, b, lambda ureg, x, n: ureg.speed_of_light / x / n) 58 d.add_transformation(b, a, lambda ureg, x, n: ureg.speed_of_light / x / n) 59 60 ureg.add_context(d) 61 62 a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[current]": 1}) 63 d = Context("ab") 64 d.add_transformation(a, b, lambda ureg, x: ureg.ampere * ureg.meter / x) 65 d.add_transformation(b, a, lambda ureg, x: ureg.ampere * ureg.meter / x) 66 67 ureg.add_context(d) 68 69 70def add_sharedargdef_ctxs(ureg): 71 a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1}) 72 d = Context("lc", defaults=dict(n=1)) 73 assert d.defaults == dict(n=1) 74 75 d.add_transformation(a, b, lambda ureg, x, n: ureg.speed_of_light / x / n) 76 d.add_transformation(b, a, lambda ureg, x, n: ureg.speed_of_light / x / n) 77 78 ureg.add_context(d) 79 80 a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[current]": 1}) 81 d = Context("ab", defaults=dict(n=0)) 82 d.add_transformation(a, b, lambda ureg, x, n: ureg.ampere * ureg.meter * n / x) 83 d.add_transformation(b, a, lambda ureg, x, n: ureg.ampere * ureg.meter * n / x) 84 85 ureg.add_context(d) 86 87 88class TestContexts: 89 def test_known_context(self, func_registry): 90 ureg = UnitRegistry() 91 add_ctxs(ureg) 92 with ureg.context("lc"): 93 assert ureg._active_ctx 94 assert ureg._active_ctx.graph 95 96 assert not ureg._active_ctx 97 assert not ureg._active_ctx.graph 98 99 with ureg.context("lc", n=1): 100 assert ureg._active_ctx 101 assert ureg._active_ctx.graph 102 103 assert not ureg._active_ctx 104 assert not ureg._active_ctx.graph 105 106 def test_known_context_enable(self, func_registry): 107 ureg = UnitRegistry() 108 add_ctxs(ureg) 109 ureg.enable_contexts("lc") 110 assert ureg._active_ctx 111 assert ureg._active_ctx.graph 112 ureg.disable_contexts(1) 113 114 assert not ureg._active_ctx 115 assert not ureg._active_ctx.graph 116 117 ureg.enable_contexts("lc", n=1) 118 assert ureg._active_ctx 119 assert ureg._active_ctx.graph 120 ureg.disable_contexts(1) 121 122 assert not ureg._active_ctx 123 assert not ureg._active_ctx.graph 124 125 def test_graph(self, func_registry): 126 ureg = UnitRegistry() 127 add_ctxs(ureg) 128 l = UnitsContainer({"[length]": 1.0}) # noqa: E741 129 t = UnitsContainer({"[time]": -1.0}) 130 c = UnitsContainer({"[current]": 1.0}) 131 132 g_sp = defaultdict(set) 133 g_sp.update({l: {t}, t: {l}}) 134 135 g_ab = defaultdict(set) 136 g_ab.update({l: {c}, c: {l}}) 137 138 g = defaultdict(set) 139 g.update({l: {t, c}, t: {l}, c: {l}}) 140 141 with ureg.context("lc"): 142 assert ureg._active_ctx.graph == g_sp 143 144 with ureg.context("lc", n=1): 145 assert ureg._active_ctx.graph == g_sp 146 147 with ureg.context("ab"): 148 assert ureg._active_ctx.graph == g_ab 149 150 with ureg.context("lc"): 151 with ureg.context("ab"): 152 assert ureg._active_ctx.graph == g 153 154 with ureg.context("ab"): 155 with ureg.context("lc"): 156 assert ureg._active_ctx.graph == g 157 158 with ureg.context("lc", "ab"): 159 assert ureg._active_ctx.graph == g 160 161 with ureg.context("ab", "lc"): 162 assert ureg._active_ctx.graph == g 163 164 def test_graph_enable(self, func_registry): 165 ureg = UnitRegistry() 166 add_ctxs(ureg) 167 l = UnitsContainer({"[length]": 1.0}) # noqa: E741 168 t = UnitsContainer({"[time]": -1.0}) 169 c = UnitsContainer({"[current]": 1.0}) 170 171 g_sp = defaultdict(set) 172 g_sp.update({l: {t}, t: {l}}) 173 174 g_ab = defaultdict(set) 175 g_ab.update({l: {c}, c: {l}}) 176 177 g = defaultdict(set) 178 g.update({l: {t, c}, t: {l}, c: {l}}) 179 180 ureg.enable_contexts("lc") 181 assert ureg._active_ctx.graph == g_sp 182 ureg.disable_contexts(1) 183 184 ureg.enable_contexts("lc", n=1) 185 assert ureg._active_ctx.graph == g_sp 186 ureg.disable_contexts(1) 187 188 ureg.enable_contexts("ab") 189 assert ureg._active_ctx.graph == g_ab 190 ureg.disable_contexts(1) 191 192 ureg.enable_contexts("lc") 193 ureg.enable_contexts("ab") 194 assert ureg._active_ctx.graph == g 195 ureg.disable_contexts(2) 196 197 ureg.enable_contexts("ab") 198 ureg.enable_contexts("lc") 199 assert ureg._active_ctx.graph == g 200 ureg.disable_contexts(2) 201 202 ureg.enable_contexts("lc", "ab") 203 assert ureg._active_ctx.graph == g 204 ureg.disable_contexts(2) 205 206 ureg.enable_contexts("ab", "lc") 207 assert ureg._active_ctx.graph == g 208 ureg.disable_contexts(2) 209 210 def test_known_nested_context(self, func_registry): 211 ureg = UnitRegistry() 212 add_ctxs(ureg) 213 214 with ureg.context("lc"): 215 x = dict(ureg._active_ctx) 216 y = dict(ureg._active_ctx.graph) 217 assert ureg._active_ctx 218 assert ureg._active_ctx.graph 219 220 with ureg.context("ab"): 221 assert ureg._active_ctx 222 assert ureg._active_ctx.graph 223 assert x != ureg._active_ctx 224 assert y != ureg._active_ctx.graph 225 226 assert x == ureg._active_ctx 227 assert y == ureg._active_ctx.graph 228 229 assert not ureg._active_ctx 230 assert not ureg._active_ctx.graph 231 232 def test_unknown_context(self, func_registry): 233 ureg = func_registry 234 add_ctxs(ureg) 235 with pytest.raises(KeyError): 236 with ureg.context("la"): 237 pass 238 assert not ureg._active_ctx 239 assert not ureg._active_ctx.graph 240 241 def test_unknown_nested_context(self, func_registry): 242 ureg = UnitRegistry() 243 add_ctxs(ureg) 244 245 with ureg.context("lc"): 246 x = dict(ureg._active_ctx) 247 y = dict(ureg._active_ctx.graph) 248 with pytest.raises(KeyError): 249 with ureg.context("la"): 250 pass 251 252 assert x == ureg._active_ctx 253 assert y == ureg._active_ctx.graph 254 255 assert not ureg._active_ctx 256 assert not ureg._active_ctx.graph 257 258 def test_one_context(self, func_registry): 259 ureg = UnitRegistry() 260 261 add_ctxs(ureg) 262 263 q = 500 * ureg.meter 264 s = (ureg.speed_of_light / q).to("Hz") 265 266 meter_units = ureg.get_compatible_units(ureg.meter) 267 hertz_units = ureg.get_compatible_units(ureg.hertz) 268 269 with pytest.raises(DimensionalityError): 270 q.to("Hz") 271 with ureg.context("lc"): 272 assert q.to("Hz") == s 273 assert ureg.get_compatible_units(q) == meter_units | hertz_units 274 with pytest.raises(DimensionalityError): 275 q.to("Hz") 276 assert ureg.get_compatible_units(q) == meter_units 277 278 def test_multiple_context(self, func_registry): 279 ureg = UnitRegistry() 280 281 add_ctxs(ureg) 282 283 q = 500 * ureg.meter 284 s = (ureg.speed_of_light / q).to("Hz") 285 286 meter_units = ureg.get_compatible_units(ureg.meter) 287 hertz_units = ureg.get_compatible_units(ureg.hertz) 288 ampere_units = ureg.get_compatible_units(ureg.ampere) 289 290 with pytest.raises(DimensionalityError): 291 q.to("Hz") 292 with ureg.context("lc", "ab"): 293 assert q.to("Hz") == s 294 assert ( 295 ureg.get_compatible_units(q) == meter_units | hertz_units | ampere_units 296 ) 297 with pytest.raises(DimensionalityError): 298 q.to("Hz") 299 assert ureg.get_compatible_units(q) == meter_units 300 301 def test_nested_context(self, func_registry): 302 ureg = UnitRegistry() 303 304 add_ctxs(ureg) 305 306 q = 500 * ureg.meter 307 s = (ureg.speed_of_light / q).to("Hz") 308 309 with pytest.raises(DimensionalityError): 310 q.to("Hz") 311 with ureg.context("lc"): 312 assert q.to("Hz") == s 313 with ureg.context("ab"): 314 assert q.to("Hz") == s 315 assert q.to("Hz") == s 316 317 with ureg.context("ab"): 318 with pytest.raises(DimensionalityError): 319 q.to("Hz") 320 with ureg.context("lc"): 321 assert q.to("Hz") == s 322 with pytest.raises(DimensionalityError): 323 q.to("Hz") 324 325 def test_context_with_arg(self, func_registry): 326 327 ureg = UnitRegistry() 328 329 add_arg_ctxs(ureg) 330 331 q = 500 * ureg.meter 332 s = (ureg.speed_of_light / q).to("Hz") 333 334 with pytest.raises(DimensionalityError): 335 q.to("Hz") 336 with ureg.context("lc", n=1): 337 assert q.to("Hz") == s 338 with ureg.context("ab"): 339 assert q.to("Hz") == s 340 assert q.to("Hz") == s 341 342 with ureg.context("ab"): 343 with pytest.raises(DimensionalityError): 344 q.to("Hz") 345 with ureg.context("lc", n=1): 346 assert q.to("Hz") == s 347 with pytest.raises(DimensionalityError): 348 q.to("Hz") 349 350 with ureg.context("lc"): 351 with pytest.raises(TypeError): 352 q.to("Hz") 353 354 def test_enable_context_with_arg(self, func_registry): 355 356 ureg = UnitRegistry() 357 358 add_arg_ctxs(ureg) 359 360 q = 500 * ureg.meter 361 s = (ureg.speed_of_light / q).to("Hz") 362 363 with pytest.raises(DimensionalityError): 364 q.to("Hz") 365 ureg.enable_contexts("lc", n=1) 366 assert q.to("Hz") == s 367 ureg.enable_contexts("ab") 368 assert q.to("Hz") == s 369 assert q.to("Hz") == s 370 ureg.disable_contexts(1) 371 ureg.disable_contexts(1) 372 373 ureg.enable_contexts("ab") 374 with pytest.raises(DimensionalityError): 375 q.to("Hz") 376 ureg.enable_contexts("lc", n=1) 377 assert q.to("Hz") == s 378 ureg.disable_contexts(1) 379 with pytest.raises(DimensionalityError): 380 q.to("Hz") 381 ureg.disable_contexts(1) 382 383 ureg.enable_contexts("lc") 384 with pytest.raises(TypeError): 385 q.to("Hz") 386 ureg.disable_contexts(1) 387 388 def test_context_with_arg_def(self, func_registry): 389 390 ureg = UnitRegistry() 391 392 add_argdef_ctxs(ureg) 393 394 q = 500 * ureg.meter 395 s = (ureg.speed_of_light / q).to("Hz") 396 397 with pytest.raises(DimensionalityError): 398 q.to("Hz") 399 with ureg.context("lc"): 400 assert q.to("Hz") == s 401 with ureg.context("ab"): 402 assert q.to("Hz") == s 403 assert q.to("Hz") == s 404 405 with ureg.context("ab"): 406 with pytest.raises(DimensionalityError): 407 q.to("Hz") 408 with ureg.context("lc"): 409 assert q.to("Hz") == s 410 with pytest.raises(DimensionalityError): 411 q.to("Hz") 412 413 with pytest.raises(DimensionalityError): 414 q.to("Hz") 415 with ureg.context("lc", n=2): 416 assert q.to("Hz") == s / 2 417 with ureg.context("ab"): 418 assert q.to("Hz") == s / 2 419 assert q.to("Hz") == s / 2 420 421 with ureg.context("ab"): 422 with pytest.raises(DimensionalityError): 423 q.to("Hz") 424 with ureg.context("lc", n=2): 425 assert q.to("Hz") == s / 2 426 with pytest.raises(DimensionalityError): 427 q.to("Hz") 428 429 def test_context_with_sharedarg_def(self, func_registry): 430 431 ureg = UnitRegistry() 432 433 add_sharedargdef_ctxs(ureg) 434 435 q = 500 * ureg.meter 436 s = (ureg.speed_of_light / q).to("Hz") 437 u = (1 / 500) * ureg.ampere 438 439 with ureg.context("lc"): 440 assert q.to("Hz") == s 441 with ureg.context("ab"): 442 assert q.to("ampere") == u 443 444 with ureg.context("ab"): 445 assert q.to("ampere") == 0 * u 446 with ureg.context("lc"): 447 with pytest.raises(ZeroDivisionError): 448 ureg.Quantity.to(q, "Hz") 449 450 with ureg.context("lc", n=2): 451 assert q.to("Hz") == s / 2 452 with ureg.context("ab"): 453 assert q.to("ampere") == 2 * u 454 455 with ureg.context("ab", n=3): 456 assert q.to("ampere") == 3 * u 457 with ureg.context("lc"): 458 assert q.to("Hz") == s / 3 459 460 with ureg.context("lc", n=2): 461 assert q.to("Hz") == s / 2 462 with ureg.context("ab", n=4): 463 assert q.to("ampere") == 4 * u 464 465 with ureg.context("ab", n=3): 466 assert q.to("ampere") == 3 * u 467 with ureg.context("lc", n=6): 468 assert q.to("Hz") == s / 6 469 470 def test_anonymous_context(self, func_registry): 471 ureg = UnitRegistry() 472 c = Context() 473 c.add_transformation("[length]", "[time]", lambda ureg, x: x / ureg("5 cm/s")) 474 with pytest.raises(ValueError): 475 ureg.add_context(c) 476 477 x = ureg("10 cm") 478 expect = ureg("2 s") 479 helpers.assert_quantity_equal(x.to("s", c), expect) 480 481 with ureg.context(c): 482 helpers.assert_quantity_equal(x.to("s"), expect) 483 484 ureg.enable_contexts(c) 485 helpers.assert_quantity_equal(x.to("s"), expect) 486 ureg.disable_contexts(1) 487 with pytest.raises(DimensionalityError): 488 x.to("s") 489 490 # Multiple anonymous contexts 491 c2 = Context() 492 c2.add_transformation("[length]", "[time]", lambda ureg, x: x / ureg("10 cm/s")) 493 c2.add_transformation("[mass]", "[time]", lambda ureg, x: x / ureg("10 kg/s")) 494 with ureg.context(c2, c): 495 helpers.assert_quantity_equal(x.to("s"), expect) 496 # Transformations only in c2 are still working even if c takes priority 497 helpers.assert_quantity_equal(ureg("100 kg").to("s"), ureg("10 s")) 498 with ureg.context(c, c2): 499 helpers.assert_quantity_equal(x.to("s"), ureg("1 s")) 500 501 def _test_ctx(self, ctx): 502 ureg = UnitRegistry() 503 q = 500 * ureg.meter 504 s = (ureg.speed_of_light / q).to("Hz") 505 506 nctx = len(ureg._contexts) 507 508 assert ctx.name not in ureg._contexts 509 ureg.add_context(ctx) 510 511 assert ctx.name in ureg._contexts 512 assert len(ureg._contexts) == nctx + 1 + len(ctx.aliases) 513 514 with ureg.context(ctx.name): 515 assert q.to("Hz") == s 516 assert s.to("meter") == q 517 518 ureg.remove_context(ctx.name) 519 assert ctx.name not in ureg._contexts 520 assert len(ureg._contexts) == nctx 521 522 @pytest.mark.parametrize( 523 "badrow", 524 ( 525 "[length] = 1 / [time]: c / value", 526 "1 / [time] = [length]: c / value", 527 "[length] <- [time] = c / value", 528 "[length] - [time] = c / value", 529 ), 530 ) 531 def test_parse_invalid(self, badrow): 532 with pytest.raises(DefinitionSyntaxError): 533 Context.from_lines(["@context c", badrow]) 534 535 def test_parse_simple(self): 536 537 a = Context.__keytransform__( 538 UnitsContainer({"[time]": -1}), UnitsContainer({"[length]": 1}) 539 ) 540 b = Context.__keytransform__( 541 UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1}) 542 ) 543 544 s = [ 545 "@context longcontextname", 546 "[length] -> 1 / [time]: c / value", 547 "1 / [time] -> [length]: c / value", 548 ] 549 550 c = Context.from_lines(s) 551 assert c.name == "longcontextname" 552 assert c.aliases == () 553 assert c.defaults == {} 554 assert c.funcs.keys() == {a, b} 555 self._test_ctx(c) 556 557 s = ["@context longcontextname = lc", "[length] <-> 1 / [time]: c / value"] 558 559 c = Context.from_lines(s) 560 assert c.name == "longcontextname" 561 assert c.aliases == ("lc",) 562 assert c.defaults == {} 563 assert c.funcs.keys() == {a, b} 564 self._test_ctx(c) 565 566 s = [ 567 "@context longcontextname = lc = lcn", 568 "[length] <-> 1 / [time]: c / value", 569 ] 570 571 c = Context.from_lines(s) 572 assert c.name == "longcontextname" 573 assert c.aliases == ("lc", "lcn") 574 assert c.defaults == {} 575 assert c.funcs.keys() == {a, b} 576 self._test_ctx(c) 577 578 def test_parse_auto_inverse(self): 579 580 a = Context.__keytransform__( 581 UnitsContainer({"[time]": -1.0}), UnitsContainer({"[length]": 1.0}) 582 ) 583 b = Context.__keytransform__( 584 UnitsContainer({"[length]": 1.0}), UnitsContainer({"[time]": -1.0}) 585 ) 586 587 s = ["@context longcontextname", "[length] <-> 1 / [time]: c / value"] 588 589 c = Context.from_lines(s) 590 assert c.defaults == {} 591 assert c.funcs.keys() == {a, b} 592 self._test_ctx(c) 593 594 def test_parse_define(self): 595 a = Context.__keytransform__( 596 UnitsContainer({"[time]": -1}), UnitsContainer({"[length]": 1.0}) 597 ) 598 b = Context.__keytransform__( 599 UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1.0}) 600 ) 601 602 s = ["@context longcontextname", "[length] <-> 1 / [time]: c / value"] 603 c = Context.from_lines(s) 604 assert c.defaults == {} 605 assert c.funcs.keys() == {a, b} 606 self._test_ctx(c) 607 608 def test_parse_parameterized(self): 609 a = Context.__keytransform__( 610 UnitsContainer({"[time]": -1.0}), UnitsContainer({"[length]": 1.0}) 611 ) 612 b = Context.__keytransform__( 613 UnitsContainer({"[length]": 1.0}), UnitsContainer({"[time]": -1.0}) 614 ) 615 616 s = ["@context(n=1) longcontextname", "[length] <-> 1 / [time]: n * c / value"] 617 618 c = Context.from_lines(s) 619 assert c.defaults == {"n": 1} 620 assert c.funcs.keys() == {a, b} 621 self._test_ctx(c) 622 623 s = [ 624 "@context(n=1, bla=2) longcontextname", 625 "[length] <-> 1 / [time]: n * c / value / bla", 626 ] 627 628 c = Context.from_lines(s) 629 assert c.defaults == {"n": 1, "bla": 2} 630 assert c.funcs.keys() == {a, b} 631 632 # If the variable is not present in the definition, then raise an error 633 s = ["@context(n=1) longcontextname", "[length] <-> 1 / [time]: c / value"] 634 with pytest.raises(DefinitionSyntaxError): 635 Context.from_lines(s) 636 637 def test_warnings(self, caplog): 638 639 ureg = UnitRegistry() 640 641 with caplog.at_level(logging.DEBUG, "pint"): 642 add_ctxs(ureg) 643 644 d = Context("ab") 645 ureg.add_context(d) 646 647 assert len(caplog.records) == 1 648 assert "ab" in str(caplog.records[-1].args) 649 650 d = Context("ab1", aliases=("ab",)) 651 ureg.add_context(d) 652 653 assert len(caplog.records) == 2 654 assert "ab" in str(caplog.records[-1].args) 655 656 657class TestDefinedContexts: 658 @classmethod 659 def setup_class(cls): 660 cls.ureg = UnitRegistry() 661 662 @classmethod 663 def teardown_class(cls): 664 cls.ureg = None 665 666 def test_defined(self): 667 ureg = self.ureg 668 with ureg.context("sp"): 669 pass 670 671 a = Context.__keytransform__( 672 UnitsContainer({"[time]": -1.0}), UnitsContainer({"[length]": 1.0}) 673 ) 674 b = Context.__keytransform__( 675 UnitsContainer({"[length]": 1.0}), UnitsContainer({"[time]": -1.0}) 676 ) 677 assert a in ureg._contexts["sp"].funcs 678 assert b in ureg._contexts["sp"].funcs 679 with ureg.context("sp"): 680 assert a in ureg._active_ctx 681 assert b in ureg._active_ctx 682 683 def test_spectroscopy(self): 684 ureg = self.ureg 685 eq = (532.0 * ureg.nm, 563.5 * ureg.terahertz, 2.33053 * ureg.eV) 686 with ureg.context("sp"): 687 from pint.util import find_shortest_path 688 689 for a, b in itertools.product(eq, eq): 690 for x in range(2): 691 if x == 1: 692 a = a.to_base_units() 693 b = b.to_base_units() 694 da, db = Context.__keytransform__( 695 a.dimensionality, b.dimensionality 696 ) 697 p = find_shortest_path(ureg._active_ctx.graph, da, db) 698 assert p 699 msg = "{} <-> {}".format(a, b) 700 # assertAlmostEqualRelError converts second to first 701 helpers.assert_quantity_almost_equal(b, a, rtol=0.01, msg=msg) 702 703 for a, b in itertools.product(eq, eq): 704 helpers.assert_quantity_almost_equal(a.to(b.units, "sp"), b, rtol=0.01) 705 706 def test_textile(self): 707 ureg = self.ureg 708 qty_direct = 1.331 * ureg.tex 709 with pytest.raises(DimensionalityError): 710 qty_indirect = qty_direct.to("Nm") 711 712 with ureg.context("textile"): 713 from pint.util import find_shortest_path 714 715 qty_indirect = qty_direct.to("Nm") 716 a = qty_direct.to_base_units() 717 b = qty_indirect.to_base_units() 718 da, db = Context.__keytransform__(a.dimensionality, b.dimensionality) 719 p = find_shortest_path(ureg._active_ctx.graph, da, db) 720 assert p 721 msg = "{} <-> {}".format(a, b) 722 helpers.assert_quantity_almost_equal(b, a, rtol=0.01, msg=msg) 723 724 # Check RKM <-> cN/tex conversion 725 helpers.assert_quantity_almost_equal( 726 1 * ureg.RKM, 0.980665 * ureg.cN / ureg.tex 727 ) 728 helpers.assert_quantity_almost_equal( 729 (1 / 0.980665) * ureg.RKM, 1 * ureg.cN / ureg.tex 730 ) 731 assert ( 732 round(abs((1 * ureg.RKM).to(ureg.cN / ureg.tex).m - 0.980665), 7) == 0 733 ) 734 assert ( 735 round(abs((1 * ureg.cN / ureg.tex).to(ureg.RKM).m - 1 / 0.980665), 7) 736 == 0 737 ) 738 739 def test_decorator(self): 740 ureg = self.ureg 741 742 a = 532.0 * ureg.nm 743 with ureg.context("sp"): 744 b = a.to("terahertz") 745 746 def f(wl): 747 return wl.to("terahertz") 748 749 with pytest.raises(DimensionalityError): 750 f(a) 751 752 @ureg.with_context("sp") 753 def g(wl): 754 return wl.to("terahertz") 755 756 assert b == g(a) 757 758 def test_decorator_composition(self): 759 ureg = self.ureg 760 761 a = 532.0 * ureg.nm 762 with ureg.context("sp"): 763 b = a.to("terahertz") 764 765 @ureg.with_context("sp") 766 @ureg.check("[length]") 767 def f(wl): 768 return wl.to("terahertz") 769 770 @ureg.with_context("sp") 771 @ureg.check("[length]") 772 def g(wl): 773 return wl.to("terahertz") 774 775 assert b == f(a) 776 assert b == g(a) 777 778 779def test_redefine(subtests): 780 ureg = UnitRegistry( 781 """ 782 foo = [d] = f = foo_alias 783 bar = 2 foo = b = bar_alias 784 baz = 3 bar = _ = baz_alias 785 asd = 4 baz 786 787 @context c 788 # Note how we're redefining a symbol, not the base name, as a 789 # function of another name 790 b = 5 f 791 """.splitlines() 792 ) 793 # Units that are somehow directly or indirectly defined as a function of the 794 # overridden unit are also affected 795 foo = ureg.Quantity(1, "foo") 796 bar = ureg.Quantity(1, "bar") 797 asd = ureg.Quantity(1, "asd") 798 799 # Test without context before and after, to verify that the cache and units have 800 # not been polluted 801 for enable_ctx in (False, True, False): 802 with subtests.test(enable_ctx): 803 if enable_ctx: 804 ureg.enable_contexts("c") 805 k = 5 806 else: 807 k = 2 808 809 assert foo.to("b").magnitude == 1 / k 810 assert foo.to("bar").magnitude == 1 / k 811 assert foo.to("bar_alias").magnitude == 1 / k 812 assert foo.to("baz").magnitude == 1 / k / 3 813 assert bar.to("foo").magnitude == k 814 assert bar.to("baz").magnitude == 1 / 3 815 assert asd.to("foo").magnitude == 4 * 3 * k 816 assert asd.to("bar").magnitude == 4 * 3 817 assert asd.to("baz").magnitude == 4 818 819 ureg.disable_contexts() 820 821 822def test_define_nan(): 823 ureg = UnitRegistry( 824 """ 825 USD = [currency] 826 EUR = nan USD 827 GBP = nan USD 828 829 @context c 830 EUR = 1.11 USD 831 # Note that we're changing which unit GBP is defined against 832 GBP = 1.18 EUR 833 @end 834 """.splitlines() 835 ) 836 837 q = ureg.Quantity("10 GBP") 838 assert q.magnitude == 10 839 assert q.units.dimensionality == {"[currency]": 1} 840 assert q.to("GBP").magnitude == 10 841 assert math.isnan(q.to("USD").magnitude) 842 assert math.isclose(q.to("USD", "c").magnitude, 10 * 1.18 * 1.11) 843 844 845def test_non_multiplicative(subtests): 846 ureg = UnitRegistry( 847 """ 848 kelvin = [temperature] 849 fahrenheit = 5 / 9 * kelvin; offset: 255 850 bogodegrees = 9 * kelvin 851 852 @context nonmult_to_nonmult 853 fahrenheit = 7 * kelvin; offset: 123 854 @end 855 @context nonmult_to_mult 856 fahrenheit = 123 * kelvin 857 @end 858 @context mult_to_nonmult 859 bogodegrees = 5 * kelvin; offset: 123 860 @end 861 """.splitlines() 862 ) 863 k = ureg.Quantity(100, "kelvin") 864 865 with subtests.test("baseline"): 866 helpers.assert_quantity_almost_equal( 867 k.to("fahrenheit").magnitude, (100 - 255) * 9 / 5 868 ) 869 helpers.assert_quantity_almost_equal(k.to("bogodegrees").magnitude, 100 / 9) 870 871 with subtests.test("nonmult_to_nonmult"): 872 with ureg.context("nonmult_to_nonmult"): 873 helpers.assert_quantity_almost_equal( 874 k.to("fahrenheit").magnitude, (100 - 123) / 7 875 ) 876 877 with subtests.test("nonmult_to_mult"): 878 with ureg.context("nonmult_to_mult"): 879 helpers.assert_quantity_almost_equal( 880 k.to("fahrenheit").magnitude, 100 / 123 881 ) 882 883 with subtests.test("mult_to_nonmult"): 884 with ureg.context("mult_to_nonmult"): 885 helpers.assert_quantity_almost_equal( 886 k.to("bogodegrees").magnitude, (100 - 123) / 5 887 ) 888 889 890def test_stack_contexts(): 891 ureg = UnitRegistry( 892 """ 893 a = [dim1] 894 b = 1/2 a 895 c = 1/3 a 896 d = [dim2] 897 898 @context c1 899 b = 1/4 a 900 c = 1/6 a 901 [dim1]->[dim2]: value * 2 d/a 902 @end 903 @context c2 904 b = 1/5 a 905 [dim1]->[dim2]: value * 3 d/a 906 @end 907 """.splitlines() 908 ) 909 q = ureg.Quantity(1, "a") 910 assert q.to("b").magnitude == 2 911 assert q.to("c").magnitude == 3 912 assert q.to("b", "c1").magnitude == 4 913 assert q.to("c", "c1").magnitude == 6 914 assert q.to("d", "c1").magnitude == 2 915 assert q.to("b", "c2").magnitude == 5 916 assert q.to("c", "c2").magnitude == 3 917 assert q.to("d", "c2").magnitude == 3 918 assert q.to("b", "c1", "c2").magnitude == 5 # c2 takes precedence 919 assert q.to("c", "c1", "c2").magnitude == 6 # c2 doesn't change it, so use c1 920 assert q.to("d", "c1", "c2").magnitude == 3 # c2 takes precedence 921 922 923def test_err_change_base_unit(): 924 ureg = UnitRegistry( 925 """ 926 foo = [d1] 927 bar = [d2] 928 929 @context c 930 bar = foo 931 @end 932 """.splitlines() 933 ) 934 935 expected = "Can't redefine a base unit to a derived one" 936 with pytest.raises(ValueError, match=expected): 937 ureg.enable_contexts("c") 938 939 940def test_err_to_base_unit(): 941 expected = "Can't define base units within a context" 942 with pytest.raises(DefinitionSyntaxError, match=expected): 943 Context.from_lines(["@context c", "x = [d]"]) 944 945 946def test_err_change_dimensionality(): 947 ureg = UnitRegistry( 948 """ 949 foo = [d1] 950 bar = [d2] 951 baz = foo 952 953 @context c 954 baz = bar 955 @end 956 """.splitlines() 957 ) 958 959 expected = re.escape( 960 "Can't change dimensionality of baz from [d1] to [d2] in a context" 961 ) 962 with pytest.raises(ValueError, match=expected): 963 ureg.enable_contexts("c") 964 965 966def test_err_cyclic_dependency(): 967 ureg = UnitRegistry( 968 """ 969 foo = [d] 970 bar = foo 971 baz = bar 972 973 @context c 974 bar = baz 975 @end 976 """.splitlines() 977 ) 978 # TODO align this exception and the one you get when you implement a cyclic 979 # dependency within the base registry. Ideally this exception should be 980 # raised by enable_contexts. 981 ureg.enable_contexts("c") 982 q = ureg.Quantity("bar") 983 with pytest.raises(RecursionError): 984 q.to("foo") 985 986 987def test_err_dimension_redefinition(): 988 expected = re.escape("Expected <unit> = <converter>; got [d1] = [d2] * [d3]") 989 with pytest.raises(DefinitionSyntaxError, match=expected): 990 Context.from_lines(["@context c", "[d1] = [d2] * [d3]"]) 991 992 993def test_err_prefix_redefinition(): 994 expected = re.escape("Expected <unit> = <converter>; got [d1] = [d2] * [d3]") 995 with pytest.raises(DefinitionSyntaxError, match=expected): 996 Context.from_lines(["@context c", "[d1] = [d2] * [d3]"]) 997 998 999def test_err_redefine_alias(subtests): 1000 expected = "Can't change a unit's symbol or aliases within a context" 1001 for s in ("foo = bar = f", "foo = bar = _ = baz"): 1002 with subtests.test(s): 1003 with pytest.raises(DefinitionSyntaxError, match=expected): 1004 Context.from_lines(["@context c", s]) 1005 1006 1007def test_err_redefine_with_prefix(): 1008 ureg = UnitRegistry( 1009 """ 1010 kilo- = 1000 1011 gram = [mass] 1012 pound = 454 gram 1013 1014 @context c 1015 kilopound = 500000 gram 1016 @end 1017 """.splitlines() 1018 ) 1019 1020 expected = "Can't redefine a unit with a prefix: kilopound" 1021 with pytest.raises(ValueError, match=expected): 1022 ureg.enable_contexts("c") 1023 1024 1025def test_err_new_unit(): 1026 ureg = UnitRegistry( 1027 """ 1028 foo = [d] 1029 @context c 1030 bar = foo 1031 @end 1032 """.splitlines() 1033 ) 1034 expected = "'bar' is not defined in the unit registry" 1035 with pytest.raises(UndefinedUnitError, match=expected): 1036 ureg.enable_contexts("c") 1037