コード例 #1
0
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
コード例 #2
0
def cx():
    return Config([
        ('with_', Item(name='with')),
        ('for', Config([
            ('continue_', Item(name='continue')),
        ])),
    ])
コード例 #3
0
def test_config_of_configs():
    uploads = Config({
        'threads': 1,
        'db': {
            'user': '******',
        },
    })

    downloads = Config({
        'enabled': True,
        'tmp_dir': '/tmp',
    })

    uploads.threads.value = 5
    downloads.tmp_dir.value = '/tmp/downloads'

    config = Config({
        'uploads': uploads,
        'downloads': downloads,
    })

    assert config.uploads.threads.value == 5

    config.uploads.threads.value = 3
    assert uploads.threads.value == 3

    assert config.uploads.threads is uploads.threads
    assert config.downloads.enabled is downloads.enabled

    uploads.reset()
    assert config.uploads.threads.value == 1
コード例 #4
0
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
コード例 #5
0
def test_json_read_and_write(defaults_json_path, user_json_path):
    c1 = Config()
    c1.json.load(defaults_json_path, as_defaults=True)

    c2 = Config()
    c2.json.load([defaults_json_path], as_defaults=True)

    c3 = Config()
    with open(defaults_json_path) as f:
        c3.json.load(f, as_defaults=True)

    assert c1.dump_values(with_defaults=False) == {}
    assert c1.dump_values(with_defaults=True) == {
        'uploads': {
            'threads': 1,
            'enabled': False,
            'tmp_dir': '/tmp',
        }
    }

    assert c1.dump_values() == c2.dump_values() == c3.dump_values()

    c1.json.load(user_json_path)
    c2.json.load([user_json_path])
    with open(user_json_path) as f:
        c3.json.load(f)

    assert c1.dump_values(with_defaults=False) == {
        'uploads': {
            'threads': 5,
            'enabled': True,
        }
    }
    assert c1.dump_values() == c2.dump_values() == c3.dump_values()

    updates = {
        'uploads': {
            'threads': 10,
            'enabled': False,
        }
    }

    c1.load_values(updates)
    c2.load_values(updates)
    c3.load_values(updates)

    assert c1.dump_values() == c2.dump_values() == c3.dump_values()

    c1.json.dump(user_json_path)
    c2.json.load(user_json_path)
    assert c1.dump_values() == c2.dump_values() == c3.dump_values()

    assert c1.dump_values() == c2.dump_values() == c3.dump_values()

    with open(user_json_path, 'w') as f:
        c2.json.dump(f)
    c1.json.load(user_json_path)

    assert c1.dump_values() == c2.dump_values() == c3.dump_values()
コード例 #6
0
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'
コード例 #7
0
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,
    })
コード例 #8
0
def test_assigning_nameless_item_directly_to_config_should_set_its_name():
    config = Config()
    config.dummy = Config()
    config.dummy.x = Item(value=5)
    assert config.dummy.x.name == 'x'

    config.dummy['y'] = Item(default=True)
    assert config.dummy.y.name == 'y'

    assert config.dump_values() == {'dummy': {'x': 5, 'y': True}}
コード例 #9
0
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
コード例 #10
0
def config():
    return Config(schema={
        'uploads': {
            'enabled': False,
            'threads': 1,
            'db': Config({
                'user': '******',
                'password': '******',
            }),
        },
        'greeting': 'Hello',
    })
コード例 #11
0
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
コード例 #12
0
def c4():
    return Config([
        ('greeting', 'Hello!'),
        ('uploads',
         Config([
             ('enabled', True),
             ('db', Config([('host', 'localhost'), ('user', 'root')])),
         ])),
        ('downloads', Config([
            ('enabled', True),
            ('threads', 5),
        ])),
    ])
