Use dicts instead of dots in the internal settings representation
authorKarl O. Pinc <kop@karlpinc.com>
Sat, 12 Sep 2020 20:22:03 +0000 (15:22 -0500)
committerKarl O. Pinc <kop@karlpinc.com>
Sun, 13 Sep 2020 00:15:16 +0000 (19:15 -0500)
src/pgwui_server/__init__.py
tests/test___init__.py

index 529693ba8688786dcd5a08c26e9cf8bc2b25416a..fdc930fe82ed5e73c3b6efb8918f4c04e801ca25 100644 (file)
@@ -53,41 +53,58 @@ log = logging.getLogger(__name__)
 
 # Functions
 
-def abort_on_bad_setting(
-        errors, component_keys, component_checkers, key, settings):
-    '''Abort on a bad pgwui setting
+def dot_to_component_settings(settings, key, component):
+    '''Put a component's settings into its own dict,
+    adding to what's already there
     '''
-    if key[:6] == 'pgwui.':
-        if key in component_keys:
-            component = component_keys[key]
-            if component in component_checkers:
-                errors.extend(
-                    component_checkers[component](settings.get(key, {})))
-        else:
-            if key[6:] not in SETTINGS:
-                errors.append(common_ex.UnknownSettingKeyError(key))
+    comp_settings = settings['pgwui'].setdefault(component, dict())
+    comp_settings.update(settings[key])
+    del settings[key]
 
 
-def require_setting(errors, setting, settings):
-    if setting not in settings:
-        errors.append(common_ex.MissingSettingError(setting))
+def component_setting_into_dict(
+        errors, component_checkers, key, settings, component):
+    '''Put a component's settings in its own dict and validate them
+    '''
+    comp_settings = dot_to_component_settings(settings, key, component)
+    if component in component_checkers:
+        errors.extend(
+            component_checkers[component](comp_settings))
+
+
+def dot_to_dict(settings, key, new_key):
+    settings['pgwui'][new_key] = settings[key]
+    del settings[key]
+
+
+def key_to_ini(key):
+    '''Convert the setting key to a key used in an ini file's declaration
+    '''
+    return 'pgwui.{}'.format(key)
 
 
-def boolean_setting(errors, setting, settings):
-    if setting in settings:
+def require_setting(errors, setting, pgwui_settings):
+    if setting not in pgwui_settings:
+        errors.append(common_ex.MissingSettingError(key_to_ini(setting)))
+
+
+def boolean_setting(errors, setting, pgwui_settings):
+    if setting in pgwui_settings:
         try:
-            val = literal_eval(settings[setting])
+            val = literal_eval(pgwui_settings[setting])
         except ValueError:
             val = None
         if (val is not True
                 and val is not False):
             errors.append(common_ex.NotBooleanSettingError(
-                setting, settings[setting]))
+                key_to_ini(setting), pgwui_settings[setting]))
 
 
 def validate_setting_values(errors, settings):
     '''Check each settings value for validity
     '''
+    pgwui_settings = settings['pgwui']
+
     # pg_host can be missing, it defaults to the Unix socket (in psycopg2)
 
     # pg_port can be missing, it defaults to 5432 (in psycopg2)
@@ -95,22 +112,23 @@ def validate_setting_values(errors, settings):
     # default_db can be missing, then the user sees no default
 
     # dry_run
-    require_setting(errors, 'pgwui.dry_run', settings)
-    boolean_setting(errors, 'pgwui.dry_run', settings)
+    require_setting(errors, 'dry_run', pgwui_settings)
+    boolean_setting(errors, 'dry_run', pgwui_settings)
 
     # route_prefix can be missing, defaults to no route prefix which is fine.
 
     # routes can be missing, sensible defaults are built-in.
 
     # validate_hmac
-    boolean_setting(errors, 'pgwui.validate_hmac', settings)
+    boolean_setting(errors, 'validate_hmac', pgwui_settings)
 
 
 def do_validate_hmac(settings):
     '''True unless the user has specificly rejected hmac validation
     '''
-    return ('pgwui.validate_hmac' not in settings
-            or literal_eval(settings['pgwui.validate_hmac']))
+    pgwui_settings = settings['pgwui']
+    return ('validate_hmac' not in pgwui_settings
+            or literal_eval(pgwui_settings['validate_hmac']))
 
 
 def validate_hmac(errors, settings):
@@ -142,27 +160,33 @@ def parse_assignments(lines):
     return result
 
 
-def parse_component_settings(component_keys, key, settings):
-    '''Parse plugin component setting assigments into a dict
+def setting_into_dict(
+        errors, components, component_checkers, key, settings):
+    '''Separate a pgwui setting into a dict on '.' chars; validate
+    component settings.
     '''
