From a86b6416e792d604904a9911749d681969bbddcc Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Tue, 17 Nov 2020 14:01:10 -0600 Subject: [PATCH] Move code out of __init__.py --- setup.py | 2 +- src/pgwui_server/__init__.py | 201 ------------------ src/pgwui_server/pgwui_server.py | 201 ++++++++++++++++++ ...{test___init__.py => test_pgwui_server.py} | 80 +++---- 4 files changed, 242 insertions(+), 242 deletions(-) create mode 100644 src/pgwui_server/pgwui_server.py rename tests/{test___init__.py => test_pgwui_server.py} (85%) diff --git a/setup.py b/setup.py index ebda3db..a8ab059 100644 --- a/setup.py +++ b/setup.py @@ -181,7 +181,7 @@ setup( # Its configuration is manually coded. entry_points="""\ [paste.app_factory] - main = pgwui_server:main + main = pgwui_server.pgwui_server:main """ # }, ) diff --git a/src/pgwui_server/__init__.py b/src/pgwui_server/__init__.py index eb3b7c3..e69de29 100644 --- a/src/pgwui_server/__init__.py +++ b/src/pgwui_server/__init__.py @@ -1,201 +0,0 @@ -# Copyright (C) 2018, 2019, 2020 The Meme Factory, Inc. -# http://www.karlpinc.com/ - -# This file is part of PGWUI_Server. -# -# This program is free software: you can redistribute it and/or -# modify it under the terms of the GNU Affero General Public License -# as published by the Free Software Foundation, either version 3 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public -# License along with this program. If not, see -# . -# - -# Karl O. Pinc - -'''Provide a way to configure PGWUI. -''' - -from pyramid.config import Configurator -import logging -import sys - -from . import exceptions as server_ex -from . import checkset -from pgwui_common import exceptions as common_ex -from pgwui_common import plugin - -# Constants - -# All the settings recognized by PGWUI -SETTINGS = set( - ['pg_host', - 'pg_port', - 'default_db', - 'dry_run', - 'route_prefix', - 'routes', - 'validate_hmac', - 'autoconfigure', - ]) - - -# Logging -log = logging.getLogger(__name__) - - -# Functions - -def dot_to_component_settings(settings, key, component): - '''Put a component's settings into its own dict, - adding to what's already there - ''' - comp_settings = settings['pgwui'].setdefault(component, dict()) - comp_settings.update(settings[key]) - del settings[key] - - -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 parse_assignments(lines): - '''Return a list of key/value tuples from the lines of a setting - ''' - result = [] - if isinstance(lines, str): - for line in lines.splitlines(): - if '=' in line: - key, val = line.split('=', 1) - result.append((key.rstrip(), val.lstrip())) - else: - for key, val in lines.items(): - result.append((key, val)) - return result - - -def setting_into_dict( - errors, components, component_checkers, key, settings): - '''Separate a pgwui setting into a dict on '.' chars; validate - component settings. - ''' - 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 dictify_settings(errors, settings, components): - '''Convert . in the pgwui settings to dict mappings, and validate - the result. - ''' - component_checkers = plugin.find_pgwui_check_settings() - settings.setdefault('pgwui', dict()) - for key in list(settings.keys()): - setting_into_dict( - errors, components, component_checkers, key, settings) - checkset.validate_setting_values(errors, settings) - checkset.validate_hmac(errors, settings) - - -def exit_reporting_errors(errors): - '''Report errors and exit - ''' - tagged = [(logging.ERROR, error) for error in errors] - tagged.append((logging.CRITICAL, server_ex.BadSettingsAbort())) - - for (level, error) in tagged: - log.log(level, error) - - for (level, error) in (tagged[0], tagged[-1]): - print(error, file=sys.stderr) # in case logging is broken - - sys.exit(1) - - -def exit_on_invalid_settings(settings, components): - '''Exit when settings don't validate - ''' - errors = [] - dictify_settings(errors, settings, components) - if errors: - exit_reporting_errors(errors) - - -def add_routes(config, settings): - '''Add routes found in pgwui.routes setting - ''' - 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) - - -def autoconfigurable_components(settings, components): - '''Automatic pgwui component discovery - ''' - autoconfig = settings['pgwui'].get('autoconfigure', True) - if not autoconfig: - return [] - - if 'pyramid.include' in settings: - log.info(server_ex.AutoconfigureConflict()) - - return components - - -def apply_component_defaults(settings, components): - '''Apply component default settings to existing settings - ''' - components_to_config = autoconfigurable_components(settings, components) - - 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: - log.debug('Autoconfiguring PGWUI component: {}'.format(component)) - config.include(component) - add_routes(config, settings) - log.debug('Done autoconfiguring PGWUI components') - return config - - -def pgwui_server_config(settings): - '''Configure pyramid - ''' - components = plugin.find_pgwui_components() - exit_on_invalid_settings(settings, components) - return apply_component_defaults(settings, components) - - -def main(global_config, **settings): - '''Return a Pyramid WSGI application - ''' - config = pgwui_server_config(settings) - return config.make_wsgi_app() diff --git a/src/pgwui_server/pgwui_server.py b/src/pgwui_server/pgwui_server.py new file mode 100644 index 0000000..eb3b7c3 --- /dev/null +++ b/src/pgwui_server/pgwui_server.py @@ -0,0 +1,201 @@ +# Copyright (C) 2018, 2019, 2020 The Meme Factory, Inc. +# http://www.karlpinc.com/ + +# This file is part of PGWUI_Server. +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU Affero General Public License +# as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public +# License along with this program. If not, see +# . +# + +# Karl O. Pinc + +'''Provide a way to configure PGWUI. +''' + +from pyramid.config import Configurator +import logging +import sys + +from . import exceptions as server_ex +from . import checkset +from pgwui_common import exceptions as common_ex +from pgwui_common import plugin + +# Constants + +# All the settings recognized by PGWUI +SETTINGS = set( + ['pg_host', + 'pg_port', + 'default_db', + 'dry_run', + 'route_prefix', + 'routes', + 'validate_hmac', + 'autoconfigure', + ]) + + +# Logging +log = logging.getLogger(__name__) + + +# Functions + +def dot_to_component_settings(settings, key, component): + '''Put a component's settings into its own dict, + adding to what's already there + ''' + comp_settings = settings['pgwui'].setdefault(component, dict()) + comp_settings.update(settings[key]) + del settings[key] + + +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 parse_assignments(lines): + '''Return a list of key/value tuples from the lines of a setting + ''' + result = [] + if isinstance(lines, str): + for line in lines.splitlines(): + if '=' in line: + key, val = line.split('=', 1) + result.append((key.rstrip(), val.lstrip())) + else: + for key, val in lines.items(): + result.append((key, val)) + return result + + +def setting_into_dict( + errors, components, component_checkers, key, settings): + '''Separate a pgwui setting into a dict on '.' chars; validate + component settings. + ''' + 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 dictify_settings(errors, settings, components): + '''Convert . in the pgwui settings to dict mappings, and validate + the result. + ''' + component_checkers = plugin.find_pgwui_check_settings() + settings.setdefault('pgwui', dict()) + for key in list(settings.keys()): + setting_into_dict( + errors, components, component_checkers, key, settings) + checkset.validate_setting_values(errors, settings) + checkset.validate_hmac(errors, settings) + + +def exit_reporting_errors(errors): + '''Report errors and exit + ''' + tagged = [(logging.ERROR, error) for error in errors] + tagged.append((logging.CRITICAL, server_ex.BadSettingsAbort())) + + for (level, error) in tagged: + log.log(level, error) + + for (level, error) in (tagged[0], tagged[-1]): + print(error, file=sys.stderr) # in case logging is broken + + sys.exit(1) + + +def exit_on_invalid_settings(settings, components): + '''Exit when settings don't validate + ''' + errors = [] + dictify_settings(errors, settings, components) + if errors: + exit_reporting_errors(errors) + + +def add_routes(config, settings): + '''Add routes found in pgwui.routes setting + ''' + 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) + + +def autoconfigurable_components(settings, components): + '''Automatic pgwui component discovery + ''' + autoconfig = settings['pgwui'].get('autoconfigure', True) + if not autoconfig: + return [] + + if 'pyramid.include' in settings: + log.info(server_ex.AutoconfigureConflict()) + + return components + + +def apply_component_defaults(settings, components): + '''Apply component default settings to existing settings + ''' + components_to_config = autoconfigurable_components(settings, components) + + 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: + log.debug('Autoconfiguring PGWUI component: {}'.format(component)) + config.include(component) + add_routes(config, settings) + log.debug('Done autoconfiguring PGWUI components') + return config + + +def pgwui_server_config(settings): + '''Configure pyramid + ''' + components = plugin.find_pgwui_components() + exit_on_invalid_settings(settings, components) + return apply_component_defaults(settings, components) + + +def main(global_config, **settings): + '''Return a Pyramid WSGI application + ''' + config = pgwui_server_config(settings) + return config.make_wsgi_app() diff --git a/tests/test___init__.py b/tests/test_pgwui_server.py similarity index 85% rename from tests/test___init__.py rename to tests/test_pgwui_server.py index 9b86c39..2ba90a1 100644 --- a/tests/test___init__.py +++ b/tests/test_pgwui_server.py @@ -33,7 +33,7 @@ import pgwui_common.plugin from pgwui_testing import testing import pgwui_server.checkset -import pgwui_server.__init__ as pgwui_server_init +import pgwui_server.pgwui_server as pgwui_server # Constants @@ -90,7 +90,7 @@ def test_dot_to_component_settings_new(): key: comp_settings} expected = {'pgwui': {component: comp_settings}} - pgwui_server_init.dot_to_component_settings( + pgwui_server.dot_to_component_settings( settings, key, 'pgwui_component') assert settings == expected @@ -107,14 +107,14 @@ def test_dot_to_component_settings_old(): expected = {'pgwui': {component: {'foo': 'foo', 'bar': 'bar', 'baz': 'baz'}}} - pgwui_server_init.dot_to_component_settings( + pgwui_server.dot_to_component_settings( settings, key, 'pgwui_component') assert settings == expected mock_dot_to_component_setting = testing.make_mock_fixture( - pgwui_server_init, 'dot_to_component_settings') + pgwui_server, 'dot_to_component_settings') # component_setting_into_dict() @@ -125,7 +125,7 @@ def test_component_setting_into_dict_no_checker( ''' errors = [] - pgwui_server_init.component_setting_into_dict( + pgwui_server.component_setting_into_dict( errors, {}, 'pgwui.pgwui_component', None, 'pgwui_component') assert errors == [] @@ -140,7 +140,7 @@ def test_component_setting_into_dict_checker( expected = copy.deepcopy(errors) expected.extend(new_errors) - pgwui_server_init.component_setting_into_dict( + pgwui_server.component_setting_into_dict( errors, {'pgwui_component': lambda settings: new_errors}, 'pgwui.pgwui_component', None, 'pgwui_component') @@ -148,7 +148,7 @@ def test_component_setting_into_dict_checker( mock_component_setting_into_dict = testing.make_mock_fixture( - pgwui_server_init, 'component_setting_into_dict') + pgwui_server, 'component_setting_into_dict') # dot_to_dict() @@ -164,13 +164,13 @@ def test_dot_to_dict(): 'pgwui': {'abc': 'abc'}, 'pgwui.def': 'def'} - pgwui_server_init.dot_to_dict(settings, 'pgwui.abc', 'abc') + pgwui_server.dot_to_dict(settings, 'pgwui.abc', 'abc') assert settings == expected mock_dot_to_dict = testing.make_mock_fixture( - pgwui_server_init, 'dot_to_dict') + pgwui_server, 'dot_to_dict') # parse_assignments() @@ -183,7 +183,7 @@ def test_parse_assignments_str(): 'key2=value2\n' # missing whitespace is fine 'key3= value3=withequals\n' ) - result = pgwui_server_init.parse_assignments(lines) + result = pgwui_server.parse_assignments(lines) assert set(result) == set([('key1', 'value1'), ('key2', 'value2'), ('key3', 'value3=withequals')]) @@ -195,14 +195,14 @@ def test_parse_assignments_dict(): lines = {'key1': 'value1', 'key2': 'value2', } - result = pgwui_server_init.parse_assignments(lines) + result = pgwui_server.parse_assignments(lines) assert set(result) == set([('key1', 'value1'), ('key2', 'value2'), ]) mock_parse_assignments = testing.make_mock_fixture( - pgwui_server_init, 'parse_assignments') + pgwui_server, 'parse_assignments') # setting_into_dict() @@ -213,7 +213,7 @@ def test_setting_into_dict_unknown( mock_dot_to_dict): '''No new errors when there's a non-pgwui setting''' errors = [] - pgwui_server_init.setting_into_dict(errors, [], {}, 'foo', {}) + pgwui_server.setting_into_dict(errors, [], {}, 'foo', {}) assert errors == [] @@ -225,7 +225,7 @@ def test_setting_into_dict_bad( '''Delivers an error on a bad pgwui setting''' errors = [] - pgwui_server_init.setting_into_dict( + pgwui_server.setting_into_dict( errors, [], {}, 'pgwui.foo', {}) assert errors @@ -239,7 +239,7 @@ def test_setting_into_dict_good( '''Calls dot_to_dict when a known pgwui setting is supplied''' errors = [] - pgwui_server_init.setting_into_dict( + pgwui_server.setting_into_dict( errors, [], {}, 'pgwui.pg_host', {}) mock_dot_to_dict.assert_called_once() @@ -258,7 +258,7 @@ def test_setting_into_dict_plugin_component( errors = [] mock_parse_assignments.return_value = {} - pgwui_server_init.setting_into_dict( + pgwui_server.setting_into_dict( errors, ['pgwui_component'], {}, key, settings) mock_parse_assignments.assert_called_once() @@ -267,7 +267,7 @@ def test_setting_into_dict_plugin_component( mock_setting_into_dict = testing.make_mock_fixture( - pgwui_server_init, 'setting_into_dict') + pgwui_server, 'setting_into_dict') # dictify_settings() @@ -284,7 +284,7 @@ def test_dictify_settings(mock_find_pgwui_check_settings, components = ['pgwui_server'] errors = [] - pgwui_server_init.dictify_settings(errors, settings, components) + pgwui_server.dictify_settings(errors, settings, components) assert mock_validate_setting_values.called assert mock_validate_hmac.called @@ -294,7 +294,7 @@ def test_dictify_settings(mock_find_pgwui_check_settings, mock_dictify_settings = testing.make_mock_fixture( - pgwui_server_init, 'dictify_settings') + pgwui_server, 'dictify_settings') # exit_reporting_errors() @@ -323,7 +323,7 @@ def test_exit_reporting_errors_logged( monkeypatch.setattr(sys, 'exit', assert_exit1()) caplog.set_level(logging.INFO) errors = ['one', 'two', 'three'] - pgwui_server_init.exit_reporting_errors(errors) + pgwui_server.exit_reporting_errors(errors) logs = caplog.record_tuples @@ -341,7 +341,7 @@ def test_exit_reporting_errors_printed( ''' monkeypatch.setattr(sys, 'exit', assert_exit1()) errors = ['one', 'two', 'three'] - pgwui_server_init.exit_reporting_errors(errors) + pgwui_server.exit_reporting_errors(errors) (out, err) = capsys.readouterr() errlines = err.split('\n')[:-1] @@ -354,7 +354,7 @@ def test_exit_reporting_errors_printed( mock_exit_reporting_errors = testing.make_mock_fixture( - pgwui_server_init, 'exit_reporting_errors') + pgwui_server, 'exit_reporting_errors') # exit_on_invalid_settings() @@ -367,10 +367,10 @@ def test_exit_on_invalid_settings_invalid(monkeypatch, def mock_dictify_settings(errors, settings, components): errors.append('error1') - monkeypatch.setattr(pgwui_server_init, 'dictify_settings', + monkeypatch.setattr(pgwui_server, 'dictify_settings', mock_dictify_settings) - pgwui_server_init.exit_on_invalid_settings({}, []) + pgwui_server.exit_on_invalid_settings({}, []) assert mock_exit_reporting_errors.called @@ -378,13 +378,13 @@ def test_exit_on_invalid_settings_invalid(monkeypatch, 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({}, []) + pgwui_server.exit_on_invalid_settings({}, []) assert True mock_exit_on_invalid_settings = testing.make_mock_fixture( - pgwui_server_init, 'exit_on_invalid_settings') + pgwui_server, 'exit_on_invalid_settings') # autoconfigurable_components() @@ -394,7 +394,7 @@ def test_autoconfiguable_components_no_autoconfig(): ''' test_components = ['some', 'components'] - result = pgwui_server_init.autoconfigurable_components( + result = pgwui_server.autoconfigurable_components( {'pgwui': {'autoconfigure': False}}, test_components) assert result == [] @@ -405,7 +405,7 @@ def test_autoconfigurable_components_log_info(caplog): ''' caplog.set_level(logging.INFO) - pgwui_server_init.autoconfigurable_components( + pgwui_server.autoconfigurable_components( {'pgwui': {'autoconfigure': True}, 'pyramid.include': None}, []) @@ -423,14 +423,14 @@ def test_autoconfigurable_components_components_returned(): ''' test_components = ['some', 'components'] - result = pgwui_server_init.autoconfigurable_components( + result = pgwui_server.autoconfigurable_components( {'pgwui': {'pgwui.autoconfigure': True}}, test_components) assert result == test_components mock_autoconfigurable_components = testing.make_mock_fixture( - pgwui_server_init, 'autoconfigurable_components') + pgwui_server, 'autoconfigurable_components') # add_routes() @@ -439,7 +439,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': {}}) + pgwui_server.add_routes(config, {'pgwui': {}}) assert not mocked_add_route.called @@ -452,13 +452,13 @@ 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.add_routes(config, {'pgwui': {'routes': ''}}) assert mocked_add_route.call_count == len(test_routes) mock_add_routes = testing.make_mock_fixture( - pgwui_server_init, 'add_routes') + pgwui_server, 'add_routes') # apply_component_defaults() @@ -474,10 +474,10 @@ def test_apply_component_defaults(monkeypatch, caplog, mock_autoconfigurable_components.return_value = \ ['pgwui_mock_component_name'] - monkeypatch.setattr(pgwui_server_init, 'Configurator', + monkeypatch.setattr(pgwui_server, 'Configurator', MockConfigurator) - result = pgwui_server_init.apply_component_defaults({'pgwui': {}}, []) + result = pgwui_server.apply_component_defaults({'pgwui': {}}, []) assert isinstance(result, MockConfigurator) logs = caplog.record_tuples @@ -490,7 +490,7 @@ def test_apply_component_defaults(monkeypatch, caplog, mock_apply_component_defaults = testing.make_mock_fixture( - pgwui_server_init, 'apply_component_defaults') + pgwui_server, 'apply_component_defaults') # pgwui_server_config() @@ -502,7 +502,7 @@ def test_pgwui_server_config( test_configurator = 'test configurator' mock_apply_component_defaults.return_value = test_configurator - result = pgwui_server_init.pgwui_server_config({}) + result = pgwui_server.pgwui_server_config({}) assert result == test_configurator @@ -510,14 +510,14 @@ def test_pgwui_server_config( # main() def test_main(monkeypatch): '''Returns a wsgi app''' - monkeypatch.setattr(pgwui_server_init, 'pgwui_server_config', + monkeypatch.setattr(pgwui_server, 'pgwui_server_config', lambda *args: MockConfigurator()) - result = pgwui_server_init.main({}) + result = pgwui_server.main({}) assert result == 'wsgi_app' # Integration tests def test_main_integrated(): '''Does not raise errors or warnings''' - pgwui_server_init.main({}, **TEST_SETTINGS) + pgwui_server.main({}, **TEST_SETTINGS) -- 2.34.1