def test_config_option_default(): class Config(config.Config): # Implicit default=None a = config.option(int, help="") # Default of correct type b = config.option(int, default=1, help="") # Default is function returning correct type c = config.option(int, default=lambda: 2, help="") c = config.structure({}, Config) assert c.a is None assert c.b == 1 assert c.c == 2 class BadDefault(config.Config): a = config.option(int, default="blah", help="") b = config.option(int, default=lambda: "blah", help="") with pytest.raises(config.ConfigError): config.structure( { # Set b, so only a's default is tested "b": 3, }, BadDefault) with pytest.raises(config.ConfigError): config.structure( { # Set a, so only b's default is tested "a": 3, }, BadDefault)
def test_config_nested(): class Inner(config.Config): x = config.option(int, default=1, help="") y = config.option(int, default=2, help="") class Config(config.Config): a = config.option(Inner, help="") b = config.option(Inner, default=Inner, help="") c = config.option(Inner, default=lambda: Inner(dict(x=3, y=4)), help="") # Check all default values c1 = config.structure({}, Config) assert c1.a is None assert c1.b.x == 1 assert c1.b.y == 2 assert c1.c.x == 3 assert c1.c.y == 4 # Check structuring from unstructured data c2 = config.structure({ "a": { "x": 10 }, }, Config) assert isinstance(c2.a, Inner) assert c2.a.x == 10 assert c2.a.y == 2
def test_config_option_implicitly_required(): """Check that a non-None default value forces the field to be required.""" class Config(config.Config): a = config.option(int, default=12, help="") c = config.structure({}, Config) assert c.a == 12 with pytest.raises(config.ConfigError): config.structure({"a": None}, Config)
def test_config_option_required_example(): """Check behaviour of ``required=True`` with an example value (but no default).""" class Config(config.Config): a = config.option(int, required=True, example=12, help="") with pytest.raises(config.ConfigError): config.structure({}, Config) c = config.make_example(Config) assert c.a == 12
def test_config_option_map(): default_map = { "x": 1, "y": 2, } class Config(config.Config): a = config.option_map(int, help="") b = config.option_map(int, default=default_map, help="") c = config.option_map(int, default=lambda: {"y": 3, "z": 4}, help="") # Check all default values c1 = config.structure({}, Config) assert c1.a == {} assert c1.b == {"x": 1, "y": 2} assert c1.c == {"y": 3, "z": 4} # Avoid pitfall of mutable default values default_map["x"] = 20 assert c1.b == {"x": 1, "y": 2} # Check supplied values c2 = config.structure( { "a": { "n": -1, "m": -2 }, "b": {}, "c": { "i": 0, "j": -1 }, }, Config) assert c2.a == {"n": -1, "m": -2} assert c2.b == {} assert c2.c == {"i": 0, "j": -1} # Check the type of keys is enforced c3 = config.structure({ "a": { (1, 2): 2 }, }, Config) assert c3.a == {"(1, 2)": 2} # Check the type of values with pytest.raises(config.ConfigError): config.structure({ "a": { "x": None }, }, Config)
def test_config_option_required_default(): """Check behaviour of ``required=True`` with a default value.""" class Config(config.Config): a = config.option(int, required=True, default=12, help="") c = config.structure({}, Config) assert c.a == 12 c = config.structure({"a": 23}, Config) assert c.a == 23 with pytest.raises(config.ConfigError): config.structure({"a": None}, Config)
def test_config_option_wordlist(): class Config(config.Config): a = config.option(config.WordList, help="") b = config.option(config.WordList, help="") c = config.option(config.WordList, default="foo bar baz", help="") d = config.option(config.WordList, default=["ab", "cd", "ef"], help="") e = config.option_list(config.WordList, help="") f = config.option_map(config.WordList, help="") c = config.structure( { "a": "foo bar baz", "b": ["ab", "cd", "ef"], "e": ["foo bar baz", ["ab", "cd", "ef"]], "f": { "x": "foo bar baz", "y": ["ab", "cd", "ef"] }, }, Config) assert c.a == ["foo", "bar", "baz"] assert c.b == ["ab", "cd", "ef"] assert c.c == ["foo", "bar", "baz"] assert c.d == ["ab", "cd", "ef"] assert c.e == [["foo", "bar", "baz"], ["ab", "cd", "ef"]] assert c.f == {"x": ["foo", "bar", "baz"], "y": ["ab", "cd", "ef"]}
def test_config_option_not_required_default(): """Check that ``required=False`` overrides the "implicitly required" behaviour of having a default.""" class Config(config.Config): a = config.option(int, default=12, required=False, help="") c = config.structure({"a": None}, Config) assert c.a is None
def test_config_option_not_required_no_default(): """Check that default value of None is the default behaviour.""" class Config(config.Config): a = config.option(int, help="") c = config.structure({}, Config) assert c.a is None
def test_config_nested_list(): class Inner(config.Config): x = config.option(int, default=1, help="") y = config.option(int, default=2, help="") class Config(config.Config): a = config.option_list(Inner, help="") # Check default value c1 = config.structure({}, Config) assert c1.a == [] # Check structuring from unstructured data c2 = config.structure( { "a": [ { "x": 10, "y": 20 }, { "x": 11, "y": 21 }, { "x": 12, "y": 22 }, ], }, Config) assert all(isinstance(o, Inner) for o in c2.a) assert c2.a[0].x == 10 assert c2.a[0].y == 20 assert c2.a[1].x == 11 assert c2.a[1].y == 21 assert c2.a[2].x == 12 assert c2.a[2].y == 22 # Check type enforcement with pytest.raises(config.ConfigError): config.structure({ "a": [ { "x": None }, ], }, Config)
def test_config_option_env(): class Config(config.Config): a = config.option(str, env=["A_PRIMARY", "A_FALLBACK"], help="") b = config.option(int, env=["B_ENV"], help="") c = config.option(bool, env="C_ENV", help="") c1 = config.structure({}, Config) assert c1.a is None assert c1.b is None # Environment variable precedence with TempEnvVars({"A_PRIMARY": "foo", "A_FALLBACK": "bar"}): c2 = config.structure({}, Config) assert c2.a == "foo" with TempEnvVars({"A_FALLBACK": "bar"}): c3 = config.structure({}, Config) assert c3.a == "bar" # Type conversion: int with TempEnvVars({"B_ENV": "2"}): c4 = config.structure({}, Config) assert c4.b == 2 # Type conversion: bool with TempEnvVars({"C_ENV": "true"}): c5 = config.structure({}, Config) assert c5.c is True with TempEnvVars({"C_ENV": "false"}): c5 = config.structure({}, Config) assert c5.c is False # Supplied value causes environment variable to be ignored with TempEnvVars({"C_ENV": "false"}): c5 = config.structure({"c": True}, Config) assert c5.c is True
def test_config_nested_map(): class Inner(config.Config): x = config.option(int, default=1, help="") y = config.option(int, default=2, help="") class Config(config.Config): a = config.option_map(Inner, help="") # Check default value c1 = config.structure({}, Config) assert c1.a == {} # Check structuring from unstructured data c2 = config.structure( { "a": { "b": { "x": 10, "y": 20 }, "c": { "x": 11, "y": 21 }, }, }, Config) assert all(isinstance(v, Inner) for v in c2.a.values()) assert c2.a["b"].x == 10 assert c2.a["b"].y == 20 assert c2.a["c"].x == 11 assert c2.a["c"].y == 21 # Check type enforcement with pytest.raises(config.ConfigError): config.structure({ "a": { "b": { "x": None }, }, }, Config)
def test_config_option_validate(): class Config(config.Config): # Option with default=None (or omitted) can be None a = config.option(int, help="") # Option without default=None must not be None b = config.option(int, default=1, help="") # Both have correctly typed values c1 = config.structure({ "a": 3, "b": 4, }, Config) assert c1.a == 3 assert c1.b == 4 # None value where default=None is allowed c2 = config.structure({ "a": None, }, Config) assert c2.a is None # (But incorrect type is not allowed) with pytest.raises(config.ConfigError): config.structure({ "a": "blah", }, Config) # None value where default!=None is not allowed with pytest.raises(config.ConfigError): config.structure({ "b": None, }, Config)
def test_config_option_list(): default_list = [1, 2, 3] class Config(config.Config): a = config.option_list(int, help="") b = config.option_list(int, default=default_list, help="") c = config.option_list(int, default=lambda: [4, 5, 6], help="") # Check all default values c1 = config.structure({}, Config) assert c1.a == [] assert c1.b == [1, 2, 3] assert c1.c == [4, 5, 6] # Avoid pitfall of mutable default values default_list[1] = 20 assert c1.b == [1, 2, 3] # Check supplied values c2 = config.structure({ "a": [9, 8], "b": [], "c": [7, 6, 5], }, Config) assert c2.a == [9, 8] assert c2.b == [] assert c2.c == [7, 6, 5] # Check other sequences are read as lists c3 = config.structure({ "a": (10, 11), }, Config) assert c3.a == [10, 11] # Check the type of sequence values with pytest.raises(config.ConfigError): config.structure({ "a": [1, None], }, Config)