-    if key in component_keys:
-        settings[key] = dict(parse_assignments(settings[key]))
-
-
-def map_keys_to_components(components):
-    return dict([(plugin.component_to_key(component), component)
-                 for component in components])
+    if key[:6] == 'pgwui.':
+        new_key = key[6:]
+        if new_key in components:
+            settings[key] = dict(parse_assignments(settings[key]))
+            component_setting_into_dict(
+                errors, component_checkers, key, settings, new_key)
+        else:
+            if new_key in SETTINGS:
+                dot_to_dict(settings, key, new_key)
+            else:
+                errors.append(common_ex.UnknownSettingKeyError(key))
 
 
-def validate_settings(errors, settings, components):
-    '''Be sure all settings validate
+def dictify_settings(errors, settings, components):
+    '''Convert . in the pgwui settings to dict mappings, and validate
+    the result.
     '''
-    component_keys = map_keys_to_components(components)
     component_checkers = plugin.find_pgwui_check_settings()
-    for key in settings.keys():
-        parse_component_settings(component_keys, key, settings)
-        abort_on_bad_setting(
-            errors, component_keys, component_checkers, key, settings)
+    settings.setdefault('pgwui', dict())
+    for key in list(settings.keys()):
+        setting_into_dict(
+            errors, components, component_checkers, key, settings)
     validate_setting_values(errors, settings)
     validate_hmac(errors, settings)
 
@@ -186,7 +210,7 @@ def exit_on_invalid_settings(settings, components):
     '''Exit when settings don't validate
     '''
     errors = []
-    validate_settings(errors, settings, components)
+    dictify_settings(errors, settings, components)
     if errors:
         exit_reporting_errors(errors)
 
@@ -194,8 +218,9 @@ def exit_on_invalid_settings(settings, components):
 def add_routes(config, settings):
     '''Add routes found in pgwui.routes setting
     '''
-    if 'pgwui.routes' in settings:
-        routes = parse_assignments(settings['pgwui.routes'])
+    pgwui_settings = settings['pgwui']
+    if 'routes' in pgwui_settings:
+        routes = parse_assignments(pgwui_settings['routes'])
         for name, route in routes:
             config.add_route(name, route)
 
@@ -203,7 +228,7 @@ def add_routes(config, settings):
 def autoconfigurable_components(settings, components):
     '''Automatic pgwui component discovery
     '''
-    autoconfig = settings.get('pgwui.autoconfigure', True)
+    autoconfig = settings['pgwui'].get('autoconfigure', True)
     if not autoconfig:
         return []
 
@@ -218,7 +243,7 @@ def apply_component_defaults(settings, components):
     '''
     components_to_config = autoconfigurable_components(settings, components)
 
-    rp = settings.get('pgwui.route_prefix')
+    rp = settings['pgwui'].get('route_prefix')
     with Configurator(settings=settings, route_prefix=rp) as config:
         config.include('pgwui_common')
         for component in components_to_config:
index edce6cc79f42472b94ca10d2507235c987f1a860..6f786f6442b47faf7be848915c611627d0b96be1 100644 (file)
@@ -24,7 +24,6 @@ import copy
 import logging
 import pytest
 import sys
-import unittest.mock
 
 import pyramid.testing
 
