import itertools
import math
from collections import defaultdict
from pint import (
DefinitionSyntaxError,
DimensionalityError,
UndefinedUnitError,
UnitRegistry,
)
from pint.context import Context
from pint.testsuite import QuantityTestCase
from pint.util import UnitsContainer
def add_ctxs(ureg):
a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1})
d = Context("lc")
d.add_transformation(a, b, lambda ureg, x: ureg.speed_of_light / x)
d.add_transformation(b, a, lambda ureg, x: ureg.speed_of_light / x)
ureg.add_context(d)
a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[current]": 1})
d = Context("ab")
d.add_transformation(a, b, lambda ureg, x: ureg.ampere * ureg.meter / x)
d.add_transformation(b, a, lambda ureg, x: ureg.ampere * ureg.meter / x)
ureg.add_context(d)
def add_arg_ctxs(ureg):
a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1})
d = Context("lc")
d.add_transformation(a, b, lambda ureg, x, n: ureg.speed_of_light / x / n)
d.add_transformation(b, a, lambda ureg, x, n: ureg.speed_of_light / x / n)
ureg.add_context(d)
a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[current]": 1})
d = Context("ab")
d.add_transformation(a, b, lambda ureg, x: ureg.ampere * ureg.meter / x)
d.add_transformation(b, a, lambda ureg, x: ureg.ampere * ureg.meter / x)
ureg.add_context(d)
def add_argdef_ctxs(ureg):
a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1})
d = Context("lc", defaults=dict(n=1))
assert d.defaults == dict(n=1)
d.add_transformation(a, b, lambda ureg, x, n: ureg.speed_of_light / x / n)
d.add_transformation(b, a, lambda ureg, x, n: ureg.speed_of_light / x / n)
ureg.add_context(d)
a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[current]": 1})
d = Context("ab")
d.add_transformation(a, b, lambda ureg, x: ureg.ampere * ureg.meter / x)
d.add_transformation(b, a, lambda ureg, x: ureg.ampere * ureg.meter / x)
ureg.add_context(d)
def add_sharedargdef_ctxs(ureg):
a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1})
d = Context("lc", defaults=dict(n=1))
assert d.defaults == dict(n=1)
d.add_transformation(a, b, lambda ureg, x, n: ureg.speed_of_light / x / n)
d.add_transformation(b, a, lambda ureg, x, n: ureg.speed_of_light / x / n)
ureg.add_context(d)
a, b = UnitsContainer({"[length]": 1}), UnitsContainer({"[current]": 1})
d = Context("ab", defaults=dict(n=0))
d.add_transformation(a, b, lambda ureg, x, n: ureg.ampere * ureg.meter * n / x)
d.add_transformation(b, a, lambda ureg, x, n: ureg.ampere * ureg.meter * n / x)
ureg.add_context(d)
[docs]class TestContexts(QuantityTestCase):
def test_known_context(self):
ureg = UnitRegistry()
add_ctxs(ureg)
with ureg.context("lc"):
self.assertTrue(ureg._active_ctx)
self.assertTrue(ureg._active_ctx.graph)
self.assertFalse(ureg._active_ctx)
self.assertFalse(ureg._active_ctx.graph)
with ureg.context("lc", n=1):
self.assertTrue(ureg._active_ctx)
self.assertTrue(ureg._active_ctx.graph)
self.assertFalse(ureg._active_ctx)
self.assertFalse(ureg._active_ctx.graph)
def test_known_context_enable(self):
ureg = UnitRegistry()
add_ctxs(ureg)
ureg.enable_contexts("lc")
self.assertTrue(ureg._active_ctx)
self.assertTrue(ureg._active_ctx.graph)
ureg.disable_contexts(1)
self.assertFalse(ureg._active_ctx)
self.assertFalse(ureg._active_ctx.graph)
ureg.enable_contexts("lc", n=1)
self.assertTrue(ureg._active_ctx)
self.assertTrue(ureg._active_ctx.graph)
ureg.disable_contexts(1)
self.assertFalse(ureg._active_ctx)
self.assertFalse(ureg._active_ctx.graph)
def test_graph(self):
ureg = UnitRegistry()
add_ctxs(ureg)
l = UnitsContainer({"[length]": 1.0}) # noqa: E741
t = UnitsContainer({"[time]": -1.0})
c = UnitsContainer({"[current]": 1.0})
g_sp = defaultdict(set)
g_sp.update({l: {t}, t: {l}})
g_ab = defaultdict(set)
g_ab.update({l: {c}, c: {l}})
g = defaultdict(set)
g.update({l: {t, c}, t: {l}, c: {l}})
with ureg.context("lc"):
self.assertEqual(ureg._active_ctx.graph, g_sp)
with ureg.context("lc", n=1):
self.assertEqual(ureg._active_ctx.graph, g_sp)
with ureg.context("ab"):
self.assertEqual(ureg._active_ctx.graph, g_ab)
with ureg.context("lc"):
with ureg.context("ab"):
self.assertEqual(ureg._active_ctx.graph, g)
with ureg.context("ab"):
with ureg.context("lc"):
self.assertEqual(ureg._active_ctx.graph, g)
with ureg.context("lc", "ab"):
self.assertEqual(ureg._active_ctx.graph, g)
with ureg.context("ab", "lc"):
self.assertEqual(ureg._active_ctx.graph, g)
def test_graph_enable(self):
ureg = UnitRegistry()
add_ctxs(ureg)
l = UnitsContainer({"[length]": 1.0}) # noqa: E741
t = UnitsContainer({"[time]": -1.0})
c = UnitsContainer({"[current]": 1.0})
g_sp = defaultdict(set)
g_sp.update({l: {t}, t: {l}})
g_ab = defaultdict(set)
g_ab.update({l: {c}, c: {l}})
g = defaultdict(set)
g.update({l: {t, c}, t: {l}, c: {l}})
ureg.enable_contexts("lc")
self.assertEqual(ureg._active_ctx.graph, g_sp)
ureg.disable_contexts(1)
ureg.enable_contexts("lc", n=1)
self.assertEqual(ureg._active_ctx.graph, g_sp)
ureg.disable_contexts(1)
ureg.enable_contexts("ab")
self.assertEqual(ureg._active_ctx.graph, g_ab)
ureg.disable_contexts(1)
ureg.enable_contexts("lc")
ureg.enable_contexts("ab")
self.assertEqual(ureg._active_ctx.graph, g)
ureg.disable_contexts(2)
ureg.enable_contexts("ab")
ureg.enable_contexts("lc")
self.assertEqual(ureg._active_ctx.graph, g)
ureg.disable_contexts(2)
ureg.enable_contexts("lc", "ab")
self.assertEqual(ureg._active_ctx.graph, g)
ureg.disable_contexts(2)
ureg.enable_contexts("ab", "lc")
self.assertEqual(ureg._active_ctx.graph, g)
ureg.disable_contexts(2)
def test_known_nested_context(self):
ureg = UnitRegistry()
add_ctxs(ureg)
with ureg.context("lc"):
x = dict(ureg._active_ctx)
y = dict(ureg._active_ctx.graph)
self.assertTrue(ureg._active_ctx)
self.assertTrue(ureg._active_ctx.graph)
with ureg.context("ab"):
self.assertTrue(ureg._active_ctx)
self.assertTrue(ureg._active_ctx.graph)
self.assertNotEqual(x, ureg._active_ctx)
self.assertNotEqual(y, ureg._active_ctx.graph)
self.assertEqual(x, ureg._active_ctx)
self.assertEqual(y, ureg._active_ctx.graph)
self.assertFalse(ureg._active_ctx)
self.assertFalse(ureg._active_ctx.graph)
def test_unknown_context(self):
ureg = UnitRegistry()
add_ctxs(ureg)
with self.assertRaises(KeyError):
with ureg.context("la"):
pass
self.assertFalse(ureg._active_ctx)
self.assertFalse(ureg._active_ctx.graph)
def test_unknown_nested_context(self):
ureg = UnitRegistry()
add_ctxs(ureg)
with ureg.context("lc"):
x = dict(ureg._active_ctx)
y = dict(ureg._active_ctx.graph)
with self.assertRaises(KeyError):
with ureg.context("la"):
pass
self.assertEqual(x, ureg._active_ctx)
self.assertEqual(y, ureg._active_ctx.graph)
self.assertFalse(ureg._active_ctx)
self.assertFalse(ureg._active_ctx.graph)
def test_one_context(self):
ureg = UnitRegistry()
add_ctxs(ureg)
q = 500 * ureg.meter
s = (ureg.speed_of_light / q).to("Hz")
meter_units = ureg.get_compatible_units(ureg.meter)
hertz_units = ureg.get_compatible_units(ureg.hertz)
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc"):
self.assertEqual(q.to("Hz"), s)
self.assertEqual(ureg.get_compatible_units(q), meter_units | hertz_units)
self.assertRaises(DimensionalityError, q.to, "Hz")
self.assertEqual(ureg.get_compatible_units(q), meter_units)
def test_multiple_context(self):
ureg = UnitRegistry()
add_ctxs(ureg)
q = 500 * ureg.meter
s = (ureg.speed_of_light / q).to("Hz")
meter_units = ureg.get_compatible_units(ureg.meter)
hertz_units = ureg.get_compatible_units(ureg.hertz)
ampere_units = ureg.get_compatible_units(ureg.ampere)
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc", "ab"):
self.assertEqual(q.to("Hz"), s)
self.assertEqual(
ureg.get_compatible_units(q), meter_units | hertz_units | ampere_units
)
self.assertRaises(DimensionalityError, q.to, "Hz")
self.assertEqual(ureg.get_compatible_units(q), meter_units)
def test_nested_context(self):
ureg = UnitRegistry()
add_ctxs(ureg)
q = 500 * ureg.meter
s = (ureg.speed_of_light / q).to("Hz")
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc"):
self.assertEqual(q.to("Hz"), s)
with ureg.context("ab"):
self.assertEqual(q.to("Hz"), s)
self.assertEqual(q.to("Hz"), s)
with ureg.context("ab"):
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc"):
self.assertEqual(q.to("Hz"), s)
self.assertRaises(DimensionalityError, q.to, "Hz")
def test_context_with_arg(self):
ureg = UnitRegistry()
add_arg_ctxs(ureg)
q = 500 * ureg.meter
s = (ureg.speed_of_light / q).to("Hz")
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc", n=1):
self.assertEqual(q.to("Hz"), s)
with ureg.context("ab"):
self.assertEqual(q.to("Hz"), s)
self.assertEqual(q.to("Hz"), s)
with ureg.context("ab"):
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc", n=1):
self.assertEqual(q.to("Hz"), s)
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc"):
self.assertRaises(TypeError, q.to, "Hz")
def test_enable_context_with_arg(self):
ureg = UnitRegistry()
add_arg_ctxs(ureg)
q = 500 * ureg.meter
s = (ureg.speed_of_light / q).to("Hz")
self.assertRaises(DimensionalityError, q.to, "Hz")
ureg.enable_contexts("lc", n=1)
self.assertEqual(q.to("Hz"), s)
ureg.enable_contexts("ab")
self.assertEqual(q.to("Hz"), s)
self.assertEqual(q.to("Hz"), s)
ureg.disable_contexts(1)
ureg.disable_contexts(1)
ureg.enable_contexts("ab")
self.assertRaises(DimensionalityError, q.to, "Hz")
ureg.enable_contexts("lc", n=1)
self.assertEqual(q.to("Hz"), s)
ureg.disable_contexts(1)
self.assertRaises(DimensionalityError, q.to, "Hz")
ureg.disable_contexts(1)
ureg.enable_contexts("lc")
self.assertRaises(TypeError, q.to, "Hz")
ureg.disable_contexts(1)
def test_context_with_arg_def(self):
ureg = UnitRegistry()
add_argdef_ctxs(ureg)
q = 500 * ureg.meter
s = (ureg.speed_of_light / q).to("Hz")
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc"):
self.assertEqual(q.to("Hz"), s)
with ureg.context("ab"):
self.assertEqual(q.to("Hz"), s)
self.assertEqual(q.to("Hz"), s)
with ureg.context("ab"):
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc"):
self.assertEqual(q.to("Hz"), s)
self.assertRaises(DimensionalityError, q.to, "Hz")
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc", n=2):
self.assertEqual(q.to("Hz"), s / 2)
with ureg.context("ab"):
self.assertEqual(q.to("Hz"), s / 2)
self.assertEqual(q.to("Hz"), s / 2)
with ureg.context("ab"):
self.assertRaises(DimensionalityError, q.to, "Hz")
with ureg.context("lc", n=2):
self.assertEqual(q.to("Hz"), s / 2)
self.assertRaises(DimensionalityError, q.to, "Hz")
def test_context_with_sharedarg_def(self):
ureg = UnitRegistry()
add_sharedargdef_ctxs(ureg)
q = 500 * ureg.meter
s = (ureg.speed_of_light / q).to("Hz")
u = (1 / 500) * ureg.ampere
with ureg.context("lc"):
self.assertEqual(q.to("Hz"), s)
with ureg.context("ab"):
self.assertEqual(q.to("ampere"), u)
with ureg.context("ab"):
self.assertEqual(q.to("ampere"), 0 * u)
with ureg.context("lc"):
self.assertRaises(ZeroDivisionError, ureg.Quantity.to, q, "Hz")
with ureg.context("lc", n=2):
self.assertEqual(q.to("Hz"), s / 2)
with ureg.context("ab"):
self.assertEqual(q.to("ampere"), 2 * u)
with ureg.context("ab", n=3):
self.assertEqual(q.to("ampere"), 3 * u)
with ureg.context("lc"):
self.assertEqual(q.to("Hz"), s / 3)
with ureg.context("lc", n=2):
self.assertEqual(q.to("Hz"), s / 2)
with ureg.context("ab", n=4):
self.assertEqual(q.to("ampere"), 4 * u)
with ureg.context("ab", n=3):
self.assertEqual(q.to("ampere"), 3 * u)
with ureg.context("lc", n=6):
self.assertEqual(q.to("Hz"), s / 6)
def test_anonymous_context(self):
ureg = UnitRegistry()
c = Context()
c.add_transformation("[length]", "[time]", lambda ureg, x: x / ureg("5 cm/s"))
self.assertRaises(ValueError, ureg.add_context, c)
x = ureg("10 cm")
expect = ureg("2 s")
self.assertQuantityEqual(x.to("s", c), expect)
with ureg.context(c):
self.assertQuantityEqual(x.to("s"), expect)
ureg.enable_contexts(c)
self.assertQuantityEqual(x.to("s"), expect)
ureg.disable_contexts(1)
self.assertRaises(DimensionalityError, x.to, "s")
# Multiple anonymous contexts
c2 = Context()
c2.add_transformation("[length]", "[time]", lambda ureg, x: x / ureg("10 cm/s"))
c2.add_transformation("[mass]", "[time]", lambda ureg, x: x / ureg("10 kg/s"))
with ureg.context(c2, c):
self.assertQuantityEqual(x.to("s"), expect)
# Transformations only in c2 are still working even if c takes priority
self.assertQuantityEqual(ureg("100 kg").to("s"), ureg("10 s"))
with ureg.context(c, c2):
self.assertQuantityEqual(x.to("s"), ureg("1 s"))
def _test_ctx(self, ctx):
ureg = UnitRegistry()
q = 500 * ureg.meter
s = (ureg.speed_of_light / q).to("Hz")
nctx = len(ureg._contexts)
self.assertNotIn(ctx.name, ureg._contexts)
ureg.add_context(ctx)
self.assertIn(ctx.name, ureg._contexts)
self.assertEqual(len(ureg._contexts), nctx + 1 + len(ctx.aliases))
with ureg.context(ctx.name):
self.assertEqual(q.to("Hz"), s)
self.assertEqual(s.to("meter"), q)
ureg.remove_context(ctx.name)
self.assertNotIn(ctx.name, ureg._contexts)
self.assertEqual(len(ureg._contexts), nctx)
def test_parse_invalid(self):
for badrow in (
"[length] = 1 / [time]: c / value",
"1 / [time] = [length]: c / value",
"[length] <- [time] = c / value",
"[length] - [time] = c / value",
):
with self.subTest(badrow):
with self.assertRaises(DefinitionSyntaxError):
Context.from_lines(["@context c", badrow])
def test_parse_simple(self):
a = Context.__keytransform__(
UnitsContainer({"[time]": -1}), UnitsContainer({"[length]": 1})
)
b = Context.__keytransform__(
UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1})
)
s = [
"@context longcontextname",
"[length] -> 1 / [time]: c / value",
"1 / [time] -> [length]: c / value",
]
c = Context.from_lines(s)
self.assertEqual(c.name, "longcontextname")
self.assertEqual(c.aliases, ())
self.assertEqual(c.defaults, {})
self.assertEqual(c.funcs.keys(), {a, b})
self._test_ctx(c)
s = ["@context longcontextname = lc", "[length] <-> 1 / [time]: c / value"]
c = Context.from_lines(s)
self.assertEqual(c.name, "longcontextname")
self.assertEqual(c.aliases, ("lc",))
self.assertEqual(c.defaults, {})
self.assertEqual(c.funcs.keys(), {a, b})
self._test_ctx(c)
s = [
"@context longcontextname = lc = lcn",
"[length] <-> 1 / [time]: c / value",
]
c = Context.from_lines(s)
self.assertEqual(c.name, "longcontextname")
self.assertEqual(c.aliases, ("lc", "lcn"))
self.assertEqual(c.defaults, {})
self.assertEqual(c.funcs.keys(), {a, b})
self._test_ctx(c)
def test_parse_auto_inverse(self):
a = Context.__keytransform__(
UnitsContainer({"[time]": -1.0}), UnitsContainer({"[length]": 1.0})
)
b = Context.__keytransform__(
UnitsContainer({"[length]": 1.0}), UnitsContainer({"[time]": -1.0})
)
s = ["@context longcontextname", "[length] <-> 1 / [time]: c / value"]
c = Context.from_lines(s)
self.assertEqual(c.defaults, {})
self.assertEqual(c.funcs.keys(), {a, b})
self._test_ctx(c)
def test_parse_define(self):
a = Context.__keytransform__(
UnitsContainer({"[time]": -1}), UnitsContainer({"[length]": 1.0})
)
b = Context.__keytransform__(
UnitsContainer({"[length]": 1}), UnitsContainer({"[time]": -1.0})
)
s = ["@context longcontextname", "[length] <-> 1 / [time]: c / value"]
c = Context.from_lines(s)
self.assertEqual(c.defaults, {})
self.assertEqual(c.funcs.keys(), {a, b})
self._test_ctx(c)
def test_parse_parameterized(self):
a = Context.__keytransform__(
UnitsContainer({"[time]": -1.0}), UnitsContainer({"[length]": 1.0})
)
b = Context.__keytransform__(
UnitsContainer({"[length]": 1.0}), UnitsContainer({"[time]": -1.0})
)
s = ["@context(n=1) longcontextname", "[length] <-> 1 / [time]: n * c / value"]
c = Context.from_lines(s)
self.assertEqual(c.defaults, {"n": 1})
self.assertEqual(c.funcs.keys(), {a, b})
self._test_ctx(c)
s = [
"@context(n=1, bla=2) longcontextname",
"[length] <-> 1 / [time]: n * c / value / bla",
]
c = Context.from_lines(s)
self.assertEqual(c.defaults, {"n": 1, "bla": 2})
self.assertEqual(c.funcs.keys(), {a, b})
# If the variable is not present in the definition, then raise an error
s = ["@context(n=1) longcontextname", "[length] <-> 1 / [time]: c / value"]
self.assertRaises(DefinitionSyntaxError, Context.from_lines, s)
def test_warnings(self):
ureg = UnitRegistry()
with self.capture_log() as buffer:
add_ctxs(ureg)
d = Context("ab")
ureg.add_context(d)
self.assertEqual(len(buffer), 1)
self.assertIn("ab", str(buffer[-1]))
d = Context("ab1", aliases=("ab",))
ureg.add_context(d)
self.assertEqual(len(buffer), 2)
self.assertIn("ab", str(buffer[-1]))
[docs]class TestDefinedContexts(QuantityTestCase):
FORCE_NDARRAY = False
def test_defined(self):
ureg = self.ureg
with ureg.context("sp"):
pass
a = Context.__keytransform__(
UnitsContainer({"[time]": -1.0}), UnitsContainer({"[length]": 1.0})
)
b = Context.__keytransform__(
UnitsContainer({"[length]": 1.0}), UnitsContainer({"[time]": -1.0})
)
self.assertIn(a, ureg._contexts["sp"].funcs)
self.assertIn(b, ureg._contexts["sp"].funcs)
with ureg.context("sp"):
self.assertIn(a, ureg._active_ctx)
self.assertIn(b, ureg._active_ctx)
def test_spectroscopy(self):
ureg = self.ureg
eq = (532.0 * ureg.nm, 563.5 * ureg.terahertz, 2.33053 * ureg.eV)
with ureg.context("sp"):
from pint.util import find_shortest_path
for a, b in itertools.product(eq, eq):
for x in range(2):
if x == 1:
a = a.to_base_units()
b = b.to_base_units()
da, db = Context.__keytransform__(
a.dimensionality, b.dimensionality
)
p = find_shortest_path(ureg._active_ctx.graph, da, db)
self.assertTrue(p)
msg = "{} <-> {}".format(a, b)
# assertAlmostEqualRelError converts second to first
self.assertQuantityAlmostEqual(b, a, rtol=0.01, msg=msg)
for a, b in itertools.product(eq, eq):
self.assertQuantityAlmostEqual(a.to(b.units, "sp"), b, rtol=0.01)
def test_textile(self):
ureg = self.ureg
qty_direct = 1.331 * ureg.tex
with self.assertRaises(DimensionalityError):
qty_indirect = qty_direct.to("Nm")
with ureg.context("textile"):
from pint.util import find_shortest_path
qty_indirect = qty_direct.to("Nm")
a = qty_direct.to_base_units()
b = qty_indirect.to_base_units()
da, db = Context.__keytransform__(a.dimensionality, b.dimensionality)
p = find_shortest_path(ureg._active_ctx.graph, da, db)
self.assertTrue(p)
msg = "{} <-> {}".format(a, b)
self.assertQuantityAlmostEqual(b, a, rtol=0.01, msg=msg)
# Check RKM <-> cN/tex conversion
self.assertQuantityAlmostEqual(1 * ureg.RKM, 0.980665 * ureg.cN / ureg.tex)
self.assertQuantityAlmostEqual(
(1 / 0.980665) * ureg.RKM, 1 * ureg.cN / ureg.tex
)
self.assertAlmostEqual((1 * ureg.RKM).to(ureg.cN / ureg.tex).m, 0.980665)
self.assertAlmostEqual(
(1 * ureg.cN / ureg.tex).to(ureg.RKM).m, 1 / 0.980665
)
def test_decorator(self):
ureg = self.ureg
a = 532.0 * ureg.nm
with ureg.context("sp"):
b = a.to("terahertz")
def f(wl):
return wl.to("terahertz")
self.assertRaises(DimensionalityError, f, a)
@ureg.with_context("sp")
def g(wl):
return wl.to("terahertz")
self.assertEqual(b, g(a))
def test_decorator_composition(self):
ureg = self.ureg
a = 532.0 * ureg.nm
with ureg.context("sp"):
b = a.to("terahertz")
@ureg.with_context("sp")
@ureg.check("[length]")
def f(wl):
return wl.to("terahertz")
@ureg.with_context("sp")
@ureg.check("[length]")
def g(wl):
return wl.to("terahertz")
self.assertEqual(b, f(a))
self.assertEqual(b, g(a))
[docs]class TestContextRedefinitions(QuantityTestCase):
def test_redefine(self):
ureg = UnitRegistry(
"""
foo = [d] = f = foo_alias
bar = 2 foo = b = bar_alias
baz = 3 bar = _ = baz_alias
asd = 4 baz
@context c
# Note how we're redefining a symbol, not the base name, as a
# function of another name
b = 5 f
""".splitlines()
)
# Units that are somehow directly or indirectly defined as a function of the
# overridden unit are also affected
foo = ureg.Quantity(1, "foo")
bar = ureg.Quantity(1, "bar")
asd = ureg.Quantity(1, "asd")
# Test without context before and after, to verify that the cache and units have
# not been polluted
for enable_ctx in (False, True, False):
with self.subTest(enable_ctx):
if enable_ctx:
ureg.enable_contexts("c")
k = 5
else:
k = 2
self.assertEqual(foo.to("b").magnitude, 1 / k)
self.assertEqual(foo.to("bar").magnitude, 1 / k)
self.assertEqual(foo.to("bar_alias").magnitude, 1 / k)
self.assertEqual(foo.to("baz").magnitude, 1 / k / 3)
self.assertEqual(bar.to("foo").magnitude, k)
self.assertEqual(bar.to("baz").magnitude, 1 / 3)
self.assertEqual(asd.to("foo").magnitude, 4 * 3 * k)
self.assertEqual(asd.to("bar").magnitude, 4 * 3)
self.assertEqual(asd.to("baz").magnitude, 4)
ureg.disable_contexts()
def test_define_nan(self):
ureg = UnitRegistry(
"""
USD = [currency]
EUR = nan USD
GBP = nan USD
@context c
EUR = 1.11 USD
# Note that we're changing which unit GBP is defined against
GBP = 1.18 EUR
@end
""".splitlines()
)
q = ureg.Quantity("10 GBP")
self.assertEquals(q.magnitude, 10)
self.assertEquals(q.units.dimensionality, {"[currency]": 1})
self.assertEquals(q.to("GBP").magnitude, 10)
self.assertTrue(math.isnan(q.to("USD").magnitude))
self.assertAlmostEqual(q.to("USD", "c").magnitude, 10 * 1.18 * 1.11)
def test_non_multiplicative(self):
ureg = UnitRegistry(
"""
kelvin = [temperature]
fahrenheit = 5 / 9 * kelvin; offset: 255
bogodegrees = 9 * kelvin
@context nonmult_to_nonmult
fahrenheit = 7 * kelvin; offset: 123
@end
@context nonmult_to_mult
fahrenheit = 123 * kelvin
@end
@context mult_to_nonmult
bogodegrees = 5 * kelvin; offset: 123
@end
""".splitlines()
)
k = ureg.Quantity(100, "kelvin")
with self.subTest("baseline"):
self.assertAlmostEqual(k.to("fahrenheit").magnitude, (100 - 255) * 9 / 5)
self.assertAlmostEqual(k.to("bogodegrees").magnitude, 100 / 9)
with self.subTest("nonmult_to_nonmult"):
with ureg.context("nonmult_to_nonmult"):
self.assertAlmostEqual(k.to("fahrenheit").magnitude, (100 - 123) / 7)
with self.subTest("nonmult_to_mult"):
with ureg.context("nonmult_to_mult"):
self.assertAlmostEqual(k.to("fahrenheit").magnitude, 100 / 123)
with self.subTest("mult_to_nonmult"):
with ureg.context("mult_to_nonmult"):
self.assertAlmostEqual(k.to("bogodegrees").magnitude, (100 - 123) / 5)
def test_err_to_base_unit(self):
with self.assertRaises(DefinitionSyntaxError) as e:
Context.from_lines(["@context c", "x = [d]"])
self.assertEquals(str(e.exception), "Can't define base units within a context")
def test_err_change_base_unit(self):
ureg = UnitRegistry(
"""
foo = [d1]
bar = [d2]
@context c
bar = foo
@end
""".splitlines()
)
with self.assertRaises(ValueError) as e:
ureg.enable_contexts("c")
self.assertEquals(
str(e.exception), "Can't redefine a base unit to a derived one"
)
def test_err_change_dimensionality(self):
ureg = UnitRegistry(
"""
foo = [d1]
bar = [d2]
baz = foo
@context c
baz = bar
@end
""".splitlines()
)
with self.assertRaises(ValueError) as e:
ureg.enable_contexts("c")
self.assertEquals(
str(e.exception),
"Can't change dimensionality of baz from [d1] to [d2] in a context",
)
def test_err_cyclic_dependency(self):
ureg = UnitRegistry(
"""
foo = [d]
bar = foo
baz = bar
@context c
bar = baz
@end
""".splitlines()
)
# TODO align this exception and the one you get when you implement a cyclic
# dependency within the base registry. Ideally this exception should be
# raised by enable_contexts.
ureg.enable_contexts("c")
q = ureg.Quantity("bar")
with self.assertRaises(RecursionError):
q.to("foo")
def test_err_dimension_redefinition(self):
with self.assertRaises(DefinitionSyntaxError) as e:
Context.from_lines(["@context c", "[d1] = [d2] * [d3]"])
self.assertEquals(
str(e.exception), "Expected <unit> = <converter>; got [d1] = [d2] * [d3]"
)
def test_err_prefix_redefinition(self):
with self.assertRaises(DefinitionSyntaxError) as e:
Context.from_lines(["@context c", "[d1] = [d2] * [d3]"])
self.assertEquals(
str(e.exception), "Expected <unit> = <converter>; got [d1] = [d2] * [d3]"
)
def test_err_redefine_alias(self):
for s in ("foo = bar = f", "foo = bar = _ = baz"):
with self.subTest(s):
with self.assertRaises(DefinitionSyntaxError) as e:
Context.from_lines(["@context c", s])
self.assertEquals(
str(e.exception),
"Can't change a unit's symbol or aliases within a context",
)
def test_err_redefine_with_prefix(self):
ureg = UnitRegistry(
"""
kilo- = 1000
gram = [mass]
pound = 454 gram
@context c
kilopound = 500000 gram
@end
""".splitlines()
)
with self.assertRaises(ValueError) as e:
ureg.enable_contexts("c")
self.assertEquals(
str(e.exception), "Can't redefine a unit with a prefix: kilopound"
)
def test_err_new_unit(self):
ureg = UnitRegistry(
"""
foo = [d]
@context c
bar = foo
@end
""".splitlines()
)
with self.assertRaises(UndefinedUnitError) as e:
ureg.enable_contexts("c")
self.assertEquals(str(e.exception), "'bar' is not defined in the unit registry")