def test_fill_defaults_simple_config(oh): @oh.register(with_defaults=True) def foo(required: int, optional: str = "default value"): return 42 # test valid config filled = Config.from_str( """ [a] @call = foo required = 1 """ ) assert filled.a["required"] == 1 assert filled.a["optional"] == "default value" # test invalid config filled = Config.from_str( """ [a] @call = foo optional = "some value" """ ) with pytest.raises(RuntimeError): filled.a()
def test_save_as_dict(): c = Config.from_str(TEST_CONFIG) d = Config(c.to_dict()) assert c == d e = Config.from_str(TEST_CONFIG, interpolate=False) f = Config(e.to_dict()) assert e == f
def test_save_as_json(): c = Config.from_str(TEST_CONFIG) data = json.dumps(c) d = Config.from_json(data) assert d == c c = Config.from_str(TEST_CONFIG, interpolate=False) data = json.dumps(c) d = Config.from_json(data) assert d == c
def test_save_as_str(): c = Config.from_str(TEST_CONFIG) d = Config.from_str(c.to_str()) assert c == d e = Config.from_str(TEST_CONFIG, interpolate=False) f = Config.from_str(e.to_str(), interpolate=False) assert e == f h = Config.from_str(e.to_str()) assert h == d
def test_save_as_pickle(): c = Config.from_str(TEST_CONFIG) data = pickle.dumps(c) d = pickle.loads(data) assert isinstance(d, dict) assert not isinstance(d, Config) assert not isinstance(d, ConfigDict) assert not isinstance(d["a"], ConfigDict) assert d == c c = Config.from_str(TEST_CONFIG, interpolate=False) data = pickle.dumps(c) d = pickle.loads(data) assert isinstance(d, dict) assert not isinstance(d, Config) assert not isinstance(d, ConfigDict) assert d == c
def test_dict_access(): c = Config.from_str(""" [a] 0 = -1 x = 42 [c.d] z = {"hello": "world"} """) assert c == { "a": { "0": -1, "x": 42 }, "c": { "d": { "z": { "hello": "world" } } } } c["a"]["x"] = 43 assert c["a"]["0"] == -1 assert c["a"]["x"] == 43 assert c["c"]["d"]["z"]["hello"] == "world" # test integer access assert c["a"][0] == -1 c["a"][0] = 5 assert c["a"][0] == 5 with pytest.raises(ValueError): c["a"][-5] = 5 with pytest.raises(TypeError): c["a"][5.0] = 5 with pytest.raises(TypeError): c["a"][True] = 5 with pytest.raises(TypeError): c["a"][None] = 5 with pytest.raises(TypeError): c["a"][(1, 2)] = 5 with pytest.raises(TypeError): c["a"][[1, 2]] = 5 with pytest.raises(KeyError): c["a"]["b"]["c"]["d"]["e"] = 99
def test_dict_merging(): c = Config({"a": {"b": 1, "c": 42}}) c.update({"a": {"b": 123}}) assert c == {"a": {"b": 123, "c": 42}} c = Config.from_str(""" [a] x = 42 [b.c] y = "asdf" """) c.update({"a": {"z": 1}}, merge_schema=True) c.update({"b": {"d": 2}}, merge_schema=True) assert c == {"a": {"x": 42, "z": 1}, "b": {"c": {"y": "asdf"}, "d": 2}}
def test_no_interpolation(): """Test that interpolation is correctly preserved.""" c = Config.from_str( """ [a] b = 1 [c] d = "${a.b}" e = "hello${a.b}" f = ${a} """, interpolate=False ) assert c["c"]["d"] == "${a.b}" assert c["c"]["e"] == "hello${a.b}" assert c["c"]["f"] == {"@ref": "a"} d = Config.from_str(c.to_str(), interpolate=True) assert d["c"]["d"] == "1" assert d["c"]["e"] == "hello1" assert d["c"]["f"] == {"b": 1} c = Config.from_str( """ [a] b = 1 [c.d] @ref = a """ ) assert c.flat["a.b"] == 1 assert c.flat["c.d"] == {"@ref": "a"} d = Config.from_str(c.to_str(), interpolate=True) assert d.flat["a.b"] == 1 assert d.flat["c.d"] == {"b": 1}
def test_update(): c = Config.from_str(""" [a] x = 42 [b.c] y = "asdf" """) with pytest.raises(ValidationError): c.update({"b": {"d": 2}}) c.update({"b": {"d": 2}}, merge_schema=True) assert c == {"a": {"x": 42}, "b": {"c": {"y": "asdf"}, "d": 2}} c.update({"b": {"d": np.int64(64)}}) assert c == {"a": {"x": 42}, "b": {"c": {"y": "asdf"}, "d": 64}} assert_valid_json(c)
def test_attribute_access(): c = Config.from_str(""" [a] x = 42 [c.d] z = {"hello": "world"} """) assert c.a.x == 42 assert c.c.d.z.hello == "world" c.a.x = 43 assert c.a.x == 43 with pytest.raises(ValidationError): c.a.y = 43 with pytest.raises(AttributeError): c.a.b.c.d.e = 99
def test_flat_access(): c = Config.from_str(""" [a] x = 42 [b] y = "asdf" [c.d] z = {"hello": "world"} """) assert len(c.flat) == 3 assert list(c.flat) == ["a.x", "b.y", "c.d.z.hello"] with pytest.raises(KeyError): c.flat["c.d.z.helloooo"] assert c.flat["c.d.z.hello"] == "world" with pytest.raises(TypeError): c.flat["x.y.z"] = 1 with pytest.raises(TypeError): del c.flat["a.x"]
def test_interpolation(): with pytest.raises(ParseError): Config.from_str( """ [a] foo = "hello" [b] bar = ${foo} """ ) c = Config.from_str( """ [a] foo = "hello" [b] bar = ${a.foo} """ ) assert c["b"]["bar"] == "hello" c = Config.from_str( """ [a] foo = "hello" [b] bar = "${a.foo}!" """ ) assert c["b"]["bar"] == "hello!" with pytest.raises(ParseError): Config.from_str( """ [a] foo = "hello" [b] bar = ${a.foo}! """ ) with pytest.raises(ParseError): Config.from_str( """ [a] foo = 15 [b] bar = ${a.foo}! """ ) c = Config.from_str( """ [a] foo = ["x", "y"] [b] bar = ${a.foo} """ ) assert c["b"]["bar"] == ["x", "y"] # Interpolation within the same section c = Config.from_str( """ [a] foo = "x" bar = ${a.foo} baz = "${a.foo}y" """ ) assert c["a"]["bar"] == "x" assert c["a"]["baz"] == "xy" # multiple string interpolations c = Config.from_str( """ [a] x = "x" y = 1 z = 3.14159 zz = "foo ${a.x} ${a.y} ${a.z}" """ ) assert c.a.zz == "foo x 1 3.14159" # test all types c = Config.from_str( """ [a] int = 42 float = 3.14159 str = "foobar" bool = true null = null x = "${a.int} ${a.float} ${a.str} ${a.bool} ${a.null}" y = {"a": ${a.int}, "b": ${a.float}, "c": ${a.str}, "d": ${a.bool}, "e": ${a.null}} z = [${a.int}, ${a.float}, ${a.str}, ${a.bool}, ${a.null}] """ ) assert c.a.x == "42 3.14159 foobar True None" assert c.a.y == {"a": 42, "b": 3.14159, "c": "foobar", "d": True, "e": None} assert c.a.z == [42, 3.14159, "foobar", True, None] # leading and trailing text c = Config.from_str( """ [a] b = "ergo" c = "cogito ${a.b} sum" """ ) assert c.a.c == "cogito ergo sum" # trailing spaces c = Config.from_str( """ [a] b = "zip" c = ${a.b} """ ) assert c.a.c == "zip" c = Config.from_str( """ [a] b = "zip" c = "${a.b}${a.b}" """ ) assert c.a.c == "zipzip" with pytest.raises(ParseError): Config.from_str( """ [a] b = "zip" c = ${a.b}${a.b} """ ) # nested data in string interpolation with pytest.raises(ParseError): Config.from_str( """ [a] b = 1 c = "fof" [d] e = "${a}" """ ) # wrong order with pytest.raises(ParseError): Config.from_str( """ [a] b = ${d} [d] e = 1 """ ) # cyclic references with pytest.raises(ParseError): Config.from_str( """ [a] b = ${d.e} [d] e = ${a.b} """ ) # chained references c = Config.from_str( """ [a] b = 1 [c] d = ${a} [e] f = ${c.d.b} [g] h = ${e.f} """ ) assert c.flat["a.b"] == 1 assert c.flat["c.d"] == {"b": 1} assert c.flat["e.f"] == 1 assert c.flat["g.h"] == 1
def test_interpolation_lists(): """Test that lists are preserved correctly""" c = Config.from_str( """ [a] b = 1 [c] d = ["hello ${a.b}", "world"] """, interpolate=False ) assert c["c"]["d"] == ["hello ${a.b}", "world"] c = Config.from_str( """ [a] b = 1 [c] d = ["hello ${a.b}", "world"] """ ) assert c["c"]["d"] == ["hello 1", "world"] c = Config.from_str( """ [a] b = 1 [c] d = [${a.b}, "hello ${a.b}", "world"] """, interpolate=False ) assert c["c"]["d"] == [{"@ref": "a.b"}, "hello ${a.b}", "world"] c = Config.from_str( """ [a] b = 1 [c] d = [${a.b}, "hello ${a.b}", "world"] """ ) assert c["c"]["d"] == [1, "hello 1", "world"] c = Config.from_str( """ [a] b = 1 [c] d = ["hello", ${a}] """ ) assert c["c"]["d"] == ["hello", {"b": 1}] with pytest.raises(ParseError): Config.from_str( """ [a] b = 1 [c] d = ["hello", "hello ${a}"] """ ) c = Config.from_str( """ [a] b = 1 [c] d = ["hello", {"x": ["hello ${a.b}"], "y": 2}] """ ) assert c["c"]["d"] == ["hello", {"x": ["hello 1"], "y": 2}] c = Config.from_str( """ [a] b = 1 [c] d = ["hello", {"x": [${a.b}], "y": 2}] """ ) assert c["c"]["d"] == ["hello", {"x": [1], "y": 2}] c = Config.from_str( """ [a] b = 1 c = "fof" [d] e = ${a} """ ) assert c.d.e == {"b": 1, "c": "fof"}