@@ -71,75 +70,118 @@ mock_find_pgwui_components = testing.make_mock_fixture(
     pgwui_common.plugin, 'find_pgwui_components')
 mock_find_pgwui_check_settings = testing.make_mock_fixture(
     pgwui_common.plugin, 'find_pgwui_check_settings')
-mock_component_to_key = testing.make_mock_fixture(
-    pgwui_common.plugin, 'component_to_key')
 
 
 # Unit tests
 
-# abort_on_bad_setting()
+# dot_to_component_settings()
 
-def test_abort_on_bad_setting_unknown():
-    '''Nothing bad happens when there's a non-pgwui setting'''
-    errors = []
-    pgwui_server_init.abort_on_bad_setting(errors, {}, {}, 'foo', {})
+def test_dot_to_component_settings_new():
+    '''Adds a new dict and puts the settings in it
+    '''
+    comp_settings = {'foo': 'foo', 'bar': 'bar'}
+    component = 'pgwui_component'
+    key = 'pgwui.' + component
+    settings = {'pgwui': {},
+                key: comp_settings}
+    expected = {'pgwui': {component: comp_settings}}
 
-    assert errors == []
+    pgwui_server_init.dot_to_component_settings(
+        settings, key, 'pgwui_component')
 
+    assert settings == expected
 
-def test_abort_on_bad_setting_bad():
-    '''Delivers an error on a bad pgwui setting'''
-    errors = []
-    pgwui_server_init.abort_on_bad_setting(
-        errors, {}, {}, 'pgwui.foo', {})
 
-    assert errors
-    assert isinstance(errors[0], common_ex.UnknownSettingKeyError)
+def test_dot_to_component_settings_old():
+    '''Extends an existing dict in the settings
+    '''
+    comp_settings = {'foo': 'foo', 'bar': 'bar'}
+    component = 'pgwui_component'
+    key = 'pgwui.' + component
+    settings = {'pgwui': {component: {'foo': 'bar', 'baz': 'baz'}},
+                key: comp_settings}
+    expected = {'pgwui':
+                {component: {'foo': 'foo', 'bar': 'bar', 'baz': 'baz'}}}
 
+    pgwui_server_init.dot_to_component_settings(
+        settings, key, 'pgwui_component')
 
-def test_abort_on_bad_setting_good():
-    '''Does nothing when a known pgwui setting is supplied'''
-    errors = []
-    pgwui_server_init.abort_on_bad_setting(
-        errors, {}, {}, 'pgwui.pg_host', {})
+    assert settings == expected
 
-    assert errors == []
+
+mock_dot_to_component_setting = testing.make_mock_fixture(
+    pgwui_server_init, 'dot_to_component_settings')
 
 
-def test_abort_on_bad_setting_plugin_no_config():
-    '''Does nothing when a known plugin has a setting and there is no
-    config for the plugin'''
+# component_setting_into_dict()
+
+def test_component_setting_into_dict_no_checker(
+        mock_dot_to_component_setting):
+    '''When there's no checker nothing is done
+    '''
     errors = []
-    pgwui_server_init.abort_on_bad_setting(
-        errors, {'pgwui.pgwui_upload': None}, {}, 'pgwui.pgwui_upload', {})
+
+    pgwui_server_init.component_setting_into_dict(
+        errors, {}, 'pgwui.pgwui_component', None, 'pgwui_component')
 
     assert errors == []
 
 
