def test_list_to_dict(self): config1 = Config({'x': 1}) config2 = Config([1, 2]) with ShouldRaise(type_error( "Cannot merge <class 'list'> with <class 'dict'>" )): config1.merge(config2)
def test_type_returns_new_object(self): config1 = Config((1, 2)) config2 = Config((3, 4)) def concat(context, source, target): return target + source config1.merge(config2, mergers={tuple: concat}) compare(config1.data, expected=(1, 2, 3, 4))
def test_supplement_type_mapping(self): config1 = Config({'x': (1, 2)}) config2 = Config({'x': (3, 4)}) def concat(context, source, target): return target + source config1.merge(config2, mergers=default_mergers+{tuple: concat}) compare(config1.data, expected={'x': (1, 2, 3, 4)})
def test_override_type_mapping(self): config1 = Config([1, 2]) config2 = Config([3, 4]) def zipper(context, source, target): return zip(target, source) config1.merge(config2, mergers={list: zipper}) compare(config1.data, expected=[(1, 3), (2, 4)])
def test_top_level_list(self): config1 = Config([1, 2]) config2 = Config([3, 4]) config3 = config1 + config2 compare(config1.data, [1, 2]) compare(config2.data, [3, 4]) compare(config3.data, [1, 2, 3, 4])
def test_nested_working(self): config1 = Config(dict(x=1, y=[2, 3], z=dict(a=4, b=5))) config2 = Config(dict(w=6, y=[7], z=dict(b=8, c=9))) config1.merge(config2) compare(config1.data, expected=dict(x=1, w=6, y=[2, 3, 7], z=dict(a=4, b=8, c=9)))
def test_blank_type_mapping(self): config1 = Config({'foo': 'bar'}) config2 = Config({'baz': 'bob'}) with ShouldRaise(type_error( "Cannot merge <class 'dict'> with <class 'dict'>" )): config2.merge(config1, mergers={})
def test_top_level_dict(self): config1 = Config({'foo': 'bar'}) config2 = Config({'baz': 'bob'}) config3 = config1 + config2 compare(config1.data, {'foo': 'bar'}) compare(config2.data, {'baz': 'bob'}) compare(config3.data, {'foo': 'bar', 'baz': 'bob'})
def test_other_to_dict(self): config1 = Config(1) config2 = Config(1) with ShouldRaise(type_error( "Cannot merge <class 'int'> with <class 'int'>" )): config1.merge(config2)
def test_context_manager_push(self): config = Config({'x': 1, 'y': 2}) compare(config.x, expected=1) compare(config.y, expected=2) with config.push(Config({'x': 3})): compare(config.x, expected=3) compare(config.y, expected=2) compare(config.x, expected=1) compare(config.y, expected=2)
def test_push_pop(self): config = Config({'x': 1, 'y': 2}) compare(config.x, expected=1) compare(config.y, expected=2) config.push(Config({'x': 3})) compare(config.x, expected=3) compare(config.y, expected=2) config.pop() compare(config.x, expected=1) compare(config.y, expected=2)
def test_push_empty(self): config = Config({'x': 1, 'y': 2}) compare(config.x, expected=1) compare(config.y, expected=2) config.push(Config({'x': 3}), empty=True) compare(config.x, expected=3) with ShouldRaise(AttributeError('y')): config.y config.pop() compare(config.x, expected=1) compare(config.y, expected=2)
def test_mapping_type_conversion(self): config = Config({'x': 0}) data = {'y': '1'} config.merge(data, mapping={ convert(source['y'], int): target['x'] }) compare(config.data, expected={'x': 1})
def test_mapping_dotted_strings(self): config = Config({'a': {'b': 'old'}}) data = {'c': {'d': 'new'}} config.merge(data, mapping={ 'c.d': 'a.b' }) compare(config.data, expected={'a': {'b': 'new'}})
def test_mapping_with_top_level_merge(self): config = Config({'x': {'y': 1}}) data = {'z': 2} config.merge(data, mapping={ source: target.merge() }) compare(config.data, expected={'x': {'y': 1}, 'z': 2})
def load_db(use=None, config=None): if not config: # Can't test this line, because it reads the real configuration files # on the tester's machine, and so cannot be made to produce consistent # results. config = load_config() # pragma: no cover if not use: try: use = config['use'] except KeyError as err: raise LoadError("no database specified.") from None try: config_use = config['database'][use] except KeyError as err: raise LoadError(f"unknown database {use!r}") from None try: type_use = config_use['type'] except KeyError as err: raise LoadError(f"no 'type' specified for database {use!r}") from None try: plugin = DB_PLUGINS[type_use].load() except KeyError as err: raise LoadError(f"no {err} database plugin found") from None if defaults := getattr(plugin, 'default_config'): config_use = (Config(defaults) + config_use).data
def test_mapping_strings(self): config = Config({'x': 'old'}) data = {'foo': 'bar'} config.merge(data, mapping={ 'foo': 'x' }) compare(config.data, expected={'x': 'bar'})
def test_mapping_paths(self): config = Config({'x': 'old'}) data = {'foo': 'bar'} config.merge(data, mapping={ source['foo']: target['x'] }) compare(config.data, expected={'x': 'bar'})
def test_mapping_extensive_conversation(self): config = Config({'a': 0}) data = {'x': 2, 'y': -1} def best(possible): return max(possible.values()) config.merge(data, mapping={ convert(source, best): target['a'] }) compare(config.data, expected={'a': 2})
def test_defaults_and_env(self): config = Config({'present': 'dp', 'absent': 'da'}) environ = {'ENV_PRESENT': '1'} config.merge( environ, { convert('ENV_PRESENT', int): 'present', convert('ENV_ABSENT', int): 'absent', }) compare(config.present, expected=1) compare(config.absent, expected='da')
def test_layered(self, dir): # defaults config = Config({'database': {'user': '******'}, 'special': False}) # from system file: path = dir.write('etc/myapp.json', '{"special": true}') config.merge(Config.from_path(path)) # from user file: path = dir.write('home/user/myapp.json', '{"database": {"password": "******"}}') config.merge(Config.from_path(path)) # end result: compare(config.database.user, expected='foo') compare(config.database.password, expected='123') compare(config.special, expected=True)
def load_config(): config = Config() cwd = Path.cwd() dirs = [cwd, *cwd.parents] for dir in reversed(dirs): paths = [ dir / '.config' / 'freezerbox' / 'conf.toml', dir / '.freezerboxrc', ] for path in paths: if path.exists(): subconf = Config.from_path(path, parser='toml') config.merge(subconf) dir = dir.parent return config.data
def test_context_manager_push_pathological(self): config = Config({'x': 1, 'y': 2}) compare(config.x, expected=1) compare(config.y, expected=2) with config.push(): config.data['a'] = 5 config.push({'x': 3}) config.push({'z': 4}) compare(config.a, expected=5) compare(config.x, expected=3) compare(config.y, expected=2) compare(config.z, expected=4) compare(config.x, expected=1) compare(config.y, expected=2) with ShouldRaise(AttributeError('a')): config.a with ShouldRaise(AttributeError('z')): config.z
def test_defaults_and_argparse(self): parser = ArgumentParser() parser.add_argument('--first-url') parser.add_argument('--attempts', type=int, default=2) parser.add_argument('--bool-a', action='store_true') parser.add_argument('--bool-b', action='store_false') args = parser.parse_args(['--first-url', 'override_url', '--bool-a']) compare(args.bool_b, expected=True) config = Config({'urls': ['default_url'], 'bool_b': False}) config.merge( args, { 'first_url': target['urls'].insert(0), 'attempts': target['attempts'], 'bool_a': target['bool_a'], 'bool_b': target['bool_b'], }) compare(config.urls, expected=['override_url', 'default_url']) compare(config.attempts, expected=2) compare(config.bool_a, expected=True) compare(config.bool_b, expected=True)
config = Config( { "user": { "name": "Spandan", "affiliation": "NISER", "style": "double", }, "reconfig": False, "compiler": "none", "script": { "location": cdir, "locations": { "~/.reportinator/scripts", "~/.config/reportinator/scripts", "~/AppData/Local/Programs/reportinator/scripts", }, }, "layout": { "location": cdir, "locations": { "~/.reportinator/layouts", "~/.config/reportinator/layouts", "~/AppData/Local/Programs/reportinator/layouts", }, }, "locations": { "/etc/reportinator.yaml", "~/.reportinator/config.yaml", "~/.config/reportinator/config.yaml", "~/AppData/Local/Programs/reportinator/config.yaml", }, "location": "", } )
def test_failure(self): with ShouldRaise(type_error( "Cannot merge <class 'int'> with <class 'dict'>" )): Config({'foo': 'bar'}) + 1
def test_non_config_rhs(self): config = Config({'foo': 'bar'}) + {'baz': 'bob'} compare(config.data, {'foo': 'bar', 'baz': 'bob'})
def test_list_to_list(self): config = Config([1, 2]) config.merge([3, 4]) compare(config.data, expected=[1, 2, 3, 4])
def test_list(self): config = Config([1, 2]) compare(config[0], expected=1) compare(config[1], expected=2) compare(list(config), expected=[1, 2])
def test_int(self): # not very useful... config = Config(1) compare(config.data, expected=1)