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
def test_dynamic_override_of_envvar_name(monkeypatch): config = Section({'uploads': {'threads': 1}}) assert config.uploads.threads.envvar_name is None @config.item_attribute def envvar_name(item=None, **kwargs): return 'TEST_{}'.format('_'.join(item.get_path()).upper()) assert config.uploads.threads.envvar_name == 'TEST_UPLOADS_THREADS' assert config.uploads.threads.value == 1 # This has no immediate effect because envvar is still None monkeypatch.setenv('TEST_UPLOADS_THREADS', '23') assert config.uploads.threads.value == 1 # This changes everything config.uploads.threads.envvar = True assert config.uploads.threads.value == 23 # But, if envvar is set to a name, envvar_name doesn't matter again config.uploads.threads.envvar = 'OTHER_UPLOADS_THREADS' assert config.uploads.threads.value == 1 monkeypatch.setenv('OTHER_UPLOADS_THREADS', '42') assert config.uploads.threads.value == 42
def test_envvar_attribute_enables_value_override_via_envvars(monkeypatch): config = Section({'uploads': {'threads': 1}}) assert config.uploads.threads.envvar is None assert config.uploads.threads.envvar_name is None assert config.uploads.threads.value == 1 config.uploads.threads.envvar = True assert config.uploads.threads.value == 1 monkeypatch.setenv('UPLOADS_THREADS', '23') assert config.uploads.threads.value == 23 config.uploads.threads.envvar_name = 'OTHER_UPLOADS_THREADS' assert config.uploads.threads.value == 1 monkeypatch.setenv('OTHER_UPLOADS_THREADS', '42') assert config.uploads.threads.value == 42 config.uploads.threads.envvar = False assert config.uploads.threads.value == 1 config.uploads.threads.envvar = 'UPLOADS_THREADS' assert config.uploads.threads.value == 23 config.uploads.threads.envvar = 'OTHER_UPLOADS_THREADS' assert config.uploads.threads.value == 42 config.uploads.threads.envvar = 'SOMETHING_NONEXISTENT' assert config.uploads.threads.value == 1
class AuthenticationBackend(ABC): config_section = 'authentication' config_schema = Section(section='authentication') config = None @abstractmethod def authenticate(self, username, password) -> dict or False: raise NotImplemented
class StaticAuth(AuthenticationBackend): config_section = 'static_auth' config_schema = {'users': Section()} def authenticate(self, username, password): users = { u.lower(): p for u, p in self.config.static_auth.users.get().items() } if username.lower() in users and users[username] == password: return {'username': username.lower(), 'email': ''}
def test_get_item_and_get_section_for_rich_config(): config = Config({'uploads': Section({'db': Section({'user': '******'})})}) assert config.get_section('uploads').is_section assert config.get_section('uploads', 'db').is_section assert config.uploads.get_section('db').is_section assert config.get_item('uploads', 'db', 'user').is_item assert config.uploads.db.get_item('user').is_item with pytest.raises(NotFound): config.get_item('downloads') with pytest.raises(NotFound): config.get_section('downloads') with pytest.raises(NotFound): config.get_item('uploads', 'tmp_dir') with pytest.raises(NotFound): config.get_section('uploads', 'tmp_dir')
def test_nested_section_settings_are_the_settings_of_the_nearest_manager_in_tree( ): c = Config({ 'main': Config({ 'a1': Section({ 'a2': Config({ 'a3': Section({'a4': Section()}), }), }), 'b1': Section({ 'b2': Section({ 'b3': Section({ 'b4': Config({'b5': Section()}), }), }), }), }), }) assert c.settings assert c.main.settings assert c.settings is not c.main.settings assert c.main.a1.settings is c.main.settings assert c.main.a1.a2.settings is not c.main.settings assert c.main.a1.a2.a3.settings is c.main.a1.a2.settings assert c.main.a1.a2.a3.a4.settings is c.main.a1.a2.settings assert c.main.b1.settings is c.main.settings assert c.main.b1.b2.settings is c.main.settings assert c.main.b1.b2.b3.settings is c.main.settings assert c.main.b1.b2.b3.b4.settings is not c.main.settings assert c.main.b1.b2.b3.b4.b5.settings is c.main.b1.b2.b3.b4.settings
def test_not_found_hook(): calls = [] config = Config({'uploads': Section()}) @config.hooks.not_found def first_hook(*args, **kwargs): calls.append(('first', args, sub_dict(kwargs, ('section', 'name')))) @config.hooks.not_found def second_hook(*args, **kwargs): calls.append(('second', args, sub_dict(kwargs, ('section', 'name')))) assert len(calls) == 0 with pytest.raises(NotFound): _ = config.db assert len(calls) == 2 assert calls[0] == ('first', (), {'section': config, 'name': 'db'}) assert calls[1] == ('second', (), {'section': config, 'name': 'db'}) with pytest.raises(NotFound): _ = config.uploads.threads assert len(calls) == 4 assert calls[2] == ('first', (), { 'section': config.uploads, 'name': 'threads' }) assert calls[3] == ('second', (), { 'section': config.uploads, 'name': 'threads' }) # A hook that creates the missing item so further calls won't trigger # the hook handlers again, including any subsequent hook handlers as part of current event. @config.hooks.not_found def third_hook(*args, **kwargs): calls.append(('third', args, sub_dict(kwargs, ('section', 'name')))) assert kwargs['section'] assert kwargs['name'] item = kwargs['section'].create_item(name=kwargs['name']) kwargs['section'].add_item(item.name, item) return item # Fourth hook will never be called because the third hook already resolves the missing name @config.hooks.not_found def fourth_hook(*args, **kwargs): calls.append(('fourth', args, sub_dict(kwargs, ('section', 'name')))) assert len(calls) == 4 assert config.uploads.threads assert len(calls) == 7 assert calls[4] == ('first', (), { 'section': config.uploads, 'name': 'threads' }) assert calls[5] == ('second', (), { 'section': config.uploads, 'name': 'threads' }) assert calls[6] == ('third', (), { 'section': config.uploads, 'name': 'threads' }) assert config.uploads.threads assert len(calls) == 7