-def test_abort_on_bad_setting_plugin_config():
-    '''Calls the component checker with the component's config
-    and appends the result of the call to the errors.
+def test_component_setting_into_dict_checker(
+        mock_dot_to_component_setting):
+    '''When there's a checker its result is appended to the errors
+    '''
+    errors = ['someerror']
+    new_errors = ['new1', 'new2']
+    expected = copy.deepcopy(errors)
+    expected.extend(new_errors)
+
+    pgwui_server_init.component_setting_into_dict(
+        errors, {'pgwui_component': lambda settings: new_errors},
+        'pgwui.pgwui_component', None, 'pgwui_component')
+
+    assert errors == expected
+
+
+mock_component_setting_into_dict = testing.make_mock_fixture(
+    pgwui_server_init, 'component_setting_into_dict')
+
+
+# dot_to_dict()
+
+def test_dot_to_dict():
+    '''Removes pgwui.* settings, replaces them with a dict entry
     '''
-    orig_errors = ['old', 'errors']
-    new_errors = ['some', 'errors']
-    component = 'test component'
-    sample_key = 'pgwui.pgwui_upload'
-    sample_config = {sample_key: 'some sample config'}
+    settings = {'foo': 1,
+                'pgwui': {},
+                'pgwui.abc': 'abc',
+                'pgwui.def': 'def'}
+    expected = {'foo': 1,
+                'pgwui': {'abc': 'abc'},
+                'pgwui.def': 'def'}
 
-    errors = copy.deepcopy(orig_errors)
-    mock_checker = unittest.mock.Mock(side_effect=lambda *args: new_errors)
+    pgwui_server_init.dot_to_dict(settings, 'pgwui.abc', 'abc')
 
-    pgwui_server_init.abort_on_bad_setting(
-        errors, {sample_key: component}, {component: mock_checker},
-        sample_key, sample_config)
+    assert settings == expected
 
-    mock_checker.assert_called_once
-    assert list(mock_checker.call_args[0]) == [sample_config[sample_key]]
-    assert errors == orig_errors + new_errors
 
+mock_dot_to_dict = testing.make_mock_fixture(
+    pgwui_server_init, 'dot_to_dict')
 
-mock_abort_on_bad_setting = testing.make_mock_fixture(
-    pgwui_server_init, 'abort_on_bad_setting')
+
+# key_to_ini()
+
+def test_key_to_ini():
+    '''The return value is as expected
+    '''
+    key = 'pgwui_example'
+    result = pgwui_server_init.key_to_ini(key)
+
+    assert result == 'pgwui.' + key
+
+
+mock_key_to_ini = testing.make_mock_fixture(
+    pgwui_server_init, 'key_to_ini')
 
 
 # require_setting()
@@ -220,7 +262,7 @@ mock_boolean_setting = testing.make_mock_fixture(
 def test_validate_setting_values(mock_require_setting, mock_boolean_setting):
     '''Calls require_setting() and boolean_setting()'''
 
-    pgwui_server_init.validate_setting_values([], {})
+    pgwui_server_init.validate_setting_values([], {'pgwui': {}})
 
     assert mock_require_setting.called
     assert mock_boolean_setting.called
@@ -234,20 +276,20 @@ mock_validate_setting_values = testing.make_mock_fixture(
 
 def test_do_validate_hmac_none():
     '''pgwui.validate_hmac defaults to True'''
-    assert pgwui_server_init.do_validate_hmac({}) is True
+    assert pgwui_server_init.do_validate_hmac({'pgwui': {}}) is True
 
 
 def test_do_validate_hmac_True():
     '''Require hmac validation when pgwui.validate_hmac is True'''
     result = pgwui_server_init.do_validate_hmac(
-        {'pgwui.validate_hmac': 'True'})
+        {'pgwui': {'validate_hmac': 'True'}})
     assert result is True
 
 
 def test_do_validate_hmac_False():
     '''No hmac validation when pgwui.validate_hmac is False'''
     result = pgwui_server_init.do_validate_hmac(
-        {'pgwui.validate_hmac': 'False'})
+        {'pgwui': {'validate_hmac': 'False'}})
     assert result is False
 
 
@@ -332,74 +374,96 @@ mock_parse_assignments = testing.make_mock_fixture(
     pgwui_server_init, 'parse_assignments')
 
 
-# parse_component_settings()
+# setting_into_dict()
+
+def test_setting_into_dict_unknown(
+        mock_parse_assignments,
+        mock_component_setting_into_dict,
+        mock_dot_to_dict):
+    '''No new errors when there's a non-pgwui setting'''
+    errors = []
+    pgwui_server_init.setting_into_dict(errors, [], {}, 'foo', {})
+
+    assert errors == []
 
