def test_prefix_standard(): Schalter.clear() @Schalter.prefix("test") @Schalter.configure("a") def foo(*, a, b=3): return a Schalter["test/a"] = 3 foo() @Schalter.prefix("b") @Schalter.configure def bar(*, b: int, c: str): return b, c Schalter["b/b"] = 2 Schalter["b/c"] = "baz" assert bar() == (2, "baz") @Schalter.configure("non_prefixed") @Schalter.prefix("some_prefix") @Schalter.configure("prefixed") def baz(*, prefixed, non_prefixed): return prefixed, non_prefixed Schalter["some_prefix/prefixed"] = 123 Schalter["non_prefixed"] = 456 assert baz() == (123, 456) with pytest.raises(KeyError): _ = Schalter["prefixed"] with pytest.raises(KeyError): _ = Schalter["some_prefix/non_prefixed"]
def test_misc(): Schalter.clear() # make sure that this does not configure the function arg 'a' # and does not contain a config value 'b' @Schalter.configure(b="a") def foo(*, a: int = 4, b: int): return a * b assert foo(b=5) == 20 assert Schalter["a"] == 5 assert not bool("b" in Schalter.get_config()) assert "b" not in Schalter.get_config() assert not bool("b" in Schalter) with pytest.raises(KeyError): _ = Schalter["b"] Schalter["c"] = 0 Schalter["d"] = 1 @Schalter.configure(c="d", d="c") def bar(*, c: int = 4, d: int = 5): return c, d assert Schalter["c"] == 0 assert Schalter["d"] == 1 assert (1, 0) == bar()
def test_functions_callable(): Schalter.clear() @Schalter.configure("x") def foo(*, x): _ = x # This raises a key error, because there is no value for x # instead of a TypeError for a missing argument with pytest.raises(KeyError): foo() @Schalter.configure("a") def not_callable(*, c, a): _ = c, a # c is a kwonly arg that is not configured. May not be marked as default available. with pytest.raises(TypeError): not_callable() @Schalter.configure("a") @Schalter.configure("c") def is_callable(*, c, a): _ = c, a Schalter["a"] = 0 Schalter["c"] = 0 is_callable()
def test_subset_configures_and_overrides(): Schalter.clear() @Schalter.configure("a") def foo(*, a: int = 1, b: int = 2): return a * b assert Schalter["a"] == 1 with pytest.raises(KeyError): _ = Schalter["b"] @Schalter.configure("b") def bar(*, a: int = 2, b: int = 3): return a * b assert Schalter["b"] == 3 assert bar() == 6 @Schalter.configure def baz(*, a, b): return a * b # Schalter decorator marks the kwonly args as 'with default' # to make this call possible assert baz() == 3 assert foo(b=3) == 3 # default is overridden assert baz(a=7) == 21 assert baz(b=4) == 28 assert foo(b=3) == 21 assert bar() == 8
def test_do_not_override_with_default_values(): Schalter.clear() # A (later defined) default value does not override a manually supplied parameter. Schalter["constant"] = 4 @Schalter.configure("constant") def foo(*, constant=-1): return constant assert foo() == 4
def test_default_config(): Schalter.clear() d = Schalter.get_config() assert type(d) == Schalter assert len(d.config) == 0 @Schalter.configure() def foo(*, a): pass foo(a=3) assert Schalter["a"] == 3
def test_prevent_prefix_with_default_values(): Schalter.clear() def wrapper(): @Schalter.prefix("p") @Schalter.configure def foo(*, some_arg=1): pass assert Schalter['p/some_arg'] == 1 with pytest.raises(NotImplementedError): wrapper()
def test_scoped_function(): Schalter.clear() @Schalter.scoped_configure def foo(*, a): return a @Schalter.Scope("A") def do_stuff_a(): foo(a=3) @Schalter.Scope("B") def do_stuff_b(): foo(a=3)
def test_multiple_prefixed_configures(): Schalter.clear() Schalter["p/leaf_a"] = 8 Schalter["p/leaf_b"] = "foo" @Schalter.prefix("p") @Schalter.configure("leaf_a") @Schalter.configure("leaf_b") def foo(_unused, *, leaf_a, leaf_b): return _unused, leaf_a, leaf_b with pytest.raises(TypeError): _ = foo() assert foo(None) == (None, 8, "foo")
def test_multiple_configures(): Schalter.clear() @Schalter.configure("a") @Schalter.configure("b") def foo(*, a, b, c, d): return a, b, c, d with pytest.raises(KeyError): _aa, _bb, _cc, _dd = foo(c=3, d=4) Schalter["a"] = 1 Schalter["b"] = 2 aa, bb, cc, dd = foo(c=3, d=4) assert aa == 1 and bb == 2
def test_multiple_prefixes(): Schalter.clear() Schalter["first/second/leaf_a"] = 8 Schalter["first/second/leaf_b"] = "foo" @Schalter.prefix("first") @Schalter.prefix("second") @Schalter.configure def foo(_unused, *, leaf_a, leaf_b): return _unused, leaf_a, leaf_b with pytest.raises(TypeError): _ = foo() assert foo(None) == (None, 8, "foo")
def test_value_change(): Schalter.clear() Schalter["x"] = 1 Schalter["y"] = 2 @Schalter.configure(a="x") @Schalter.configure(b="y") def foo(*, a, b): return a + b assert foo() == 3 Schalter["x"] = 3 assert foo() == 2 + 3 Schalter["y"] = 7 assert foo() == 3 + 7
def test_scope(): Schalter.clear() @Schalter.scoped_configure def foo(*, a): return a with Schalter.Scope("A") as config_scope: foo(a=3) assert config_scope.fullname == "A" with Schalter.Scope("B") as config_scope: foo(a=4) assert config_scope.fullname == "B" assert Schalter["A/a"] == 3 assert Schalter["B/a"] == 4
def test_empty_configures(caplog): Schalter.clear() # should issue a warning @Schalter.configure() def empty(): pass assert "WARNING" in caplog.text assert caplog.records caplog.clear() @Schalter.configure() def empty(*_args): pass assert "WARNING" in caplog.text assert caplog.records caplog.clear()
def test_broad_and_narrow_configures(): Schalter.clear() @Schalter.configure @Schalter.configure("b") def foo(*, a, b): return a + b @Schalter.configure @Schalter.configure(d="a", c="b") def bar(*, c, d): return c, d Schalter["a"] = 1 Schalter["b"] = 2 Schalter["c"] = 3 assert foo() == 3 assert bar() == (2, 1)
def test_contradicting_remap(caplog): Schalter.clear() Schalter["x"] = "x" Schalter["y"] = "y" @Schalter.configure(a="x") @Schalter.configure(a="y") def foo(*, a): return a # expecting a warning assert "WARNING" in caplog.text assert caplog.records caplog.clear() # the mapping is expected to point to "y" config entry assert foo() == "y" # this does not warn, because the mapping target is identical. @Schalter.configure @Schalter.configure("a") def bar(*, a): return a assert not caplog.text assert not caplog.records caplog.clear() # this warns for two remaps. @Schalter.configure @Schalter.configure(a="y") @Schalter.configure("a", b="y") def bar(*, a, b): return a, b # expecting a warning assert "WARNING" in caplog.text assert len(caplog.records) == 2 caplog.clear()
def test_multiple_default_values(): Schalter.clear() with pytest.raises(ValueError): @Schalter.configure def add_constant_a(x: int, *, constant_value: int = 1): return x + constant_value @Schalter.configure def add_constant_b(x: int, *, constant_value: int = 2): return x + constant_value assert Schalter["constant_value"] == 1 @Schalter.configure def add_constant_c(x: int, *, constant_value: int = 1): return x + constant_value Schalter.clear() @Schalter.configure def foo(*, _name: str = "baz"): pass @Schalter.configure def bar(*, _name: str = "baz"): pass with pytest.raises(ValueError): @Schalter.configure def foo(*, _name2: str = "baz"): pass @Schalter.configure def bar(*, _name2: str = "bar"): pass
def test_remap_key(): Schalter.clear() @Schalter.configure(local_name="config_name") def f(*, local_name: str = "default_value"): print(local_name) assert Schalter["config_name"] == "default_value" @Schalter.configure("foo", bar="baz") def x(*, foo: int = 3, bar: int = 5): _ = foo * bar assert Schalter["foo"] == 3 assert Schalter["baz"] == 5 with pytest.raises(KeyError): _ = Schalter["bar"] @Schalter.configure(foo="bar", bar="foo") def y_swap(*, foo, bar): return foo, bar assert y_swap(foo=42) == (42, 3)
def test_schalter(): Schalter.clear() @Schalter.configure def add_constant(x: int, *, constant_value: int = 1): return x + constant_value assert Schalter["constant_value"] == 1 assert add_constant(1) == 2 assert Schalter["constant_value"] == 1 assert add_constant(1, constant_value=3) == 4 assert Schalter["constant_value"] == 3 Schalter["another_value"] = 42 @Schalter.configure def check_value(expected, *, another_value: int = -1): assert expected == another_value check_value(42) check_value(1, another_value=1) check_value(1)
def schalter_object_cleared(): Schalter.clear() return Schalter