* Code used to establish `routes`_, called by PGWUI_Server
or whatever else is used to configure `Pyramid`_.
+ * Code used to override `assets`_, called by PGWUI_Server
+ or whatever else is used to configure `Pyramid`_.
+
* Functionality which validates all installed PGWUI component
settings. Validation happens when the PGWUI_Common component
is configured.
.. _Pyramid: https://trypyramid.com/
.. _Pyramid's: `Pyramid`_
.. _routes: https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/urldispatch.html
+.. _assets: https://docs.pylonsproject.org/projects/pyramid/en/1.10-branch/narr/assets.html
--- /dev/null
+# Copyright (C) 2018, 2020 The Meme Factory, Inc. http://www.karlpinc.com/
+
+# This file is part of PGWUI_Common.
+#
+# 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
+# <http://www.gnu.org/licenses/>.
+#
+
+# Karl O. Pinc <kop@karlpinc.com>
+
+'''Override assets based on settings
+'''
+
+import logging
+
+# Logging
+log = logging.getLogger(__name__)
+
+
+def override_assets(config, settings):
+ pgwui = settings['pgwui']
+ if 'override_assets' not in pgwui:
+ return
+
+ for asset, replacement in pgwui['override_assets'].items():
+ config.override_asset(asset, replacement)
+ log.debug(f'Overriding asset ({asset}) with ({replacement})')
.format(key))
+class BadPathError(Error):
+ pass
+
+
+class BadRouteError(BadPathError):
+ def __init__(self, page, ex):
+ super().__init__(
+ page, ex,
+ f'The "pgwui:{page}:source" configuration setting refers to '
+ 'a route that does not exist')
+
+
+class BadAssetError(BadPathError):
+ def __init__(self, page, ex):
+ super().__init__(
+ page, ex,
+ f'The "pgwui:{page}:source" configuration setting refers to '
+ 'an asset that does not exist')
+
+
class ViewError(Error):
pass
page, ex,
f'The "pgwui:{page}:source" configuration setting refers to '
f'a directory ({ex.filename}), not a file')
-
-
-class BadRouteError(BadPageError):
- def __init__(self, page, ex):
- super().__init__(
- page, ex,
- f'The "pgwui:{page}:source" configuration setting refers to '
- 'a route that does not exist')
-
-
-class BadAssetError(BadPageError):
- def __init__(self, page, ex):
- super().__init__(
- page, ex,
- f'The "pgwui:{page}:source" configuration setting refers to '
- 'an asset that does not exist')
try:
menu_url = request.route_path('pgwui_menu')
except KeyError:
- return
+ return [] # the pgwui_menu component is optional
+ except ex.BadPathError as exp:
+ return [exp]
if menu_url != urls['pgwui_home']:
urls['pgwui_menu'] = menu_url
+ return []
def set_component_urls(request, urls):
'''Add urls for each pgwui component to the 'urls' dict
'''
- set_menu_url(request, urls)
+ errors = set_menu_url(request, urls)
components = find_pgwui_components()
if 'pgwui_menu' in components:
components.remove('pgwui_menu')
else:
urls.setdefault(component, url)
+ return errors
+
def set_urls(request, urls):
'''Build 'urls' dict with all the urls
'''
- home_url = url_of_page(request, 'home_page')
- urls.setdefault('pgwui_home', home_url)
- set_component_urls(request, urls)
+ errors = []
+ try:
+ home_url = url_of_page(request, 'home_page')
+ except ex.BadPathError as exp:
+ errors.append(exp)
+ # set_component_urls() requires a 'pgwui_home' key, and the error
+ # means the program will exit and the value never be used.
+ urls.setdefault('pgwui_home', None)
+ else:
+ urls.setdefault('pgwui_home', home_url)
+ errors.extend(set_component_urls(request, urls))
+ return errors
def add_urls_setting(config, settings):
# be installed in a production enviornment. And some RAM.
request = pyramid.request.Request.blank('/')
request.registry = config.registry
- set_urls(request, urls)
+ errors = set_urls(request, urls)
settings['pgwui']['urls'] = urls
log.debug('Routing map of route names to url paths which is given'
f' to the templates: {urls}')
+ return errors
--- /dev/null
+# Copyright (C) 2018, 2020 The Meme Factory, Inc. http://www.karlpinc.com/
+
+# This file is part of PGWUI_Common.
+#
+# 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
+# <http://www.gnu.org/licenses/>.
+#
+
+# Karl O. Pinc <kop@karlpinc.com>
+
+import pytest
+
+import logging
+import pgwui_common.assets as assets
+
+from pgwui_testing import testing
+
+# Activiate the PGWUI pytest plugin
+pytest_plugins = ("pgwui",)
+
+# Mark all tests with "unittest"
+pytestmark = pytest.mark.unittest
+
+mock_override_asset = testing.instance_method_mock_fixture('override_asset')
+
+
+# override_asset()
+
+@pytest.mark.parametrize(
+ ('overrides', 'call_count'), [
+ ({}, 0),
+ ({'some_asset': 'new_asset'}, 1),
+ ({'some_asset': 'new_asset',
+ 'other_asset': 'other new asset'}, 2)])
+def test_override_asset(
+ caplog, pyramid_config, mock_override_asset, overrides, call_count):
+ '''override_asset() is called for each override
+ '''
+ caplog.set_level(logging.DEBUG)
+ mocked_override_asset = mock_override_asset(pyramid_config)
+
+ if overrides:
+ settings = {'override_assets': overrides}
+ else:
+ settings = {}
+ assets.override_assets(pyramid_config, {'pgwui': settings})
+
+ assert mocked_override_asset.call_count == call_count
+ assert len(caplog.record_tuples) == call_count
# set_menu_url()
@pytest.mark.parametrize(
- "test_urls,expected",
+ ('test_urls', 'expected_urls'),
[
# menu and home have identical urls, no url is added for menu
({'pgwui_menu': '/', 'pgwui_home': '/'},
# menu and home have different urls, url is added for menu
({'pgwui_menu': '/menu', 'pgwui_home': '/'},
{'pgwui_menu': '/menu'})])
-def test_set_menu_url(
+def test_set_menu_url_good_path(
pyramid_request_config, mock_method_route_path, mock_url_of_page,
- test_urls, expected):
- '''The expected urls are returned
+ test_urls, expected_urls):
+ '''The expected urls are returned without errors when the path
+ settings are good, when the page's assets and routes have paths
'''
def path_func(name):
return test_urls[name]
mocked_route_path.side_effect = path_func
urls_dict = {'pgwui_home': test_urls['pgwui_home']}
- expected.update(urls_dict)
- urls.set_menu_url(request, urls_dict)
+ expected_urls.update(urls_dict)
+ result = urls.set_menu_url(request, urls_dict)
- assert urls_dict == expected
+ assert urls_dict == expected_urls
+ assert result == []
+
+
+def test_set_menu_url_bad_page(
+ pyramid_request_config, mock_method_route_path, mock_url_of_page):
+ '''The expected urls are returned with errors when the page settings
+ are bad, when the page's asset or route has no path
+ '''
+ expected_urls = {'home_page': '/', 'pgwui_logout': '/logout'}
+
+ mock_url_of_page.side_effect = common_ex.BadPathError
+ request = get_current_request
+ new_urls = expected_urls.copy()
+ result = urls.set_menu_url(request, new_urls)
+
+ assert new_urls == expected_urls
+ assert len(result) == 1
+ assert isinstance(result[0], common_ex.BadPathError)
mock_set_menu_url = testing.make_mock_fixture(
pyramid_request_config, mock_method_route_path, mock_set_menu_url,
mock_find_pgwui_components, test_urls):
'''Urls are set for every component which has a route, except for
- pgwui_menu
+ pgwui_menu, expected errors are returned
'''
+ test_errors = ['some error']
test_components = list(test_urls) + ['pgwui_noroute']
def url_func(url):
return test_urls[url]
+ mock_set_menu_url.return_value = test_errors
request = get_current_request()
mocked_route_path = mock_method_route_path(request)
mocked_route_path.side_effect = url_func
mock_find_pgwui_components.return_value = test_components
urls_dict = dict()
- urls.set_component_urls(request, urls_dict)
+ result = urls.set_component_urls(request, urls_dict)
expected_urls = test_urls.copy()
if 'pgwui_menu' in expected_urls:
mock_set_menu_url.assert_called_once()
assert urls_dict == expected_urls
+ assert result == test_errors
mock_set_component_urls = testing.make_mock_fixture(
# set_urls()
-def test_set_urls(
+def test_set_urls_good_path(
pyramid_request_config, mock_url_of_page, mock_set_component_urls):
- '''The 'home' url is added and set_component_urls() called
+ '''When the 'home_page' route path is good the 'home' url is added,
+ set_component_urls() called, and the expected errors are returned
'''
+ component_errors = ['some error']
test_home_route = '/'
request = get_current_request()
+ mock_set_component_urls.return_value = component_errors
mock_url_of_page.return_value = test_home_route
urls_dict = dict()
- urls.set_urls(request, urls_dict)
+ result = urls.set_urls(request, urls_dict)
assert urls_dict['pgwui_home'] == test_home_route
mock_set_component_urls.assert_called_once()
+ assert result == component_errors
+
+
+def test_set_urls_bad_path(
+ pyramid_request_config, mock_url_of_page, mock_set_component_urls):
+ '''When the 'home_page' route is bad a bad path error is one of the errors
+ returned
+ '''
+ component_errors = ['some error']
+ home_error = common_ex.BadPathError()
+ request = get_current_request()
+
+ mock_url_of_page.side_effect = home_error
+ mock_set_component_urls.return_value = component_errors
+
+ urls_dict = dict()
+ result = urls.set_urls(request, urls_dict)
+
+ assert 'pgwui_home' in urls_dict
+ mock_set_component_urls.assert_called_once()
+ assert home_error in result
mock_set_urls = testing.make_mock_fixture(
are put in the pgwui dict in the settings
'''
caplog.set_level(logging.DEBUG)
+ expected_errors = ['some error']
expected_urls = {'key1': 'val1', 'key2': 'val2'}
def set_urls(request, urls):
urls.update(expected_urls)
+ return expected_errors
mock_set_urls.side_effect = set_urls
request.registry.settings = request_settings
settings = {'pgwui': {}}
- urls.add_urls_setting(pyramid_request_config, settings)
+ result = urls.add_urls_setting(pyramid_request_config, settings)
mock_request_blank.blank.assert_called_once()
mock_set_urls.assert_called_once()
assert settings['pgwui']['urls'] == expected_urls
+ assert result == expected_errors
+
logs = caplog.record_tuples
assert len(logs) == 1
assert logs[0][1] == logging.DEBUG