-def test_parse_component_settings(mock_parse_assignments):
-    '''Plugin component settings are properly parsed
-    '''
-    expected_server_settings = {'a': '1', 'b': '2'}
-    component = 'pgwui.pgwui_server'
-    settings = {component: 'ignored'}
 
-    mock_parse_assignments.return_value = [('a', '1'), ('b', '2')]
-    pgwui_server_init.parse_component_settings(
-        [component], component, settings)
+def test_setting_into_dict_bad(
+        mock_parse_assignments,
+        mock_component_setting_into_dict,
+        mock_dot_to_dict):
+    '''Delivers an error on a bad pgwui setting'''
+    errors = []
 
-    assert settings[component] == expected_server_settings
+    pgwui_server_init.setting_into_dict(
+        errors, [], {}, 'pgwui.foo', {})
 
+    assert errors
+    assert isinstance(errors[0], common_ex.UnknownSettingKeyError)
 
-mock_parse_component_settings = testing.make_mock_fixture(
-    pgwui_server_init, 'parse_component_settings')
 
+def test_setting_into_dict_good(
+        mock_parse_assignments,
+        mock_component_setting_into_dict,
+        mock_dot_to_dict):
+    '''Calls dot_to_dict when a known pgwui setting is supplied'''
+    errors = []
 
-# map_keys_to_components()
+    pgwui_server_init.setting_into_dict(
+        errors, [], {}, 'pgwui.pg_host', {})
 
-def test_map_keys_to_components(mock_component_to_key):
-    '''Returns expected result
+    mock_dot_to_dict.assert_called_once()
+    assert errors == []
+
+
+def test_setting_into_dict_plugin_component(
+        mock_parse_assignments,
+        mock_component_setting_into_dict,
+        mock_dot_to_dict):
+    '''When a setting is for a component the setting is parsed and
+    moved into a dict
     '''
-    components = ['a', 'b', 'c']
-    mock_component_to_key.side_effect = ['keya', 'keyb', 'keyc']
+    key = 'pgwui.pgwui_component'
+    settings = {key: None}
+    errors = []
+    mock_parse_assignments.return_value = {}
 
-    result = pgwui_server_init.map_keys_to_components(components)
+    pgwui_server_init.setting_into_dict(
+        errors, ['pgwui_component'], {}, key, settings)
 
-    assert result == {'keya': 'a', 'keyb': 'b', 'keyc': 'c'}
+    mock_parse_assignments.assert_called_once()
+    mock_component_setting_into_dict.assert_called_once()
+    assert errors == []
 
 
-mock_map_keys_to_components = testing.make_mock_fixture(
-    pgwui_server_init, 'map_keys_to_components')
+mock_setting_into_dict = testing.make_mock_fixture(
+    pgwui_server_init, 'setting_into_dict')
 
 
-# validate_settings()
+# dictify_settings()
 
-def test_validate_settings(mock_map_keys_to_components,
-                           mock_parse_component_settings,
-                           mock_find_pgwui_check_settings,
-                           mock_abort_on_bad_setting,
-                           mock_validate_setting_values,
-                           mock_validate_hmac):
-    '''Calls abort_on_bad_setting() for each key in setting,
-    with the proper list of config declaration names for the
-    plugin components
+def test_dictify_settings(mock_find_pgwui_check_settings,
+                          mock_setting_into_dict,
+                          mock_validate_setting_values,
+                          mock_validate_hmac):
+    '''Calls setting_into_dict() for each key in setting,
+    with the proper list of plugin components
     '''
     settings = {'key1': 'value1',
                 'key2': 'value2'}
     components = ['pgwui_server']
-    key_map = {'pgwui.server': 'anentrypoint'}
-
-    mock_map_keys_to_components.side_effect = lambda *args: key_map
 
     errors = []
-    pgwui_server_init.validate_settings(errors, settings, components)
+    pgwui_server_init.dictify_settings(errors, settings, components)
 
     assert mock_validate_setting_values.called
     assert mock_validate_hmac.called
 
