diff options
author | Pierre <pierre.equoy@protonmail.com> | 2022-02-20 10:29:06 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-20 10:29:06 +0800 |
commit | 34ca2e1de23a7b3afa87bd7afcf5801801938dcd (patch) | |
tree | 171e5cce85de88a59eeb515fa5042385af6222c4 | |
parent | 4794752dd9ecd4f80eac52f84bd15abbf5bb9d4b (diff) | |
parent | 77e5381be73347d7f1ad6e616d1460ca023e920f (diff) | |
download | pelican-34ca2e1de23a7b3afa87bd7afcf5801801938dcd.tar.gz |
Merge branch 'getpelican:master' into master
-rw-r--r-- | docs/changelog.rst | 7 | ||||
-rw-r--r-- | docs/settings.rst | 9 | ||||
-rw-r--r-- | pelican/__init__.py | 50 | ||||
-rw-r--r-- | pelican/cache.py | 2 | ||||
-rw-r--r-- | pelican/settings.py | 23 | ||||
-rw-r--r-- | pelican/tests/output/basic/categories.html | 6 | ||||
-rw-r--r-- | pelican/tests/output/custom/categories.html | 6 | ||||
-rw-r--r-- | pelican/tests/output/custom_locale/categories.html | 6 | ||||
-rw-r--r-- | pelican/tests/test_cli.py | 72 | ||||
-rw-r--r-- | pelican/tests/test_settings.py | 17 | ||||
-rw-r--r-- | pelican/themes/notmyidea/templates/categories.html | 16 | ||||
-rw-r--r-- | pyproject.toml | 4 | ||||
-rw-r--r-- | requirements/docs.pip | 2 | ||||
-rwxr-xr-x | setup.py | 2 |
14 files changed, 154 insertions, 68 deletions
diff --git a/docs/changelog.rst b/docs/changelog.rst index b76a4575..576e3d0c 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,13 @@ Release history ############### +4.7.2 - 2022-02-09 +================== + +* Fix incorrect parsing of parameters specified via `-e` / `--extra-settings` option flags `(#2938) <https://github.com/getpelican/pelican/pull/2938>`_ +* Add ``categories.html`` template to default theme `(#2973) <https://github.com/getpelican/pelican/pull/2973>`_ +* Document how to use plugins to inject content `(#2922) <https://github.com/getpelican/pelican/pull/2922>`_ + 4.7.1 - 2021-10-12 ================== diff --git a/docs/settings.rst b/docs/settings.rst index 47852527..675e6b8f 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -9,11 +9,12 @@ line:: If you used the ``pelican-quickstart`` command, your primary settings file will be named ``pelicanconf.py`` by default. -You can also specify extra settings via ``-e`` / ``--extra-settings`` option -flags, which will override default settings as well as any defined within -settings files:: +You can also specify settings via ``-e`` / ``--extra-settings`` option +flags. It will override default settings as well as any defined within the +setting file. Note that values must follow JSON notation:: + + pelican content -e SITENAME='"A site"' READERS='{"html": null}' CACHE_CONTENT=true - pelican content -e DELETE_OUTPUT_DIRECTORY=true .. note:: diff --git a/pelican/__init__.py b/pelican/__init__.py index 99aa2776..9858dbd3 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -1,4 +1,5 @@ import argparse +import json import logging import multiprocessing import os @@ -24,7 +25,7 @@ from pelican.plugins import signals from pelican.plugins._utils import get_plugin_name, load_plugins from pelican.readers import Readers from pelican.server import ComplexHTTPRequestHandler, RootedHTTPServer -from pelican.settings import coerce_overrides, read_settings +from pelican.settings import read_settings from pelican.utils import (FileSystemWatcher, clean_output_dir, maybe_pluralize) from pelican.writers import Writer @@ -259,16 +260,29 @@ class PrintSettings(argparse.Action): parser.exit() -class ParseDict(argparse.Action): +class ParseOverrides(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): - d = {} - if values: - for item in values: - split_items = item.split("=", 1) - key = split_items[0].strip() - value = split_items[1].strip() - d[key] = value - setattr(namespace, self.dest, d) + overrides = {} + for item in values: + try: + k, v = item.split("=", 1) + except ValueError: + raise ValueError( + 'Extra settings must be specified as KEY=VALUE pairs ' + f'but you specified {item}' + ) + try: + overrides[k] = json.loads(v) + except json.decoder.JSONDecodeError: + raise ValueError( + f'Invalid JSON value: {v}. ' + 'Values specified via -e / --extra-settings flags ' + 'must be in JSON notation. ' + 'Use -e KEY=\'"string"\' to specify a string value; ' + '-e KEY=null to specify None; ' + '-e KEY=false (or true) to specify False (or True).' + ) + setattr(namespace, self.dest, overrides) def parse_arguments(argv=None): @@ -366,13 +380,13 @@ def parse_arguments(argv=None): parser.add_argument('-e', '--extra-settings', dest='overrides', help='Specify one or more SETTING=VALUE pairs to ' - 'override settings. If VALUE contains spaces, ' - 'add quotes: SETTING="VALUE". Values other than ' - 'integers and strings can be specified via JSON ' - 'notation. (e.g., SETTING=none)', + 'override settings. VALUE must be in JSON notation: ' + 'specify string values as SETTING=\'"some string"\'; ' + 'booleans as SETTING=true or SETTING=false; ' + 'None as SETTING=null.', nargs='*', - action=ParseDict - ) + action=ParseOverrides, + default={}) args = parser.parse_args(argv) @@ -385,6 +399,8 @@ def parse_arguments(argv=None): def get_config(args): + """Builds a config dictionary based on supplied `args`. + """ config = {} if args.path: config['PATH'] = os.path.abspath(os.path.expanduser(args.path)) @@ -409,7 +425,7 @@ def get_config(args): if args.bind is not None: config['BIND'] = args.bind config['DEBUG'] = args.verbosity == logging.DEBUG - config.update(coerce_overrides(args.overrides)) + config.update(args.overrides) return config diff --git a/pelican/cache.py b/pelican/cache.py index c1d4a55d..d2665691 100644 --- a/pelican/cache.py +++ b/pelican/cache.py @@ -74,7 +74,7 @@ class FileStampDataCacher(FileDataCacher): """Subclass that also caches the stamp of the file""" def __init__(self, settings, cache_name, caching_policy, load_policy): - """This sublcass additionally sets filestamp function + """This subclass additionally sets filestamp function and base path for filestamping operations """ diff --git a/pelican/settings.py b/pelican/settings.py index ea3ee8eb..5b495e86 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -1,7 +1,6 @@ import copy import importlib.util import inspect -import json import locale import logging import os @@ -659,25 +658,3 @@ def configure_settings(settings): continue # setting not specified, nothing to do return settings - - -def coerce_overrides(overrides): - if overrides is None: - return {} - coerced = {} - types_to_cast = {int, str, bool} - for k, v in overrides.items(): - if k not in DEFAULT_CONFIG: - logger.warning('Override for unknown setting %s, ignoring', k) - continue - setting_type = type(DEFAULT_CONFIG[k]) - if setting_type not in types_to_cast: - coerced[k] = json.loads(v) - else: - try: - coerced[k] = setting_type(v) - except ValueError: - logger.debug('ValueError for %s override with %s, try to ' - 'load as json', k, v) - coerced[k] = json.loads(v) - return coerced diff --git a/pelican/tests/output/basic/categories.html b/pelican/tests/output/basic/categories.html index 930503a8..f6c17dd5 100644 --- a/pelican/tests/output/basic/categories.html +++ b/pelican/tests/output/basic/categories.html @@ -22,13 +22,17 @@ <li><a href="/category/yeah.html">yeah</a></li> </ul></nav> </header><!-- /#banner --> - <h1>Categories on A Pelican Blog</h1> + + <section id="content" class="body"> + <h1>Categories for A Pelican Blog</h1> <ul> <li><a href="/category/bar.html">bar</a> (1)</li> <li><a href="/category/cat1.html">cat1</a> (4)</li> <li><a href="/category/misc.html">misc</a> (4)</li> <li><a href="/category/yeah.html">yeah</a> (1)</li> </ul> + </section> + <section id="extras" class="body"> <div class="social"> <h2>social</h2> diff --git a/pelican/tests/output/custom/categories.html b/pelican/tests/output/custom/categories.html index 543eb1ee..74990d4b 100644 --- a/pelican/tests/output/custom/categories.html +++ b/pelican/tests/output/custom/categories.html @@ -26,13 +26,17 @@ <li><a href="./category/bar.html">bar</a></li> </ul></nav> </header><!-- /#banner --> - <h1>Categories on Alexis' log</h1> + + <section id="content" class="body"> + <h1>Categories for Alexis' log</h1> <ul> <li><a href="./category/bar.html">bar</a> (1)</li> <li><a href="./category/cat1.html">cat1</a> (4)</li> <li><a href="./category/misc.html">misc</a> (4)</li> <li><a href="./category/yeah.html">yeah</a> (1)</li> </ul> + </section> + <section id="extras" class="body"> <div class="blogroll"> <h2>links</h2> diff --git a/pelican/tests/output/custom_locale/categories.html b/pelican/tests/output/custom_locale/categories.html index 0d46b9a0..1decaabd 100644 --- a/pelican/tests/output/custom_locale/categories.html +++ b/pelican/tests/output/custom_locale/categories.html @@ -26,13 +26,17 @@ <li><a href="./category/bar.html">bar</a></li> </ul></nav> </header><!-- /#banner --> - <h1>Categories on Alexis' log</h1> + + <section id="content" class="body"> + <h1>Categories for Alexis' log</h1> <ul> <li><a href="./category/bar.html">bar</a> (1)</li> <li><a href="./category/cat1.html">cat1</a> (4)</li> <li><a href="./category/misc.html">misc</a> (4)</li> <li><a href="./category/yeah.html">yeah</a> (1)</li> </ul> + </section> + <section id="extras" class="body"> <div class="blogroll"> <h2>links</h2> diff --git a/pelican/tests/test_cli.py b/pelican/tests/test_cli.py new file mode 100644 index 00000000..13b307e7 --- /dev/null +++ b/pelican/tests/test_cli.py @@ -0,0 +1,72 @@ +import unittest + +from pelican import get_config, parse_arguments + + +class TestParseOverrides(unittest.TestCase): + def test_flags(self): + for flag in ['-e', '--extra-settings']: + args = parse_arguments([flag, 'k=1']) + self.assertDictEqual(args.overrides, {'k': 1}) + + def test_parse_multiple_items(self): + args = parse_arguments('-e k1=1 k2=2'.split()) + self.assertDictEqual(args.overrides, {'k1': 1, 'k2': 2}) + + def test_parse_valid_json(self): + json_values_python_values_map = { + '""': '', + 'null': None, + '"string"': 'string', + '["foo", 12, "4", {}]': ['foo', 12, '4', {}] + } + for k, v in json_values_python_values_map.items(): + args = parse_arguments(['-e', 'k=' + k]) + self.assertDictEqual(args.overrides, {'k': v}) + + def test_parse_invalid_syntax(self): + invalid_items = ['k= 1', 'k =1', 'k', 'k v'] + for item in invalid_items: + with self.assertRaises(ValueError): + parse_arguments(f'-e {item}'.split()) + + def test_parse_invalid_json(self): + invalid_json = { + '', 'False', 'True', 'None', 'some other string', + '{"foo": bar}', '[foo]' + } + for v in invalid_json: + with self.assertRaises(ValueError): + parse_arguments(['-e ', 'k=' + v]) + + +class TestGetConfigFromArgs(unittest.TestCase): + def test_overrides_known_keys(self): + args = parse_arguments([ + '-e', + 'DELETE_OUTPUT_DIRECTORY=false', + 'OUTPUT_RETENTION=["1.txt"]', + 'SITENAME="Title"' + ]) + config = get_config(args) + config_must_contain = { + 'DELETE_OUTPUT_DIRECTORY': False, + 'OUTPUT_RETENTION': ['1.txt'], + 'SITENAME': 'Title' + } + self.assertDictEqual(config, {**config, **config_must_contain}) + + def test_overrides_non_default_type(self): + args = parse_arguments([ + '-e', + 'DISPLAY_PAGES_ON_MENU=123', + 'PAGE_TRANSLATION_ID=null', + 'TRANSLATION_FEED_RSS_URL="someurl"' + ]) + config = get_config(args) + config_must_contain = { + 'DISPLAY_PAGES_ON_MENU': 123, + 'PAGE_TRANSLATION_ID': None, + 'TRANSLATION_FEED_RSS_URL': 'someurl' + } + self.assertDictEqual(config, {**config, **config_must_contain}) diff --git a/pelican/tests/test_settings.py b/pelican/tests/test_settings.py index 83203ae5..c407f7c8 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -7,7 +7,7 @@ from sys import platform from pelican.settings import (DEFAULT_CONFIG, DEFAULT_THEME, _printf_s_to_format_field, - coerce_overrides, configure_settings, + configure_settings, handle_deprecated_settings, read_settings) from pelican.tests.support import unittest @@ -304,18 +304,3 @@ class TestSettingsConfiguration(unittest.TestCase): [(r'C\+\+', 'cpp')] + self.settings['SLUG_REGEX_SUBSTITUTIONS']) self.assertNotIn('SLUG_SUBSTITUTIONS', settings) - - def test_coerce_overrides(self): - overrides = coerce_overrides({ - 'ARTICLE_EXCLUDES': '["testexcl"]', - 'READERS': '{"foo": "bar"}', - 'STATIC_EXCLUDE_SOURCES': 'true', - 'THEME_STATIC_DIR': 'theme', - }) - expected = { - 'ARTICLE_EXCLUDES': ["testexcl"], - 'READERS': {"foo": "bar"}, - 'STATIC_EXCLUDE_SOURCES': True, - 'THEME_STATIC_DIR': 'theme', - } - self.assertDictEqual(overrides, expected) diff --git a/pelican/themes/notmyidea/templates/categories.html b/pelican/themes/notmyidea/templates/categories.html new file mode 100644 index 00000000..07f6290a --- /dev/null +++ b/pelican/themes/notmyidea/templates/categories.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + +{% block title %}{{ SITENAME }} - Categories{% endblock %} + +{% block content %} + +<section id="content" class="body"> + <h1>Categories for {{ SITENAME }}</h1> + <ul> + {% for category, articles in categories|sort %} + <li><a href="{{ SITEURL }}/{{ category.url }}">{{ category }}</a> ({{ articles|count }})</li> + {% endfor %} + </ul> +</section> + +{% endblock %} diff --git a/pyproject.toml b/pyproject.toml index 8d7749b0..3d226c5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pelican" -version = "4.7.1" +version = "4.7.2" description = "Static site generator supporting Markdown and reStructuredText" authors = ["Justin Mayer <entrop@gmail.com>"] license = "AGPLv3" @@ -48,7 +48,7 @@ jinja2 = "~2.11" lxml = "^4.3" markdown = "~3.3.4" typogrify = "^2.0" -sphinx = "^3.0" +sphinx = "<4.4.0" sphinx_rtd_theme = "^0.5" livereload = "^2.6" psutil = {version = "^5.7", optional = true} diff --git a/requirements/docs.pip b/requirements/docs.pip index bd25c939..a5a184ef 100644 --- a/requirements/docs.pip +++ b/requirements/docs.pip @@ -1,3 +1,3 @@ -sphinx +sphinx<4.4.0 sphinx_rtd_theme livereload @@ -6,7 +6,7 @@ from os.path import join, relpath from setuptools import find_packages, setup -version = "4.7.1" +version = "4.7.2" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', |