From c34552b41ef238585d17835bc39a5ed585549c36 Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Sat, 28 Dec 2019 15:32:05 -0600 Subject: [PATCH] pgwui.autoconfigure declaration; autodetect pgwui components --- examples/etc/pgwui.ini | 13 ++++-- examples/misc/development.ini | 10 +++-- setup.py | 4 ++ src/pgwui_server/__init__.py | 32 ++++++++++++++ tests/test___init__.py | 80 ++++++++++++++++++++++++++++++++++- 5 files changed, 131 insertions(+), 8 deletions(-) diff --git a/examples/etc/pgwui.ini b/examples/etc/pgwui.ini index 0aeb2e8..bf00341 100644 --- a/examples/etc/pgwui.ini +++ b/examples/etc/pgwui.ini @@ -28,10 +28,15 @@ pgwui.pg_port = 5432 # There are occasions when PGWUI uses a default database. (optional) pgwui.default_db = template1 -# What PGWUI modules to use. -pyramid.includes = - pgwui_logout - pgwui_upload +# Whether to auto-discover the pgwui component modules. (optional) +# When False pgwui component names must be listed in pyramid.includes=... +# pgwui.autoconfigure = True + +# What PGWUI components and other pyramid modules to use. +# (Required when pgwui.autoconfigure is False.) +#pyramid.includes = +# pgwui_logout +# pgwui_upload # Whether or not to change the db content. (required) pgwui.dry_run = False diff --git a/examples/misc/development.ini b/examples/misc/development.ini index b3d8dd2..2ac1cd7 100644 --- a/examples/misc/development.ini +++ b/examples/misc/development.ini @@ -28,10 +28,14 @@ pgwui.pg_port = 5432 # There are occasions when PGWUI uses a default database. (optional) pgwui.default_db = template1 -# What PGWUI modules to use. + +# Whether to auto-discover the pgwui component modules. (optional) +# When False pgwui component names must be listed in pyramid.includes=... +# pgwui.autoconfigure = True + +# What PGWUI components and other pyramid modules to use. +# (Required when pgwui.autoconfigure is False.) pyramid.includes = - pgwui_logout - pgwui_upload pyramid_debugtoolbar # Whether or not to change the db content. (required) diff --git a/setup.py b/setup.py index 0373f3c..f207711 100644 --- a/setup.py +++ b/setup.py @@ -149,6 +149,7 @@ setup( # Run-time dependencies. install_requires=[ 'pgwui_common==' + version, + 'setuptools', # for pkg_resources module 'pyramid', ], @@ -173,6 +174,9 @@ setup( # To provide executable scripts, use entry points in preference to the # "scripts" keyword. Entry points provide cross-platform support and allow # pip to create the appropriate form of executable for the target platform. + # + # Do not register PGWUI_Server in entry_points as a PGWUI component. + # Its configuration is manually coded. entry_points="""\ [paste.app_factory] main = pgwui_server:main diff --git a/src/pgwui_server/__init__.py b/src/pgwui_server/__init__.py index 73ca789..a85140d 100644 --- a/src/pgwui_server/__init__.py +++ b/src/pgwui_server/__init__.py @@ -25,6 +25,7 @@ from ast import literal_eval from pyramid.config import Configurator import logging +import pkg_resources import sys # Constants @@ -38,6 +39,7 @@ SETTINGS = set( 'route_prefix', 'routes', 'validate_hmac', + 'autoconfigure', 'literal_column_headings', ]) @@ -56,6 +58,12 @@ class Error(Exception): pass +class AutoconfigureConflict(Error): + def __init__(self): + super().__init__( + 'Autoconfigure is True and there is a pyramid.include setting') + + class BadSettingsAbort(Error): def __init__(self): super().__init__('Aborting due to bad setting(s)') @@ -237,14 +245,38 @@ def add_routes(config, settings): config.add_route(name, route) +def find_pgwui_components(): + '''Return list of all pgwui component names as strings + ''' + return [entry_point.resolve().__name__ for entry_point in + pkg_resources.iter_entry_points('pgwui.components')] + + +def autoconfig_components(settings): + '''Automatic pgwui component discovery + ''' + autoconfig = settings.get('pgwui.autoconfigure') + if not autoconfig: + return [] + + if 'pyramid.include' in settings: + log.info(AutoconfigureConflict()) + + return find_pgwui_components() + + def pgwui_server_config(settings): '''Configure pyramid ''' exit_on_invalid_settings(settings) + components = autoconfig_components(settings) + rp = settings.get('pgwui.route_prefix') with Configurator(settings=settings, route_prefix=rp) as config: config.include('pgwui_common') + for component in components: + config.include(component) add_routes(config, settings) return config diff --git a/tests/test___init__.py b/tests/test___init__.py index 17fbfa0..9ceda1d 100644 --- a/tests/test___init__.py +++ b/tests/test___init__.py @@ -431,6 +431,82 @@ def test_parse_assignments_dict(): ]) +# find_pgwui_components() + +def test_find_pgwui_components(monkeypatch): + '''Returns list of entry points via iter_entry_points() + ''' + entry_points = ['a', 'b', 'c'] + + class MockEntryPoint(): + def __init__(self, val): + self.__name__ = val + + def resolve(self): + return self + + class MockPkgResources(): + def iter_entry_points(*args): + return [MockEntryPoint(name) for name in entry_points] + + monkeypatch.setattr( + pgwui_server_init, 'pkg_resources', MockPkgResources()) + + result = pgwui_server_init.find_pgwui_components() + + assert result == entry_points + + +# autoconfig_components() + +def test_autoconfig_components_no_autoconfig(monkeypatch): + '''When the settings have no pgwui.autoconfigure return an empty list + ''' + monkeypatch.setattr(pgwui_server_init, 'find_pgwui_components', + lambda *args: []) + + result = pgwui_server_init.autoconfig_components({}) + + assert result == [] + + +def test_autoconfig_components_log_info(monkeypatch, caplog): + '''When pyramid.include is in the settings an INFO message is logged + ''' + monkeypatch.setattr(pgwui_server_init, 'find_pgwui_components', + lambda *args: []) + + caplog.set_level(logging.INFO) + + pgwui_server_init.autoconfig_components({'pgwui.autoconfigure': True, + 'pyramid.include': None}) + + logs = caplog.record_tuples + + assert len(logs) == 1 + + level = logs[0][1] + assert level == logging.INFO + + +def test_autoconfig_components_find_pgwui_components_called(monkeypatch): + '''When pyramid.include is in the settings an INFO message is logged + ''' + find_pgwui_components_called = False + + def mock_find_pgwui_components(*args): + nonlocal find_pgwui_components_called + find_pgwui_components_called = True + + monkeypatch.setattr(pgwui_server_init, 'find_pgwui_components', + mock_find_pgwui_components) + + pgwui_server_init.autoconfig_components({'pgwui.autoconfigure': True, + 'mock_pgwui_component': 'foo'}) + + assert find_pgwui_components_called + + # add_routes() def test_add_routes_empty(): @@ -455,8 +531,10 @@ def test_add_routes_notempty(monkeypatch): def test_pgwui_server_config(monkeypatch): '''Returns a configuration''' - monkeypatch.setattr(pgwui_server_init, 'validate_settings', + monkeypatch.setattr(pgwui_server_init, 'exit_on_invalid_settings', lambda *args: True) + monkeypatch.setattr(pgwui_server_init, 'autoconfig_components', + lambda *args: ['pgwui_mock_component_name']) monkeypatch.setattr(pgwui_server_init, 'Configurator', MockConfigurator) monkeypatch.setattr(pgwui_server_init, 'add_routes', -- 2.34.1