-    assert mock_abort_on_bad_setting.call_count == len(settings)
-    assert mock_abort_on_bad_setting.call_args[0][1] == key_map
+    assert mock_setting_into_dict.call_count == len(settings)
+    assert mock_setting_into_dict.call_args[0][1] == components
 
 
-mock_validate_settings = testing.make_mock_fixture(
-    pgwui_server_init, 'validate_settings')
+mock_dictify_settings = testing.make_mock_fixture(
+    pgwui_server_init, 'dictify_settings')
 
 
 # exit_reporting_errors()
@@ -466,21 +530,21 @@ mock_exit_reporting_errors = testing.make_mock_fixture(
 
 def test_exit_on_invalid_settings_invalid(monkeypatch,
                                           mock_exit_reporting_errors):
-    '''Calls validate_settings and exit_reporting_errors() when
+    '''Calls dictify_settings and exit_reporting_errors() when
     setting is invalid
     '''
-    def mock_validate_settings(errors, settings, components):
+    def mock_dictify_settings(errors, settings, components):
         errors.append('error1')
 
-    monkeypatch.setattr(pgwui_server_init, 'validate_settings',
-                        mock_validate_settings)
+    monkeypatch.setattr(pgwui_server_init, 'dictify_settings',
+                        mock_dictify_settings)
 
     pgwui_server_init.exit_on_invalid_settings({}, [])
 
     assert mock_exit_reporting_errors.called
 
 
-def test_exit_on_invalid_settings_valid(mock_validate_settings):
+def test_exit_on_invalid_settings_valid(mock_dictify_settings):
     '''Returns, without exiting, when all settings are valid
     '''
     pgwui_server_init.exit_on_invalid_settings({}, [])
@@ -500,7 +564,7 @@ def test_autoconfiguable_components_no_autoconfig():
     test_components = ['some', 'components']
 
     result = pgwui_server_init.autoconfigurable_components(
-        {'pgwui.autoconfigure': False}, test_components)
+        {'pgwui': {'autoconfigure': False}}, test_components)
 
     assert result == []
 
@@ -511,7 +575,9 @@ def test_autoconfigurable_components_log_info(caplog):
     caplog.set_level(logging.INFO)
 
     pgwui_server_init.autoconfigurable_components(
-        {'pgwui.autoconfigure': True, 'pyramid.include': None}, [])
+        {'pgwui': {'autoconfigure': True},
+         'pyramid.include': None},
+        [])
 
     logs = caplog.record_tuples
 
@@ -527,7 +593,7 @@ def test_autoconfigurable_components_components_returned():
     test_components = ['some', 'components']
 
     result = pgwui_server_init.autoconfigurable_components(
-        {'pgwui.autoconfigure': True}, test_components)
+        {'pgwui': {'pgwui.autoconfigure': True}}, test_components)
 
     assert result == test_components
 
@@ -542,7 +608,7 @@ def test_add_routes_empty(mock_add_route):
     '''When there is no pgwui.routes setting nothing gets added'''
     with pyramid.testing.testConfig() as config:
         mocked_add_route = mock_add_route(config)
-        pgwui_server_init.add_routes(config, {})
+        pgwui_server_init.add_routes(config, {'pgwui': {}})
 
     assert not mocked_add_route.called
 
@@ -555,7 +621,7 @@ def test_add_routes_notempty(mock_add_route, mock_parse_assignments):
     mock_parse_assignments.return_value = test_routes
     with pyramid.testing.testConfig() as config:
         mocked_add_route = mock_add_route(config)
-        pgwui_server_init.add_routes(config, {'pgwui.routes': ''})
+        pgwui_server_init.add_routes(config, {'pgwui': {'routes': ''}})
 
     assert mocked_add_route.call_count == len(test_routes)
 
@@ -580,7 +646,7 @@ def test_apply_component_defaults(monkeypatch, caplog,
     monkeypatch.setattr(pgwui_server_init, 'Configurator',
                         MockConfigurator)
 
-    result = pgwui_server_init.apply_component_defaults({}, [])
+    result = pgwui_server_init.apply_component_defaults({'pgwui': {}}, [])
     assert isinstance(result, MockConfigurator)
 
     logs = caplog.record_tuples