def test_read_stacked_sources_with_strategies(): config = StackedConfig( DictSource({ 'a': 1, 'x': [5, 6], 'b': { 'c': 2, 'd': [3, 4] } }), DictSource({ 'a': 10, 'x': [50, 60], 'b': { 'c': 20, 'd': [30, 40] } }), strategy_map={ 'a': strategies.add, 'x': strategies.collect, # keep lists intact 'c': strategies.collect, # collect values into list 'd': strategies.merge, # merge lists }) assert config.a == 11 assert config.x == [[50, 60], [5, 6]] assert config.b.c == [20, 2] assert config.b.d == [30, 40, 3, 4]
def test_write_stacked_source(): source1 = DictSource({'a': 1, 'b': {'c': 2}}) source2 = DictSource({'x': 6, 'b': {'y': 7, 'd': {'e': 8}}}) config = StackedConfig(source1, source2) assert config.a == 1 assert config.b.c == 2 assert config.b.y == 7 config.a = 10 config['x'] = 60 config['b'].c = 20 config.b['y'] = 70 config.b['m'] = 'n' # add new key config.b.d.e = 80 assert config.a == 10 assert config.x == 60 assert config.b.c == 20 assert config.b.y == 70 assert config.b.m == 'n' assert config.b.d.e == 80 assert source1.a == 10 assert source1.b.c == 20 assert source2.x == 60 assert source2.b.y == 70 assert source2.b.m == 'n' assert source2.b.d.e == 80
def test_reverse_source_order(reverse, values): sources = [ DictSource({ 'a': 1, 'b': {} }), DictSource({ 'a': 10, 'b': { 'c': 20 } }), DictSource({ 'a': 100, 'b': { 'c': 200 } }), DictSource({ 'a': 1000, 'b': {} }), ] config = StackedConfig(*sources, reverse=reverse) assert config.a == values[0] assert config.b.c == values[1]
def test_read_stacked_sources(): config = StackedConfig(DictSource({ 'a': 1, 'b': { 'c': 2 } }), DictSource({ 'x': 6, 'b': { 'y': 7, 'd': { 'e': 8 } } })) assert config.a == 1 assert config.x == 6 assert config.b.c == 2 assert config.b.y == 7 assert config['a'] == 1 assert config['x'] == 6 assert config['b'].c == 2 assert config.b['y'] == 7 assert config.b.d.e == 8
def test_add_source_after_instantiation(): source1 = DictSource({'a': 1, 'b': {'c': 2}}) source2 = DictSource({'m': 10, 'b': {'o': 20}}) sources = SourceList(source1) sources.append(source2) assert list(sources) == [source2, source1]
def test_iterate_sources_with_keychain(): source1 = DictSource({'a': 1, 'b': {'c': 2}}) source2 = DictSource({'m': 10, 'b': {'o': 20}}) subsource1 = DictSource({'c': 2}) subsource2 = DictSource({'o': 20}) sources = SourceList(source1, source2, keychain=['b']) assert list(sources) == [subsource2, subsource1]
def test_iterate_sources(): source1 = DictSource({'a': 1, 'b': {'c': 2}}) source2 = DictSource({'m': 10, 'b': {'o': 20}}) sources = SourceList(source1, source2) # sources will be returned reversed to start with the highest # priority assert list(sources) == [source2, source1]
def test_change_source_after_instantiation(): source1 = DictSource({'a': 1, 'b': {'c': 2}}) source2 = DictSource({'m': 10, 'b': {'o': 20}}) updated = DictSource({'x': 100, 'b': {'y': 200}}) sources = SourceList(source1, source2) sources[0] = updated assert list(sources) == [source2, updated]
def test_read_stacked_sources_with_joining_strategy(): source1 = DictSource({'path': '/path/to/default/file'}) source2 = DictSource({'path': '/path/to/user/file'}) config = StackedConfig( source1, source2, strategy_map={'path': strategies.make_join(separator=':')}) assert config.path == '/path/to/user/file:/path/to/default/file'
def test_read_complex_stacked_sources(monkeypatch): monkeypatch.setenv('MVP1_A', '1000') monkeypatch.setenv('MVP2_B_M_E', '4000') config = StackedConfig( Environment('MVP1_'), # untyped shadowing DictSource({ 'a': 1, 'b': { 'c': 2, 'e': 400 } }), DictSource({ 'x': 6, 'b': { 'y': 7, 'd': { 'e': 8 } } }), DictSource({ 'a': 100, 'b': { 'm': { 'e': 800 } } }), # shadowing DictSource({ 'x': 'x', 'b': { 'y': 0.7, 'd': 800 } }), # type changing Environment('MVP2_'), # untyped shadowing ) assert config.a == 100 assert config.x == 'x' # changed int to str assert config.b.c == 2 assert config.b.y == 0.7 # changed int to float assert config.b.d == 800 # changed subsection (dict) to single value assert config.b.e == 400 # 'e' should not be shadowed by other 'e' assert config.b.m.e == 4000 # shadowed by untyped but casted to type with pytest.raises(AttributeError) as exc_info: config.b.x # config.b.d.e overrides a dict with a value with pytest.raises(AttributeError) as exc_info: config.b.d.e assert "no attribute 'e'" in str(exc_info.value)
def test_read_stacked_sources_with_strategies_for_none_values(): config = StackedConfig(DictSource({'a': None}), DictSource({'a': None}), strategy_map={ 'a': strategies.collect, }) result = [None, None] assert config.a == result assert list(config.items()) == [('a', result)]
def test_write_to_empty_sources(): source1 = DictSource(auto_subsection=True) source2 = DictSource() config = StackedConfig(source2, source1) config.a = 10 config['b'].c = 20 assert source1.a == 10 assert source1.b.c == 20 assert source2.dump() == {}
def test_remove_source_after_instantiation(): source1 = DictSource({'a': 1, 'b': {'c': 2}}) source2 = DictSource({'m': 10, 'b': {'o': 20}}) sources = SourceList(source1, source2) sources.remove(source1) assert list(sources) == [source2] del sources[0] assert list(sources) == []
def test_stacked_setdefault(): source1 = DictSource({'a': 1, 'b': {'c': 2}}) source2 = DictSource({'x': 6, 'b': {'y': 7}}) config = StackedConfig(source1, source2) assert config.setdefault('a', 10) == 1 assert config.setdefault('nonexisting', 10) == 10 assert config.nonexisting == 10 assert 'nonexisting' in source2 assert config.b.setdefault('nonexisting', 20) == 20 assert config.b.nonexisting == 20 assert 'nonexisting' in source2.b
def test_stacked_dump(): config = StackedConfig(DictSource({ 'a': 1, 'b': { 'c': 2 } }), DictSource({'a': '10'}), DictSource({ 'x': 6, 'b': { 'y': 7 } })) assert config.dump() == {'a': '10', 'b': {'c': 2, 'y': 7}, 'x': 6}
def test_source_get(): config = DictSource({'a': 1}) assert config.get('a') == 1 assert config.get('nonexisting') is None assert config.get('nonexisting', 'default') == 'default' assert 'nonexisting' not in config
def test_expose_sources_for_manipulation(): source1 = DictSource({'a': 1, 'b': {'c': 2}}) source2 = DictSource({'a': 10, 'b': {'c': 20}}) source3 = DictSource({'x': 6, 'b': {'y': 7}}) config = StackedConfig() assert config.dump() == {} config.source_list.append(source1) assert config == source1 config.source_list.append(source2) assert config == source2 config.source_list.insert(0, source3) assert config.dump() == {'a': 10, 'b': {'c': 20, 'y': 7}, 'x': 6}
def test_set_missing_key_to_default_value(): config = DictSource(auto_subsection=True) config.a.b = 1 config['x'].y = 2 assert config.a.b == 1 assert config.x.y == 2
def test_write_stacked_source_fails(key, message): source1 = DictSource({'a': 1, 'b': {'c': 2}}, readonly=True) config = StackedConfig(source1) with pytest.raises(TypeError) as exc_info: config[key] = 10 assert message in str(exc_info.value)
def test_stacked_len(): config = StackedConfig(DictSource({ 'a': 1, 'b': { 'c': 2 } }), DictSource({ 'x': 6, 'b': { 'y': 7, 'd': { 'e': 8 } } })) assert len(config) == 3
def test_source_setdefault_as_subsection(): config = DictSource() with pytest.raises(AttributeError): config.a.b = 1 config.setdefault('a', {}).b = 1 assert config.a.b == 1
def test_source_values(): config = StackedConfig(DictSource({ 'a': 1, 'b': { 'c': 2 } }), DictSource({ 'x': 6, 'b': { 'y': 7, 'd': { 'e': 8 } } })) values = list(config.b.values()) assert values == [2, config.b.d, 7]
def test_source_keys(): config = StackedConfig(DictSource({ 'a': 1, 'b': { 'c': 2 } }), DictSource({ 'x': 6, 'b': { 'y': 7, 'd': { 'e': 8 } } })) keys = list(config.b.keys()) assert keys == ['c', 'd', 'y']
def test_source_values_with_converters(): data = {'a': {'b': 1}} converter_list = [ ('b', lambda v: 2 * v, lambda v: v / 2), ] config = DictSource(data, converters=converter_list) items = [i for i in config.a.values()] assert items == [2]
def test_prevent_changes_to_source_of_subconfig(): source1 = DictSource({'a': 1, 'b': {'c': 2}}) source2 = DictSource({'m': 10, 'b': {'o': 20}}) updated = DictSource({'x': 100, 'b': {'y': 200}}) sources = SourceList(source1, source2, keychain=['b']) with pytest.raises(TypeError) as exc_info: sources.append(updated) assert 'cannot be mutated' in str(exc_info.value) with pytest.raises(TypeError) as exc_info: sources.insert(0, updated) assert 'cannot be mutated' in str(exc_info.value) with pytest.raises(TypeError) as exc_info: sources[0] = updated assert 'cannot be mutated' in str(exc_info.value)
def test_set_keychain(): config = StackedConfig(DictSource({'a': { 'b': { 'c': 2 } }}), keychain=('a', 'b')) assert config.dump() == {'c': 2}
def test_stacked_config_with_untyped_source(inimaker): typed_source1 = {'x': 5, 'b': {'y': 6}} typed_source2 = {'a': 1, 'b': {'c': 2}} untyped_source1 = inimaker(u""" [__root__] a=11 """) untyped_source2 = inimaker(u""" [__root__] a=10 x=50 [b] c=20 y=60 [b.d] e=30 """) typed1 = DictSource(typed_source1) typed2 = DictSource(typed_source2) untyped1 = INIFile(untyped_source1) untyped2 = INIFile(untyped_source2, subsection_token='.') config = StackedConfig(typed1, typed2, untyped1, untyped2) assert typed1.x == 5 assert typed1.b.y == 6 assert typed2.a == 1 assert typed2.b.c == 2 with pytest.raises(AttributeError): typed2.b.d.e assert untyped1.a == '11' assert untyped2.a == '10' assert untyped2.b.c == '20' assert untyped2.b.d.e == '30' assert config.a == 10 # found in first typed source assert config.x == 50 # found in second typed source assert config.b.c == 20 assert config.b.y == 60 assert config.b.d.e == '30'
def test_builtin_converters(converter, value, customized, reset): data = {'a': value} config = DictSource(data, converters=[converter]) assert config.a == customized del config.a config.a = customized assert config._data['a'] == reset
def test_source_items(monkeypatch): monkeypatch.setenv('MVP_A', '10') config = StackedConfig(DictSource({ 'a': 1, 'b': { 'c': 2 } }), Environment('MVP'), DictSource({ 'x': 6, 'b': { 'y': 7 } })) items = list(config.items()) assert items == [('a', 10), ('b', config.b), ('x', 6)] items = list(config.b.items()) assert items == [('c', 2), ('y', 7)]
def test_source_items_prevent_shadowing_between_subsections_and_values( reverse): sources = [ DictSource({ 'a': 1, 'b': { 'c': 2 } }), DictSource({ 'x': 6, 'b': 5 }), ] config = StackedConfig(*sources, reverse=reverse) with pytest.raises(ValueError) as exc_info: list(config.items()) assert "conflicts" in str(exc_info.value)