diff options
Diffstat (limited to 'tests/config/test_basics.py')
-rw-r--r-- | tests/config/test_basics.py | 396 |
1 files changed, 218 insertions, 178 deletions
diff --git a/tests/config/test_basics.py b/tests/config/test_basics.py index 825c1a0c..374fc135 100644 --- a/tests/config/test_basics.py +++ b/tests/config/test_basics.py @@ -9,15 +9,19 @@ def passthrough(*args, **kwargs): def test_invalid_config_types(): - for var in ('class', 'inherit'): - @configurable({var: 'str'}) + for var in ("class", "inherit"): + + @configurable({var: "str"}) def testtype(): pass + with pytest.raises(errors.TypeDefinitionError): basics.ConfigType(testtype) - @configurable(positional=['foo']) + + @configurable(positional=["foo"]) def test(*args): pass + with pytest.raises(errors.TypeDefinitionError): basics.ConfigType(test) @@ -25,9 +29,11 @@ def test_invalid_config_types(): # the docstrings aren't part of the test, but using 'pass' instead # makes trial's --coverage complain about them. + def argsfunc(*args): """Function taking a variable number of arguments.""" + def kwargsfunc(**kwargs): """Function taking keyword arguments.""" @@ -36,14 +42,12 @@ def nonopt(one, two): """Function taking two non-optional args.""" -def alltypes(alist=(), astr='astr', abool=True, aref=object(), anint=3, - along=int(3)): +def alltypes(alist=(), astr="astr", abool=True, aref=object(), anint=3, along=int(3)): """Function taking lots of kinds of args.""" class NewStyleStrClass: - - def __init__(self, one, two='two'): + def __init__(self, one, two="two"): """Newstyle testclass.""" def test_member(self, one): @@ -51,7 +55,6 @@ class NewStyleStrClass: class NewStyleClass: - def __init__(self, one, two=object()): """Newstyle testclass.""" @@ -60,7 +63,6 @@ class NewStyleClass: class OldStyleClass: - def __init__(self, one, two=object()): """Newstyle testclass.""" @@ -69,7 +71,6 @@ class OldStyleClass: class TestConfigTypeFromFunction: - def test_invalid(self): with pytest.raises(TypeError): basics.ConfigType(argsfunc) @@ -78,62 +79,68 @@ class TestConfigTypeFromFunction: def test_basic(self): nonopt_type = basics.ConfigType(nonopt) - assert nonopt_type.name == 'nonopt' - assert nonopt_type.types == {'one': 'str', 'two': 'str'} - assert nonopt_type.required == ('one', 'two') - assert nonopt_type.positional == ('one', 'two') + assert nonopt_type.name == "nonopt" + assert nonopt_type.types == {"one": "str", "two": "str"} + assert nonopt_type.required == ("one", "two") + assert nonopt_type.positional == ("one", "two") def test_default_types(self): test_type = basics.ConfigType(alltypes) assert test_type.types == { - 'alist': 'list', 'astr': 'str', 'abool': 'bool', - 'anint': 'int', 'along': 'int'} + "alist": "list", + "astr": "str", + "abool": "bool", + "anint": "int", + "along": "int", + } assert not test_type.required - @pytest.mark.parametrize('func', ( - pytest.param(NewStyleClass(1).member, id='newstyle_instance'), - pytest.param(OldStyleClass(1).member, id='oldstyle_instance'), - pytest.param(NewStyleClass.member, id='newstyle_class'), - pytest.param(OldStyleClass.member, id='oldstyle_class'), - )) + @pytest.mark.parametrize( + "func", + ( + pytest.param(NewStyleClass(1).member, id="newstyle_instance"), + pytest.param(OldStyleClass(1).member, id="oldstyle_instance"), + pytest.param(NewStyleClass.member, id="newstyle_class"), + pytest.param(OldStyleClass.member, id="oldstyle_class"), + ), + ) def test_class_member(self, func): test_type = basics.ConfigType(func) - assert test_type.name == 'member' - assert test_type.required == ('one',) + assert test_type.name == "member" + assert test_type.required == ("one",) class TestConfigTypeFromClass: - def _test_basics(self, klass, name, two_override=None): test_type = basics.ConfigType(klass) assert test_type.name == name - assert set(test_type.required) == {'one'} - target_types = {'one': 'str'} + assert set(test_type.required) == {"one"} + target_types = {"one": "str"} if two_override is not None: - target_types['two'] = two_override + target_types["two"] = two_override assert target_types == test_type.types assert test_type.name == name def test_oldstyle(self): - self._test_basics(OldStyleClass, 'OldStyleClass') + self._test_basics(OldStyleClass, "OldStyleClass") def test_newstyle(self): - self._test_basics(NewStyleClass, 'NewStyleClass') + self._test_basics(NewStyleClass, "NewStyleClass") def test_defaults_str(self): - self._test_basics(NewStyleStrClass, 'NewStyleStrClass', - two_override='str') + self._test_basics(NewStyleStrClass, "NewStyleStrClass", two_override="str") def test_config_hint(self): class Class(NewStyleClass): - pkgcore_config_type = ConfigHint( - types={'two':'bool'}, doc='interesting') - self._test_basics(Class, 'Class', two_override='bool') - assert 'interesting' == basics.ConfigType(Class).doc + pkgcore_config_type = ConfigHint(types={"two": "bool"}, doc="interesting") + + self._test_basics(Class, "Class", two_override="bool") + assert "interesting" == basics.ConfigType(Class).doc def test_object_init(self): class kls: pass + conf = basics.ConfigType(kls) assert not conf.types assert not conf.required @@ -151,12 +158,16 @@ class TestConfigTypeFromClass: # have to be accurate however class cls(dict): __slots__ = () + with pytest.raises(TypeError): basics.ConfigType(cls) - raw_hint = ConfigHint(types={"filename":"str", "mode":"r", - "buffering":"int"}, typename='file', - required=['filename'], positional=['filename']) + raw_hint = ConfigHint( + types={"filename": "str", "mode": "r", "buffering": "int"}, + typename="file", + required=["filename"], + positional=["filename"], + ) # make sure it still tries to introspect, and throws typeerror. # introspection is generally wanted- if it must be skipped, the @@ -166,41 +177,45 @@ class TestConfigTypeFromClass: basics.ConfigType(cls) cls.pkgcore_config_type = raw_hint.clone(authorative=True) conf = basics.ConfigType(cls) - assert conf.name == 'file' - assert list(conf.required) == ['filename'] - assert list(conf.positional) == ['filename'] - assert set(conf.types) == {'buffering', 'filename', 'mode'} + assert conf.name == "file" + assert list(conf.required) == ["filename"] + assert list(conf.positional) == ["filename"] + assert set(conf.types) == {"buffering", "filename", "mode"} -class TestConfigHint: +class TestConfigHint: def test_configurable_decorator(self): - @configurable(typename='spork', types={'foon': 'str'}) + @configurable(typename="spork", types={"foon": "str"}) def stuff(*args, **kwargs): return args, kwargs - assert 'spork' == stuff.pkgcore_config_type.typename - assert 'str' == basics.ConfigType(stuff).types['foon'] - assert (('spork',), {}) == stuff('spork') - + assert "spork" == stuff.pkgcore_config_type.typename + assert "str" == basics.ConfigType(stuff).types["foon"] + assert (("spork",), {}) == stuff("spork") def test_clone(self): - c = ConfigHint(types={'foo': 'list', 'one': 'str'}, - positional=['one'], required=['one'], - typename='barn', doc='orig doc') - c2 = c.clone(types={'foo': 'list', 'one': 'str', 'two': 'str'}, - required=['one', 'two']) - assert c2.types == {'foo': 'list', 'one': 'str', 'two': 'str'} + c = ConfigHint( + types={"foo": "list", "one": "str"}, + positional=["one"], + required=["one"], + typename="barn", + doc="orig doc", + ) + c2 = c.clone( + types={"foo": "list", "one": "str", "two": "str"}, required=["one", "two"] + ) + assert c2.types == {"foo": "list", "one": "str", "two": "str"} assert c2.positional == c.positional - assert c2.required == ['one', 'two'] + assert c2.required == ["one", "two"] assert c2.typename == c.typename assert c2.allow_unknowns == c.allow_unknowns assert c2.doc == c.doc -class TestConfigSection: +class TestConfigSection: def test_section_ref_collapse(self): # Silly testcase just to make something drop off the --coverage radar. - ref = basics.LazySectionRef(None, 'ref:foon') + ref = basics.LazySectionRef(None, "ref:foon") pytest.raises(NotImplementedError, ref._collapse) pytest.raises(NotImplementedError, ref.collapse) pytest.raises(NotImplementedError, ref.instantiate) @@ -209,108 +224,123 @@ class TestConfigSection: section = basics.ConfigSection() pytest.raises(NotImplementedError, section.__contains__, 42) pytest.raises(NotImplementedError, section.keys) - pytest.raises(NotImplementedError, section.render_value, None, 'a', 'str') + pytest.raises(NotImplementedError, section.render_value, None, "a", "str") class TestDictConfigSection: - def test_misc(self): def convert(central, value, arg_type): return central, value, arg_type - section = basics.DictConfigSection(convert, {'list': [1, 2]}) - assert 'foo' not in section - assert 'list' in section - assert ['list'] == list(section.keys()) - assert (None, [1, 2], 'spoon') == section.render_value(None, 'list', 'spoon') + + section = basics.DictConfigSection(convert, {"list": [1, 2]}) + assert "foo" not in section + assert "list" in section + assert ["list"] == list(section.keys()) + assert (None, [1, 2], "spoon") == section.render_value(None, "list", "spoon") def test_failure(self): def fail(central, value, arg_type): - raise errors.ConfigurationError('fail') - section = basics.DictConfigSection(fail, {'list': [1, 2]}) + raise errors.ConfigurationError("fail") + + section = basics.DictConfigSection(fail, {"list": [1, 2]}) with pytest.raises(errors.ConfigurationError): - section.render_value(None, 'list', 'spoon') + section.render_value(None, "list", "spoon") class TestFakeIncrementalDictConfigSection: - @staticmethod def _convert(central, value, arg_type): return central, value, arg_type @staticmethod def _fail(central, value, arg_type): - raise errors.ConfigurationError('fail') + raise errors.ConfigurationError("fail") def test_misc(self): section = basics.FakeIncrementalDictConfigSection( - self._convert, {'list': [1, 2]}) - assert 'foo' not in section - assert 'list' in section - assert ['list'] == list(section.keys()) + self._convert, {"list": [1, 2]} + ) + assert "foo" not in section + assert "list" in section + assert ["list"] == list(section.keys()) with pytest.raises(errors.ConfigurationError): - obj = basics.FakeIncrementalDictConfigSection(self._fail, {'a': 'b'}) - obj.render_value(None, 'a', 'str') + obj = basics.FakeIncrementalDictConfigSection(self._fail, {"a": "b"}) + obj.render_value(None, "a", "str") def test_fake_incrementals(self): section = basics.FakeIncrementalDictConfigSection( - self._convert, {'seq.append': [1, 2]}) + self._convert, {"seq.append": [1, 2]} + ) manager = object() - assert [None, None, (manager, [1, 2], 'list')] == section.render_value(manager, 'seq', 'list') + assert [None, None, (manager, [1, 2], "list")] == section.render_value( + manager, "seq", "list" + ) + def _repr(central, value, arg_type): - return 'list', ['thing'] - section = basics.FakeIncrementalDictConfigSection( - _repr, {'foo': None}) - assert ('list', (None, ['thing'], None)) == section.render_value(manager, 'foo', 'repr') + return "list", ["thing"] + + section = basics.FakeIncrementalDictConfigSection(_repr, {"foo": None}) + assert ("list", (None, ["thing"], None)) == section.render_value( + manager, "foo", "repr" + ) with pytest.raises(errors.ConfigurationError): - obj = basics.FakeIncrementalDictConfigSection(self._fail, {'a.prepend': 'b'}) - obj.render_value(None, 'a', 'list') + obj = basics.FakeIncrementalDictConfigSection( + self._fail, {"a.prepend": "b"} + ) + obj.render_value(None, "a", "list") def test_repr(self): def asis(central, value, arg_type): - assert arg_type == 'repr', arg_type + assert arg_type == "repr", arg_type return value + source_dict = { - 'seq.append': ('list', [1, 2]), - 'simple': ('bool', True), - 'multistr': ('str', 'body'), - 'multistr.prepend': ('str', 'head'), - 'refs': ('str', 'lost'), - 'refs.append': ('ref', 'main'), - 'refs.prepend': ('refs', ['a', 'b']), - 'strlist': ('callable', asis), - 'strlist.prepend': ('str', 'whatever'), - 'wrong.prepend': ('wrong', 'wrong'), + "seq.append": ("list", [1, 2]), + "simple": ("bool", True), + "multistr": ("str", "body"), + "multistr.prepend": ("str", "head"), + "refs": ("str", "lost"), + "refs.append": ("ref", "main"), + "refs.prepend": ("refs", ["a", "b"]), + "strlist": ("callable", asis), + "strlist.prepend": ("str", "whatever"), + "wrong.prepend": ("wrong", "wrong"), } section = basics.FakeIncrementalDictConfigSection(asis, source_dict) manager = object() with pytest.raises(KeyError): - section.render_value(manager, 'spoon', 'repr') - assert ('list', [None, None, [1, 2]]) == section.render_value(manager, 'seq', 'repr') - assert ('bool', True) == section.render_value(manager, 'simple', 'repr') - assert ('str', ['head', 'body', None]) == section.render_value(manager, 'multistr', 'repr') - assert ('refs', [['a', 'b'], ['lost'], ['main']]) == section.render_value(manager, 'refs', 'repr') - assert ('list', [ - ['whatever'], - ['tests.config.test_basics.asis'], - None]) == section.render_value(manager, 'strlist', 'repr') + section.render_value(manager, "spoon", "repr") + assert ("list", [None, None, [1, 2]]) == section.render_value( + manager, "seq", "repr" + ) + assert ("bool", True) == section.render_value(manager, "simple", "repr") + assert ("str", ["head", "body", None]) == section.render_value( + manager, "multistr", "repr" + ) + assert ("refs", [["a", "b"], ["lost"], ["main"]]) == section.render_value( + manager, "refs", "repr" + ) + assert ( + "list", + [["whatever"], ["tests.config.test_basics.asis"], None], + ) == section.render_value(manager, "strlist", "repr") with pytest.raises(errors.ConfigurationError): - section.render_value(manager, 'wrong', 'repr') + section.render_value(manager, "wrong", "repr") class TestConvertString: - def test_render_value(self): source = { - 'str': 'tests', - 'bool': 'yes', - 'list': '0 1 2', - 'callable': 'tests.config.test_basics.passthrough', + "str": "tests", + "bool": "yes", + "list": "0 1 2", + "callable": "tests.config.test_basics.passthrough", } destination = { - 'str': 'tests', - 'bool': True, - 'list': ['0', '1', '2'], - 'callable': passthrough, + "str": "tests", + "bool": True, + "list": ["0", "1", "2"], + "callable": passthrough, } # valid gets @@ -319,49 +349,59 @@ class TestConvertString: # reprs for typename, value in source.items(): - assert ('str', value) == basics.convert_string(None, source[typename], 'repr') + assert ("str", value) == basics.convert_string( + None, source[typename], "repr" + ) # invalid gets # not callable with pytest.raises(errors.ConfigurationError): - basics.convert_string(None, source['str'], 'callable') + basics.convert_string(None, source["str"], "callable") # not importable with pytest.raises(errors.ConfigurationError): - basics.convert_string(None, source['bool'], 'callable') + basics.convert_string(None, source["bool"], "callable") # Bogus type. with pytest.raises(errors.ConfigurationError): - basics.convert_string(None, source['bool'], 'frob') + basics.convert_string(None, source["bool"], "frob") def test_section_ref(self): def spoon(): """Noop.""" - target_config = central.CollapsedConfig( - basics.ConfigType(spoon), {}, None) + + target_config = central.CollapsedConfig(basics.ConfigType(spoon), {}, None) + class TestCentral: def collapse_named_section(self, section): try: - return {'target': target_config}[section] + return {"target": target_config}[section] except KeyError: raise errors.ConfigurationError(section) - assert basics.convert_string(TestCentral(), 'target', 'ref:spoon').collapse() == target_config + + assert ( + basics.convert_string(TestCentral(), "target", "ref:spoon").collapse() + == target_config + ) with pytest.raises(errors.ConfigurationError): - basics.convert_string(TestCentral(), 'missing', 'ref:spoon').instantiate() + basics.convert_string(TestCentral(), "missing", "ref:spoon").instantiate() def test_section_refs(self): def spoon(): """Noop.""" - config1 = central.CollapsedConfig( - basics.ConfigType(spoon), {}, None) - config2 = central.CollapsedConfig( - basics.ConfigType(spoon), {}, None) + + config1 = central.CollapsedConfig(basics.ConfigType(spoon), {}, None) + config2 = central.CollapsedConfig(basics.ConfigType(spoon), {}, None) + class TestCentral: def collapse_named_section(self, section): try: - return {'1': config1, '2': config2}[section] + return {"1": config1, "2": config2}[section] except KeyError: raise errors.ConfigurationError(section) - assert [config1, config2] == list(ref.collapse() for ref in basics.convert_string( - TestCentral(), '1 2', 'refs:spoon')) - lazy_refs = basics.convert_string(TestCentral(), '2 3', 'refs:spoon') + + assert [config1, config2] == list( + ref.collapse() + for ref in basics.convert_string(TestCentral(), "1 2", "refs:spoon") + ) + lazy_refs = basics.convert_string(TestCentral(), "2 3", "refs:spoon") assert len(lazy_refs) == 2 with pytest.raises(errors.ConfigurationError): lazy_refs[1].collapse() @@ -370,10 +410,10 @@ class TestConvertString: class TestConvertAsIs: source = { - 'str': 'tests', - 'bool': True, - 'list': ['0', '1', '2'], - 'callable': passthrough, + "str": "tests", + "bool": True, + "list": ["0", "1", "2"], + "callable": passthrough, } def test_render_value(self): @@ -388,91 +428,89 @@ class TestConvertAsIs: def test_repr(self): for typename, value in self.source.items(): - assert (typename, value) == basics.convert_asis(None, value, 'repr') + assert (typename, value) == basics.convert_asis(None, value, "repr") with pytest.raises(errors.ConfigurationError): - basics.convert_asis(None, object(), 'repr') + basics.convert_asis(None, object(), "repr") def test_section_ref(self): ref = basics.HardCodedConfigSection({}) with pytest.raises(errors.ConfigurationError): - basics.convert_asis(None, 42, 'ref:spoon') - assert ref is basics.convert_asis(None, ref, 'ref:spoon').section - assert ('ref', ref) == basics.convert_asis(None, ref, 'repr') + basics.convert_asis(None, 42, "ref:spoon") + assert ref is basics.convert_asis(None, ref, "ref:spoon").section + assert ("ref", ref) == basics.convert_asis(None, ref, "repr") def test_section_refs(self): ref = basics.HardCodedConfigSection({}) with pytest.raises(errors.ConfigurationError): - basics.convert_asis(None, [1, 2], 'refs:spoon') - assert ref is basics.convert_asis(None, [ref], 'refs:spoon')[0].section - assert ('refs', [ref]) == basics.convert_asis(None, [ref], 'repr') + basics.convert_asis(None, [1, 2], "refs:spoon") + assert ref is basics.convert_asis(None, [ref], "refs:spoon")[0].section + assert ("refs", [ref]) == basics.convert_asis(None, [ref], "repr") def test_alias(): def spoon(): """Noop.""" + foon = central.CollapsedConfig(basics.ConfigType(spoon), {}, None) + class MockManager: def collapse_named_section(self, name): - if name == 'foon': + if name == "foon": return foon return object() + manager = MockManager() - alias = basics.section_alias('foon', 'spoon') - type_obj = basics.ConfigType(alias.render_value(manager, 'class', - 'callable')) - assert 'spoon' == type_obj.name - assert foon is alias.render_value(manager, 'target', 'ref:spoon').collapse() + alias = basics.section_alias("foon", "spoon") + type_obj = basics.ConfigType(alias.render_value(manager, "class", "callable")) + assert "spoon" == type_obj.name + assert foon is alias.render_value(manager, "target", "ref:spoon").collapse() class TestParsers: - def test_str_to_bool(self): # abuse assert is to make sure we get actual booleans, not some # weird object that happens to be True or False when converted # to a bool for string, output in [ - ('True', True), - ('yes', True), - ('1', True), - ('False', False), - ('no', False), - ('0', False), - ]: + ("True", True), + ("yes", True), + ("1", True), + ("False", False), + ("no", False), + ("0", False), + ]: assert basics.str_to_bool(string) is output def test_str_to_int(self): - for string, output in [ - ('\t 1', 1), - ('1', 1), - ('-100', -100)]: + for string, output in [("\t 1", 1), ("1", 1), ("-100", -100)]: assert basics.str_to_int(string) == output with pytest.raises(errors.ConfigurationError): - basics.str_to_int('f') + basics.str_to_int("f") def test_str_to_str(self): for string, output in [ - ('\t ', ''), - (' foo ', 'foo'), - (' " foo " ', ' foo '), + ("\t ", ""), + (" foo ", "foo"), + (' " foo " ', " foo "), ('\t"', '"'), - ('\nfoo\t\n bar\t', 'foo bar'), - ('"a"', 'a'), + ("\nfoo\t\n bar\t", "foo bar"), + ('"a"', "a"), ("'a'", "a"), ("'a", "'a"), ('"a', '"a'), - ]: + ]: assert basics.str_to_str(string) == output def test_str_to_list(self): for string, output in [ - ('foo', ['foo']), - ('"f\'oo" \'b"ar\'', ["f'oo", 'b"ar']), - ('', []), - (' ', []), + ("foo", ["foo"]), + ("\"f'oo\" 'b\"ar'", ["f'oo", 'b"ar']), + ("", []), + (" ", []), ('\\"hi ', ['"hi']), - ('\'"hi\'', ['"hi']), + ("'\"hi'", ['"hi']), ('"\\"hi"', ['"hi']), - ]: + ]: assert basics.str_to_list(string) == output for string in ['"', "'foo", 'ba"r', 'baz"']: with pytest.raises(errors.QuoteInterpretationError): @@ -480,13 +518,15 @@ class TestParsers: # make sure this explodes instead of returning something # confusing so we explode much later with pytest.raises(TypeError): - basics.str_to_list(['no', 'string']) + basics.str_to_list(["no", "string"]) def test_parse_config_file(tmp_path): - (fp := tmp_path / 'file').write_text('foon') + (fp := tmp_path / "file").write_text("foon") with pytest.raises(errors.ConfigurationError): - basics.parse_config_file('/spork', None) + basics.parse_config_file("/spork", None) + def parser(f): return f.read() - assert 'foon' == basics.parse_config_file(fp, parser) + + assert "foon" == basics.parse_config_file(fp, parser) |