def test_change_is_a_merge_of_all_changes_in_context(): config = Config({ 'a': 1, }) config.a.set('2') with config.changeset_context() as ctx: config.a.set(3) config.a.set(4) config.a.set('5') assert ctx.changes[config.a].old_value == 2 assert ctx.changes[config.a].old_raw_str_value == '2' assert ctx.changes[config.a].new_value == 5 assert ctx.changes[config.a].new_raw_str_value == '5' config.a.set(6) assert ctx.changes[config.a].old_value == 2 assert ctx.changes[config.a].old_raw_str_value == '2' assert ctx.changes[config.a].new_value == 6 assert ctx.changes[config.a].new_raw_str_value is not_set ctx.reset() assert config.a not in ctx.changes
def test_can_have_a_dict_as_a_config_value_if_wrapped_inside_item(): # You may want to have a dictionary as a config value if you only # change it all together or you only pass it all in one piece. config = Config({ 'db': { 'user': '******', 'password': '******', }, 'aws': Item(default={ 'access_key': '123', 'secret_key': 'secret', }) }) assert isinstance(config.aws, Item) assert config.aws.name == 'aws' with pytest.raises(AttributeError): assert config.aws.access_key.value == '123' assert config.aws.value['access_key'] == '123' # This should have no effect because it is working on a copy of the default # value, not the real thing. config.aws.value['secret_key'] = 'NEW_SECRET' assert config.dump_values()['aws'] == { 'access_key': '123', 'secret_key': 'secret' }
def test_config_of_configs(): uploads = Config({ 'threads': 1, 'enabled': True, 'api': { 'port': 8000, } }) downloads = Config({ 'tmp_dir': '/tmp', 'db': { 'user': '******', } }) config = Config({ 'uploads': uploads, 'downloads': downloads, 'messages': { 'greeting': 'Hello', } }) assert config.uploads.threads.is_item assert config.uploads.api.port.is_item assert config.downloads.db.user.is_item assert config.messages.greeting.is_item
def cx(): return Config([ ('with_', Item(name='with')), ('for', Config([ ('continue_', Item(name='continue')), ])), ])
def test_dict_with_all_keys_prefixed_with_at_symbol_is_treated_as_item_meta(): config = Config({ 'enabled': { '@default': False, }, 'db': { 'user': '******', 'name': { '@help': 'Name of the database' }, '@help': 'db configuration', # This should be ignored for now }, }) paths = set(path for path, _ in config.iter_items(recursive=True, key='str_path')) assert 'enabled' in paths assert 'enabled.@default' not in paths assert 'db' not in paths assert 'db.@help' not in paths assert 'db.user' in paths assert 'db.name' in paths assert 'db.name.@help' not in paths assert config.enabled.default is False assert config.db.name.help == 'Name of the database' # Make sure it still behaves like a bool config.enabled.value = 'yes' assert config.enabled.value is True # Name is excluded because it has no value assert config.db.dump_values() == {'user': '******'} config.db.name.value = 'testdb' assert config.db.dump_values() == {'user': '******', 'name': 'testdb'}
def test_resets_single_item_changes(): config = Config({'a': 'aaa', 'b': 'bbb', 'c': 'ccc'}) config.c.value = 'CCC' assert config.a.raw_str_value is not_set assert config.c.raw_str_value == 'CCC' with config.changeset_context() as ctx: config.a.value = 'AAA' config.b.value = 'BBB' config.c.value = 'seeseesee' assert config.c.raw_str_value == 'seeseesee' assert len(ctx.values) == 3 ctx.reset(config.a) assert len(ctx.values) == 2 assert ctx.values[config.b] == 'BBB' assert ctx.values[config.c] == 'seeseesee' assert config.a.value == 'aaa' assert config.b.value == 'BBB' assert config.c.value == 'seeseesee' ctx.reset(config.c) assert config.a.value == 'aaa' assert config.b.value == 'BBB' assert config.c.value == 'CCC' assert config.a.raw_str_value is not_set assert config.c.raw_str_value == 'CCC'
def test_config_of_configs(): config = Config({ 'uploads': Config({ 'a': 1, 'b': True, 'c': 'ccc', }), 'downloads': Config({ 'd': { 'e': 'eee', }, 'f': 'fff', }), }) with config.changeset_context() as ctx: config.uploads.a.value = 2 config.downloads.d.e.value = 'EEE' config.downloads.f.value = 'FFF' assert len(ctx.values) == 3 assert ctx.values[config.uploads.a] == 2 assert ctx.values[config.downloads.d.e] == 'EEE' assert ctx.values[config.downloads.f] == 'FFF' assert not config.is_default assert config.uploads.a.value == 2 assert config.downloads.d.e.value == 'EEE' assert config.downloads.f.value == 'FFF' ctx.reset() assert config.is_default
def test_read_reads_multiple_files_in_order(tmpdir): m = Config({ 'a': { 'x': Item(type=float), 'y': 'aye', }, 'b': { 'm': False, 'n': Item(type=int), } }) path1 = tmpdir.join('config1.ini').strpath path2 = tmpdir.join('config2.ini').strpath path3 = tmpdir.join('config3.ini').strpath # Empty file m.configparser.dump(path1) # Can load from one empty file m.configparser.load(path1) assert m.is_default m.a.x.value = 0.33 m.b.n.value = 42 m.configparser.dump(path2) # Can load from one non-empty file m.reset() m.configparser.load(path2) assert not m.is_default assert m.a.x.value == 0.33 m.reset() m.a.x.value = 0.66 m.b.m.value = 'YES' m.configparser.dump(path3) m.reset() m.configparser.load([path1, path2, path3]) assert m.a.x.value == 0.66 assert m.a.y.is_default assert m.b.m.value is True assert m.b.m.raw_str_value == 'YES' assert m.b.n.value == 42 m.reset() m.configparser.load([path3, path2, path1]) assert m.a.x.value == 0.33 # this is the only difference with the above order assert m.a.y.is_default assert m.b.m.value is True assert m.b.m.raw_str_value == 'YES' assert m.b.n.value == 42 # Make sure multiple paths not supported in non-list syntax. with pytest.raises(TypeError): m.configparser.load(path3, path2, path1)
def test_section_knows_its_alias(): config = Config() config.uploads = Config({'enabled': True}) assert config.uploads.alias == 'uploads' config.uploads.db = Config({'connection': {'user': '******'}}) assert config.uploads.db.alias == 'db' assert config.uploads.db.connection.alias == 'connection'
def mixed_app_config(raw_logging_config, raw_db_config): """ A config that contains a previously existing config as well as one generated on initialisation. """ return Config({ 'logging': Config(raw_logging_config), 'db': raw_db_config, })
def initialize_db_process(config_values): from aleph.web import app from configmanager import Config from aleph.config import get_defaults config = Config(schema=get_defaults()) app['config'] = config config.load_values(config_values) filestore.init_store(config)
def test_item_value_changed_hook_not_called_when_resetting_a_not_set(): config = Config({'a': Item()}) @config.hooks.item_value_changed def item_value_changed(item, old_value, new_value): raise AssertionError('This should not have been called') config.reset() config.a.value = not_set
def test_tracking_with_no_default_and_no_custom_value(): config = Config({'a': {'@type': 'int'}}) with config.changeset_context() as ctx: config.a.value = 55 assert ctx.values[config.a] == 55 assert len(ctx.values) == 1 ctx.reset() assert len(ctx.values) == 0
def test_item_name_and_alias_must_be_a_string(): config = Config() with pytest.raises(TypeError): config.x = Item(name=5) with pytest.raises(TypeError): config[5] = Item() with pytest.raises(TypeError): config[5] = Item(name='x')
def config(): return Config(schema={ 'uploads': { 'enabled': False, 'threads': 1, 'db': Config({ 'user': '******', 'password': '******', }), }, 'greeting': 'Hello', })
def test_can_use__setitem__to_create_new_deep_paths(): config = Config() config['uploads'] = Config({'enabled': True}) with pytest.raises(TypeError): config['uploads', 'threads'] = 5 config['uploads', 'threads'] = Item(value=5) assert config.uploads.threads.type == Types.int config['uploads', 'db'] = Config({'user': '******'}) assert config.uploads.db
def test_hooks_work_across_nested_configs(): config = Config({ 'a': Config({ 'aa': Config({ 'aaa': 'aaa-default', }), 'ab': { 'aba': 'aba-default', }, 'ac': 'ac-default', }), 'b': { 'ba': Config({ 'baa': 'baa-default', }), 'bb': { 'bba': 'bba-default', }, 'bc': 'bc-default', }, 'c': 'c-default', }) calls = [] @config.hooks.item_value_changed def item_value_changed(item): calls.append(('root', '.'.join(item.get_path()))) assert len(calls) == 0 config.c.value = 'c-1' assert len(calls) == 1 config.a.ac.value = 'ac-1' assert len(calls) == 2 config.a.aa.aaa.value = 'aaa-1' assert len(calls) == 3 config.a.ab.aba.value = 'aba-1' assert len(calls) == 4 config.b.bc.value = 'bc-1' assert len(calls) == 5 config.b.ba.baa.value = 'baa-1' assert len(calls) == 6 config.b.bb.bba.value = 'bba-1' assert len(calls) == 7
def test_item_reset_is_tracked(): config = Config({'a': 'aaa'}) config.a.value = 'bbb' with config.changeset_context() as ctx: config.a.value = 'ccc' assert len(ctx.values) == 1 config.reset() assert len(ctx.values) == 1 assert ctx.values[config.a] is not_set
def test_json_reads_and_writes_strings(): c = Config({'greeting': 'Hello'}) assert c.json.dumps() == '{}' assert c.json.dumps(with_defaults=True) == '{\n "greeting": "Hello"\n}' c.json.loads('{"something_nonexistent": 1}') assert c.dump_values() == {'greeting': 'Hello'} c.json.loads('{"something_nonexistent": 1}', as_defaults=True) assert c.dump_values() == {'greeting': 'Hello', 'something_nonexistent': 1} c.json.loads('{"greeting": "Hello, world!"}') assert c.dump_values() == {'greeting': 'Hello, world!', 'something_nonexistent': 1}
def test_reset_resets_values_to_defaults(): config = Config({'x': Item(type=int), 'y': Item(default='YES')}) config.x.value = 23 config.y.value = 'NO' assert config.x.value == 23 assert config.y.value == 'NO' config.reset() assert config.x.is_default assert config.y.value == 'YES'
def c4(): return Config([ ('greeting', 'Hello!'), ('uploads', Config([ ('enabled', True), ('db', Config([('host', 'localhost'), ('user', 'root')])), ])), ('downloads', Config([ ('enabled', True), ('threads', 5), ])), ])
def load_config(self, airgapped=False): print("Loading Config") if exists(self.config_file): self.config = Config( schema=CONFIG_SCHEMA, load_sources=[self.config_file], auto_load=True, ) self.config.json.load(self.config_file) elif airgapped: self.get_config(CONFIG_SCHEMA) else: self.login() return self.config
def test_not_found_raised_by_iterators_on_first_not_found_name_in_path(): config = Config({'uploads': {'db': {'user': '******'}}}) with pytest.raises(NotFound) as exc1: list(config.iter_all(recursive=True, path=('downloads', ))) assert exc1.value.name == 'downloads' with pytest.raises(NotFound) as exc2: _ = config['uploads', 'enabled'] assert exc2.value.name == 'enabled' with pytest.raises(NotFound) as exc3: _ = config['uploads', 'something', 'deep'] assert exc3.value.name == 'something'
def test_to_dict_should_not_include_items_with_no_usable_value(): config = Config() assert config.dump_values() == {} config.a = Item() config.b = Item() config.dummies = Config({'x': Item(), 'y': Item()}) assert config.dump_values() == {} config.dummies.x.value = 'yes' assert config.dump_values() == {'dummies': {'x': 'yes'}} config.b.value = 'no' assert config.dump_values() == {'dummies': {'x': 'yes'}, 'b': 'no'}
def test_accepts_configmanager_settings_which_are_passed_to_all_subsections(): configmanager_settings = { 'message': 'everyone should know this', } config1 = Config(configmanager_settings=configmanager_settings) assert config1.settings.message == 'everyone should know this' config2 = Config({'greeting': 'Hello'}, configmanager_settings=configmanager_settings) assert config2.settings.message == 'everyone should know this' config3 = Config({'db': {'user': '******'}}, configmanager_settings=configmanager_settings) assert config3.settings.message == 'everyone should know this' assert config3.db.settings.message == 'everyone should know this' assert config3.db.settings is config3.settings
def test_hooks_arent_handled_if_hooks_enabled_setting_is_set_to_falsey_value(): config = Config({'uploads': {'db': {'user': '******'}}}) calls = [] @config.hooks.item_value_changed def item_value_changed(**kwargs): calls.append(1) config.uploads.db.user.value = 'admin1' assert len(calls) == 1 config.uploads.db.user.value = 'admin2' assert len(calls) == 2 config.settings.hooks_enabled = False config.uploads.db.user.value = 'admin3' assert len(calls) == 2 config.settings.hooks_enabled = None config.uploads.db.user.value = 'admin4' assert len(calls) == 2 config.settings.hooks_enabled = True config.uploads.db.user.value = 'admin5' assert len(calls) == 3
def test_read_as_defaults_treats_all_values_as_schemas(tmpdir): path = tmpdir.join('conf.ini').strpath with open(path, 'w') as f: f.write('[uploads]\nthreads = 5\nenabled = no\n') f.write('[messages]\ngreeting = Hello, home!\n') m = Config() m.configparser.load(path, as_defaults=True) assert m.uploads assert m.uploads.threads.value == '5' assert m.uploads.enabled.value == 'no' with pytest.raises(NotFound): assert m.uploads.something_else assert m.messages.greeting.value == 'Hello, home!' # Reading again with as_defaults=True should not change the values, only the defaults m.uploads.threads.value = '55' m.configparser.load(path, as_defaults=True) assert m.uploads.threads.value == '55' assert m.uploads.threads.default == '5' # But reading with as_defaults=False should change the value m.configparser.load(path) assert m.uploads.threads.value == '5' assert m.uploads.threads.default == '5'
def test_adding_new_attributes_to_item(): # This demonstrates two ways of adding new attributes: # 1) by declaring ItemAttribute, 2) by creating them in initialiser class CustomItem(Item): help = ItemAttribute(name='help', default='No help available!') def __init__(self, *args, **kwargs): description = kwargs.pop('description', None) super(CustomItem, self).__init__(*args, **kwargs) self.description = description config = Config({ 'a': { 'b': 1, 'c': True, }, 'd': 'efg', }, item_factory=CustomItem) assert config.a.b.help assert config.a.b.description is None assert config.d.help assert config.d.description is None
def test_configparser_integration(tmpdir): defaults_ini = tmpdir.join('defaults.ini') defaults_ini.write('') defaults_ini_path = defaults_ini.strpath custom_ini = tmpdir.join('custom.ini') custom_ini.write('') custom_ini_path = custom_ini.strpath # Config sections expose ConfigParser adapter as configparser property: config = Config() # assuming that defaults.ini exists, this would initialise Config # with all values mentioned in defaults.ini set as defaults. # Just like with ConfigParser, this won't fail if the file does not exist. config.configparser.load(defaults_ini_path, as_defaults=True) # if you have already declared defaults, you can load custom # configuration without specifying as_defaults=True: config.configparser.load(custom_ini_path) # when you are done setting config values, you can write them to a file. config.configparser.dump(custom_ini_path) # Note that default values won't be written unless you explicitly request it # by passing with_defaults=True config.configparser.dump(custom_ini_path, with_defaults=True)
def test_load_sources_setting(config, tmpdir): json1 = tmpdir.join('config1.json').strpath json2 = tmpdir.join('config2.json').strpath json3 = tmpdir.join('config3.json').strpath config.uploads.db.settings.load_sources.append(json1) config.settings.load_sources.append(json2) # None of the files exist, shouldn't fail config.load() assert config.dump_values(with_defaults=False) == {} with open(json1, 'w') as f: json.dump({'user': '******', 'password': '******'}, f) config.load() assert config.dump_values(with_defaults=False) == { 'uploads': {'db': {'user': '******', 'password': '******'}} } assert config.uploads.enabled.value is False assert config.uploads.db.user.value == 'Administrator' with open(json2, 'w') as f: json.dump({'uploads': {'db': {'user': '******'}, 'enabled': True}}, f) config.load() assert config.uploads.enabled.value is True assert config.uploads.db.user.value == 'admin' # should override "Administrator" assert config.uploads.db.password.value == 'SECRET' with open(json3, 'w') as f: json.dump({'main': {'greeting': 'Hey!'}}, f) wrapper = Config({ 'main': config }) wrapper.settings.load_sources.append(json3) wrapper.load() assert wrapper.main.uploads.db.user.value == 'admin' assert wrapper.main.uploads.db.password.value == 'SECRET' assert wrapper.main.greeting.value == 'Hey!'