コード例 #13
0
def test_config_item_value_can_be_unicode_str(tmpdir):
    config1 = Config({'greeting': u'Hello, {name}', 'name': u'Anonymous'})
    config1.name.value = u'Jānis Bērziņš'
    assert config1.name.type == Types.str

    path = tmpdir.join('config.ini').strpath
    config1.configparser.dump(path, with_defaults=True)

    config2 = Config({'greeting': '', 'name': ''})
    config2.configparser.load(path)
    assert config2.name.value == u'Jānis Bērziņš'
    assert config1.dump_values(with_defaults=True) == config2.dump_values(
        with_defaults=True)
コード例 #14
0
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'}
コード例 #15
0
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
コード例 #16
0
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
コード例 #17
0
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'
    }
コード例 #18
0
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
コード例 #19
0
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
コード例 #20
0
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'
コード例 #21
0
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)
コード例 #22
0
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'}
コード例 #23
0
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'
コード例 #24
0
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)
コード例 #25
0
def test_config_written_to_and_read_from_yaml_string():
    config_str = (
        'uploads:\n'
        '  enabled: True\n'
        '  threads: 5\n'
        '  db:\n'
        '    user: root\n\n'
    )

    config = Config()
    config.yaml.loads(config_str, as_defaults=True)

    # Make sure the order is preserved
    assert list(config.iter_paths(recursive=True, key='str_path')) == [
        'uploads', 'uploads.enabled', 'uploads.threads', 'uploads.db', 'uploads.db.user'
    ]

    # And the values
    assert config.dump_values() == {
        'uploads': {
            'enabled': True,
            'threads': 5,
            'db': {
                'user': '******',
            }
        }
    }

    config_str2 = config.yaml.dumps(with_defaults=True)

    # TODO Fix this in Python 2
    if six.PY3:
        assert config_str2 == (
            'uploads:\n'
            '  enabled: true\n'
            '  threads: 5\n'
            '  db:\n'
            '    user: root\n'
        )

    config2 = Config()
    config2.yaml.loads(config_str2, as_defaults=True)
    assert list(config2.iter_paths(recursive=True, key='str_path')) == [
        'uploads', 'uploads.enabled', 'uploads.threads', 'uploads.db', 'uploads.db.user'
    ]
    assert config2.dump_values() == config.dump_values()
コード例 #26
0
def test_schema_parser_does_not_modify_config(raw_logging_config):
    logging_config = Config(raw_logging_config)
    assert isinstance(logging_config, Config)

    assert logging_config['version']
    assert logging_config['formatters']

    config = Config({'logging': logging_config})
    assert isinstance(config, Config)

    assert config['logging']['version']
    assert config['logging']['formatters']

    assert config['logging']['formatters'] is logging_config['formatters']

    logging_config['version'].value = '2'
    assert config['logging']['version'].value == 2
コード例 #27
0
def test_empty_dict_is_an_item_with_dict_type():
    config = Config({
        'uploads': {}
    })

    assert config.uploads.is_item
    assert config.uploads.default == {}
    assert config.uploads.type == Types.dict
コード例 #28
0
def test_default_value_is_deep_copied():
    things = [1, 2, 3, 4]

    config = Config({'items': things})
    assert config['items'].value == [1, 2, 3, 4]

    things.remove(2)
    assert config['items'].value == [1, 2, 3, 4]
コード例 #29
0
def test_standard_exceptions_raised_for_unknown_attributes_of_item():
    config = Config({'greeting': 'Hello'})

    with pytest.raises(AttributeError):
        _ = config.greeting.something

    with pytest.raises(TypeError):
        _ = config.greeting['something']
コード例 #30
0
def test_must_not_allow_item_or_section_names_with_str_path_separator_in_them(
):
    with pytest.raises(ValueError):
        Config({
            'a.b': True,
        })

    Config({'a.b': True}, str_path_separator='/')

    with pytest.raises(ValueError):
        Config({'a/b': True}, str_path_separator='/')

    # Sections

    with pytest.raises(ValueError):
        Config({'a.b': Section()})

    with pytest.raises(ValueError):
        Config({'a/b': Section()}, str_path_separator='/')

    config = Config({'a/b': Section()}, str_path_separator='.')

    assert config['a/b'].is_section

    config = Config({'a.b': Section()}, str_path_separator='/')

    assert config['a.b'].is_section