From d8172318366f8147c1bee07f0f1cabf3d7256d5d Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 23 Apr 2020 12:49:44 -0600 Subject: [PATCH 001/465] Allow generators to deal with settings that are `pathlib.Path`s --- pelican/generators.py | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index 63e20a0a..424e9c22 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -18,7 +18,6 @@ from pelican.readers import Readers from pelican.utils import (DateFormatter, copy, mkdir_p, order_content, posixize_path, process_translations) - logger = logging.getLogger(__name__) @@ -322,8 +321,9 @@ class ArticlesGenerator(CachingGenerator): all_articles = list(self.articles) for article in self.articles: all_articles.extend(article.translations) - order_content(all_articles, - order_by=self.settings['ARTICLE_ORDER_BY']) + order_content( + all_articles, order_by=self.settings['ARTICLE_ORDER_BY'] + ) if self.settings.get('FEED_ALL_ATOM'): writer.write_feed( @@ -352,7 +352,7 @@ class ArticlesGenerator(CachingGenerator): self.settings['CATEGORY_FEED_ATOM'].format(slug=cat.slug), self.settings.get( 'CATEGORY_FEED_ATOM_URL', - self.settings['CATEGORY_FEED_ATOM']).format( + str(self.settings['CATEGORY_FEED_ATOM'])).format( slug=cat.slug ), feed_title=cat.name @@ -365,7 +365,7 @@ class ArticlesGenerator(CachingGenerator): self.settings['CATEGORY_FEED_RSS'].format(slug=cat.slug), self.settings.get( 'CATEGORY_FEED_RSS_URL', - self.settings['CATEGORY_FEED_RSS']).format( + str(self.settings['CATEGORY_FEED_RSS'])).format( slug=cat.slug ), feed_title=cat.name, @@ -380,8 +380,9 @@ class ArticlesGenerator(CachingGenerator): self.settings['AUTHOR_FEED_ATOM'].format(slug=auth.slug), self.settings.get( 'AUTHOR_FEED_ATOM_URL', - self.settings['AUTHOR_FEED_ATOM'] - ).format(slug=auth.slug), + str(self.settings['AUTHOR_FEED_ATOM'])).format( + slug=auth.slug + ), feed_title=auth.name ) @@ -392,8 +393,9 @@ class ArticlesGenerator(CachingGenerator): self.settings['AUTHOR_FEED_RSS'].format(slug=auth.slug), self.settings.get( 'AUTHOR_FEED_RSS_URL', - self.settings['AUTHOR_FEED_RSS'] - ).format(slug=auth.slug), + str(self.settings['AUTHOR_FEED_RSS'])).format( + slug=auth.slug + ), feed_title=auth.name, feed_type='rss' ) @@ -408,8 +410,9 @@ class ArticlesGenerator(CachingGenerator): self.settings['TAG_FEED_ATOM'].format(slug=tag.slug), self.settings.get( 'TAG_FEED_ATOM_URL', - self.settings['TAG_FEED_ATOM'] - ).format(slug=tag.slug), + str(self.settings['TAG_FEED_ATOM'])).format( + slug=tag.slug + ), feed_title=tag.name ) @@ -420,8 +423,9 @@ class ArticlesGenerator(CachingGenerator): self.settings['TAG_FEED_RSS'].format(slug=tag.slug), self.settings.get( 'TAG_FEED_RSS_URL', - self.settings['TAG_FEED_RSS'] - ).format(slug=tag.slug), + str(self.settings['TAG_FEED_RSS'])).format( + slug=tag.slug + ), feed_title=tag.name, feed_type='rss' ) @@ -443,7 +447,8 @@ class ArticlesGenerator(CachingGenerator): .format(lang=lang), self.settings.get( 'TRANSLATION_FEED_ATOM_URL', - self.settings['TRANSLATION_FEED_ATOM'] + str( + self.settings['TRANSLATION_FEED_ATOM']) ).format(lang=lang), ) if self.settings.get('TRANSLATION_FEED_RSS'): @@ -454,8 +459,9 @@ class ArticlesGenerator(CachingGenerator): .format(lang=lang), self.settings.get( 'TRANSLATION_FEED_RSS_URL', - self.settings['TRANSLATION_FEED_RSS'] - ).format(lang=lang), + str(self.settings['TRANSLATION_FEED_RSS'])).format( + lang=lang + ), feed_type='rss' ) From cfba3d72beef60de10311a75f83ebb259269fed8 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 23 Apr 2020 13:47:10 -0600 Subject: [PATCH 002/465] fix testing failures when settings could be pathlib.Path --- pelican/contents.py | 2 +- pelican/generators.py | 52 +++++++++++++++++++++++------------------- pelican/settings.py | 4 ++-- pelican/urlwrappers.py | 3 +++ pelican/utils.py | 34 ++++++++++++++++++--------- pelican/writers.py | 2 +- 6 files changed, 58 insertions(+), 39 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 2bb2e3a0..2e29d84e 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -215,7 +215,7 @@ class Content: if not klass: klass = self.__class__.__name__ fq_key = ('{}_{}'.format(klass, key)).upper() - return self.settings[fq_key].format(**self.url_format) + return str(self.settings[fq_key]).format(**self.url_format) def get_url_setting(self, key): if hasattr(self, 'override_' + key): diff --git a/pelican/generators.py b/pelican/generators.py index 424e9c22..d92e8ff8 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -349,12 +349,12 @@ class ArticlesGenerator(CachingGenerator): writer.write_feed( arts, self.context, - self.settings['CATEGORY_FEED_ATOM'].format(slug=cat.slug), + str(self.settings['CATEGORY_FEED_ATOM']).format(slug=cat.slug), self.settings.get( 'CATEGORY_FEED_ATOM_URL', - str(self.settings['CATEGORY_FEED_ATOM'])).format( + str(self.settings['CATEGORY_FEED_ATOM']).format( slug=cat.slug - ), + )), feed_title=cat.name ) @@ -362,12 +362,12 @@ class ArticlesGenerator(CachingGenerator): writer.write_feed( arts, self.context, - self.settings['CATEGORY_FEED_RSS'].format(slug=cat.slug), + str(self.settings['CATEGORY_FEED_RSS']).format(slug=cat.slug), self.settings.get( 'CATEGORY_FEED_RSS_URL', - str(self.settings['CATEGORY_FEED_RSS'])).format( + str(self.settings['CATEGORY_FEED_RSS']).format( slug=cat.slug - ), + )), feed_title=cat.name, feed_type='rss' ) @@ -377,12 +377,12 @@ class ArticlesGenerator(CachingGenerator): writer.write_feed( arts, self.context, - self.settings['AUTHOR_FEED_ATOM'].format(slug=auth.slug), + str(self.settings['AUTHOR_FEED_ATOM']).format(slug=auth.slug), self.settings.get( 'AUTHOR_FEED_ATOM_URL', - str(self.settings['AUTHOR_FEED_ATOM'])).format( + str(self.settings['AUTHOR_FEED_ATOM']).format( slug=auth.slug - ), + )), feed_title=auth.name ) @@ -390,12 +390,12 @@ class ArticlesGenerator(CachingGenerator): writer.write_feed( arts, self.context, - self.settings['AUTHOR_FEED_RSS'].format(slug=auth.slug), + str(self.settings['AUTHOR_FEED_RSS']).format(slug=auth.slug), self.settings.get( 'AUTHOR_FEED_RSS_URL', - str(self.settings['AUTHOR_FEED_RSS'])).format( + str(self.settings['AUTHOR_FEED_RSS']).format( slug=auth.slug - ), + )), feed_title=auth.name, feed_type='rss' ) @@ -407,12 +407,12 @@ class ArticlesGenerator(CachingGenerator): writer.write_feed( arts, self.context, - self.settings['TAG_FEED_ATOM'].format(slug=tag.slug), + str(self.settings['TAG_FEED_ATOM']).format(slug=tag.slug), self.settings.get( 'TAG_FEED_ATOM_URL', - str(self.settings['TAG_FEED_ATOM'])).format( + str(self.settings['TAG_FEED_ATOM']).format( slug=tag.slug - ), + )), feed_title=tag.name ) @@ -420,12 +420,12 @@ class ArticlesGenerator(CachingGenerator): writer.write_feed( arts, self.context, - self.settings['TAG_FEED_RSS'].format(slug=tag.slug), + str(self.settings['TAG_FEED_RSS']).format(slug=tag.slug), self.settings.get( 'TAG_FEED_RSS_URL', - str(self.settings['TAG_FEED_RSS'])).format( + str(self.settings['TAG_FEED_RSS']).format( slug=tag.slug - ), + )), feed_title=tag.name, feed_type='rss' ) @@ -443,27 +443,31 @@ class ArticlesGenerator(CachingGenerator): writer.write_feed( items, self.context, - self.settings['TRANSLATION_FEED_ATOM'] - .format(lang=lang), + str( + self.settings['TRANSLATION_FEED_ATOM'] + ).format(lang=lang), self.settings.get( 'TRANSLATION_FEED_ATOM_URL', str( - self.settings['TRANSLATION_FEED_ATOM']) + self.settings['TRANSLATION_FEED_ATOM'] ).format(lang=lang), ) + ) if self.settings.get('TRANSLATION_FEED_RSS'): writer.write_feed( items, self.context, - self.settings['TRANSLATION_FEED_RSS'] - .format(lang=lang), + str( + self.settings['TRANSLATION_FEED_RSS'] + ).format(lang=lang), self.settings.get( 'TRANSLATION_FEED_RSS_URL', - str(self.settings['TRANSLATION_FEED_RSS'])).format( + str(self.settings['TRANSLATION_FEED_RSS']).format( lang=lang ), feed_type='rss' ) + ) def generate_articles(self, write): """Generate the articles.""" diff --git a/pelican/settings.py b/pelican/settings.py index 7b333de8..ddb6748d 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -406,7 +406,7 @@ def handle_deprecated_settings(settings): for key in ['TRANSLATION_FEED_ATOM', 'TRANSLATION_FEED_RSS' ]: - if settings.get(key) and '%s' in settings[key]: + if settings.get(key) and not isinstance(settings[key], Path) and '%s' in settings[key]: logger.warning('%%s usage in %s is deprecated, use {lang} ' 'instead.', key) try: @@ -423,7 +423,7 @@ def handle_deprecated_settings(settings): 'TAG_FEED_ATOM', 'TAG_FEED_RSS', ]: - if settings.get(key) and '%s' in settings[key]: + if settings.get(key) and not isinstance(settings[key], Path) and '%s' in settings[key]: logger.warning('%%s usage in %s is deprecated, use {slug} ' 'instead.', key) try: diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index efe09fbc..e00b914c 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -1,6 +1,7 @@ import functools import logging import os +import pathlib from pelican.utils import slugify @@ -110,6 +111,8 @@ class URLWrapper: """ setting = "{}_{}".format(self.__class__.__name__.upper(), key) value = self.settings[setting] + if isinstance(value, pathlib.Path): + value = str(value) if not isinstance(value, str): logger.warning('%s is set to %s', setting, value) return value diff --git a/pelican/utils.py b/pelican/utils.py index e82117d3..a3ece8ce 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -3,6 +3,7 @@ import fnmatch import locale import logging import os +import pathlib import re import shutil import sys @@ -921,17 +922,28 @@ def split_all(path): >>> split_all(os.path.join('a', 'b', 'c')) ['a', 'b', 'c'] """ - components = [] - path = path.lstrip('/') - while path: - head, tail = os.path.split(path) - if tail: - components.insert(0, tail) - elif head == path: - components.insert(0, head) - break - path = head - return components + if isinstance(path, str): + components = [] + path = path.lstrip('/') + while path: + head, tail = os.path.split(path) + if tail: + components.insert(0, tail) + elif head == path: + components.insert(0, head) + break + path = head + return components + elif isinstance(path, pathlib.Path): + return path.parts + elif path is None: + return None + else: + raise TypeError( + '"path" was {}, must be string, None, or pathlib.Path'.format( + type(path) + ) + ) def is_selected_for_writing(settings, path): diff --git a/pelican/writers.py b/pelican/writers.py index 9b27a748..379af1f4 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -29,7 +29,7 @@ class Writer: self.urljoiner = posix_join else: self.urljoiner = lambda base, url: urljoin( - base if base.endswith('/') else base + '/', url) + base if base.endswith('/') else base + '/', str(url)) def _create_new_feed(self, feed_type, feed_title, context): feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed From a13371670970661c7d3f673e9c634df83f7a95bf Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 21 May 2020 21:43:06 -0600 Subject: [PATCH 003/465] flake8 fixes --- pelican/generators.py | 3 +-- pelican/settings.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index d92e8ff8..ecc06851 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -462,11 +462,10 @@ class ArticlesGenerator(CachingGenerator): ).format(lang=lang), self.settings.get( 'TRANSLATION_FEED_RSS_URL', - str(self.settings['TRANSLATION_FEED_RSS']).format( + str(self.settings['TRANSLATION_FEED_RSS'])).format( lang=lang ), feed_type='rss' - ) ) def generate_articles(self, write): diff --git a/pelican/settings.py b/pelican/settings.py index ddb6748d..a5e39161 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -6,6 +6,7 @@ import logging import os import re from os.path import isabs +from pathlib import Path from pelican.log import LimitFilter @@ -406,7 +407,10 @@ def handle_deprecated_settings(settings): for key in ['TRANSLATION_FEED_ATOM', 'TRANSLATION_FEED_RSS' ]: - if settings.get(key) and not isinstance(settings[key], Path) and '%s' in settings[key]: + if ( + settings.get(key) and not isinstance(settings[key], Path) + and '%s' in settings[key] + ): logger.warning('%%s usage in %s is deprecated, use {lang} ' 'instead.', key) try: @@ -423,7 +427,10 @@ def handle_deprecated_settings(settings): 'TAG_FEED_ATOM', 'TAG_FEED_RSS', ]: - if settings.get(key) and not isinstance(settings[key], Path) and '%s' in settings[key]: + if ( + settings.get(key) and not isinstance(settings[key], Path) + and '%s' in settings[key] + ): logger.warning('%%s usage in %s is deprecated, use {slug} ' 'instead.', key) try: From d3aa4f7c7c875b39ae78c394ee43d46ab6a386ed Mon Sep 17 00:00:00 2001 From: David Beitey Date: Sun, 7 Jun 2020 01:46:16 +0000 Subject: [PATCH 004/465] Remove duplicate port arguments in Makefile The PORT variable check earlier in the Makefile sets up the `-p` argument as part of PELICANOPTS so prior to this change `-p` was duplicated on each of the serve targets. --- pelican/tools/templates/Makefile.jinja2 | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/pelican/tools/templates/Makefile.jinja2 b/pelican/tools/templates/Makefile.jinja2 index 0184cc88..ab447135 100644 --- a/pelican/tools/templates/Makefile.jinja2 +++ b/pelican/tools/templates/Makefile.jinja2 @@ -101,32 +101,16 @@ regenerate: "$(PELICAN)" -r "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) serve: -ifdef PORT - "$(PELICAN)" -l "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -p $(PORT) -else "$(PELICAN)" -l "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -endif serve-global: -ifdef PORT - "$(PELICAN)" -l "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -p $(PORT) -b $(SERVER) -else "$(PELICAN)" -l "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -b $(SERVER) -endif devserver: -ifdef PORT - "$(PELICAN)" -lr "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -p $(PORT) -else "$(PELICAN)" -lr "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -endif devserver-global: -ifdef PORT - $(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b 0.0.0.0 -else $(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -b 0.0.0.0 -endif publish: "$(PELICAN)" "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(PUBLISHCONF)" $(PELICANOPTS) From f05a806164a4ac202b109eeaf01db95c79743026 Mon Sep 17 00:00:00 2001 From: shakram02 Date: Sat, 11 Jul 2020 17:39:30 +0200 Subject: [PATCH 005/465] fix usage of pelican_open in docs --- docs/internals.rst | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/internals.rst b/docs/internals.rst index 5b41070e..01d60c39 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -44,17 +44,22 @@ HTML content and some metadata. Take a look at the Markdown reader:: + from pelican.readers import BaseReader + from pelican.utils import pelican_open + from markdown import Markdown + class MarkdownReader(BaseReader): - enabled = bool(Markdown) + enabled = True def read(self, source_path): """Parse content and metadata of markdown files""" - text = pelican_open(source_path) - md_extensions = {'markdown.extensions.meta': {}, - 'markdown.extensions.codehilite': {}} - md = Markdown(extensions=md_extensions.keys(), - extension_configs=md_extensions) - content = md.convert(text) + + with pelican_open(source_path) as text: + md_extensions = {'markdown.extensions.meta': {}, + 'markdown.extensions.codehilite': {}} + md = Markdown(extensions=md_extensions.keys(), + extension_configs=md_extensions) + content = md.convert(text) metadata = {} for name, value in md.Meta.items(): From 7986c17611c7e883ee1d6f595d04072f878175b7 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 10 May 2020 10:17:05 +0200 Subject: [PATCH 006/465] Add missing entry points to pyproject file --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 20196d15..0616629c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,10 @@ markdown = ["markdown"] [tool.poetry.scripts] pelican = "pelican.__main__:main" +pelican-import = "pelican.tools.pelican_import:main" +pelican-plugins = "pelican.plugins._utils:list_plugins" +pelican-quickstart = "pelican.tools.pelican_quickstart:main" +pelican-themes = "pelican.tools.pelican_themes:main" [tool.autopub] project-name = "Pelican" From 7b67829872603a907bea504e6493c57b52630d98 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 10 May 2020 10:18:02 +0200 Subject: [PATCH 007/465] Switch build system from setuptools to Poetry --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0616629c..9643a97b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,4 +80,5 @@ version-strings = ["setup.py"] build-system = "setuptools" [build-system] -requires = ["setuptools >= 40.6.0", "wheel"] +requires = ["poetry>=1.0"] +build-backend = "poetry.masonry.api" From 9c3bb6bf91effe3bd13db383cba32c1cff1aebb6 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 10 May 2020 10:19:17 +0200 Subject: [PATCH 008/465] Update & re-order dependencies in pyproject --- pyproject.toml | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9643a97b..9503a311 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,30 +27,29 @@ classifiers = [ ] [tool.poetry.dependencies] -python = "^3.6" +python = "^3.5" +blinker = "^1.4" +docutils = "^0.16" feedgenerator = "^1.9" jinja2 = "~2.11" pygments = "~2.6.1" -pytz = "^2019.1" -blinker = "^1.4" -unidecode = "^1.1" python-dateutil = "^2.8" -docutils = "^0.15" -markdown = {version = "~3.1.1", optional = true} +pytz = "^2020.1" +unidecode = "^1.1" +markdown = {version = "~3.2.2", optional = true} [tool.poetry.dev-dependencies] BeautifulSoup4 = "^4.7" lxml = "^4.3" -markdown = "~3.1.1" +markdown = "~3.2.2" typogrify = "^2.0" -sphinx = "=1.4.9" +sphinx = "^3.0" sphinx_rtd_theme = "^0.4.3" livereload = "^2.6" -mock = "^3.0" -pytest = "~5.3.5" +pytest = "^5.0" pytest-cov = "^2.8" pytest-pythonpath = "^0.7.3" -pytest-sugar = "^0.9.2" +pytest-sugar = "^0.9.3" pytest-xdist = "^1.31" tox = "^3.13" flake8 = "^3.7" From 30d782fd8411f786000addac428f58ea71a04ee9 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 10 May 2020 10:20:05 +0200 Subject: [PATCH 009/465] Test package installation on Windows in CI --- .github/workflows/main.yml | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bcbd94d4..ab1181e3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -125,6 +125,44 @@ jobs: run: tox -e docs + install_sdist: + name: Install sdist on Windows + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v1.1.1 + with: + python-version: 3.6 + - name: Test sdist installation on Windows + run: | + python -m pip install -U pip poetry + poetry build + python -m pip install ${env:GITHUB_WORKSPACE}\dist\pelican-4.2.0.tar.gz + echo "Pelican version:" + pelican --version + + + install_wheel: + name: Install wheel on Windows + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v1.1.1 + with: + python-version: 3.6 + - name: Test wheel installation on Windows + run: | + python -m pip install -U pip poetry + poetry build + python -m pip install ${env:GITHUB_WORKSPACE}\dist\pelican-4.2.0-py3-none-any.whl + echo "Pelican version:" + pelican --version + + deploy: name: Deploy needs: [test, lint, docs] From 2d9f1a47199eb3b3813622c037916a7b2de032b1 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 19 May 2020 15:19:21 +0200 Subject: [PATCH 010/465] Test package uninstallation on Windows --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ab1181e3..f5bb2ac8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -142,6 +142,7 @@ jobs: python -m pip install ${env:GITHUB_WORKSPACE}\dist\pelican-4.2.0.tar.gz echo "Pelican version:" pelican --version + python -m pip uninstall -y pelican install_wheel: @@ -161,6 +162,7 @@ jobs: python -m pip install ${env:GITHUB_WORKSPACE}\dist\pelican-4.2.0-py3-none-any.whl echo "Pelican version:" pelican --version + python -m pip uninstall -y pelican deploy: From b10bb2f4722452dd7c52aa8ceefbfb2bd7475aae Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 29 Jul 2020 07:14:25 +0200 Subject: [PATCH 011/465] Print Pip version during test install on Windows --- .github/workflows/main.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f5bb2ac8..3c6334c0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -139,6 +139,9 @@ jobs: run: | python -m pip install -U pip poetry poetry build + python -m pip install -U pip + echo "Pip version:" + python -m pip --version python -m pip install ${env:GITHUB_WORKSPACE}\dist\pelican-4.2.0.tar.gz echo "Pelican version:" pelican --version @@ -159,6 +162,9 @@ jobs: run: | python -m pip install -U pip poetry poetry build + python -m pip install -U pip + echo "Pip version:" + python -m pip --version python -m pip install ${env:GITHUB_WORKSPACE}\dist\pelican-4.2.0-py3-none-any.whl echo "Pelican version:" pelican --version From 6a40e24d851ae9cc6082b8c0c88758b3c233ea22 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 29 Jul 2020 07:34:16 +0200 Subject: [PATCH 012/465] Test new Pip 20.2 dependency resolver in GitHub CI --- .github/workflows/main.yml | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3c6334c0..4b81530f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -66,7 +66,9 @@ jobs: with: pandoc-version: "2.9.2" - name: Install tox - run: python -m pip install -U pip tox + run: | + python -m pip install -U pip + python -m pip install --use-feature=2020-resolver -U tox - name: Info run: | echo "===== PYTHON =====" @@ -96,7 +98,9 @@ jobs: restore-keys: | ${{ runner.os }}-pip- - name: Install tox - run: python -m pip install -U pip tox + run: | + python -m pip install -U pip + python -m pip install --use-feature=2020-resolver -U tox - name: Check run: tox -e flake8 @@ -120,7 +124,9 @@ jobs: restore-keys: | ${{ runner.os }}-pip- - name: Install tox - run: python -m pip install -U pip tox + run: | + python -m pip install -U pip + python -m pip install --use-feature=2020-resolver -U tox - name: Check run: tox -e docs @@ -137,12 +143,12 @@ jobs: python-version: 3.6 - name: Test sdist installation on Windows run: | - python -m pip install -U pip poetry - poetry build python -m pip install -U pip + python -m pip install --use-feature=2020-resolver -U poetry + poetry build echo "Pip version:" python -m pip --version - python -m pip install ${env:GITHUB_WORKSPACE}\dist\pelican-4.2.0.tar.gz + python -m pip install --use-feature=2020-resolver ${env:GITHUB_WORKSPACE}\dist\pelican-4.2.0.tar.gz echo "Pelican version:" pelican --version python -m pip uninstall -y pelican @@ -160,12 +166,12 @@ jobs: python-version: 3.6 - name: Test wheel installation on Windows run: | - python -m pip install -U pip poetry - poetry build python -m pip install -U pip + python -m pip install --use-feature=2020-resolver -U poetry + poetry build echo "Pip version:" python -m pip --version - python -m pip install ${env:GITHUB_WORKSPACE}\dist\pelican-4.2.0-py3-none-any.whl + python -m pip install --use-feature=2020-resolver ${env:GITHUB_WORKSPACE}\dist\pelican-4.2.0-py3-none-any.whl echo "Pelican version:" pelican --version python -m pip uninstall -y pelican @@ -186,10 +192,10 @@ jobs: - name: Check release id: check_release run: | - python -m pip install pip --upgrade - pip install poetry - pip install githubrelease - pip install --pre autopub + python -m pip install --use-feature=2020-resolver pip --upgrade + pip install --use-feature=2020-resolver poetry + pip install --use-feature=2020-resolver githubrelease + pip install --use-feature=2020-resolver --pre autopub echo "##[set-output name=release;]$(autopub check)" - name: Publish if: ${{ steps.check_release.outputs.release=='' }} From dc6fb57c418c7b60138ad5c8105cc7138ac8339d Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 29 Jul 2020 09:38:14 +0200 Subject: [PATCH 013/465] Update & re-order dependencies in pyproject --- pyproject.toml | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 20196d15..ccd1e34e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,35 +28,34 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.6" +blinker = "^1.4" +docutils = "^0.16" feedgenerator = "^1.9" jinja2 = "~2.11" pygments = "~2.6.1" -pytz = "^2019.1" -blinker = "^1.4" -unidecode = "^1.1" python-dateutil = "^2.8" -docutils = "^0.15" -markdown = {version = "~3.1.1", optional = true} +pytz = "^2020.1" +unidecode = "^1.1" +markdown = {version = "~3.2.2", optional = true} [tool.poetry.dev-dependencies] -BeautifulSoup4 = "^4.7" +BeautifulSoup4 = "^4.9" lxml = "^4.3" -markdown = "~3.1.1" +markdown = "~3.2.2" typogrify = "^2.0" -sphinx = "=1.4.9" -sphinx_rtd_theme = "^0.4.3" +sphinx = "^3.0" +sphinx_rtd_theme = "^0.5" livereload = "^2.6" -mock = "^3.0" -pytest = "~5.3.5" +pytest = "^6.0" pytest-cov = "^2.8" pytest-pythonpath = "^0.7.3" -pytest-sugar = "^0.9.2" +pytest-sugar = "^0.9.4" pytest-xdist = "^1.31" tox = "^3.13" -flake8 = "^3.7" +flake8 = "^3.8" flake8-import-order = "^0.18.1" invoke = "^1.3" -isort = "^4.3.21" +isort = "^5.2" black = {version = "^19.10b0", allow-prereleases = true} [tool.poetry.extras] From 18b626aa8b43f513067d6c9ed00711e5e046ebca Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Thu, 30 Jul 2020 14:39:46 -0400 Subject: [PATCH 014/465] Lowercase metadata field name when comparing with FORMATTED_FIELDS in rST reader --- pelican/readers.py | 2 +- .../article_with_capitalized_metadata.rst | 16 ++++++++++++++++ pelican/tests/test_generators.py | 2 ++ pelican/tests/test_readers.py | 18 ++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 pelican/tests/content/article_with_capitalized_metadata.rst diff --git a/pelican/readers.py b/pelican/readers.py index 8c108510..15d09908 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -227,7 +227,7 @@ class RstReader(BaseReader): if element.tagname == 'field': # custom fields (e.g. summary) name_elem, body_elem = element.children name = name_elem.astext() - if name in formatted_fields: + if name.lower() in formatted_fields: value = render_node_to_html( document, body_elem, self.field_body_translator_class) diff --git a/pelican/tests/content/article_with_capitalized_metadata.rst b/pelican/tests/content/article_with_capitalized_metadata.rst new file mode 100644 index 00000000..93ed5b15 --- /dev/null +++ b/pelican/tests/content/article_with_capitalized_metadata.rst @@ -0,0 +1,16 @@ + +This is a super article ! +######################### + +:TAGS: foo, bar, foobar +:DATE: 2010-12-02 10:14 +:MODIFIED: 2010-12-02 10:20 +:CATEGORY: yeah +:AUTHOR: Alexis Métaireau +:SUMMARY: + Multi-line metadata should be supported + as well as **inline markup** and stuff to "typogrify"... +:CUSTOM_FIELD: http://notmyidea.org +:CUSTOM_FORMATTED_FIELD: + Multi-line metadata should also be supported + as well as *inline markup* and stuff to "typogrify"... diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 332d7e78..169765ac 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -260,6 +260,7 @@ class TestArticlesGenerator(unittest.TestCase): ['This is a super article !', 'published', 'yeah', 'article'], ['This is a super article !', 'published', 'yeah', 'article'], ['This is a super article !', 'published', 'yeah', 'article'], + ['This is a super article !', 'published', 'yeah', 'article'], ['This is a super article !', 'published', 'Default', 'article'], ['Article with an inline SVG', 'published', 'Default', 'article'], ['This is an article with category !', 'published', 'yeah', @@ -576,6 +577,7 @@ class TestArticlesGenerator(unittest.TestCase): 'This is a super article !', 'This is a super article !', 'This is a super article !', + 'This is a super article !', 'This is an article with category !', ('This is an article with multiple authors in lastname, ' 'firstname format!'), diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index de2a1b22..ea5f3bdd 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -155,6 +155,24 @@ class RstReaderTest(ReaderTest): self.assertDictHasSubset(page.metadata, expected) + def test_article_with_capitalized_metadata(self): + page = self.read_file(path='article_with_capitalized_metadata.rst') + expected = { + 'category': 'yeah', + 'author': 'Alexis Métaireau', + 'title': 'This is a super article !', + 'summary': '

Multi-line metadata should be' + ' supported\nas well as inline' + ' markup and stuff to "typogrify' + '"...

\n', + 'date': SafeDatetime(2010, 12, 2, 10, 14), + 'modified': SafeDatetime(2010, 12, 2, 10, 20), + 'tags': ['foo', 'bar', 'foobar'], + 'custom_field': 'http://notmyidea.org', + } + + self.assertDictHasSubset(page.metadata, expected) + def test_article_with_filename_metadata(self): page = self.read_file( path='2012-11-29_rst_w_filename_meta#foo-bar.rst', From d2bbfd967e70351d014ab11ec3e8dc8dbcf58867 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 2 Aug 2020 10:58:32 +0200 Subject: [PATCH 015/465] Remove self-referential Template Pages link in docs --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index 2b99072f..b6ace4bd 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -770,7 +770,7 @@ Template pages .. data:: TEMPLATE_PAGES = None A mapping containing template pages that will be rendered with the blog - entries. See :ref:`template_pages`. + entries. If you want to generate custom pages besides your blog entries, you can point any Jinja2 template file with a path pointing to the file and the From 18a2720ea73da4ccb05739a57864c3cb3f008973 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 11 Aug 2020 09:13:00 +0200 Subject: [PATCH 016/465] Adjust isort task: v5.2+ is now recursive by default --- tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks.py b/tasks.py index 52bfe6a3..76c48834 100644 --- a/tasks.py +++ b/tasks.py @@ -64,7 +64,7 @@ def isort(c, check=False, diff=False): if diff: diff_flag = "--diff" c.run( - f"{VENV_BIN}/isort {check_flag} {diff_flag} --recursive {PKG_PATH}/* tasks.py" + f"{VENV_BIN}/isort {check_flag} {diff_flag} ." ) From f80bf187a9c2a7d3d41fd91ebcdd09a7b0d2dd13 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 11 Aug 2020 09:14:55 +0200 Subject: [PATCH 017/465] Update PyPI classifiers; add funding & tracker links --- pyproject.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ccd1e34e..1b09716a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,14 +18,18 @@ classifiers = [ "License :: OSI Approved :: GNU Affero General Public License v3", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Internet :: WWW/HTTP", "Topic :: Software Development :: Libraries :: Python Modules", ] +[tool.poetry.urls] +"Funding" = "https://donate.getpelican.com/" +"Tracker" = "https://github.com/getpelican/pelican/issues" + [tool.poetry.dependencies] python = "^3.6" blinker = "^1.4" From 40d09875e6d45f6609b19ea367d1fd70affa3332 Mon Sep 17 00:00:00 2001 From: Arnaud Rebillout Date: Tue, 11 Aug 2020 16:23:35 +0700 Subject: [PATCH 018/465] Makefile, include tags directory in rsync command By default, the rsync option '--cvs-exclude' excludes the 'tags' directory. For a blog, it's a bit unfortunate, as it's quite common to have a `tags` directory in a blog, either for the tag pages or the tag feeds. With this commit, we force rsync to include this directory, and save a little headache to users who wonder why their tags are present in the output directory, but are not present on the server. --- pelican/tools/templates/Makefile.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/templates/Makefile.jinja2 b/pelican/tools/templates/Makefile.jinja2 index 0184cc88..94f68e27 100644 --- a/pelican/tools/templates/Makefile.jinja2 +++ b/pelican/tools/templates/Makefile.jinja2 @@ -139,7 +139,7 @@ ssh_upload: publish {% set upload = upload + ["rsync_upload"] %} rsync_upload: publish - rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --cvs-exclude --delete "$(OUTPUTDIR)"/ "$(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)" + rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --include tags --cvs-exclude --delete "$(OUTPUTDIR)"/ "$(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)" {% endif %} {% if dropbox %} From 1c50a18d0a45d76f6b50647536faf25954740d20 Mon Sep 17 00:00:00 2001 From: Peter Sabaini Date: Sat, 9 May 2020 18:15:16 +0200 Subject: [PATCH 019/465] Override settings from the command line Add a --setting-overrides KEY=VAL command line option to override default settings or those defined in settings files. This adds flexibility in running Pelican and helps reduce sprawl of settings files. Cast int and str setting overrides to their respective types. Support other setting types by treating them as JSON. Fall back to JSON when an override typecast errors. This should make it possible to set int values to None, resp. to JSON 'none' --- pelican/__init__.py | 27 ++++++++++++++++++++++++++- pelican/settings.py | 21 +++++++++++++++++++++ pelican/tests/test_settings.py | 19 +++++++++++++++++-- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 6469c607..9204d952 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -23,7 +23,7 @@ from pelican.plugins import signals from pelican.plugins._utils import load_plugins from pelican.readers import Readers from pelican.server import ComplexHTTPRequestHandler, RootedHTTPServer -from pelican.settings import read_settings +from pelican.settings import coerce_overrides, read_settings from pelican.utils import (FileSystemWatcher, clean_output_dir, maybe_pluralize) from pelican.writers import Writer @@ -230,6 +230,18 @@ class PrintSettings(argparse.Action): parser.exit() +class ParseDict(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) + + def parse_arguments(argv=None): parser = argparse.ArgumentParser( description='A tool to generate a static blog, ' @@ -323,6 +335,18 @@ def parse_arguments(argv=None): help='IP to bind to when serving files via HTTP ' '(default: 127.0.0.1)') + parser.add_argument('-c', '--setting-overrides', dest='overrides', + help='Specify one ore more SETTING=VALUE pairs ' + '(without spaces around =) to override ' + 'settings files. If VALUE contains spaces, add quotes: ' + 'SETTING="VALUE". ' + 'Integers and strings are autoconverted, other values ' + 'can be passed in in json notation, ' + 'e.g. SETTING=none', + nargs='*', + action=ParseDict + ) + args = parser.parse_args(argv) if args.port is not None and not args.listen: @@ -358,6 +382,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)) return config diff --git a/pelican/settings.py b/pelican/settings.py index 7b333de8..40804e0b 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -1,6 +1,7 @@ import copy import importlib.util import inspect +import json import locale import logging import os @@ -658,3 +659,23 @@ def configure_settings(settings): continue # setting not specified, nothing to do return settings + + +def coerce_overrides(overrides): + coerced = {} + types_to_cast = {int, str} + for k, v in overrides.items(): + if k not in overrides: + 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/test_settings.py b/pelican/tests/test_settings.py index 708c0981..83203ae5 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -7,8 +7,8 @@ from sys import platform from pelican.settings import (DEFAULT_CONFIG, DEFAULT_THEME, _printf_s_to_format_field, - configure_settings, handle_deprecated_settings, - read_settings) + coerce_overrides, configure_settings, + handle_deprecated_settings, read_settings) from pelican.tests.support import unittest @@ -304,3 +304,18 @@ 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) From d728cd2696ecaef0f1070faf3c12ce1fd2819c6f Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 11 Aug 2020 12:48:15 +0200 Subject: [PATCH 020/465] Rename CLI flag syntax for settings override feature --- pelican/__init__.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 9204d952..82366117 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -335,14 +335,12 @@ def parse_arguments(argv=None): help='IP to bind to when serving files via HTTP ' '(default: 127.0.0.1)') - parser.add_argument('-c', '--setting-overrides', dest='overrides', - help='Specify one ore more SETTING=VALUE pairs ' - '(without spaces around =) to override ' - 'settings files. If VALUE contains spaces, add quotes: ' - 'SETTING="VALUE". ' - 'Integers and strings are autoconverted, other values ' - 'can be passed in in json notation, ' - 'e.g. SETTING=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)', nargs='*', action=ParseDict ) From 04c0ea53e6bc79a1447e1ead246037103f39aca9 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 11 Aug 2020 12:50:08 +0200 Subject: [PATCH 021/465] Document -e / --extra-settings option flags --- docs/settings.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/settings.rst b/docs/settings.rst index b6ace4bd..cc67815b 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -9,6 +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:: + + pelican content -e DELETE_OUTPUT_DIRECTORY=True + .. note:: When experimenting with different settings (especially the metadata ones) From 45c5cb9029bc2d475ae7440ee1fed3065e353811 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 13 Aug 2020 12:07:58 +0200 Subject: [PATCH 022/465] Fix error in --extra-settings feature. Refs #2789 --- docs/settings.rst | 2 +- pelican/settings.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index cc67815b..fbd6169c 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -13,7 +13,7 @@ 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:: - pelican content -e DELETE_OUTPUT_DIRECTORY=True + pelican content -e DELETE_OUTPUT_DIRECTORY=true .. note:: diff --git a/pelican/settings.py b/pelican/settings.py index 40804e0b..1554c1f1 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -662,10 +662,12 @@ def configure_settings(settings): def coerce_overrides(overrides): + if overrides is None: + return {} coerced = {} types_to_cast = {int, str} for k, v in overrides.items(): - if k not in overrides: + if k not in DEFAULT_CONFIG: logger.warning('Override for unknown setting %s, ignoring', k) continue setting_type = type(DEFAULT_CONFIG[k]) From b769144a6380e7fd49ec4aa6bcdf66620e4b9eba Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sat, 15 Aug 2020 08:36:56 +0200 Subject: [PATCH 023/465] Support Booleans in --extra-settings. Refs #2789 --- pelican/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/settings.py b/pelican/settings.py index 1554c1f1..11bbafbd 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -665,7 +665,7 @@ def coerce_overrides(overrides): if overrides is None: return {} coerced = {} - types_to_cast = {int, str} + 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) From adbefe836355305c69818f8abb59decf0adf9cf9 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 16 Aug 2020 07:19:47 +0200 Subject: [PATCH 024/465] Upgrade pytest-xdist dev dependency to v2.0 Also, stop pinning pytest version, since older version combined with pytest-xdist 2.0 caused CI test errors. --- pyproject.toml | 2 +- requirements/test.pip | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1b09716a..4456c913 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ pytest = "^6.0" pytest-cov = "^2.8" pytest-pythonpath = "^0.7.3" pytest-sugar = "^0.9.4" -pytest-xdist = "^1.31" +pytest-xdist = "^2.0" tox = "^3.13" flake8 = "^3.8" flake8-import-order = "^0.18.1" diff --git a/requirements/test.pip b/requirements/test.pip index 973b27ca..647a8694 100644 --- a/requirements/test.pip +++ b/requirements/test.pip @@ -1,6 +1,6 @@ # Tests Pygments==2.6.1 -pytest==5.3.5 +pytest pytest-cov pytest-xdist From 64bb392fefef1ad895626c6d4dc989fe73173766 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 16 Aug 2020 07:23:13 +0200 Subject: [PATCH 025/465] Auto-detect physical CPU cores via pytest-xdist --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 18b0e7a4..69281e30 100644 --- a/tox.ini +++ b/tox.ini @@ -28,7 +28,7 @@ commands = filterwarnings = default::DeprecationWarning error:.*:Warning:pelican -addopts = -n 2 -r a +addopts = -n auto -r a [flake8] application-import-names = pelican From 863421b1b8fbd352dbf34d1b66d9dfd0494c5fd2 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 17 Aug 2020 06:45:38 +0200 Subject: [PATCH 026/465] Use more reliable installation command in docs Some shell environments may interpret brackets as glob patterns. Wrapping the argument in quotation marks should help ensure correct behavior in those environments. Refs #2786 --- docs/install.rst | 4 ++-- docs/quickstart.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 03480e79..bf2647ad 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -11,7 +11,7 @@ You can install Pelican via several different methods. The simplest is via Or, if you plan on using Markdown:: - pip install pelican[Markdown] + pip install "pelican[markdown]" (Keep in mind that operating systems will often require you to prefix the above command with ``sudo`` in order to install Pelican system-wide.) @@ -46,7 +46,7 @@ Optional packages If you plan on using `Markdown `_ as a markup format, you can install Pelican with Markdown support:: - pip install pelican[Markdown] + pip install "pelican[markdown]" Or you might need to install it a posteriori:: diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 1f6358a7..8d6d6a5b 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -11,7 +11,7 @@ Install Pelican (and optionally Markdown if you intend to use it) on Python 2.7.x or Python 3.5+ by running the following command in your preferred terminal, prefixing with ``sudo`` if permissions warrant:: - pip install pelican[Markdown] + pip install "pelican[markdown]" Create a project ---------------- From 16975bc3a259b77b03673cf9cface69c77ff50f9 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 17 Aug 2020 07:04:00 +0200 Subject: [PATCH 027/465] Prefix Pip commands with `python -m` in docs This ensures Pip commands will be executed for the current Python interpreter and not, say, whichever Python interpreter happens to be associated with `/usr/local/bin/pip`. --- CONTRIBUTING.rst | 2 +- docs/contribute.rst | 4 ++-- docs/faq.rst | 2 +- docs/install.rst | 16 ++++++++-------- docs/publish.rst | 4 ++-- docs/quickstart.rst | 2 +- docs/settings.rst | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 9561b04f..61935f62 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -43,7 +43,7 @@ publicly-accessible location: * Describe what version of Pelican you are running (output of ``pelican --version`` or the HEAD commit hash if you cloned the repo) and how exactly you installed - it (the full command you used, e.g. ``pip install pelican``). + it (the full command you used, e.g. ``python -m pip install pelican``). * If you are looking for a way to get some end result, prepare a detailed description of what the end result should look like (preferably in the form of an image or a mock-up page) and explain in detail what you have done so far to diff --git a/docs/contribute.rst b/docs/contribute.rst index 66890ae7..62ed51f9 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -44,9 +44,9 @@ to manually create and activate a virtual environment:: Install the needed dependencies and set up the project:: - pip install invoke + python -m pip install invoke invoke setup - pip install -e ~/projects/pelican + python -m pip install -e ~/projects/pelican Your local environment should now be ready to go! diff --git a/docs/faq.rst b/docs/faq.rst index c35cd6c6..8532ab3f 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -74,7 +74,7 @@ Markdown format, you will need to explicitly install the Markdown library. You can do so by typing the following command, prepending ``sudo`` if permissions require it:: - pip install markdown + python -m pip install markdown Can I use arbitrary metadata in my templates? ============================================= diff --git a/docs/install.rst b/docs/install.rst index bf2647ad..69bb3e3a 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -7,11 +7,11 @@ Python are not supported. You can install Pelican via several different methods. The simplest is via `pip `_:: - pip install pelican + python -m pip install pelican Or, if you plan on using Markdown:: - pip install "pelican[markdown]" + python -m pip install "pelican[markdown]" (Keep in mind that operating systems will often require you to prefix the above command with ``sudo`` in order to install Pelican system-wide.) @@ -26,7 +26,7 @@ session and create a new virtual environment for Pelican:: source bin/activate Once the virtual environment has been created and activated, Pelican can be -installed via ``pip install pelican`` as noted above. Alternatively, if you +installed via ``python -m pip install pelican`` as noted above. Alternatively, if you have the project source, you can install Pelican using the distutils method:: cd path-to-Pelican-source @@ -35,7 +35,7 @@ have the project source, you can install Pelican using the distutils method:: If you have Git installed and prefer to install the latest bleeding-edge version of Pelican rather than a stable release, use the following command:: - pip install -e "git+https://github.com/getpelican/pelican.git#egg=pelican" + python -m pip install -e "git+https://github.com/getpelican/pelican.git#egg=pelican" Once Pelican is installed, you can run ``pelican --help`` to see basic usage options. For more detail, refer to the :doc:`Publish` section. @@ -46,17 +46,17 @@ Optional packages If you plan on using `Markdown `_ as a markup format, you can install Pelican with Markdown support:: - pip install "pelican[markdown]" + python -m pip install "pelican[markdown]" Or you might need to install it a posteriori:: - pip install Markdown + python -m pip install Markdown Typographical enhancements can be enabled in your settings file, but first the requisite `Typogrify `_ library must be installed:: - pip install typogrify + python -m pip install typogrify Dependencies ------------ @@ -88,7 +88,7 @@ Upgrading If you installed a stable Pelican release via ``pip`` and wish to upgrade to the latest stable release, you can do so by adding ``--upgrade``:: - pip install --upgrade pelican + python -m pip install --upgrade pelican If you installed Pelican via distutils or the bleeding-edge method, simply perform the same step to install the most recent version. diff --git a/docs/publish.rst b/docs/publish.rst index 9bea8938..ae1428f5 100644 --- a/docs/publish.rst +++ b/docs/publish.rst @@ -118,7 +118,7 @@ in a wide range of environments. The downside is that it must be installed separately. Use the following command to install Invoke, prefixing with ``sudo`` if your environment requires it:: - pip install invoke + python -m pip install invoke Take a moment to open the ``tasks.py`` file that was generated in your project root. You will see a number of commands, any one of which can be renamed, @@ -139,7 +139,7 @@ http://localhost:8000/:: invoke serve To serve the generated site with automatic browser reloading every time a -change is detected, first ``pip install livereload``, then use the +change is detected, first ``python -m pip install livereload``, then use the following command:: invoke livereload diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 8d6d6a5b..a8387a54 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -11,7 +11,7 @@ Install Pelican (and optionally Markdown if you intend to use it) on Python 2.7.x or Python 3.5+ by running the following command in your preferred terminal, prefixing with ``sudo`` if permissions warrant:: - pip install "pelican[markdown]" + python -m pip install "pelican[markdown]" Create a project ---------------- diff --git a/docs/settings.rst b/docs/settings.rst index fbd6169c..e05d7abf 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -277,7 +277,7 @@ Basic settings If set to True, several typographical improvements will be incorporated into the generated HTML via the `Typogrify `_ library, which can be installed - via: ``pip install typogrify`` + via: ``python -m pip install typogrify`` .. data:: TYPOGRIFY_IGNORE_TAGS = [] From a0335711afdb3354ca887673729992d9048139e6 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 17 Aug 2020 07:14:29 +0200 Subject: [PATCH 028/465] Remove outdated references to Python 2.7 in docs --- docs/install.rst | 4 +--- docs/quickstart.rst | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 69bb3e3a..b0a851c5 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -1,8 +1,7 @@ Installing Pelican ################## -Pelican currently runs best on Python 2.7.x and 3.5+; earlier versions of -Python are not supported. +Pelican currently runs best on 3.6+; earlier versions of Python are not supported. You can install Pelican via several different methods. The simplest is via `pip `_:: @@ -75,7 +74,6 @@ automatically installed without any action on your part: broadcast signaling system * `unidecode `_, for ASCII transliterations of Unicode text -* `six `_, for Python 2 and 3 compatibility utilities * `MarkupSafe `_, for a markup safe string implementation diff --git a/docs/quickstart.rst b/docs/quickstart.rst index a8387a54..ddc42651 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -8,8 +8,8 @@ Installation ------------ Install Pelican (and optionally Markdown if you intend to use it) on Python -2.7.x or Python 3.5+ by running the following command in your preferred -terminal, prefixing with ``sudo`` if permissions warrant:: +3.6+ by running the following command in your preferred terminal, prefixing +with ``sudo`` if permissions warrant:: python -m pip install "pelican[markdown]" From 145cd4be92c60cf1ae2eabc17c00f37660bdc7e1 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 17 Aug 2020 07:21:57 +0200 Subject: [PATCH 029/465] Minor improvements to installation docs --- docs/install.rst | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index b0a851c5..eb618035 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -3,8 +3,7 @@ Installing Pelican Pelican currently runs best on 3.6+; earlier versions of Python are not supported. -You can install Pelican via several different methods. The simplest is via -`pip `_:: +You can install Pelican via several different methods. The simplest is via Pip_:: python -m pip install pelican @@ -12,7 +11,7 @@ Or, if you plan on using Markdown:: python -m pip install "pelican[markdown]" -(Keep in mind that operating systems will often require you to prefix the above +(Keep in mind that some operating systems will require you to prefix the above command with ``sudo`` in order to install Pelican system-wide.) While the above is the simplest method, the recommended approach is to create a @@ -47,10 +46,6 @@ markup format, you can install Pelican with Markdown support:: python -m pip install "pelican[markdown]" -Or you might need to install it a posteriori:: - - python -m pip install Markdown - Typographical enhancements can be enabled in your settings file, but first the requisite `Typogrify `_ library must be installed:: @@ -75,7 +70,7 @@ automatically installed without any action on your part: * `unidecode `_, for ASCII transliterations of Unicode text utilities -* `MarkupSafe `_, for a markup safe +* `MarkupSafe `_, for a markup-safe string implementation * `python-dateutil `_, to read the date metadata @@ -83,7 +78,7 @@ automatically installed without any action on your part: Upgrading --------- -If you installed a stable Pelican release via ``pip`` and wish to upgrade to +If you installed a stable Pelican release via Pip_ and wish to upgrade to the latest stable release, you can do so by adding ``--upgrade``:: python -m pip install --upgrade pelican @@ -124,4 +119,5 @@ content):: The next step is to begin to adding content to the *content* folder that has been created for you. +.. _Pip: https://pip.pypa.io/ .. _virtualenv: https://virtualenv.pypa.io/en/latest/ From ade70cb2e2622da5b136d6a01e04483805fc5dee Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 17 Aug 2020 09:47:57 +0200 Subject: [PATCH 030/465] Rename branch for GitHub Pages repo quickstart --- pelican/tools/pelican_quickstart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 1a98ab14..41ddea3c 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -41,7 +41,7 @@ _jinja_env = Environment( _GITHUB_PAGES_BRANCHES = { - 'personal': 'master', + 'personal': 'main', 'project': 'gh-pages' } From 64fcdb51723609550a7e28cb9529f23860ffdb32 Mon Sep 17 00:00:00 2001 From: kernc Date: Wed, 19 Aug 2020 20:25:19 +0200 Subject: [PATCH 031/465] =?UTF-8?q?Rename=20setting=20SUMMARY=5FEND=5FMARK?= =?UTF-8?q?ER=20=E2=86=92=20SUMMARY=5FEND=5FSUFFIX=20(#2792)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoids clash with 'summary' plugin. Refs: https://github.com/getpelican/pelican-plugins/pull/1284#issuecomment-660715086 --- docs/settings.rst | 4 ++-- pelican/contents.py | 2 +- pelican/settings.py | 2 +- pelican/tests/test_contents.py | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index e05d7abf..377051b0 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -303,10 +303,10 @@ Basic settings does not otherwise specify a summary. Setting to ``None`` will cause the summary to be a copy of the original content. -.. data:: SUMMARY_END_MARKER = '…' +.. data:: SUMMARY_END_SUFFIX = '…' When creating a short summary of an article and the result was truncated to - match the required word length, this will be used as the truncation marker. + match the required word length, this will be used as the truncation suffix. .. data:: WITH_FUTURE_DATES = True diff --git a/pelican/contents.py b/pelican/contents.py index 2bb2e3a0..6470ee45 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -392,7 +392,7 @@ class Content: return truncate_html_words(self.content, self.settings['SUMMARY_MAX_LENGTH'], - self.settings['SUMMARY_END_MARKER']) + self.settings['SUMMARY_END_SUFFIX']) @property def summary(self): diff --git a/pelican/settings.py b/pelican/settings.py index 11bbafbd..ea3ee8eb 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -136,7 +136,7 @@ DEFAULT_CONFIG = { 'TYPOGRIFY': False, 'TYPOGRIFY_IGNORE_TAGS': [], 'TYPOGRIFY_DASHES': 'default', - 'SUMMARY_END_MARKER': '…', + 'SUMMARY_END_SUFFIX': '…', 'SUMMARY_MAX_LENGTH': 50, 'PLUGIN_PATHS': [], 'PLUGINS': None, diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 93f5b212..1a520bc7 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -110,14 +110,14 @@ class TestPage(TestBase): page = Page(**page_kwargs) self.assertEqual(page.summary, '') - def test_summary_end_marker(self): - # If a :SUMMARY_END_MARKER: is set, and there is no other summary, + def test_summary_end_suffix(self): + # If a :SUMMARY_END_SUFFIX: is set, and there is no other summary, # generated summary should contain the specified marker at the end. page_kwargs = self._copy_page_kwargs() settings = get_settings() page_kwargs['settings'] = settings del page_kwargs['metadata']['summary'] - settings['SUMMARY_END_MARKER'] = 'test_marker' + settings['SUMMARY_END_SUFFIX'] = 'test_marker' settings['SUMMARY_MAX_LENGTH'] = 10 page = Page(**page_kwargs) self.assertEqual(page.summary, truncate_html_words(TEST_CONTENT, 10, From fafe0207ca69ab3bce5a6a8cf6d39200ce9cbc7d Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 13 Oct 2019 11:58:41 -0700 Subject: [PATCH 032/465] Include the tests package in the source distribution Fixes #2635 --- setup.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index 2d831aa4..af96c726 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from os import walk from os.path import join, relpath -from setuptools import setup +from setuptools import setup, find_packages version = "4.2.0" @@ -44,19 +44,14 @@ setup( keywords='static web site generator SSG reStructuredText Markdown', license='AGPLv3', long_description=description, - packages=['pelican', 'pelican.tools', 'pelican.plugins'], - package_data={ - # we manually collect the package data, as opposed to using, - # include_package_data=True because we don't want the tests to be - # included automatically as package data (MANIFEST.in is too greedy) + packages=find_packages(), + include_package_data=True, # includes all in MANIFEST.in if in package + # NOTE : This will collect any files that happen to be in the themes + # directory, even though they may not be checked into version control. + package_data={ # pelican/themes is not a package, so include manually 'pelican': [relpath(join(root, name), 'pelican') for root, _, names in walk(join('pelican', 'themes')) for name in names], - 'pelican.tools': [relpath(join(root, name), join('pelican', 'tools')) - for root, _, names in walk(join('pelican', - 'tools', - 'templates')) - for name in names], }, install_requires=requires, extras_require={ From 4ca6506909196d23c2991e4b824b0ad461f4e646 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 29 Jul 2020 09:38:14 +0200 Subject: [PATCH 033/465] Update & re-order dependencies in pyproject --- pyproject.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9503a311..22796092 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ classifiers = [ ] [tool.poetry.dependencies] -python = "^3.5" +python = "^3.6" blinker = "^1.4" docutils = "^0.16" feedgenerator = "^1.9" @@ -39,23 +39,23 @@ unidecode = "^1.1" markdown = {version = "~3.2.2", optional = true} [tool.poetry.dev-dependencies] -BeautifulSoup4 = "^4.7" +BeautifulSoup4 = "^4.9" lxml = "^4.3" markdown = "~3.2.2" typogrify = "^2.0" sphinx = "^3.0" -sphinx_rtd_theme = "^0.4.3" +sphinx_rtd_theme = "^0.5" livereload = "^2.6" -pytest = "^5.0" +pytest = "^6.0" pytest-cov = "^2.8" pytest-pythonpath = "^0.7.3" -pytest-sugar = "^0.9.3" +pytest-sugar = "^0.9.4" pytest-xdist = "^1.31" tox = "^3.13" -flake8 = "^3.7" +flake8 = "^3.8" flake8-import-order = "^0.18.1" invoke = "^1.3" -isort = "^4.3.21" +isort = "^5.2" black = {version = "^19.10b0", allow-prereleases = true} [tool.poetry.extras] From f739598289a2b87a59866743e632090e186f577e Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 2 Aug 2020 10:58:32 +0200 Subject: [PATCH 034/465] Remove self-referential Template Pages link in docs --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index 2b99072f..b6ace4bd 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -770,7 +770,7 @@ Template pages .. data:: TEMPLATE_PAGES = None A mapping containing template pages that will be rendered with the blog - entries. See :ref:`template_pages`. + entries. If you want to generate custom pages besides your blog entries, you can point any Jinja2 template file with a path pointing to the file and the From 5c93a86eeac16a294791bf1c5610c9669f0a48f0 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 11 Aug 2020 09:13:00 +0200 Subject: [PATCH 035/465] Adjust isort task: v5.2+ is now recursive by default --- tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks.py b/tasks.py index 52bfe6a3..76c48834 100644 --- a/tasks.py +++ b/tasks.py @@ -64,7 +64,7 @@ def isort(c, check=False, diff=False): if diff: diff_flag = "--diff" c.run( - f"{VENV_BIN}/isort {check_flag} {diff_flag} --recursive {PKG_PATH}/* tasks.py" + f"{VENV_BIN}/isort {check_flag} {diff_flag} ." ) From 8a3cff38b3e93c4cd466ba2d7fad25f72289e16d Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 11 Aug 2020 09:14:55 +0200 Subject: [PATCH 036/465] Update PyPI classifiers; add funding & tracker links --- pyproject.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 22796092..6bbfffc2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,14 +18,18 @@ classifiers = [ "License :: OSI Approved :: GNU Affero General Public License v3", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Internet :: WWW/HTTP", "Topic :: Software Development :: Libraries :: Python Modules", ] +[tool.poetry.urls] +"Funding" = "https://donate.getpelican.com/" +"Tracker" = "https://github.com/getpelican/pelican/issues" + [tool.poetry.dependencies] python = "^3.6" blinker = "^1.4" From 923a29aaad18fb6d6131b314e922a31bb051c7fb Mon Sep 17 00:00:00 2001 From: Peter Sabaini Date: Sat, 9 May 2020 18:15:16 +0200 Subject: [PATCH 037/465] Override settings from the command line Add a --setting-overrides KEY=VAL command line option to override default settings or those defined in settings files. This adds flexibility in running Pelican and helps reduce sprawl of settings files. Cast int and str setting overrides to their respective types. Support other setting types by treating them as JSON. Fall back to JSON when an override typecast errors. This should make it possible to set int values to None, resp. to JSON 'none' --- pelican/__init__.py | 27 ++++++++++++++++++++++++++- pelican/settings.py | 21 +++++++++++++++++++++ pelican/tests/test_settings.py | 19 +++++++++++++++++-- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 6469c607..9204d952 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -23,7 +23,7 @@ from pelican.plugins import signals from pelican.plugins._utils import load_plugins from pelican.readers import Readers from pelican.server import ComplexHTTPRequestHandler, RootedHTTPServer -from pelican.settings import read_settings +from pelican.settings import coerce_overrides, read_settings from pelican.utils import (FileSystemWatcher, clean_output_dir, maybe_pluralize) from pelican.writers import Writer @@ -230,6 +230,18 @@ class PrintSettings(argparse.Action): parser.exit() +class ParseDict(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) + + def parse_arguments(argv=None): parser = argparse.ArgumentParser( description='A tool to generate a static blog, ' @@ -323,6 +335,18 @@ def parse_arguments(argv=None): help='IP to bind to when serving files via HTTP ' '(default: 127.0.0.1)') + parser.add_argument('-c', '--setting-overrides', dest='overrides', + help='Specify one ore more SETTING=VALUE pairs ' + '(without spaces around =) to override ' + 'settings files. If VALUE contains spaces, add quotes: ' + 'SETTING="VALUE". ' + 'Integers and strings are autoconverted, other values ' + 'can be passed in in json notation, ' + 'e.g. SETTING=none', + nargs='*', + action=ParseDict + ) + args = parser.parse_args(argv) if args.port is not None and not args.listen: @@ -358,6 +382,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)) return config diff --git a/pelican/settings.py b/pelican/settings.py index 7b333de8..40804e0b 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -1,6 +1,7 @@ import copy import importlib.util import inspect +import json import locale import logging import os @@ -658,3 +659,23 @@ def configure_settings(settings): continue # setting not specified, nothing to do return settings + + +def coerce_overrides(overrides): + coerced = {} + types_to_cast = {int, str} + for k, v in overrides.items(): + if k not in overrides: + 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/test_settings.py b/pelican/tests/test_settings.py index 708c0981..83203ae5 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -7,8 +7,8 @@ from sys import platform from pelican.settings import (DEFAULT_CONFIG, DEFAULT_THEME, _printf_s_to_format_field, - configure_settings, handle_deprecated_settings, - read_settings) + coerce_overrides, configure_settings, + handle_deprecated_settings, read_settings) from pelican.tests.support import unittest @@ -304,3 +304,18 @@ 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) From e545a47cdcf491eb395d62c85e31f29bf231dd7a Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 11 Aug 2020 12:48:15 +0200 Subject: [PATCH 038/465] Rename CLI flag syntax for settings override feature --- pelican/__init__.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 9204d952..82366117 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -335,14 +335,12 @@ def parse_arguments(argv=None): help='IP to bind to when serving files via HTTP ' '(default: 127.0.0.1)') - parser.add_argument('-c', '--setting-overrides', dest='overrides', - help='Specify one ore more SETTING=VALUE pairs ' - '(without spaces around =) to override ' - 'settings files. If VALUE contains spaces, add quotes: ' - 'SETTING="VALUE". ' - 'Integers and strings are autoconverted, other values ' - 'can be passed in in json notation, ' - 'e.g. SETTING=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)', nargs='*', action=ParseDict ) From 75ab2b219e4665c0276558ca2f6a2b1111f9ff2e Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 11 Aug 2020 12:50:08 +0200 Subject: [PATCH 039/465] Document -e / --extra-settings option flags --- docs/settings.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/settings.rst b/docs/settings.rst index b6ace4bd..cc67815b 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -9,6 +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:: + + pelican content -e DELETE_OUTPUT_DIRECTORY=True + .. note:: When experimenting with different settings (especially the metadata ones) From c6241180ccb31d6d6f24363047f1b047ea77e774 Mon Sep 17 00:00:00 2001 From: Arnaud Rebillout Date: Tue, 11 Aug 2020 16:23:35 +0700 Subject: [PATCH 040/465] Makefile, include tags directory in rsync command By default, the rsync option '--cvs-exclude' excludes the 'tags' directory. For a blog, it's a bit unfortunate, as it's quite common to have a `tags` directory in a blog, either for the tag pages or the tag feeds. With this commit, we force rsync to include this directory, and save a little headache to users who wonder why their tags are present in the output directory, but are not present on the server. --- pelican/tools/templates/Makefile.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/templates/Makefile.jinja2 b/pelican/tools/templates/Makefile.jinja2 index 0184cc88..94f68e27 100644 --- a/pelican/tools/templates/Makefile.jinja2 +++ b/pelican/tools/templates/Makefile.jinja2 @@ -139,7 +139,7 @@ ssh_upload: publish {% set upload = upload + ["rsync_upload"] %} rsync_upload: publish - rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --cvs-exclude --delete "$(OUTPUTDIR)"/ "$(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)" + rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --include tags --cvs-exclude --delete "$(OUTPUTDIR)"/ "$(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)" {% endif %} {% if dropbox %} From 541dd72cc005f98a5d328a4b9abd5674ccd71554 Mon Sep 17 00:00:00 2001 From: David Beitey Date: Sun, 7 Jun 2020 01:46:16 +0000 Subject: [PATCH 041/465] Remove duplicate port arguments in Makefile The PORT variable check earlier in the Makefile sets up the `-p` argument as part of PELICANOPTS so prior to this change `-p` was duplicated on each of the serve targets. --- pelican/tools/templates/Makefile.jinja2 | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/pelican/tools/templates/Makefile.jinja2 b/pelican/tools/templates/Makefile.jinja2 index 94f68e27..a60f1e16 100644 --- a/pelican/tools/templates/Makefile.jinja2 +++ b/pelican/tools/templates/Makefile.jinja2 @@ -101,32 +101,16 @@ regenerate: "$(PELICAN)" -r "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) serve: -ifdef PORT - "$(PELICAN)" -l "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -p $(PORT) -else "$(PELICAN)" -l "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -endif serve-global: -ifdef PORT - "$(PELICAN)" -l "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -p $(PORT) -b $(SERVER) -else "$(PELICAN)" -l "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -b $(SERVER) -endif devserver: -ifdef PORT - "$(PELICAN)" -lr "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -p $(PORT) -else "$(PELICAN)" -lr "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -endif devserver-global: -ifdef PORT - $(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b 0.0.0.0 -else $(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -b 0.0.0.0 -endif publish: "$(PELICAN)" "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(PUBLISHCONF)" $(PELICANOPTS) From de793fa9f0132469e6f17f6da2ba73735113751a Mon Sep 17 00:00:00 2001 From: shakram02 Date: Sat, 11 Jul 2020 17:39:30 +0200 Subject: [PATCH 042/465] fix usage of pelican_open in docs --- docs/internals.rst | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/internals.rst b/docs/internals.rst index 5b41070e..01d60c39 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -44,17 +44,22 @@ HTML content and some metadata. Take a look at the Markdown reader:: + from pelican.readers import BaseReader + from pelican.utils import pelican_open + from markdown import Markdown + class MarkdownReader(BaseReader): - enabled = bool(Markdown) + enabled = True def read(self, source_path): """Parse content and metadata of markdown files""" - text = pelican_open(source_path) - md_extensions = {'markdown.extensions.meta': {}, - 'markdown.extensions.codehilite': {}} - md = Markdown(extensions=md_extensions.keys(), - extension_configs=md_extensions) - content = md.convert(text) + + with pelican_open(source_path) as text: + md_extensions = {'markdown.extensions.meta': {}, + 'markdown.extensions.codehilite': {}} + md = Markdown(extensions=md_extensions.keys(), + extension_configs=md_extensions) + content = md.convert(text) metadata = {} for name, value in md.Meta.items(): From 4c440e6b7d2e42389110c2ee84bec930a5daa283 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 13 Aug 2020 12:07:58 +0200 Subject: [PATCH 043/465] Fix error in --extra-settings feature. Refs #2789 --- docs/settings.rst | 2 +- pelican/settings.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index cc67815b..fbd6169c 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -13,7 +13,7 @@ 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:: - pelican content -e DELETE_OUTPUT_DIRECTORY=True + pelican content -e DELETE_OUTPUT_DIRECTORY=true .. note:: diff --git a/pelican/settings.py b/pelican/settings.py index 40804e0b..1554c1f1 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -662,10 +662,12 @@ def configure_settings(settings): def coerce_overrides(overrides): + if overrides is None: + return {} coerced = {} types_to_cast = {int, str} for k, v in overrides.items(): - if k not in overrides: + if k not in DEFAULT_CONFIG: logger.warning('Override for unknown setting %s, ignoring', k) continue setting_type = type(DEFAULT_CONFIG[k]) From 3564e6bbe7317ad2e6a88b817d10396e61f4fdd7 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sat, 15 Aug 2020 08:36:56 +0200 Subject: [PATCH 044/465] Support Booleans in --extra-settings. Refs #2789 --- pelican/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/settings.py b/pelican/settings.py index 1554c1f1..11bbafbd 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -665,7 +665,7 @@ def coerce_overrides(overrides): if overrides is None: return {} coerced = {} - types_to_cast = {int, str} + 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) From 8844357d5376541b603498900638b444a3a56f21 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 16 Aug 2020 07:19:47 +0200 Subject: [PATCH 045/465] Upgrade pytest-xdist dev dependency to v2.0 Also, stop pinning pytest version, since older version combined with pytest-xdist 2.0 caused CI test errors. --- pyproject.toml | 2 +- requirements/test.pip | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6bbfffc2..6aafee89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ pytest = "^6.0" pytest-cov = "^2.8" pytest-pythonpath = "^0.7.3" pytest-sugar = "^0.9.4" -pytest-xdist = "^1.31" +pytest-xdist = "^2.0" tox = "^3.13" flake8 = "^3.8" flake8-import-order = "^0.18.1" diff --git a/requirements/test.pip b/requirements/test.pip index 973b27ca..647a8694 100644 --- a/requirements/test.pip +++ b/requirements/test.pip @@ -1,6 +1,6 @@ # Tests Pygments==2.6.1 -pytest==5.3.5 +pytest pytest-cov pytest-xdist From 177f8a8a0cd2409716486adb62608805007100c6 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 16 Aug 2020 07:23:13 +0200 Subject: [PATCH 046/465] Auto-detect physical CPU cores via pytest-xdist --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 18b0e7a4..69281e30 100644 --- a/tox.ini +++ b/tox.ini @@ -28,7 +28,7 @@ commands = filterwarnings = default::DeprecationWarning error:.*:Warning:pelican -addopts = -n 2 -r a +addopts = -n auto -r a [flake8] application-import-names = pelican From c7cb9a75872be8846d39d631562537c38428e147 Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Thu, 30 Jul 2020 14:39:46 -0400 Subject: [PATCH 047/465] Lowercase metadata field name when comparing with FORMATTED_FIELDS in rST reader --- pelican/readers.py | 2 +- .../article_with_capitalized_metadata.rst | 16 ++++++++++++++++ pelican/tests/test_generators.py | 2 ++ pelican/tests/test_readers.py | 18 ++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 pelican/tests/content/article_with_capitalized_metadata.rst diff --git a/pelican/readers.py b/pelican/readers.py index 8c108510..15d09908 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -227,7 +227,7 @@ class RstReader(BaseReader): if element.tagname == 'field': # custom fields (e.g. summary) name_elem, body_elem = element.children name = name_elem.astext() - if name in formatted_fields: + if name.lower() in formatted_fields: value = render_node_to_html( document, body_elem, self.field_body_translator_class) diff --git a/pelican/tests/content/article_with_capitalized_metadata.rst b/pelican/tests/content/article_with_capitalized_metadata.rst new file mode 100644 index 00000000..93ed5b15 --- /dev/null +++ b/pelican/tests/content/article_with_capitalized_metadata.rst @@ -0,0 +1,16 @@ + +This is a super article ! +######################### + +:TAGS: foo, bar, foobar +:DATE: 2010-12-02 10:14 +:MODIFIED: 2010-12-02 10:20 +:CATEGORY: yeah +:AUTHOR: Alexis Métaireau +:SUMMARY: + Multi-line metadata should be supported + as well as **inline markup** and stuff to "typogrify"... +:CUSTOM_FIELD: http://notmyidea.org +:CUSTOM_FORMATTED_FIELD: + Multi-line metadata should also be supported + as well as *inline markup* and stuff to "typogrify"... diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 332d7e78..169765ac 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -260,6 +260,7 @@ class TestArticlesGenerator(unittest.TestCase): ['This is a super article !', 'published', 'yeah', 'article'], ['This is a super article !', 'published', 'yeah', 'article'], ['This is a super article !', 'published', 'yeah', 'article'], + ['This is a super article !', 'published', 'yeah', 'article'], ['This is a super article !', 'published', 'Default', 'article'], ['Article with an inline SVG', 'published', 'Default', 'article'], ['This is an article with category !', 'published', 'yeah', @@ -576,6 +577,7 @@ class TestArticlesGenerator(unittest.TestCase): 'This is a super article !', 'This is a super article !', 'This is a super article !', + 'This is a super article !', 'This is an article with category !', ('This is an article with multiple authors in lastname, ' 'firstname format!'), diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index de2a1b22..ea5f3bdd 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -155,6 +155,24 @@ class RstReaderTest(ReaderTest): self.assertDictHasSubset(page.metadata, expected) + def test_article_with_capitalized_metadata(self): + page = self.read_file(path='article_with_capitalized_metadata.rst') + expected = { + 'category': 'yeah', + 'author': 'Alexis Métaireau', + 'title': 'This is a super article !', + 'summary': '

Multi-line metadata should be' + ' supported\nas well as inline' + ' markup and stuff to "typogrify' + '"...

\n', + 'date': SafeDatetime(2010, 12, 2, 10, 14), + 'modified': SafeDatetime(2010, 12, 2, 10, 20), + 'tags': ['foo', 'bar', 'foobar'], + 'custom_field': 'http://notmyidea.org', + } + + self.assertDictHasSubset(page.metadata, expected) + def test_article_with_filename_metadata(self): page = self.read_file( path='2012-11-29_rst_w_filename_meta#foo-bar.rst', From d473dbfdafb5b740e49891e5bd994595df8bde5b Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 17 Aug 2020 06:45:38 +0200 Subject: [PATCH 048/465] Use more reliable installation command in docs Some shell environments may interpret brackets as glob patterns. Wrapping the argument in quotation marks should help ensure correct behavior in those environments. Refs #2786 --- docs/install.rst | 4 ++-- docs/quickstart.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 03480e79..bf2647ad 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -11,7 +11,7 @@ You can install Pelican via several different methods. The simplest is via Or, if you plan on using Markdown:: - pip install pelican[Markdown] + pip install "pelican[markdown]" (Keep in mind that operating systems will often require you to prefix the above command with ``sudo`` in order to install Pelican system-wide.) @@ -46,7 +46,7 @@ Optional packages If you plan on using `Markdown `_ as a markup format, you can install Pelican with Markdown support:: - pip install pelican[Markdown] + pip install "pelican[markdown]" Or you might need to install it a posteriori:: diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 1f6358a7..8d6d6a5b 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -11,7 +11,7 @@ Install Pelican (and optionally Markdown if you intend to use it) on Python 2.7.x or Python 3.5+ by running the following command in your preferred terminal, prefixing with ``sudo`` if permissions warrant:: - pip install pelican[Markdown] + pip install "pelican[markdown]" Create a project ---------------- From 3d5a2b90140036cc6ef2d172e3d1eb1752938ca4 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 17 Aug 2020 07:04:00 +0200 Subject: [PATCH 049/465] Prefix Pip commands with `python -m` in docs This ensures Pip commands will be executed for the current Python interpreter and not, say, whichever Python interpreter happens to be associated with `/usr/local/bin/pip`. --- CONTRIBUTING.rst | 2 +- docs/contribute.rst | 4 ++-- docs/faq.rst | 2 +- docs/install.rst | 16 ++++++++-------- docs/publish.rst | 4 ++-- docs/quickstart.rst | 2 +- docs/settings.rst | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 9561b04f..61935f62 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -43,7 +43,7 @@ publicly-accessible location: * Describe what version of Pelican you are running (output of ``pelican --version`` or the HEAD commit hash if you cloned the repo) and how exactly you installed - it (the full command you used, e.g. ``pip install pelican``). + it (the full command you used, e.g. ``python -m pip install pelican``). * If you are looking for a way to get some end result, prepare a detailed description of what the end result should look like (preferably in the form of an image or a mock-up page) and explain in detail what you have done so far to diff --git a/docs/contribute.rst b/docs/contribute.rst index 66890ae7..62ed51f9 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -44,9 +44,9 @@ to manually create and activate a virtual environment:: Install the needed dependencies and set up the project:: - pip install invoke + python -m pip install invoke invoke setup - pip install -e ~/projects/pelican + python -m pip install -e ~/projects/pelican Your local environment should now be ready to go! diff --git a/docs/faq.rst b/docs/faq.rst index c35cd6c6..8532ab3f 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -74,7 +74,7 @@ Markdown format, you will need to explicitly install the Markdown library. You can do so by typing the following command, prepending ``sudo`` if permissions require it:: - pip install markdown + python -m pip install markdown Can I use arbitrary metadata in my templates? ============================================= diff --git a/docs/install.rst b/docs/install.rst index bf2647ad..69bb3e3a 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -7,11 +7,11 @@ Python are not supported. You can install Pelican via several different methods. The simplest is via `pip `_:: - pip install pelican + python -m pip install pelican Or, if you plan on using Markdown:: - pip install "pelican[markdown]" + python -m pip install "pelican[markdown]" (Keep in mind that operating systems will often require you to prefix the above command with ``sudo`` in order to install Pelican system-wide.) @@ -26,7 +26,7 @@ session and create a new virtual environment for Pelican:: source bin/activate Once the virtual environment has been created and activated, Pelican can be -installed via ``pip install pelican`` as noted above. Alternatively, if you +installed via ``python -m pip install pelican`` as noted above. Alternatively, if you have the project source, you can install Pelican using the distutils method:: cd path-to-Pelican-source @@ -35,7 +35,7 @@ have the project source, you can install Pelican using the distutils method:: If you have Git installed and prefer to install the latest bleeding-edge version of Pelican rather than a stable release, use the following command:: - pip install -e "git+https://github.com/getpelican/pelican.git#egg=pelican" + python -m pip install -e "git+https://github.com/getpelican/pelican.git#egg=pelican" Once Pelican is installed, you can run ``pelican --help`` to see basic usage options. For more detail, refer to the :doc:`Publish` section. @@ -46,17 +46,17 @@ Optional packages If you plan on using `Markdown `_ as a markup format, you can install Pelican with Markdown support:: - pip install "pelican[markdown]" + python -m pip install "pelican[markdown]" Or you might need to install it a posteriori:: - pip install Markdown + python -m pip install Markdown Typographical enhancements can be enabled in your settings file, but first the requisite `Typogrify `_ library must be installed:: - pip install typogrify + python -m pip install typogrify Dependencies ------------ @@ -88,7 +88,7 @@ Upgrading If you installed a stable Pelican release via ``pip`` and wish to upgrade to the latest stable release, you can do so by adding ``--upgrade``:: - pip install --upgrade pelican + python -m pip install --upgrade pelican If you installed Pelican via distutils or the bleeding-edge method, simply perform the same step to install the most recent version. diff --git a/docs/publish.rst b/docs/publish.rst index 9bea8938..ae1428f5 100644 --- a/docs/publish.rst +++ b/docs/publish.rst @@ -118,7 +118,7 @@ in a wide range of environments. The downside is that it must be installed separately. Use the following command to install Invoke, prefixing with ``sudo`` if your environment requires it:: - pip install invoke + python -m pip install invoke Take a moment to open the ``tasks.py`` file that was generated in your project root. You will see a number of commands, any one of which can be renamed, @@ -139,7 +139,7 @@ http://localhost:8000/:: invoke serve To serve the generated site with automatic browser reloading every time a -change is detected, first ``pip install livereload``, then use the +change is detected, first ``python -m pip install livereload``, then use the following command:: invoke livereload diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 8d6d6a5b..a8387a54 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -11,7 +11,7 @@ Install Pelican (and optionally Markdown if you intend to use it) on Python 2.7.x or Python 3.5+ by running the following command in your preferred terminal, prefixing with ``sudo`` if permissions warrant:: - pip install "pelican[markdown]" + python -m pip install "pelican[markdown]" Create a project ---------------- diff --git a/docs/settings.rst b/docs/settings.rst index fbd6169c..e05d7abf 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -277,7 +277,7 @@ Basic settings If set to True, several typographical improvements will be incorporated into the generated HTML via the `Typogrify `_ library, which can be installed - via: ``pip install typogrify`` + via: ``python -m pip install typogrify`` .. data:: TYPOGRIFY_IGNORE_TAGS = [] From d2b119387ee03214a786f177a5a38dbf9269f55b Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 17 Aug 2020 07:14:29 +0200 Subject: [PATCH 050/465] Remove outdated references to Python 2.7 in docs --- docs/install.rst | 4 +--- docs/quickstart.rst | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 69bb3e3a..b0a851c5 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -1,8 +1,7 @@ Installing Pelican ################## -Pelican currently runs best on Python 2.7.x and 3.5+; earlier versions of -Python are not supported. +Pelican currently runs best on 3.6+; earlier versions of Python are not supported. You can install Pelican via several different methods. The simplest is via `pip `_:: @@ -75,7 +74,6 @@ automatically installed without any action on your part: broadcast signaling system * `unidecode `_, for ASCII transliterations of Unicode text -* `six `_, for Python 2 and 3 compatibility utilities * `MarkupSafe `_, for a markup safe string implementation diff --git a/docs/quickstart.rst b/docs/quickstart.rst index a8387a54..ddc42651 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -8,8 +8,8 @@ Installation ------------ Install Pelican (and optionally Markdown if you intend to use it) on Python -2.7.x or Python 3.5+ by running the following command in your preferred -terminal, prefixing with ``sudo`` if permissions warrant:: +3.6+ by running the following command in your preferred terminal, prefixing +with ``sudo`` if permissions warrant:: python -m pip install "pelican[markdown]" From af1c1773bb292ba5a732a06bed5a45cba96ec8df Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 17 Aug 2020 07:21:57 +0200 Subject: [PATCH 051/465] Minor improvements to installation docs --- docs/install.rst | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index b0a851c5..eb618035 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -3,8 +3,7 @@ Installing Pelican Pelican currently runs best on 3.6+; earlier versions of Python are not supported. -You can install Pelican via several different methods. The simplest is via -`pip `_:: +You can install Pelican via several different methods. The simplest is via Pip_:: python -m pip install pelican @@ -12,7 +11,7 @@ Or, if you plan on using Markdown:: python -m pip install "pelican[markdown]" -(Keep in mind that operating systems will often require you to prefix the above +(Keep in mind that some operating systems will require you to prefix the above command with ``sudo`` in order to install Pelican system-wide.) While the above is the simplest method, the recommended approach is to create a @@ -47,10 +46,6 @@ markup format, you can install Pelican with Markdown support:: python -m pip install "pelican[markdown]" -Or you might need to install it a posteriori:: - - python -m pip install Markdown - Typographical enhancements can be enabled in your settings file, but first the requisite `Typogrify `_ library must be installed:: @@ -75,7 +70,7 @@ automatically installed without any action on your part: * `unidecode `_, for ASCII transliterations of Unicode text utilities -* `MarkupSafe `_, for a markup safe +* `MarkupSafe `_, for a markup-safe string implementation * `python-dateutil `_, to read the date metadata @@ -83,7 +78,7 @@ automatically installed without any action on your part: Upgrading --------- -If you installed a stable Pelican release via ``pip`` and wish to upgrade to +If you installed a stable Pelican release via Pip_ and wish to upgrade to the latest stable release, you can do so by adding ``--upgrade``:: python -m pip install --upgrade pelican @@ -124,4 +119,5 @@ content):: The next step is to begin to adding content to the *content* folder that has been created for you. +.. _Pip: https://pip.pypa.io/ .. _virtualenv: https://virtualenv.pypa.io/en/latest/ From 01b08bd81207293e13b910969aa10b63fdeaf828 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 17 Aug 2020 09:47:57 +0200 Subject: [PATCH 052/465] Rename branch for GitHub Pages repo quickstart --- pelican/tools/pelican_quickstart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 1a98ab14..41ddea3c 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -41,7 +41,7 @@ _jinja_env = Environment( _GITHUB_PAGES_BRANCHES = { - 'personal': 'master', + 'personal': 'main', 'project': 'gh-pages' } From 21c9ce1b73b74aee04d1551edab9d459a26bc1f9 Mon Sep 17 00:00:00 2001 From: kernc Date: Wed, 19 Aug 2020 20:25:19 +0200 Subject: [PATCH 053/465] =?UTF-8?q?Rename=20setting=20SUMMARY=5FEND=5FMARK?= =?UTF-8?q?ER=20=E2=86=92=20SUMMARY=5FEND=5FSUFFIX=20(#2792)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoids clash with 'summary' plugin. Refs: https://github.com/getpelican/pelican-plugins/pull/1284#issuecomment-660715086 --- docs/settings.rst | 4 ++-- pelican/contents.py | 2 +- pelican/settings.py | 2 +- pelican/tests/test_contents.py | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index e05d7abf..377051b0 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -303,10 +303,10 @@ Basic settings does not otherwise specify a summary. Setting to ``None`` will cause the summary to be a copy of the original content. -.. data:: SUMMARY_END_MARKER = '…' +.. data:: SUMMARY_END_SUFFIX = '…' When creating a short summary of an article and the result was truncated to - match the required word length, this will be used as the truncation marker. + match the required word length, this will be used as the truncation suffix. .. data:: WITH_FUTURE_DATES = True diff --git a/pelican/contents.py b/pelican/contents.py index 2bb2e3a0..6470ee45 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -392,7 +392,7 @@ class Content: return truncate_html_words(self.content, self.settings['SUMMARY_MAX_LENGTH'], - self.settings['SUMMARY_END_MARKER']) + self.settings['SUMMARY_END_SUFFIX']) @property def summary(self): diff --git a/pelican/settings.py b/pelican/settings.py index 11bbafbd..ea3ee8eb 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -136,7 +136,7 @@ DEFAULT_CONFIG = { 'TYPOGRIFY': False, 'TYPOGRIFY_IGNORE_TAGS': [], 'TYPOGRIFY_DASHES': 'default', - 'SUMMARY_END_MARKER': '…', + 'SUMMARY_END_SUFFIX': '…', 'SUMMARY_MAX_LENGTH': 50, 'PLUGIN_PATHS': [], 'PLUGINS': None, diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 93f5b212..1a520bc7 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -110,14 +110,14 @@ class TestPage(TestBase): page = Page(**page_kwargs) self.assertEqual(page.summary, '') - def test_summary_end_marker(self): - # If a :SUMMARY_END_MARKER: is set, and there is no other summary, + def test_summary_end_suffix(self): + # If a :SUMMARY_END_SUFFIX: is set, and there is no other summary, # generated summary should contain the specified marker at the end. page_kwargs = self._copy_page_kwargs() settings = get_settings() page_kwargs['settings'] = settings del page_kwargs['metadata']['summary'] - settings['SUMMARY_END_MARKER'] = 'test_marker' + settings['SUMMARY_END_SUFFIX'] = 'test_marker' settings['SUMMARY_MAX_LENGTH'] = 10 page = Page(**page_kwargs) self.assertEqual(page.summary, truncate_html_words(TEST_CONTENT, 10, From 0731d061f909c8b5f6a06f1ca90397325b9521aa Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 13 Oct 2019 11:58:41 -0700 Subject: [PATCH 054/465] Include the tests package in the source distribution Fixes #2635 --- setup.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index 2d831aa4..af96c726 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from os import walk from os.path import join, relpath -from setuptools import setup +from setuptools import setup, find_packages version = "4.2.0" @@ -44,19 +44,14 @@ setup( keywords='static web site generator SSG reStructuredText Markdown', license='AGPLv3', long_description=description, - packages=['pelican', 'pelican.tools', 'pelican.plugins'], - package_data={ - # we manually collect the package data, as opposed to using, - # include_package_data=True because we don't want the tests to be - # included automatically as package data (MANIFEST.in is too greedy) + packages=find_packages(), + include_package_data=True, # includes all in MANIFEST.in if in package + # NOTE : This will collect any files that happen to be in the themes + # directory, even though they may not be checked into version control. + package_data={ # pelican/themes is not a package, so include manually 'pelican': [relpath(join(root, name), 'pelican') for root, _, names in walk(join('pelican', 'themes')) for name in names], - 'pelican.tools': [relpath(join(root, name), join('pelican', 'tools')) - for root, _, names in walk(join('pelican', - 'tools', - 'templates')) - for name in names], }, install_requires=requires, extras_require={ From 94bdcb7f185e14b9ef946ff5cc49b800e9aaad70 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 20 Aug 2020 13:46:09 +0200 Subject: [PATCH 055/465] Remove Travis since we've switched to GitHub Actions CI --- .travis.yml | 53 ----------------------------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c0637a7f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,53 +0,0 @@ -language: python -python: - - "3.6" -env: - global: - - PYPI_USERNAME=autopub - - secure: "h5V/+YL+CrqvfAesNkSb824Ngk5x+f0eFzj/LBbmnzjvArKAmc6R6WGyx8SDD7WF/PlaTf0M1fH3a7pjIS8Ee+TS1Rb0Lt1HPqUs1yntg1+Js2ZQp3p20wfsDc+bZ4/2g8xLsSMv1EJ4np7/GJ5fXqpSxjr/Xs5LYA7ZLwNNwDw=" - - secure: "GiDFfmjH7uzYNnkjQMV/mIkbRdmgkGmtbFPeaj9taBNA5tPp3IBt3GOOS6UL/zm9xiwu9Xo6sxZWkGzY19Hsdv28YPH34N3abo0QSnz4IGiHs152Hi7Qi6Tb0QkT5D3OxuSIm8LmFL7+su89Q7vBFowrT6HL1Mn8CDDWSj3eqbo=" - - TWINE_USERNAME=$PYPI_USERNAME - - TWINE_PASSWORD=$PYPI_PASSWORD - matrix: - - TOX_ENV=docs - - TOX_ENV=flake8 - - TOX_ENV=py3.5 - - TOX_ENV=py3.6 -matrix: - include: - - python: 3.7 - sudo: true - dist: xenial - env: - - TOX_ENV=py3.7 -addons: - apt_packages: - - pandoc -before_install: - - sudo apt-get update -qq - - sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8 -install: - - pip install tox==2.5.0 -script: tox -e $TOX_ENV -before_deploy: - - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then travis_terminate 0; fi' - - pip install githubrelease - - pip install --pre autopub - - autopub check || travis_terminate 0 - - pip install poetry - - pip install twine - - git checkout ${TRAVIS_BRANCH} - - git remote set-url origin https://$GITHUB_TOKEN@github.com/$TRAVIS_REPO_SLUG -deploy: - provider: script - script: autopub deploy - skip_cleanup: true - on: - branch: master - python: "3.7" -# The channel name "irc.freenode.org#pelican" is encrypted against getpelican/pelican to prevent IRC spam of forks -notifications: - irc: - channels: - - secure: "JP57f61QovrhmLoAF6oPOzIK2aXGfSO06FHg7yiuKBOWMiaxQejZUGJX919muCLhWJXDugsviIqCMoAWwNV3o1WQbqIr+G5TR+N9MrtCs4Zi6vpGj09bR8giKUKx+PPKEoe1Ew56E4y2LxzGO4Lj9hZx8M2YVdwPNWrWZgp6WXE=" - on_success: change From e11a7de051e3a8bb0158ba462bc8e69322047bf0 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 20 Aug 2020 13:51:23 +0200 Subject: [PATCH 056/465] Add missing Pelican entry points to pyproject.toml --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 4456c913..ce86ef72 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,6 +67,10 @@ markdown = ["markdown"] [tool.poetry.scripts] pelican = "pelican.__main__:main" +pelican-import = "pelican.tools.pelican_import:main" +pelican-plugins = "pelican.plugins._utils:list_plugins" +pelican-quickstart = "pelican.tools.pelican_quickstart:main" +pelican-themes = "pelican.tools.pelican_themes:main" [tool.autopub] project-name = "Pelican" From 4bda2745ac2d654efd436d20e31895f3d7705036 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 20 Aug 2020 17:57:21 +0200 Subject: [PATCH 057/465] Prepare to release Pelican 4.5 --- docs/changelog.rst | 22 +++++++++++++++++++--- pyproject.toml | 2 +- setup.py | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index febc5322..019eb55e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,22 @@ Release history ############### +4.5.0 - 2020-08-20 +================== + +* Add namespace plugin support; list plugins via ``pelican-plugins`` command +* Override settings via ``-e`` / ``--extra-settings`` CLI option flags +* Add settings for custom Jinja globals and tests +* Customize article summary ellipsis via ``SUMMARY_END_MARKER`` setting +* Customize Typogrify dash handling via new ``TYPOGRIFY_DASHES`` setting +* Support Unicode when generating slugs +* Support Asciidoc ``.adoc`` file generation in Pelican importer +* Improve user experience when ``pelican --listen`` web server is quit +* Improve Invoke tasks template +* Include tests in source distributions +* Switch CI from Travis to GitHub Actions +* Remove support for Python 2.7 + 4.2.0 - 2019-10-17 ================== @@ -12,7 +28,7 @@ Release history 4.1.3 - 2019-10-09 ================== -* Fix quick-start docs regarding `pelican --listen` +* Fix quick-start docs regarding ``pelican --listen`` * Set default listen address to 127.0.0.1 * Add extra/optional Markdown dependency to setup.py * Use correct SSH port syntax for rsync in tasks.py @@ -30,8 +46,8 @@ Fix pelican.settings.load_source to avoid caching issues - PR #2621 * Add AutoPub to auto-publish releases on PR merge * Add CSS classes for reStructuredText figures -* Pass `argv` to Pelican `main` entrypoint -* Set default content status to a blank string rather than `None` +* Pass ``argv`` to Pelican ``main`` entrypoint +* Set default content status to a blank string rather than ``None`` 4.1.0 - 2019-07-14 ================== diff --git a/pyproject.toml b/pyproject.toml index ce86ef72..ba0573fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pelican" -version = "4.2.0" +version = "4.5.0" description = "Static site generator supporting Markdown and reStructuredText" authors = ["Justin Mayer "] license = "AGPLv3" diff --git a/setup.py b/setup.py index af96c726..e5aa7141 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from os.path import join, relpath from setuptools import setup, find_packages -version = "4.2.0" +version = "4.5.0" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.11', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', From 0ab6ac97983952c00b7b7e8b8de3c1e0cd22127f Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 20 Aug 2020 19:20:51 +0200 Subject: [PATCH 058/465] Fix PyPI-related reST/README issues --- README.rst | 2 +- setup.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index dee92234..f0685483 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ Pelican |build-status| |pypi-version| |repology| -===================================== +================================================ Pelican is a static site generator, written in Python_. diff --git a/setup.py b/setup.py index e5aa7141..00aff78b 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from os import walk from os.path import join, relpath -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = "4.5.0" @@ -44,6 +44,7 @@ setup( keywords='static web site generator SSG reStructuredText Markdown', license='AGPLv3', long_description=description, + long_description_content_type='text/x-rst', packages=find_packages(), include_package_data=True, # includes all in MANIFEST.in if in package # NOTE : This will collect any files that happen to be in the themes @@ -68,6 +69,7 @@ setup( 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Software Development :: Libraries :: Python Modules', From 3565094b10b74217f0b78d9c1f2f06f2c1cf61ea Mon Sep 17 00:00:00 2001 From: Jiachen Yang Date: Mon, 24 Aug 2020 22:34:02 +0900 Subject: [PATCH 059/465] MANIFEST.in include jinja2 templates --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 138c8f00..469f6fff 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include *.rst -recursive-include pelican *.html *.css *png *.rst *.markdown *.md *.mkd *.xml *.py +recursive-include pelican *.html *.css *png *.rst *.markdown *.md *.mkd *.xml *.py *.jinja2 include LICENSE THANKS docs/changelog.rst pyproject.toml From 7a6686f4679adabdd690668a4ba7cedaf494e243 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sat, 29 Aug 2020 12:46:44 +0200 Subject: [PATCH 060/465] Fix SUMMARY_END_SUFFIX variable name in changelog --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 019eb55e..8287f8d6 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -7,7 +7,7 @@ Release history * Add namespace plugin support; list plugins via ``pelican-plugins`` command * Override settings via ``-e`` / ``--extra-settings`` CLI option flags * Add settings for custom Jinja globals and tests -* Customize article summary ellipsis via ``SUMMARY_END_MARKER`` setting +* Customize article summary ellipsis via ``SUMMARY_END_SUFFIX`` setting * Customize Typogrify dash handling via new ``TYPOGRIFY_DASHES`` setting * Support Unicode when generating slugs * Support Asciidoc ``.adoc`` file generation in Pelican importer From aed71c30f86f5b929e735e17a36e838b5ee527ec Mon Sep 17 00:00:00 2001 From: Kevin Deldycke Date: Thu, 3 Sep 2020 11:21:52 +0200 Subject: [PATCH 061/465] No need to maintain License and Python version classifiers. Poetry takes care of it: https://python-poetry.org/docs/pyproject/#classifiers --- pyproject.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ba0573fb..0e920513 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,12 +15,7 @@ classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Framework :: Pelican", - "License :: OSI Approved :: GNU Affero General Public License v3", "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Internet :: WWW/HTTP", "Topic :: Software Development :: Libraries :: Python Modules", From 30c9f6bb523e291f336932de063d60e4b40550aa Mon Sep 17 00:00:00 2001 From: Kevin Deldycke Date: Thu, 3 Sep 2020 11:30:11 +0200 Subject: [PATCH 062/465] Refine classification. --- pyproject.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0e920513..d766400e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,8 +17,11 @@ classifiers = [ "Framework :: Pelican", "Operating System :: OS Independent", "Programming Language :: Python :: Implementation :: CPython", - "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System", + "Topic :: Internet :: WWW/HTTP :: Site Management", "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Text Processing :: Markup :: Markdown", + "Topic :: Text Processing :: Markup :: HTML", ] [tool.poetry.urls] From 8fffcbef7af4d6e6f8fa07257e562cde10fe9f15 Mon Sep 17 00:00:00 2001 From: Kevin Deldycke Date: Fri, 4 Sep 2020 13:04:50 +0200 Subject: [PATCH 063/465] Add new reStructuredText classifier. Just been added to the canonical list in: https://github.com/pypa/trove-classifiers/pull/46 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d766400e..1cf84312 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: Markdown", "Topic :: Text Processing :: Markup :: HTML", + "Topic :: Text Processing :: Markup :: reStructuredText", ] [tool.poetry.urls] From 991c00af95ac3cdb197b0d04e7349d801d6c951d Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Mon, 12 Oct 2020 14:50:42 +0300 Subject: [PATCH 064/465] resolve packages for legacy plugins Updated legacy plugin loader to handle pakcages if the PLUGINS entry contains `.`. Also adds a test for it. --- pelican/plugins/_utils.py | 5 +++++ .../normal_submodule_plugin/__init__.py | 0 .../subpackage/__init__.py | 0 .../subpackage/subpackage.py | 5 +++++ .../normal_submodule_plugin/subplugin.py | 5 +++++ pelican/tests/test_plugins.py | 15 +++++++++++++++ 6 files changed, 30 insertions(+) create mode 100644 pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/__init__.py create mode 100644 pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/__init__.py create mode 100644 pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/subpackage.py create mode 100644 pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subplugin.py diff --git a/pelican/plugins/_utils.py b/pelican/plugins/_utils.py index 78c19e54..4e6ec3c5 100644 --- a/pelican/plugins/_utils.py +++ b/pelican/plugins/_utils.py @@ -40,6 +40,11 @@ def list_plugins(ns_pkg=None): def load_legacy_plugin(plugin, plugin_paths): + if '.' in plugin: + # it is in a package, try to resolve package first + package, _, _ = plugin.rpartition('.') + load_legacy_plugin(package, plugin_paths) + # Try to find plugin in PLUGIN_PATHS spec = importlib.machinery.PathFinder.find_spec(plugin, plugin_paths) if spec is None: diff --git a/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/__init__.py b/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/__init__.py b/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/subpackage.py b/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/subpackage.py new file mode 100644 index 00000000..ddb0eeca --- /dev/null +++ b/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/subpackage.py @@ -0,0 +1,5 @@ +NAME = 'normal subpackage plugin' + + +def register(): + pass diff --git a/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subplugin.py b/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subplugin.py new file mode 100644 index 00000000..377c788b --- /dev/null +++ b/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subplugin.py @@ -0,0 +1,5 @@ +NAME = 'normal submodule plugin' + + +def register(): + pass diff --git a/pelican/tests/test_plugins.py b/pelican/tests/test_plugins.py index 730aa6d5..06940884 100644 --- a/pelican/tests/test_plugins.py +++ b/pelican/tests/test_plugins.py @@ -116,6 +116,21 @@ class PluginTest(unittest.TestCase): {'normal plugin'}, get_plugin_names(plugins)) + # normal submodule/subpackage plugins + SETTINGS = { + 'PLUGINS': [ + 'normal_submodule_plugin.subplugin', + 'normal_submodule_plugin.subpackage.subpackage', + ], + 'PLUGIN_PATHS': [self._NORMAL_PLUGIN_FOLDER] + } + plugins = load_plugins(SETTINGS) + self.assertEqual(len(plugins), 2, plugins) + self.assertEqual( + {'normal submodule plugin', + 'normal subpackage plugin'}, + get_plugin_names(plugins)) + # namespace plugin short SETTINGS = { 'PLUGINS': ['ns_plugin'] From 6da975fc73311d683593e59c1b4b24822ddaa0e4 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Mon, 12 Oct 2020 14:53:18 +0300 Subject: [PATCH 065/465] restore pelican.signals with an explicit ImportError mentioning move --- pelican/signals.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 pelican/signals.py diff --git a/pelican/signals.py b/pelican/signals.py new file mode 100644 index 00000000..9b84a92a --- /dev/null +++ b/pelican/signals.py @@ -0,0 +1,4 @@ +raise ImportError( + 'Importing from `pelican.signals` is deprecated. ' + 'Use `from pelican import signals` or `import pelican.plugins.signals` instead.' +) From 8b8a0147e886d6a42b6d8c5c76d96627b7e1241d Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Mon, 12 Oct 2020 18:38:46 +0300 Subject: [PATCH 066/465] add python3.9 to CI --- .github/workflows/main.yml | 10 ++++++---- tox.ini | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bcbd94d4..5c284a6d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,6 +23,8 @@ jobs: python: 3.7 - os: ubuntu python: 3.8 + - os: ubuntu + python: 3.9 - os: macos python: 3.7 - os: windows @@ -31,7 +33,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup Python ${{ matrix.config.python }} - uses: actions/setup-python@v1.1.1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.config.python }} - name: Set pip cache (Linux) @@ -84,7 +86,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup Python - uses: actions/setup-python@v1.1.1 + uses: actions/setup-python@v2 with: python-version: 3.6 - name: Set pip cache (Linux) @@ -108,7 +110,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup Python - uses: actions/setup-python@v1.1.1 + uses: actions/setup-python@v2 with: python-version: 3.6 - name: Set pip cache (Linux) @@ -134,7 +136,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup Python - uses: actions/setup-python@v1.1.1 + uses: actions/setup-python@v2 with: python-version: 3.7 - name: Check release diff --git a/tox.ini b/tox.ini index 69281e30..b4664e98 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{3.5,3.6,3.7,3.8},docs,flake8 +envlist = py{3.5,3.6,3.7,3.8,3.9},docs,flake8 [testenv] basepython = @@ -7,6 +7,7 @@ basepython = py3.6: python3.6 py3.7: python3.7 py3.8: python3.8 + py3.9: python3.9 passenv = * usedevelop=True deps = From fd0923d2f24c9833021c01f247f97f4d1d8e67b4 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Sun, 4 Oct 2020 19:29:32 +0300 Subject: [PATCH 067/465] Try unescaped paths in intrasite link discovery Some content parsers escape link paths in their html output (i.e. docutils uses HTML escaping and markdown uses URL encoding. Intrasite link discovery is refactored to also attempt HTML or URL unescaped versions of the path in order to match more permissively. --- pelican/contents.py | 80 +++++++++++++++++++++------------- pelican/tests/test_contents.py | 57 ++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 31 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 6470ee45..75cedcdc 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -4,7 +4,8 @@ import locale import logging import os import re -from urllib.parse import urljoin, urlparse, urlunparse +from html import unescape +from urllib.parse import unquote, urljoin, urlparse, urlunparse import pytz @@ -250,38 +251,55 @@ class Content: # XXX Put this in a different location. if what in {'filename', 'static', 'attach'}: - if path.startswith('/'): - path = path[1:] + def _get_linked_content(key, url): + nonlocal value + + def _find_path(path): + if path.startswith('/'): + path = path[1:] + else: + # relative to the source path of this content + path = self.get_relative_source_path( + os.path.join(self.relative_dir, path) + ) + return self._context[key].get(path, None) + + # try path + result = _find_path(url.path) + if result is not None: + return result + + # try unquoted path + result = _find_path(unquote(url.path)) + if result is not None: + return result + + # try html unescaped url + unescaped_url = urlparse(unescape(url.geturl())) + result = _find_path(unescaped_url.path) + if result is not None: + value = unescaped_url + return result + + # check if a static file is linked with {filename} + if what == 'filename' and key == 'generated_content': + linked_content = _get_linked_content('static_content', value) + if linked_content: + logger.warning( + '{filename} used for linking to static' + ' content %s in %s. Use {static} instead', + value.path, + self.get_relative_source_path()) + return linked_content + + return None + + if what == 'filename': + key = 'generated_content' else: - # relative to the source path of this content - path = self.get_relative_source_path( - os.path.join(self.relative_dir, path) - ) + key = 'static_content' - key = 'static_content' if what in ('static', 'attach')\ - else 'generated_content' - - def _get_linked_content(key, path): - try: - return self._context[key][path] - except KeyError: - try: - # Markdown escapes spaces, try unescaping - return self._context[key][path.replace('%20', ' ')] - except KeyError: - if what == 'filename' and key == 'generated_content': - key = 'static_content' - linked_content = _get_linked_content(key, path) - if linked_content: - logger.warning( - '{filename} used for linking to static' - ' content %s in %s. Use {static} instead', - path, - self.get_relative_source_path()) - return linked_content - return None - - linked_content = _get_linked_content(key, path) + linked_content = _get_linked_content(key, value) if linked_content: if what == 'attach': linked_content.attach_to(self) diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 1a520bc7..32012d4f 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -30,6 +30,9 @@ class TestBase(LoggedTestCase): 'content': TEST_CONTENT, 'context': { 'localsiteurl': '', + 'generated_content': {}, + 'static_content': {}, + 'static_links': set() }, 'metadata': { 'summary': TEST_SUMMARY, @@ -519,6 +522,60 @@ class TestPage(TestBase): '' ) + def test_intrasite_link_escape(self): + article = type( + '_DummyArticle', (object,), {'url': 'article-spaces.html'}) + asset = type( + '_DummyAsset', (object,), {'url': 'name@example.com'}) + + args = self.page_kwargs.copy() + args['settings'] = get_settings() + args['source_path'] = 'content' + args['context']['generated_content'] = {'article spaces.rst': article} + args['context']['static_content'] = {'name@example.com': asset} + + expected_output = ( + 'A simple test with a ' + 'link ' + 'file' + ) + + # not escaped + args['content'] = ( + 'A simple test with a ' + 'link ' + 'file' + ) + content = Page(**args).get_content('http://notmyidea.org') + self.assertEqual(content, expected_output) + + # html escaped + args['content'] = ( + 'A simple test with a ' + 'link ' + 'file' + ) + content = Page(**args).get_content('http://notmyidea.org') + self.assertEqual(content, expected_output) + + # url escaped + args['content'] = ( + 'A simple test with a ' + 'link ' + 'file' + ) + content = Page(**args).get_content('http://notmyidea.org') + self.assertEqual(content, expected_output) + + # html and url escaped + args['content'] = ( + 'A simple test with a ' + 'link ' + 'file' + ) + content = Page(**args).get_content('http://notmyidea.org') + self.assertEqual(content, expected_output) + def test_intrasite_link_markdown_spaces(self): cls_name = '_DummyArticle' article = type(cls_name, (object,), {'url': 'article-spaces.html'}) From 587e1a4ad89da2be46368baafb4f00b3b3d0c64a Mon Sep 17 00:00:00 2001 From: shniubobo Date: Sat, 31 Oct 2020 20:34:45 +0800 Subject: [PATCH 068/465] Fix plugins running twice in autoreload mode Fixes #2817 --- pelican/plugins/_utils.py | 3 +++ pelican/tests/test_plugins.py | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/pelican/plugins/_utils.py b/pelican/plugins/_utils.py index 4e6ec3c5..ffe32799 100644 --- a/pelican/plugins/_utils.py +++ b/pelican/plugins/_utils.py @@ -53,6 +53,9 @@ def load_legacy_plugin(plugin, plugin_paths): if spec is None: raise ImportError('Cannot import plugin `{}`'.format(plugin)) else: + # Avoid loading the same plugin twice + if spec.name in sys.modules: + return sys.modules[spec.name] # create module object from spec mod = importlib.util.module_from_spec(spec) # place it into sys.modules cache diff --git a/pelican/tests/test_plugins.py b/pelican/tests/test_plugins.py index 06940884..29729539 100644 --- a/pelican/tests/test_plugins.py +++ b/pelican/tests/test_plugins.py @@ -131,6 +131,17 @@ class PluginTest(unittest.TestCase): 'normal subpackage plugin'}, get_plugin_names(plugins)) + # ensure normal plugins are loaded only once + SETTINGS = { + 'PLUGINS': ['normal_plugin'], + 'PLUGIN_PATHS': [self._NORMAL_PLUGIN_FOLDER], + } + plugins = load_plugins(SETTINGS) + for plugin in load_plugins(SETTINGS): + # The second load_plugins() should return the same plugin + # objects as the first one + self.assertIn(plugin, plugins) + # namespace plugin short SETTINGS = { 'PLUGINS': ['ns_plugin'] From b454f76f72dea65db9d360dc1bf51b8ddb76149d Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 2 Nov 2020 13:17:53 +0100 Subject: [PATCH 069/465] Count CPU cores via psutil for parallel testing psutil does a much better job at accurately counting CPU cores, of which pytest-xdist takes advantage for the purposes of running tests concurrently. --- pyproject.toml | 1 + requirements/test.pip | 2 +- tasks.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1cf84312..cbe7642e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ typogrify = "^2.0" sphinx = "^3.0" sphinx_rtd_theme = "^0.5" livereload = "^2.6" +psutil = {version = "^5.7", optional = true} pytest = "^6.0" pytest-cov = "^2.8" pytest-pythonpath = "^0.7.3" diff --git a/requirements/test.pip b/requirements/test.pip index 647a8694..d36405f0 100644 --- a/requirements/test.pip +++ b/requirements/test.pip @@ -2,7 +2,7 @@ Pygments==2.6.1 pytest pytest-cov -pytest-xdist +pytest-xdist[psutil] # Optional Packages Markdown >= 3.1 diff --git a/tasks.py b/tasks.py index 76c48834..171a9711 100644 --- a/tasks.py +++ b/tasks.py @@ -14,7 +14,7 @@ VENV_PATH = Path(ACTIVE_VENV) if ACTIVE_VENV else (VENV_HOME / PKG_NAME) VENV = str(VENV_PATH.expanduser()) VENV_BIN = Path(VENV) / Path(BIN_DIR) -TOOLS = ["poetry", "pre-commit"] +TOOLS = ["poetry", "pre-commit", "psutil"] POETRY = which("poetry") if which("poetry") else (VENV_BIN / "poetry") PRECOMMIT = ( which("pre-commit") if which("pre-commit") else (VENV_BIN / "pre-commit") From 98b1a46362f6217216306527fb8715316982b995 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 2 Nov 2020 14:10:02 +0100 Subject: [PATCH 070/465] Prepare release --- RELEASE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..0f25e64e --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,5 @@ +Release type: patch + +* Refactor intra-site link discovery in order to match more permissively [#2646](https://github.com/getpelican/pelican/issues/2646) +* Fix plugins running twice in auto-reload mode [#2817](https://github.com/getpelican/pelican/issues/2817) +* Add notice to use `from pelican import signals` instead of `import pelican.signals` [#2805](https://github.com/getpelican/pelican/issues/2805) From 06d4eff4a52bf75c5fc447aa779b1694f44028c9 Mon Sep 17 00:00:00 2001 From: botpub Date: Mon, 2 Nov 2020 13:15:26 +0000 Subject: [PATCH 071/465] Release Pelican 4.5.1 --- RELEASE.md | 5 ----- docs/changelog.rst | 7 +++++++ pyproject.toml | 2 +- setup.py | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) delete mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 0f25e64e..00000000 --- a/RELEASE.md +++ /dev/null @@ -1,5 +0,0 @@ -Release type: patch - -* Refactor intra-site link discovery in order to match more permissively [#2646](https://github.com/getpelican/pelican/issues/2646) -* Fix plugins running twice in auto-reload mode [#2817](https://github.com/getpelican/pelican/issues/2817) -* Add notice to use `from pelican import signals` instead of `import pelican.signals` [#2805](https://github.com/getpelican/pelican/issues/2805) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8287f8d6..e2900c48 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,13 @@ Release history ############### +4.5.1 - 2020-11-02 +================== + +* Refactor intra-site link discovery in order to match more permissively [#2646](https://github.com/getpelican/pelican/issues/2646) +* Fix plugins running twice in auto-reload mode [#2817](https://github.com/getpelican/pelican/issues/2817) +* Add notice to use `from pelican import signals` instead of `import pelican.signals` [#2805](https://github.com/getpelican/pelican/issues/2805) + 4.5.0 - 2020-08-20 ================== diff --git a/pyproject.toml b/pyproject.toml index cbe7642e..4331fce1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pelican" -version = "4.5.0" +version = "4.5.1" description = "Static site generator supporting Markdown and reStructuredText" authors = ["Justin Mayer "] license = "AGPLv3" diff --git a/setup.py b/setup.py index 00aff78b..fc4ec3ce 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from os.path import join, relpath from setuptools import find_packages, setup -version = "4.5.0" +version = "4.5.1" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.11', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', From e3c7a915e500e17b668ddebfb1cc7e30673dbf33 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 2 Nov 2020 17:18:38 +0100 Subject: [PATCH 072/465] Fix changelog entry formatting and issue links --- docs/changelog.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index e2900c48..7031b63b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,9 +4,9 @@ Release history 4.5.1 - 2020-11-02 ================== -* Refactor intra-site link discovery in order to match more permissively [#2646](https://github.com/getpelican/pelican/issues/2646) -* Fix plugins running twice in auto-reload mode [#2817](https://github.com/getpelican/pelican/issues/2817) -* Add notice to use `from pelican import signals` instead of `import pelican.signals` [#2805](https://github.com/getpelican/pelican/issues/2805) +* Refactor intra-site link discovery in order to match more permissively `(#2646) `_ +* Fix plugins running twice in auto-reload mode `(#2817) `_ +* Add notice to use ``from pelican import signals`` instead of ``import pelican.signals`` `(#2805) `_ 4.5.0 - 2020-08-20 ================== From 67e27ca7cd36f4b9cd27418d171cb2e5c25df9ea Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 20 Nov 2020 15:39:52 +0100 Subject: [PATCH 073/465] pelican-plugins only shows auto-discovered plugins Document and clarify that only Pip-installed namespace plugins will be shown when running the `pelican-plugins` command. --- docs/plugins.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index 3a56ed07..f661afb8 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -12,8 +12,8 @@ How to use plugins Starting with version 4.5, Pelican moved to a new plugin structure utilizing namespace packages that can be easily installed via Pip_. Plugins supporting this structure will install under the namespace package ``pelican.plugins`` and -can be automatically discovered by Pelican. To see a list of plugins that are -active in your environment, run:: +can be automatically discovered by Pelican. To see a list of Pip-installed +namespace plugins that are active in your environment, run:: pelican-plugins From 4b79d6dae67844ba7faff10101dc8b18780692d4 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 20 Nov 2020 15:46:05 +0100 Subject: [PATCH 074/465] Update PyPI URLs to new equivalents --- README.rst | 2 +- docs/settings.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index f0685483..995d23b0 100644 --- a/README.rst +++ b/README.rst @@ -60,7 +60,7 @@ Why the name "Pelican"? :target: https://github.com/getpelican/pelican/actions :alt: GitHub Actions CI: continuous integration status .. |pypi-version| image:: https://img.shields.io/pypi/v/pelican.svg - :target: https://pypi.python.org/pypi/pelican + :target: https://pypi.org/project/pelican/ :alt: PyPI: the Python Package Index .. |repology| image:: https://repology.org/badge/tiny-repos/pelican.svg :target: https://repology.org/project/pelican/versions diff --git a/docs/settings.rst b/docs/settings.rst index 377051b0..e7eae66a 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -276,7 +276,7 @@ Basic settings If set to True, several typographical improvements will be incorporated into the generated HTML via the `Typogrify - `_ library, which can be installed + `_ library, which can be installed via: ``python -m pip install typogrify`` .. data:: TYPOGRIFY_IGNORE_TAGS = [] From f21969a0161621642f60139b5735190ee633e7e6 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 20 Nov 2020 16:22:39 +0100 Subject: [PATCH 075/465] Ensure `invoke lint` task & its docs are in sync Someday the entire Pelican code base will perhaps have Black and isort formatting applied to it, but it doesn't make sense for the linter to check that until it's actually done. This changes the `lint` Invoke task to run Flake8 on changed lines only, which should bring it into line with both the current Pre-commit and CI configurations. Also, the docs erroneously stated that `invoke tests` would also check for code style compliance, which has herein been fixed. --- docs/contribute.rst | 13 ++++++++----- tasks.py | 4 +--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/contribute.rst b/docs/contribute.rst index 62ed51f9..2e1dc317 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -75,11 +75,14 @@ via:: invoke tests -In addition to running the test suite, the above invocation will also check code -style and let you know whether non-conforming patterns were found. In some cases -these linters will make the needed changes directly, while in other cases you -may need to make additional changes until ``invoke tests`` no longer reports any -code style violations. +In addition to running the test suite, it is important to also ensure that any +lines you changed conform to code style guidelines. You can check that via:: + + invoke lint + +If code style violations are found in lines you changed, correct those lines +and re-run the above lint command until they have all been fixed. You do not +need to address style violations, if any, for code lines you did not touch. After making your changes and running the tests, you may see a test failure mentioning that "some generated files differ from the expected functional tests diff --git a/tasks.py b/tasks.py index 171a9711..1d511859 100644 --- a/tasks.py +++ b/tasks.py @@ -70,13 +70,11 @@ def isort(c, check=False, diff=False): @task def flake8(c): - c.run(f"{VENV_BIN}/flake8 {PKG_PATH} tasks.py") + c.run(f"git diff HEAD | {VENV_BIN}/flake8 --diff --max-line-length=88") @task def lint(c): - isort(c, check=True) - black(c, check=True) flake8(c) From a9d9ccb58300c467d0b2aa721f5189706583a0b4 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 20 Nov 2020 16:30:18 +0100 Subject: [PATCH 076/465] Improve docs for plugin code style compliance --- docs/contribute.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/contribute.rst b/docs/contribute.rst index 2e1dc317..a86a5031 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -148,9 +148,20 @@ Create a topic branch for your plugin bug fix or feature:: git checkout -b name-of-your-bugfix-or-feature -After writing new tests for your plugin changes, run the plugin test suite:: +After writing new tests for your plugin changes, run the plugin test suite and +check for code style compliance via:: invoke tests + invoke lint + +If style violations are found, many of them can be addressed automatically via:: + + invoke black + invoke isort + +If style violations are found even after running the above auto-formatters, +you will need to make additional manual changes until ``invoke lint`` no longer +reports any code style violations. .. _plugin template: https://github.com/getpelican/cookiecutter-pelican-plugin .. _Simple Footnotes: https://github.com/pelican-plugins/simple-footnotes From afdf0fb3cf6cd376a6f241e2d5bbf99cf0e348c2 Mon Sep 17 00:00:00 2001 From: Leonardo Giordani Date: Sun, 22 Nov 2020 16:13:51 +0000 Subject: [PATCH 077/465] Improve logging of generators and writer loaders (#2821) --- RELEASE.md | 3 ++ pelican/__init__.py | 73 ++++++++++++++++++++--------------- pelican/tests/test_pelican.py | 4 +- 3 files changed, 47 insertions(+), 33 deletions(-) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..384c99f0 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,3 @@ +Release type: patch + +Improve logging of generators and writer loaders diff --git a/pelican/__init__.py b/pelican/__init__.py index 82366117..b6ebb762 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -93,7 +93,7 @@ class Pelican: path=self.path, theme=self.theme, output_path=self.output_path, - ) for cls in self.get_generator_classes() + ) for cls in self._get_generator_classes() ] # Delete the output directory if (1) the appropriate setting is True @@ -114,7 +114,7 @@ class Pelican: signals.all_generators_finalized.send(generators) - writer = self.get_writer() + writer = self._get_writer() for p in generators: if hasattr(p, 'generate_output'): @@ -162,46 +162,57 @@ class Pelican: pluralized_draft_pages, time.time() - start_time)) - def get_generator_classes(self): - generators = [ArticlesGenerator, PagesGenerator] + def _get_generator_classes(self): + discovered_generators = [ + (ArticlesGenerator, "internal"), + (PagesGenerator, "internal") + ] - if self.settings['TEMPLATE_PAGES']: - generators.append(TemplatePagesGenerator) - if self.settings['OUTPUT_SOURCES']: - generators.append(SourceFileGenerator) + if self.settings["TEMPLATE_PAGES"]: + discovered_generators.append((TemplatePagesGenerator, "internal")) - for pair in signals.get_generators.send(self): - (funct, value) = pair + if self.settings["OUTPUT_SOURCES"]: + discovered_generators.append((SourceFileGenerator, "internal")) - if not isinstance(value, Iterable): - value = (value, ) + for receiver, values in signals.get_generators.send(self): + if not isinstance(values, Iterable): + values = (values,) - for v in value: - if isinstance(v, type): - logger.debug('Found generator: %s', v) - generators.append(v) + discovered_generators.extend( + [(generator, receiver.__module__) for generator in values] + ) # StaticGenerator must run last, so it can identify files that # were skipped by the other generators, and so static files can # have their output paths overridden by the {attach} link syntax. - generators.append(StaticGenerator) + discovered_generators.append((StaticGenerator, "internal")) + + generators = [] + + for generator, origin in discovered_generators: + if not isinstance(generator, type): + logger.error("Generator %s (%s) cannot be loaded", generator, origin) + continue + + logger.debug("Found generator: %s (%s)", generator.__name__, origin) + generators.append(generator) + return generators - def get_writer(self): - writers = [w for (_, w) in signals.get_writer.send(self) - if isinstance(w, type)] - writers_found = len(writers) - if writers_found == 0: + def _get_writer(self): + writers = [w for _, w in signals.get_writer.send(self) if isinstance(w, type)] + num_writers = len(writers) + + if num_writers == 0: return Writer(self.output_path, settings=self.settings) - else: - writer = writers[0] - if writers_found == 1: - logger.debug('Found writer: %s', writer) - else: - logger.warning( - '%s writers found, using only first one: %s', - writers_found, writer) - return writer(self.output_path, settings=self.settings) + + if num_writers > 1: + logger.warning("%s writers found, using only first one", num_writers) + + writer = writers[0] + + logger.debug("Found writer: %s", writer) + return writer(self.output_path, settings=self.settings) class PrintSettings(argparse.Action): diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 219377ae..cacc65c1 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -84,14 +84,14 @@ class TestPelican(LoggedTestCase): # have their output paths overridden by the {attach} link syntax. pelican = Pelican(settings=read_settings(path=None)) - generator_classes = pelican.get_generator_classes() + generator_classes = pelican._get_generator_classes() self.assertTrue( generator_classes[-1] is StaticGenerator, "StaticGenerator must be the last generator, but it isn't!") self.assertIsInstance( generator_classes, Sequence, - "get_generator_classes() must return a Sequence to preserve order") + "_get_generator_classes() must return a Sequence to preserve order") def test_basic_generation_works(self): # when running pelican without settings, it should pick up the default From 246da3b4d815ac340e8bde889964022b20a5ac48 Mon Sep 17 00:00:00 2001 From: botpub Date: Sun, 22 Nov 2020 16:16:28 +0000 Subject: [PATCH 078/465] Release Pelican 4.5.2 --- RELEASE.md | 3 --- docs/changelog.rst | 5 +++++ pyproject.toml | 2 +- setup.py | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) delete mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 384c99f0..00000000 --- a/RELEASE.md +++ /dev/null @@ -1,3 +0,0 @@ -Release type: patch - -Improve logging of generators and writer loaders diff --git a/docs/changelog.rst b/docs/changelog.rst index 7031b63b..a97fb9b4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Release history ############### +4.5.2 - 2020-11-22 +================== + +Improve logging of generators and writer loaders + 4.5.1 - 2020-11-02 ================== diff --git a/pyproject.toml b/pyproject.toml index 4331fce1..b78a0374 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pelican" -version = "4.5.1" +version = "4.5.2" description = "Static site generator supporting Markdown and reStructuredText" authors = ["Justin Mayer "] license = "AGPLv3" diff --git a/setup.py b/setup.py index fc4ec3ce..91847f03 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from os.path import join, relpath from setuptools import find_packages, setup -version = "4.5.1" +version = "4.5.2" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.11', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', From 9435a6c0451a32ab3dba602fba3b79b3c06c929c Mon Sep 17 00:00:00 2001 From: Leonardo Giordani Date: Tue, 1 Dec 2020 18:07:51 +0000 Subject: [PATCH 079/465] Fix code indented incorrectly in generators logging (#2823) --- RELEASE.md | 3 +++ pelican/__init__.py | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..c1978b31 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,3 @@ +Release type: patch + +Fix a mistake made in PR #2821 diff --git a/pelican/__init__.py b/pelican/__init__.py index b6ebb762..0d723220 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -178,9 +178,9 @@ class Pelican: if not isinstance(values, Iterable): values = (values,) - discovered_generators.extend( - [(generator, receiver.__module__) for generator in values] - ) + discovered_generators.extend( + [(generator, receiver.__module__) for generator in values] + ) # StaticGenerator must run last, so it can identify files that # were skipped by the other generators, and so static files can From 8033162ba4393db60791b201fb100d1be0f04431 Mon Sep 17 00:00:00 2001 From: botpub Date: Tue, 1 Dec 2020 20:36:51 +0000 Subject: [PATCH 080/465] Release Pelican 4.5.3 --- RELEASE.md | 3 --- docs/changelog.rst | 5 +++++ pyproject.toml | 2 +- setup.py | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) delete mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index c1978b31..00000000 --- a/RELEASE.md +++ /dev/null @@ -1,3 +0,0 @@ -Release type: patch - -Fix a mistake made in PR #2821 diff --git a/docs/changelog.rst b/docs/changelog.rst index a97fb9b4..dfcd4c48 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Release history ############### +4.5.3 - 2020-12-01 +================== + +Fix a mistake made in PR #2821 + 4.5.2 - 2020-11-22 ================== diff --git a/pyproject.toml b/pyproject.toml index b78a0374..18bc2f09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pelican" -version = "4.5.2" +version = "4.5.3" description = "Static site generator supporting Markdown and reStructuredText" authors = ["Justin Mayer "] license = "AGPLv3" diff --git a/setup.py b/setup.py index 91847f03..ca1fc674 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from os.path import join, relpath from setuptools import find_packages, setup -version = "4.5.2" +version = "4.5.3" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.11', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', From 049bb2e1b3d06721023a12be8e0885b96fed9a0e Mon Sep 17 00:00:00 2001 From: Someone Date: Wed, 9 Dec 2020 22:52:42 +0100 Subject: [PATCH 081/465] add "http://" prefix to ip+port --- pelican/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 0d723220..3f6f7f1c 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -469,7 +469,7 @@ def listen(server, port, output, excqueue=None): return try: - print("\nServing site at: {}:{} - Tap CTRL-C to stop".format( + print("\nServing site at: http://{}:{} - Tap CTRL-C to stop".format( server, port)) httpd.serve_forever() except Exception as e: From 783a70da460e2d498041dabed28b2380ac8f489a Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Sun, 3 Jan 2021 21:24:10 -0800 Subject: [PATCH 082/465] Fix typo in the FAQ: steamline --> streamline. --- docs/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index 8532ab3f..e174cd4e 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -242,7 +242,7 @@ command similar to ``cd content; find -name '*.md' | head -n 10``. My tag-cloud is missing/broken since I upgraded Pelican ======================================================= -In an ongoing effort to steamline Pelican, `tag_cloud` generation has been +In an ongoing effort to streamline Pelican, `tag_cloud` generation has been moved out of the pelican core and into a separate `plugin `_. See the :ref:`plugins` documentation further information about the Pelican plugin From dc601059265bfd0722b8d07c67dd1f39b091fbfa Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Mon, 4 Jan 2021 17:13:32 +0100 Subject: [PATCH 083/465] Stringify plugin definitions so they can be pickled during caching (#2835) --- RELEASE.md | 3 ++ pelican/__init__.py | 14 ++++--- pelican/plugins/_utils.py | 16 ++++++++ .../normal_plugin/normal_plugin/__init__.py | 2 - .../subpackage/subpackage.py | 3 -- .../normal_submodule_plugin/subplugin.py | 3 -- pelican/tests/test_plugins.py | 39 +++++++++++++------ 7 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..9439717c --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,3 @@ +Release type: patch + +Replace plugin definitions in settings with string representations after registering, so they can be cached correctly (#2828). diff --git a/pelican/__init__.py b/pelican/__init__.py index 0d723220..1982f413 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -20,7 +20,7 @@ from pelican.generators import (ArticlesGenerator, # noqa: I100 PagesGenerator, SourceFileGenerator, StaticGenerator, TemplatePagesGenerator) from pelican.plugins import signals -from pelican.plugins._utils import load_plugins +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 @@ -65,14 +65,18 @@ class Pelican: sys.path.insert(0, '') def init_plugins(self): - self.plugins = load_plugins(self.settings) - for plugin in self.plugins: - logger.debug('Registering plugin `%s`', plugin.__name__) + self.plugins = [] + for plugin in load_plugins(self.settings): + name = get_plugin_name(plugin) + logger.debug('Registering plugin `%s`', name) try: plugin.register() + self.plugins.append(plugin) except Exception as e: logger.error('Cannot register plugin `%s`\n%s', - plugin.__name__, e) + name, e) + + self.settings['PLUGINS'] = [get_plugin_name(p) for p in self.plugins] def run(self): """Run the generators and return""" diff --git a/pelican/plugins/_utils.py b/pelican/plugins/_utils.py index ffe32799..87877b08 100644 --- a/pelican/plugins/_utils.py +++ b/pelican/plugins/_utils.py @@ -1,6 +1,7 @@ import importlib import importlib.machinery import importlib.util +import inspect import logging import pkgutil import sys @@ -107,3 +108,18 @@ def load_plugins(settings): plugins = list(namespace_plugins.values()) return plugins + + +def get_plugin_name(plugin): + """ + Plugins can be passed as module objects, however this breaks caching as + module objects cannot be pickled. To work around this, all plugins are + stringified post-initialization. + """ + if inspect.isclass(plugin): + return plugin.__qualname__ + + if inspect.ismodule(plugin): + return plugin.__name__ + + return type(plugin).__qualname__ diff --git a/pelican/tests/dummy_plugins/normal_plugin/normal_plugin/__init__.py b/pelican/tests/dummy_plugins/normal_plugin/normal_plugin/__init__.py index 5838a835..e714c7a6 100644 --- a/pelican/tests/dummy_plugins/normal_plugin/normal_plugin/__init__.py +++ b/pelican/tests/dummy_plugins/normal_plugin/normal_plugin/__init__.py @@ -1,7 +1,5 @@ from .submodule import noop # noqa: F401 -NAME = 'normal plugin' - def register(): pass diff --git a/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/subpackage.py b/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/subpackage.py index ddb0eeca..9e12b19f 100644 --- a/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/subpackage.py +++ b/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subpackage/subpackage.py @@ -1,5 +1,2 @@ -NAME = 'normal subpackage plugin' - - def register(): pass diff --git a/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subplugin.py b/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subplugin.py index 377c788b..9e12b19f 100644 --- a/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subplugin.py +++ b/pelican/tests/dummy_plugins/normal_plugin/normal_submodule_plugin/subplugin.py @@ -1,5 +1,2 @@ -NAME = 'normal submodule plugin' - - def register(): pass diff --git a/pelican/tests/test_plugins.py b/pelican/tests/test_plugins.py index 29729539..348c3e94 100644 --- a/pelican/tests/test_plugins.py +++ b/pelican/tests/test_plugins.py @@ -1,7 +1,9 @@ import os from contextlib import contextmanager -from pelican.plugins._utils import get_namespace_plugins, load_plugins +import pelican.tests.dummy_plugins.normal_plugin.normal_plugin as normal_plugin +from pelican.plugins._utils import (get_namespace_plugins, get_plugin_name, + load_plugins) from pelican.tests.support import unittest @@ -81,9 +83,7 @@ class PluginTest(unittest.TestCase): def test_load_plugins(self): def get_plugin_names(plugins): - return { - plugin.NAME if hasattr(plugin, 'NAME') else plugin.__name__ - for plugin in plugins} + return {get_plugin_name(p) for p in plugins} # existing namespace plugins existing_ns_plugins = load_plugins({}) @@ -93,7 +93,7 @@ class PluginTest(unittest.TestCase): plugins = load_plugins({}) self.assertEqual(len(plugins), len(existing_ns_plugins)+1, plugins) self.assertEqual( - {'namespace plugin'} | get_plugin_names(existing_ns_plugins), + {'pelican.plugins.ns_plugin'} | get_plugin_names(existing_ns_plugins), get_plugin_names(plugins)) # disable namespace plugins with `PLUGINS = []` @@ -113,7 +113,7 @@ class PluginTest(unittest.TestCase): plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 1, plugins) self.assertEqual( - {'normal plugin'}, + {'normal_plugin'}, get_plugin_names(plugins)) # normal submodule/subpackage plugins @@ -127,8 +127,8 @@ class PluginTest(unittest.TestCase): plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 2, plugins) self.assertEqual( - {'normal submodule plugin', - 'normal subpackage plugin'}, + {'normal_submodule_plugin.subplugin', + 'normal_submodule_plugin.subpackage.subpackage'}, get_plugin_names(plugins)) # ensure normal plugins are loaded only once @@ -149,7 +149,7 @@ class PluginTest(unittest.TestCase): plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 1, plugins) self.assertEqual( - {'namespace plugin'}, + {'pelican.plugins.ns_plugin'}, get_plugin_names(plugins)) # namespace plugin long @@ -159,7 +159,7 @@ class PluginTest(unittest.TestCase): plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 1, plugins) self.assertEqual( - {'namespace plugin'}, + {'pelican.plugins.ns_plugin'}, get_plugin_names(plugins)) # normal and namespace plugin @@ -170,5 +170,22 @@ class PluginTest(unittest.TestCase): plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 2, plugins) self.assertEqual( - {'normal plugin', 'namespace plugin'}, + {'normal_plugin', 'pelican.plugins.ns_plugin'}, get_plugin_names(plugins)) + + def test_get_plugin_name(self): + self.assertEqual( + get_plugin_name(normal_plugin), + 'pelican.tests.dummy_plugins.normal_plugin.normal_plugin', + ) + + class NoopPlugin: + def register(self): + pass + + self.assertEqual( + get_plugin_name(NoopPlugin), + 'PluginTest.test_get_plugin_name..NoopPlugin') + self.assertEqual( + get_plugin_name(NoopPlugin()), + 'PluginTest.test_get_plugin_name..NoopPlugin') From 9e72b29fc1a9bbd10725cc98878840e015d626a0 Mon Sep 17 00:00:00 2001 From: botpub Date: Mon, 4 Jan 2021 16:16:16 +0000 Subject: [PATCH 084/465] Release Pelican 4.5.4 --- RELEASE.md | 3 --- docs/changelog.rst | 5 +++++ pyproject.toml | 2 +- setup.py | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) delete mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 9439717c..00000000 --- a/RELEASE.md +++ /dev/null @@ -1,3 +0,0 @@ -Release type: patch - -Replace plugin definitions in settings with string representations after registering, so they can be cached correctly (#2828). diff --git a/docs/changelog.rst b/docs/changelog.rst index dfcd4c48..c302d837 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Release history ############### +4.5.4 - 2021-01-04 +================== + +Replace plugin definitions in settings with string representations after registering, so they can be cached correctly (#2828). + 4.5.3 - 2020-12-01 ================== diff --git a/pyproject.toml b/pyproject.toml index 18bc2f09..c41f8787 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pelican" -version = "4.5.3" +version = "4.5.4" description = "Static site generator supporting Markdown and reStructuredText" authors = ["Justin Mayer "] license = "AGPLv3" diff --git a/setup.py b/setup.py index ca1fc674..b4e769c0 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from os.path import join, relpath from setuptools import find_packages, setup -version = "4.5.3" +version = "4.5.4" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.11', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', From b12443f48a11f542d613f2c52a59fb38919f5604 Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Tue, 5 Jan 2021 10:40:31 -0800 Subject: [PATCH 085/465] Use a top (instead of bottom) border in hentry. `hentry` uses `clear: both` with the intention of placing a separator line (1px border) between each entry. However, it is wrongly using `border-bottom` instead of `border-top` to make that separator. CSS `clear` makes space to clear the **preceding** floats. Hence a `border-bottom` won't enjoy that `clear` effect. --- pelican/themes/notmyidea/static/css/main.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/themes/notmyidea/static/css/main.css b/pelican/themes/notmyidea/static/css/main.css index 63f5adc0..aaea9b81 100644 --- a/pelican/themes/notmyidea/static/css/main.css +++ b/pelican/themes/notmyidea/static/css/main.css @@ -384,10 +384,10 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ .hentry { display: block; clear: both; - border-bottom: 1px solid #eee; + border-top: 1px solid #eee; padding: 1.5em 0; } -li:last-child .hentry, #content > .hentry {border: 0; margin: 0;} +li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} #content > .hentry {padding: 1em 0;} .hentry img{display : none ;} .entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;} From b17e4a5ffaab2f4f235cbb76c6a05b91cf69c878 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 6 Jan 2021 07:36:21 +0100 Subject: [PATCH 086/465] Add FAQ re: Atom full-post vs. summary-only feeds Replaces a FAQ about upgrading from a very old version of Pelican, which isn't relevant to most folks at this point. --- docs/faq.rst | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index 8532ab3f..2902a0e4 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -174,28 +174,18 @@ your site. Feeds are still generated when this warning is displayed, but links within may be malformed and thus the feed may not validate. -My feeds are broken since I upgraded to Pelican 3.x -=================================================== +Can I force Atom feeds to show only summaries instead of article content? +========================================================================= -Starting in 3.0, some of the FEED setting names were changed to more explicitly -refer to the Atom feeds they inherently represent (much like the FEED_RSS -setting names). Here is an exact list of the renamed settings:: - - FEED -> FEED_ATOM - TAG_FEED -> TAG_FEED_ATOM - CATEGORY_FEED -> CATEGORY_FEED_ATOM - -Starting in 3.1, the new feed ``FEED_ALL_ATOM`` has been introduced: this feed -will aggregate all posts regardless of their language. This setting generates -``'feeds/all.atom.xml'`` by default and ``FEED_ATOM`` now defaults to ``None``. -The following feed setting has also been renamed:: - - TRANSLATION_FEED -> TRANSLATION_FEED_ATOM - -Older themes that referenced the old setting names may not link properly. In -order to rectify this, please update your theme for compatibility by changing -the relevant values in your template files. For an example of complete feed -headers and usage please check out the ``simple`` theme. +Instead of having to open a separate browser window to read articles, the +overwhelming majority of folks who use feed readers prefer to read content +within the feed reader itself. Mainly for that reason, Pelican does not support +restricting Atom feeds to only contain summaries. Unlike Atom feeds, the RSS +feed specification does not include a separate ``content`` field, so by default +Pelican publishes RSS feeds that only contain summaries (but can optionally be +set to instead publish full content RSS feeds). So the default feed generation +behavior provides users with a choice: subscribe to Atom feeds for full content +or to RSS feeds for just the summaries. Is Pelican only suitable for blogs? =================================== From 507d68e5c6cb6eb6e184283944d8f4c543996619 Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Thu, 7 Jan 2021 14:09:38 -0800 Subject: [PATCH 087/465] Update functional tests. --- pelican/tests/output/basic/theme/css/main.css | 4 ++-- pelican/tests/output/custom/author/alexis-metaireau.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau2.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom/index.html | 2 +- pelican/tests/output/custom/index2.html | 2 +- pelican/tests/output/custom/index3.html | 2 +- pelican/tests/output/custom/theme/css/main.css | 4 ++-- .../tests/output/custom_locale/author/alexis-metaireau.html | 2 +- .../tests/output/custom_locale/author/alexis-metaireau2.html | 2 +- .../tests/output/custom_locale/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom_locale/index.html | 2 +- pelican/tests/output/custom_locale/index2.html | 2 +- pelican/tests/output/custom_locale/index3.html | 2 +- pelican/tests/output/custom_locale/theme/css/main.css | 4 ++-- 15 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pelican/tests/output/basic/theme/css/main.css b/pelican/tests/output/basic/theme/css/main.css index 63f5adc0..aaea9b81 100644 --- a/pelican/tests/output/basic/theme/css/main.css +++ b/pelican/tests/output/basic/theme/css/main.css @@ -384,10 +384,10 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ .hentry { display: block; clear: both; - border-bottom: 1px solid #eee; + border-top: 1px solid #eee; padding: 1.5em 0; } -li:last-child .hentry, #content > .hentry {border: 0; margin: 0;} +li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} #content > .hentry {padding: 1em 0;} .hentry img{display : none ;} .entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;} diff --git a/pelican/tests/output/custom/author/alexis-metaireau.html b/pelican/tests/output/custom/author/alexis-metaireau.html index 7a277e2c..f768b15e 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau.html +++ b/pelican/tests/output/custom/author/alexis-metaireau.html @@ -168,4 +168,4 @@ }()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/author/alexis-metaireau2.html b/pelican/tests/output/custom/author/alexis-metaireau2.html index a5588090..96b34bff 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom/author/alexis-metaireau2.html @@ -183,4 +183,4 @@ YEAH !

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index 095b39b6..c8ee5759 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -133,4 +133,4 @@ pelican.conf, it will …

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/index.html b/pelican/tests/output/custom/index.html index e63db6ec..ff675f85 100644 --- a/pelican/tests/output/custom/index.html +++ b/pelican/tests/output/custom/index.html @@ -168,4 +168,4 @@ }()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/index2.html b/pelican/tests/output/custom/index2.html index 570e3b81..941c176e 100644 --- a/pelican/tests/output/custom/index2.html +++ b/pelican/tests/output/custom/index2.html @@ -183,4 +183,4 @@ YEAH !

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index f92bdbb1..ce73759d 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -133,4 +133,4 @@ pelican.conf, it will …

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/theme/css/main.css b/pelican/tests/output/custom/theme/css/main.css index 63f5adc0..aaea9b81 100644 --- a/pelican/tests/output/custom/theme/css/main.css +++ b/pelican/tests/output/custom/theme/css/main.css @@ -384,10 +384,10 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ .hentry { display: block; clear: both; - border-bottom: 1px solid #eee; + border-top: 1px solid #eee; padding: 1.5em 0; } -li:last-child .hentry, #content > .hentry {border: 0; margin: 0;} +li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} #content > .hentry {padding: 1em 0;} .hentry img{display : none ;} .entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;} diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau.html b/pelican/tests/output/custom_locale/author/alexis-metaireau.html index 9b4ca7dd..68054d97 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau.html @@ -168,4 +168,4 @@ }()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html index a1848677..16431fb9 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html @@ -183,4 +183,4 @@ YEAH !

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html index 78fdd866..d43663bd 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html @@ -133,4 +133,4 @@ pelican.conf, it will …

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/index.html b/pelican/tests/output/custom_locale/index.html index 29e12b1b..6e0d632d 100644 --- a/pelican/tests/output/custom_locale/index.html +++ b/pelican/tests/output/custom_locale/index.html @@ -168,4 +168,4 @@ }()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/index2.html b/pelican/tests/output/custom_locale/index2.html index c0727385..5ff21fac 100644 --- a/pelican/tests/output/custom_locale/index2.html +++ b/pelican/tests/output/custom_locale/index2.html @@ -183,4 +183,4 @@ YEAH !

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html index 0d2722f2..97ca0bb4 100644 --- a/pelican/tests/output/custom_locale/index3.html +++ b/pelican/tests/output/custom_locale/index3.html @@ -133,4 +133,4 @@ pelican.conf, it will …

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/theme/css/main.css b/pelican/tests/output/custom_locale/theme/css/main.css index 63f5adc0..aaea9b81 100644 --- a/pelican/tests/output/custom_locale/theme/css/main.css +++ b/pelican/tests/output/custom_locale/theme/css/main.css @@ -384,10 +384,10 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ .hentry { display: block; clear: both; - border-bottom: 1px solid #eee; + border-top: 1px solid #eee; padding: 1.5em 0; } -li:last-child .hentry, #content > .hentry {border: 0; margin: 0;} +li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} #content > .hentry {padding: 1em 0;} .hentry img{display : none ;} .entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;} From 89a31141ff51377ca97cde0f3da2f3651f46079d Mon Sep 17 00:00:00 2001 From: Tom Adler Date: Sun, 13 Jul 2014 08:59:09 +0200 Subject: [PATCH 088/465] Support last page pattern in PAGINATION_PATTERNS --- docs/settings.rst | 5 +++++ pelican/paginator.py | 9 +++++++-- pelican/tests/test_paginator.py | 26 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index e7eae66a..080856e3 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -1067,6 +1067,11 @@ as follows:: ) +If you want a pattern to apply to the last page in the list, use ``-1`` +as the ``minimum_page`` value:: + + (-1, '{base_name}/last/', '{base_name}/last/index.html'), + Translations ============ diff --git a/pelican/paginator.py b/pelican/paginator.py index 9d169045..7e738fe3 100644 --- a/pelican/paginator.py +++ b/pelican/paginator.py @@ -118,8 +118,13 @@ class Page: # find the last matching pagination rule for p in self.settings['PAGINATION_PATTERNS']: - if p.min_page <= self.number: - rule = p + if p.min_page == -1: + if not self.has_next(): + rule = p + break + else: + if p.min_page <= self.number: + rule = p if not rule: return '' diff --git a/pelican/tests/test_paginator.py b/pelican/tests/test_paginator.py index 5dc285ae..f8233eb4 100644 --- a/pelican/tests/test_paginator.py +++ b/pelican/tests/test_paginator.py @@ -76,3 +76,29 @@ class TestPage(unittest.TestCase): page2 = paginator.page(2) self.assertEqual(page2.save_as, 'blog/2/index.html') self.assertEqual(page2.url, '//blog.my.site/2/') + + def test_custom_pagination_pattern_last_page(self): + from pelican.paginator import PaginationRule + settings = get_settings() + settings['PAGINATION_PATTERNS'] = [PaginationRule(*r) for r in [ + (1, '/{url}1/', '{base_name}/1/index.html'), + (2, '/{url}{number}/', '{base_name}/{number}/index.html'), + (-1, '/{url}', '{base_name}/index.html'), + ]] + + self.page_kwargs['metadata']['author'] = Author('Blogger', settings) + object_list = [Article(**self.page_kwargs), + Article(**self.page_kwargs), + Article(**self.page_kwargs)] + paginator = Paginator('blog/index.html', '//blog.my.site/', + object_list, settings, 1) + # The URL *has to* stay absolute (with // in the front), so verify that + page1 = paginator.page(1) + self.assertEqual(page1.save_as, 'blog/1/index.html') + self.assertEqual(page1.url, '//blog.my.site/1/') + page2 = paginator.page(2) + self.assertEqual(page2.save_as, 'blog/2/index.html') + self.assertEqual(page2.url, '//blog.my.site/2/') + page3 = paginator.page(3) + self.assertEqual(page3.save_as, 'blog/index.html') + self.assertEqual(page3.url, '//blog.my.site/') From ab3001391e6fad0c5c6b527c8a8d4f4f7351bd3a Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 15 Jan 2021 12:49:33 +0100 Subject: [PATCH 089/465] Match test output line endings with current rendering --- pelican/tests/output/custom/author/alexis-metaireau.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau2.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom/index.html | 2 +- pelican/tests/output/custom/index2.html | 2 +- pelican/tests/output/custom/index3.html | 2 +- pelican/tests/output/custom_locale/author/alexis-metaireau.html | 2 +- .../tests/output/custom_locale/author/alexis-metaireau2.html | 2 +- .../tests/output/custom_locale/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom_locale/index.html | 2 +- pelican/tests/output/custom_locale/index2.html | 2 +- pelican/tests/output/custom_locale/index3.html | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pelican/tests/output/custom/author/alexis-metaireau.html b/pelican/tests/output/custom/author/alexis-metaireau.html index 7a277e2c..f768b15e 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau.html +++ b/pelican/tests/output/custom/author/alexis-metaireau.html @@ -168,4 +168,4 @@ }()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/author/alexis-metaireau2.html b/pelican/tests/output/custom/author/alexis-metaireau2.html index a5588090..96b34bff 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom/author/alexis-metaireau2.html @@ -183,4 +183,4 @@ YEAH !

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index 095b39b6..c8ee5759 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -133,4 +133,4 @@ pelican.conf, it will …

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/index.html b/pelican/tests/output/custom/index.html index e63db6ec..ff675f85 100644 --- a/pelican/tests/output/custom/index.html +++ b/pelican/tests/output/custom/index.html @@ -168,4 +168,4 @@ }()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/index2.html b/pelican/tests/output/custom/index2.html index 570e3b81..941c176e 100644 --- a/pelican/tests/output/custom/index2.html +++ b/pelican/tests/output/custom/index2.html @@ -183,4 +183,4 @@ YEAH !

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index f92bdbb1..ce73759d 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -133,4 +133,4 @@ pelican.conf, it will …

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau.html b/pelican/tests/output/custom_locale/author/alexis-metaireau.html index 9b4ca7dd..68054d97 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau.html @@ -168,4 +168,4 @@ }()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html index a1848677..16431fb9 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html @@ -183,4 +183,4 @@ YEAH !

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html index 78fdd866..d43663bd 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html @@ -133,4 +133,4 @@ pelican.conf, it will …

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/index.html b/pelican/tests/output/custom_locale/index.html index 29e12b1b..6e0d632d 100644 --- a/pelican/tests/output/custom_locale/index.html +++ b/pelican/tests/output/custom_locale/index.html @@ -168,4 +168,4 @@ }()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/index2.html b/pelican/tests/output/custom_locale/index2.html index c0727385..5ff21fac 100644 --- a/pelican/tests/output/custom_locale/index2.html +++ b/pelican/tests/output/custom_locale/index2.html @@ -183,4 +183,4 @@ YEAH !

}()); - + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html index 0d2722f2..97ca0bb4 100644 --- a/pelican/tests/output/custom_locale/index3.html +++ b/pelican/tests/output/custom_locale/index3.html @@ -133,4 +133,4 @@ pelican.conf, it will …

}()); - + \ No newline at end of file From 34fc7f2a8498da4d8c8f801318ba1b172ffed66f Mon Sep 17 00:00:00 2001 From: Nam Nguyen Date: Wed, 6 Jan 2021 12:19:46 -0800 Subject: [PATCH 090/465] Allow latest Pygments to be installed Otherwise, installation conflicts can result when plugins/themes require more recent Pygments versions. --- pyproject.toml | 3 ++- requirements/test.pip | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c41f8787..cef0a16f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ blinker = "^1.4" docutils = "^0.16" feedgenerator = "^1.9" jinja2 = "~2.11" -pygments = "~2.6.1" +pygments = "^2.6" python-dateutil = "^2.8" pytz = "^2020.1" unidecode = "^1.1" @@ -50,6 +50,7 @@ sphinx = "^3.0" sphinx_rtd_theme = "^0.5" livereload = "^2.6" psutil = {version = "^5.7", optional = true} +pygments = "~2.7.4" pytest = "^6.0" pytest-cov = "^2.8" pytest-pythonpath = "^0.7.3" diff --git a/requirements/test.pip b/requirements/test.pip index d36405f0..30b52845 100644 --- a/requirements/test.pip +++ b/requirements/test.pip @@ -1,5 +1,5 @@ # Tests -Pygments==2.6.1 +Pygments==2.7.4 pytest pytest-cov pytest-xdist[psutil] From e01cde7fcb6ff4e3720059cd59a498972630f3d3 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 15 Jan 2021 12:55:57 +0100 Subject: [PATCH 091/465] Adjust functional test output for Pygments 2.7.x Class `lineno` was changed to `linenos`. --- pelican/tests/output/basic/feeds/all-en.atom.xml | 2 +- pelican/tests/output/basic/feeds/all.atom.xml | 2 +- pelican/tests/output/basic/feeds/misc.atom.xml | 2 +- pelican/tests/output/basic/unbelievable.html | 2 +- pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml | 2 +- pelican/tests/output/custom/feeds/all-en.atom.xml | 2 +- pelican/tests/output/custom/feeds/all.atom.xml | 2 +- pelican/tests/output/custom/feeds/misc.atom.xml | 2 +- pelican/tests/output/custom/unbelievable.html | 2 +- .../tests/output/custom_locale/feeds/alexis-metaireau.atom.xml | 2 +- pelican/tests/output/custom_locale/feeds/all-en.atom.xml | 2 +- pelican/tests/output/custom_locale/feeds/all.atom.xml | 2 +- pelican/tests/output/custom_locale/feeds/misc.atom.xml | 2 +- .../custom_locale/posts/2010/octobre/15/unbelievable/index.html | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pelican/tests/output/basic/feeds/all-en.atom.xml b/pelican/tests/output/basic/feeds/all-en.atom.xml index f59ea651..a0166567 100644 --- a/pelican/tests/output/basic/feeds/all-en.atom.xml +++ b/pelican/tests/output/basic/feeds/all-en.atom.xml @@ -55,7 +55,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/basic/feeds/all.atom.xml b/pelican/tests/output/basic/feeds/all.atom.xml index 7407d2ff..4ab55b48 100644 --- a/pelican/tests/output/basic/feeds/all.atom.xml +++ b/pelican/tests/output/basic/feeds/all.atom.xml @@ -57,7 +57,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/basic/feeds/misc.atom.xml b/pelican/tests/output/basic/feeds/misc.atom.xml index a8243b48..b6e4ebd4 100644 --- a/pelican/tests/output/basic/feeds/misc.atom.xml +++ b/pelican/tests/output/basic/feeds/misc.atom.xml @@ -30,7 +30,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/basic/unbelievable.html b/pelican/tests/output/basic/unbelievable.html index e0836d70..378ba077 100644 --- a/pelican/tests/output/basic/unbelievable.html +++ b/pelican/tests/output/basic/unbelievable.html @@ -56,7 +56,7 @@ pelican.conf, it will have nothing in default.

Testing more sourcecode directives

-
 8 def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]
+
 8def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]

Lovely.

diff --git a/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml index 19b706e8..ad2ec539 100644 --- a/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml +++ b/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml @@ -55,7 +55,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom/feeds/all-en.atom.xml b/pelican/tests/output/custom/feeds/all-en.atom.xml index 15ec3903..99b21a5a 100644 --- a/pelican/tests/output/custom/feeds/all-en.atom.xml +++ b/pelican/tests/output/custom/feeds/all-en.atom.xml @@ -55,7 +55,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom/feeds/all.atom.xml b/pelican/tests/output/custom/feeds/all.atom.xml index cf214c5a..730e9de4 100644 --- a/pelican/tests/output/custom/feeds/all.atom.xml +++ b/pelican/tests/output/custom/feeds/all.atom.xml @@ -57,7 +57,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom/feeds/misc.atom.xml b/pelican/tests/output/custom/feeds/misc.atom.xml index 325e4c01..a2740fdd 100644 --- a/pelican/tests/output/custom/feeds/misc.atom.xml +++ b/pelican/tests/output/custom/feeds/misc.atom.xml @@ -30,7 +30,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom/unbelievable.html b/pelican/tests/output/custom/unbelievable.html index 0132162c..da3b7d29 100644 --- a/pelican/tests/output/custom/unbelievable.html +++ b/pelican/tests/output/custom/unbelievable.html @@ -63,7 +63,7 @@ pelican.conf, it will have nothing in default.

Testing more sourcecode directives

-
 8 def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]
+
 8def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]

Lovely.

diff --git a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml index 5e5b888a..54e1c1e2 100644 --- a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml @@ -55,7 +55,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom_locale/feeds/all-en.atom.xml b/pelican/tests/output/custom_locale/feeds/all-en.atom.xml index 1b6dc18f..2c08dde0 100644 --- a/pelican/tests/output/custom_locale/feeds/all-en.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/all-en.atom.xml @@ -55,7 +55,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom_locale/feeds/all.atom.xml b/pelican/tests/output/custom_locale/feeds/all.atom.xml index 67a57122..bbb04753 100644 --- a/pelican/tests/output/custom_locale/feeds/all.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/all.atom.xml @@ -57,7 +57,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom_locale/feeds/misc.atom.xml b/pelican/tests/output/custom_locale/feeds/misc.atom.xml index 4b514b47..5c3cdb50 100644 --- a/pelican/tests/output/custom_locale/feeds/misc.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/misc.atom.xml @@ -30,7 +30,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8 </span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10 </span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12 </span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14 </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16 </span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20 </span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22 </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24 </span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26 </span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html index e42c70f6..663fb397 100644 --- a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html @@ -63,7 +63,7 @@ pelican.conf, it will have nothing in default.

Testing more sourcecode directives

-
 8 def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]
+
 8def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]

Lovely.

From ce5d0635137009300b65edb15f73081f0ac4231b Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 15 Jan 2021 13:04:07 +0100 Subject: [PATCH 092/465] Allow latest Python-Markdown to be installed Pinning dependencies to specific versions creates conflicts when other packages require more recent versions. We can do our part by allowing for a wider range of dependency versions, specifying only the oldest version that will still work. Meanwhile, we ensure that test environments use a specific pinned dependency in order to match the expected functional test output. --- pyproject.toml | 4 ++-- requirements/test.pip | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cef0a16f..63fe09ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,12 +39,12 @@ pygments = "^2.6" python-dateutil = "^2.8" pytz = "^2020.1" unidecode = "^1.1" -markdown = {version = "~3.2.2", optional = true} +markdown = {version = "^3.2", optional = true} [tool.poetry.dev-dependencies] BeautifulSoup4 = "^4.9" lxml = "^4.3" -markdown = "~3.2.2" +markdown = "~3.3.3" typogrify = "^2.0" sphinx = "^3.0" sphinx_rtd_theme = "^0.5" diff --git a/requirements/test.pip b/requirements/test.pip index 30b52845..74c6bf9a 100644 --- a/requirements/test.pip +++ b/requirements/test.pip @@ -5,7 +5,7 @@ pytest-cov pytest-xdist[psutil] # Optional Packages -Markdown >= 3.1 +Markdown==3.3.3 BeautifulSoup4 lxml typogrify From 8bb5f1b786b6f2b22d1dc4501796d6df9a658a05 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 15 Jan 2021 13:19:19 +0100 Subject: [PATCH 093/465] Drop unsupported Python 3.5 from test matrix --- .github/workflows/main.yml | 2 -- setup.py | 1 - tox.ini | 3 +-- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5c284a6d..468f1654 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,8 +15,6 @@ jobs: strategy: matrix: config: - - os: ubuntu - python: 3.5 - os: ubuntu python: 3.6 - os: ubuntu diff --git a/setup.py b/setup.py index b4e769c0..89225f21 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,6 @@ setup( 'License :: OSI Approved :: GNU Affero General Public License v3', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', diff --git a/tox.ini b/tox.ini index b4664e98..f917476b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,8 @@ [tox] -envlist = py{3.5,3.6,3.7,3.8,3.9},docs,flake8 +envlist = py{3.6,3.7,3.8,3.9},docs,flake8 [testenv] basepython = - py3.5: python3.5 py3.6: python3.6 py3.7: python3.7 py3.8: python3.8 From f846191edddabb6996c4a15490439f3b2ade1249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mirek=20D=C5=82ugosz?= Date: Mon, 8 Feb 2021 22:16:25 +0100 Subject: [PATCH 094/465] livereload task improvements - use custom build command, with caching turned on - this reduces site build time by around 40% on my testing machines - collect all glob patterns in a list and then call `server.watch` on each item - this allows to have single place where callback function must be specified - use '**/*.html' as glob in template, to track changes in subdirectories --- pelican/tools/templates/tasks.py.jinja2 | 32 +++++++++++++++---------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/pelican/tools/templates/tasks.py.jinja2 b/pelican/tools/templates/tasks.py.jinja2 index b65f01d2..ff8e526e 100644 --- a/pelican/tools/templates/tasks.py.jinja2 +++ b/pelican/tools/templates/tasks.py.jinja2 @@ -99,23 +99,31 @@ def preview(c): def livereload(c): """Automatically reload browser tab upon file modification.""" from livereload import Server - build(c) + + def cached_build(): + cmd = '-s {settings_base} -e CACHE_CONTENT=True LOAD_CONTENT_CACHE=True' + pelican_run(cmd.format(**CONFIG)) + + cached_build() server = Server() - # Watch the base settings file - server.watch(CONFIG['settings_base'], lambda: build(c)) - # Watch content source files + theme_path = SETTINGS['THEME'] + watched_globs = [ + CONFIG['settings_base'], + '{}/templates/**/*.html'.format(theme_path), + ] + content_file_extensions = ['.md', '.rst'] for extension in content_file_extensions: - content_blob = '{0}/**/*{1}'.format(SETTINGS['PATH'], extension) - server.watch(content_blob, lambda: build(c)) - # Watch the theme's templates and static assets - theme_path = SETTINGS['THEME'] - server.watch('{}/templates/*.html'.format(theme_path), lambda: build(c)) + content_glob = '{0}/**/*{1}'.format(SETTINGS['PATH'], extension) + watched_globs.append(content_glob) + static_file_extensions = ['.css', '.js'] for extension in static_file_extensions: - static_file = '{0}/static/**/*{1}'.format(theme_path, extension) - server.watch(static_file, lambda: build(c)) - # Serve output path on configured host and port + static_file_glob = '{0}/static/**/*{1}'.format(theme_path, extension) + watched_globs.append(static_file_glob) + + for glob in watched_globs: + server.watch(glob, cached_build) server.serve(host=CONFIG['host'], port=CONFIG['port'], root=CONFIG['deploy_path']) {% if cloudfiles %} From aa7c821c7084252ae6e0d9b5b6bfff78f03ec7a9 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Fri, 12 Feb 2021 21:17:42 +0300 Subject: [PATCH 095/465] ignore None return value from get_generators signal --- pelican/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 8f050a2c..cb91eb59 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -181,10 +181,10 @@ class Pelican: for receiver, values in signals.get_generators.send(self): if not isinstance(values, Iterable): values = (values,) - - discovered_generators.extend( - [(generator, receiver.__module__) for generator in values] - ) + for generator in values: + if generator is None: + continue # plugin did not return a generator + discovered_generators.append((generator, receiver.__module__)) # StaticGenerator must run last, so it can identify files that # were skipped by the other generators, and so static files can From 27762d2cf70fd76f0f3f76ea02b7cf8bfa4e1236 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 18 Feb 2021 12:29:18 +0100 Subject: [PATCH 096/465] Make PKG_PATH var in Invoke tasks.py more DRY --- tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks.py b/tasks.py index 1d511859..c7e4f42c 100644 --- a/tasks.py +++ b/tasks.py @@ -5,7 +5,7 @@ from shutil import which from invoke import task PKG_NAME = "pelican" -PKG_PATH = Path("pelican") +PKG_PATH = Path(PKG_NAME) DOCS_PORT = os.environ.get("DOCS_PORT", 8000) BIN_DIR = "bin" if os.name != "nt" else "Scripts" ACTIVE_VENV = os.environ.get("VIRTUAL_ENV", None) From 68817845b048f160bb65887aaa8da1f5308da5fa Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 18 Feb 2021 12:30:44 +0100 Subject: [PATCH 097/465] Change FAQ entry to new Tag Cloud plugin repo --- docs/faq.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index 94a553ad..5ad372c5 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -218,7 +218,7 @@ which will make it compare the file checksums in a much faster way than Pelican would. When only several specific output files are of interest (e.g. when working on -some specific page or the theme templates), the `WRITE_SELECTED` option may +some specific page or the theme templates), the ``WRITE_SELECTED`` option may help, see :ref:`writing_only_selected_content`. How to process only a subset of all articles? @@ -232,11 +232,10 @@ command similar to ``cd content; find -name '*.md' | head -n 10``. My tag-cloud is missing/broken since I upgraded Pelican ======================================================= -In an ongoing effort to streamline Pelican, `tag_cloud` generation has been -moved out of the pelican core and into a separate `plugin -`_. See -the :ref:`plugins` documentation further information about the Pelican plugin -system. +In an ongoing effort to streamline Pelican, tag cloud generation has been +moved out of Pelican core and into a separate `plugin +`_. See the :ref:`plugins` +documentation for further information about the Pelican plugin system. Since I upgraded Pelican my pages are no longer rendered ======================================================== From cdec5305729c461721f4bea1477bbc4df776caf0 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 18 Feb 2021 15:06:29 +0100 Subject: [PATCH 098/465] Add .yaml file extension to EditorConfig --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index b42ca8c2..edb13c8a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,5 +11,5 @@ trim_trailing_whitespace = true [*.py] max_line_length = 79 -[*.yml] +[*.{yml,yaml}] indent_size = 2 From 872c4dbd30fd0d891a850ae8685b73139c83bcbd Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 18 Feb 2021 15:16:36 +0100 Subject: [PATCH 099/465] Update Pygments dev dependency to v2.8 --- pyproject.toml | 2 +- requirements/test.pip | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 63fe09ba..5a1de530 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ sphinx = "^3.0" sphinx_rtd_theme = "^0.5" livereload = "^2.6" psutil = {version = "^5.7", optional = true} -pygments = "~2.7.4" +pygments = "~2.8" pytest = "^6.0" pytest-cov = "^2.8" pytest-pythonpath = "^0.7.3" diff --git a/requirements/test.pip b/requirements/test.pip index 74c6bf9a..0ecfdc3c 100644 --- a/requirements/test.pip +++ b/requirements/test.pip @@ -1,5 +1,5 @@ # Tests -Pygments==2.7.4 +Pygments==2.8.0 pytest pytest-cov pytest-xdist[psutil] From 0db5afb920bd8c3b840e363a3a3eac1ea7804c20 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 18 Feb 2021 15:18:45 +0100 Subject: [PATCH 100/465] Update functional test output for Pygments 2.8 --- pelican/tests/output/basic/category/misc.html | 2 +- pelican/tests/output/basic/feeds/all-en.atom.xml | 6 +++--- pelican/tests/output/basic/feeds/all.atom.xml | 6 +++--- pelican/tests/output/basic/feeds/misc.atom.xml | 6 +++--- pelican/tests/output/basic/index.html | 2 +- pelican/tests/output/basic/unbelievable.html | 4 ++-- pelican/tests/output/custom/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom/category/misc.html | 2 +- .../tests/output/custom/feeds/alexis-metaireau.atom.xml | 8 ++++---- .../tests/output/custom/feeds/alexis-metaireau.rss.xml | 2 +- pelican/tests/output/custom/feeds/all-en.atom.xml | 8 ++++---- pelican/tests/output/custom/feeds/all.atom.xml | 8 ++++---- pelican/tests/output/custom/feeds/all.rss.xml | 2 +- pelican/tests/output/custom/feeds/misc.atom.xml | 8 ++++---- pelican/tests/output/custom/feeds/misc.rss.xml | 2 +- pelican/tests/output/custom/index3.html | 2 +- pelican/tests/output/custom/unbelievable.html | 6 +++--- .../output/custom_locale/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom_locale/category/misc.html | 2 +- .../output/custom_locale/feeds/alexis-metaireau.atom.xml | 8 ++++---- .../output/custom_locale/feeds/alexis-metaireau.rss.xml | 2 +- pelican/tests/output/custom_locale/feeds/all-en.atom.xml | 8 ++++---- pelican/tests/output/custom_locale/feeds/all.atom.xml | 8 ++++---- pelican/tests/output/custom_locale/feeds/all.rss.xml | 2 +- pelican/tests/output/custom_locale/feeds/misc.atom.xml | 8 ++++---- pelican/tests/output/custom_locale/feeds/misc.rss.xml | 2 +- pelican/tests/output/custom_locale/index3.html | 2 +- .../posts/2010/octobre/15/unbelievable/index.html | 6 +++--- 28 files changed, 63 insertions(+), 63 deletions(-) diff --git a/pelican/tests/output/basic/category/misc.html b/pelican/tests/output/basic/category/misc.html index 4f597888..119f5931 100644 --- a/pelican/tests/output/basic/category/misc.html +++ b/pelican/tests/output/basic/category/misc.html @@ -81,7 +81,7 @@ a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 
diff --git a/pelican/tests/output/basic/feeds/all-en.atom.xml b/pelican/tests/output/basic/feeds/all-en.atom.xml index a0166567..250d75db 100644 --- a/pelican/tests/output/basic/feeds/all-en.atom.xml +++ b/pelican/tests/output/basic/feeds/all-en.atom.xml @@ -31,7 +31,7 @@ YEAH !</p> <a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -42,7 +42,7 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -55,7 +55,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/basic/feeds/all.atom.xml b/pelican/tests/output/basic/feeds/all.atom.xml index 4ab55b48..1abfef18 100644 --- a/pelican/tests/output/basic/feeds/all.atom.xml +++ b/pelican/tests/output/basic/feeds/all.atom.xml @@ -33,7 +33,7 @@ YEAH !</p> <a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -44,7 +44,7 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -57,7 +57,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/basic/feeds/misc.atom.xml b/pelican/tests/output/basic/feeds/misc.atom.xml index b6e4ebd4..2b09bda3 100644 --- a/pelican/tests/output/basic/feeds/misc.atom.xml +++ b/pelican/tests/output/basic/feeds/misc.atom.xml @@ -6,7 +6,7 @@ <a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -17,7 +17,7 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -30,7 +30,7 @@ pelican.conf, it will have nothing in default.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/basic/index.html b/pelican/tests/output/basic/index.html index f215dc44..e10477db 100644 --- a/pelican/tests/output/basic/index.html +++ b/pelican/tests/output/basic/index.html @@ -220,7 +220,7 @@ YEAH !

a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 
diff --git a/pelican/tests/output/basic/unbelievable.html b/pelican/tests/output/basic/unbelievable.html index 378ba077..33e0ad53 100644 --- a/pelican/tests/output/basic/unbelievable.html +++ b/pelican/tests/output/basic/unbelievable.html @@ -43,7 +43,7 @@ a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 
@@ -56,7 +56,7 @@ pelican.conf, it will have nothing in default.

Testing more sourcecode directives

-
 8def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]
+
 8def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]

Lovely.

diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index c8ee5759..407c1a51 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -50,7 +50,7 @@ a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 
diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html index 78b9837c..053599ed 100644 --- a/pelican/tests/output/custom/category/misc.html +++ b/pelican/tests/output/custom/category/misc.html @@ -94,7 +94,7 @@ a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 
diff --git a/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml index ad2ec539..a9aa4d38 100644 --- a/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml +++ b/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml @@ -31,7 +31,7 @@ YEAH !</p> <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -42,20 +42,20 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will have nothing in default.</p> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml index e5042f26..1ea22155 100644 --- a/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml +++ b/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml @@ -19,7 +19,7 @@ YEAH !</p> <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> diff --git a/pelican/tests/output/custom/feeds/all-en.atom.xml b/pelican/tests/output/custom/feeds/all-en.atom.xml index 99b21a5a..3054a950 100644 --- a/pelican/tests/output/custom/feeds/all-en.atom.xml +++ b/pelican/tests/output/custom/feeds/all-en.atom.xml @@ -31,7 +31,7 @@ YEAH !</p> <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -42,20 +42,20 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will have nothing in default.</p> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom/feeds/all.atom.xml b/pelican/tests/output/custom/feeds/all.atom.xml index 730e9de4..f340f71f 100644 --- a/pelican/tests/output/custom/feeds/all.atom.xml +++ b/pelican/tests/output/custom/feeds/all.atom.xml @@ -33,7 +33,7 @@ YEAH !</p> <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -44,20 +44,20 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will have nothing in default.</p> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom/feeds/all.rss.xml b/pelican/tests/output/custom/feeds/all.rss.xml index 479f67ba..e93d4753 100644 --- a/pelican/tests/output/custom/feeds/all.rss.xml +++ b/pelican/tests/output/custom/feeds/all.rss.xml @@ -21,7 +21,7 @@ YEAH !</p> <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> diff --git a/pelican/tests/output/custom/feeds/misc.atom.xml b/pelican/tests/output/custom/feeds/misc.atom.xml index a2740fdd..b5a006e7 100644 --- a/pelican/tests/output/custom/feeds/misc.atom.xml +++ b/pelican/tests/output/custom/feeds/misc.atom.xml @@ -6,7 +6,7 @@ <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -17,20 +17,20 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will have nothing in default.</p> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom/feeds/misc.rss.xml b/pelican/tests/output/custom/feeds/misc.rss.xml index 1c427eea..b9958cdb 100644 --- a/pelican/tests/output/custom/feeds/misc.rss.xml +++ b/pelican/tests/output/custom/feeds/misc.rss.xml @@ -6,7 +6,7 @@ <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index ce73759d..de32dbe3 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -50,7 +50,7 @@ a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 
diff --git a/pelican/tests/output/custom/unbelievable.html b/pelican/tests/output/custom/unbelievable.html index da3b7d29..a103868e 100644 --- a/pelican/tests/output/custom/unbelievable.html +++ b/pelican/tests/output/custom/unbelievable.html @@ -50,20 +50,20 @@ a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 

Testing another case

This will now have a line number in 'custom' since it's the default in pelican.conf, it will have nothing in default.

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 

Lovely.

Testing more sourcecode directives

-
 8def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]
+
 8def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]

Lovely.

diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html index d43663bd..ab91c56a 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html @@ -50,7 +50,7 @@ a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 
diff --git a/pelican/tests/output/custom_locale/category/misc.html b/pelican/tests/output/custom_locale/category/misc.html index c9fc111f..eb98db2a 100644 --- a/pelican/tests/output/custom_locale/category/misc.html +++ b/pelican/tests/output/custom_locale/category/misc.html @@ -94,7 +94,7 @@ a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 
diff --git a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml index 54e1c1e2..a2ee64e6 100644 --- a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml @@ -31,7 +31,7 @@ YEAH !</p> <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -42,20 +42,20 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will have nothing in default.</p> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml index 6cbd64db..6b360712 100644 --- a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml +++ b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml @@ -19,7 +19,7 @@ YEAH !</p> <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> diff --git a/pelican/tests/output/custom_locale/feeds/all-en.atom.xml b/pelican/tests/output/custom_locale/feeds/all-en.atom.xml index 2c08dde0..a768d4e6 100644 --- a/pelican/tests/output/custom_locale/feeds/all-en.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/all-en.atom.xml @@ -31,7 +31,7 @@ YEAH !</p> <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -42,20 +42,20 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will have nothing in default.</p> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom_locale/feeds/all.atom.xml b/pelican/tests/output/custom_locale/feeds/all.atom.xml index bbb04753..4680594a 100644 --- a/pelican/tests/output/custom_locale/feeds/all.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/all.atom.xml @@ -33,7 +33,7 @@ YEAH !</p> <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -44,20 +44,20 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will have nothing in default.</p> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom_locale/feeds/all.rss.xml b/pelican/tests/output/custom_locale/feeds/all.rss.xml index 8e7dd649..dc4ef033 100644 --- a/pelican/tests/output/custom_locale/feeds/all.rss.xml +++ b/pelican/tests/output/custom_locale/feeds/all.rss.xml @@ -21,7 +21,7 @@ YEAH !</p> <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> diff --git a/pelican/tests/output/custom_locale/feeds/misc.atom.xml b/pelican/tests/output/custom_locale/feeds/misc.atom.xml index 5c3cdb50..eb7a72f7 100644 --- a/pelican/tests/output/custom_locale/feeds/misc.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/misc.atom.xml @@ -6,7 +6,7 @@ <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> @@ -17,20 +17,20 @@ pelican.conf, it will …</p></div>&l <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will have nothing in default.</p> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> -<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><span class="linenos special"> 8</span><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="linenos special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="linenos"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="linenos special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="linenos"> </span> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="linenos special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="linenos"> </span><br></span><span id="foo-16"><a name="foo-16"></a><span class="linenos special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="linenos"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="linenos special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="linenos"> </span><br></span><span id="foo-20"><a name="foo-20"></a><span class="linenos special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="linenos"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="linenos special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="linenos"> </span><br></span><span id="foo-24"><a name="foo-24"></a><span class="linenos special">24</span> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="linenos"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="linenos special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="linenos"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<div class="highlight"><pre><span></span><span id="foo-8"><a name="foo-8"></a><a href="#foo-8"><span class="linenos special"> 8</span></a><span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><a href="#foo-9"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><a href="#foo-10"><span class="linenos special">10</span></a> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><a href="#foo-11"><span class="linenos"> </span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><a href="#foo-12"><span class="linenos special">12</span></a> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><a href="#foo-13"><span class="linenos"> </span></a> <span class="testingc1"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><a href="#foo-14"><span class="linenos special">14</span></a> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><a href="#foo-15"><span class="linenos"> </span></a><br></span><span id="foo-16"><a name="foo-16"></a><a href="#foo-16"><span class="linenos special">16</span></a> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings1">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><a href="#foo-17"><span class="linenos"> </span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><a href="#foo-18"><span class="linenos special">18</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings1">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings1">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><a href="#foo-19"><span class="linenos"> </span></a><br></span><span id="foo-20"><a name="foo-20"></a><a href="#foo-20"><span class="linenos special">20</span></a> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings1">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings1">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><a href="#foo-21"><span class="linenos"> </span></a> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><a href="#foo-22"><span class="linenos special">22</span></a> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingkc">True</span><br></span><span id="foo-23"><a name="foo-23"></a><a href="#foo-23"><span class="linenos"> </span></a><br></span><span id="foo-24"><a name="foo-24"></a><a href="#foo-24"><span class="linenos special">24</span></a> <span class="testingc1"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><a href="#foo-25"><span class="linenos"> </span></a> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingkc">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><a href="#foo-26"><span class="linenos special">26</span></a> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings1">&#39;</span><span class="testingse">\n</span><span class="testings1">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><a href="#foo-27"><span class="linenos"> </span></a> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings1">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingnb">format</span><span class="testingo">=</span><span class="testings1">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> <p>Lovely.</p> </div> <div class="section" id="testing-even-more-sourcecode-directives"> diff --git a/pelican/tests/output/custom_locale/feeds/misc.rss.xml b/pelican/tests/output/custom_locale/feeds/misc.rss.xml index 041c7687..6ac30929 100644 --- a/pelican/tests/output/custom_locale/feeds/misc.rss.xml +++ b/pelican/tests/output/custom_locale/feeds/misc.rss.xml @@ -6,7 +6,7 @@ <a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> <div class="section" id="testing-sourcecode-directive"> <h2>Testing sourcecode directive</h2> -<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> </td></tr></table></div> <div class="section" id="testing-another-case"> diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html index 97ca0bb4..45ea5a89 100644 --- a/pelican/tests/output/custom_locale/index3.html +++ b/pelican/tests/output/custom_locale/index3.html @@ -50,7 +50,7 @@ a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 
diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html index 663fb397..407d457f 100644 --- a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html @@ -50,20 +50,20 @@ a file-relative link to markdown-article

Testing sourcecode directive

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 

Testing another case

This will now have a line number in 'custom' since it's the default in pelican.conf, it will have nothing in default.

-
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
+
1
formatter = self.options and VARIANTS[self.options.keys()[0]]
 

Lovely.

Testing more sourcecode directives

-
 8def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]
+
 8def run(self):
self.assert_has_content()
10 try:
lexer = get_lexer_by_name(self.arguments[0])
12 except ValueError:
# no lexer found - use the text one instead of an exception
14 lexer = TextLexer()

16 if ('linenos' in self.options and
self.options['linenos'] not in ('table', 'inline')):
18 self.options['linenos'] = 'table'

20 for flag in ('nowrap', 'nobackground', 'anchorlinenos'):
if flag in self.options:
22 self.options[flag] = True

24 # noclasses should already default to False, but just in case...
formatter = HtmlFormatter(noclasses=False, **self.options)
26 parsed = highlight('\n'.join(self.content), lexer, formatter)
return [nodes.raw('', parsed, format='html')]

Lovely.

From 31398d4247d4d7f78c5ee2e15145af3ffe9cacc0 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 19 Feb 2021 08:22:16 +0100 Subject: [PATCH 101/465] Improve consistency between Makefile targets & help --- pelican/tools/templates/Makefile.jinja2 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pelican/tools/templates/Makefile.jinja2 b/pelican/tools/templates/Makefile.jinja2 index a60f1e16..df5b2643 100644 --- a/pelican/tools/templates/Makefile.jinja2 +++ b/pelican/tools/templates/Makefile.jinja2 @@ -69,8 +69,11 @@ help: @echo ' make serve [PORT=8000] serve site at http://localhost:8000' @echo ' make serve-global [SERVER=0.0.0.0] serve (as root) to $(SERVER):80 ' @echo ' make devserver [PORT=8000] serve and regenerate together ' + @echo ' make devserver-global regenerate and serve on 0.0.0.0 ' +{% if ssh %} @echo ' make ssh_upload upload the web site via SSH ' @echo ' make rsync_upload upload the web site via rsync+ssh ' +{% endif %} {% if dropbox %} @echo ' make dropbox_upload upload the web site via Dropbox ' {% endif %} From 4b6b5f046166899ed7eb2c3c15db058690b7b650 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 19 Feb 2021 09:31:04 +0100 Subject: [PATCH 102/465] Clarify ARTICLE_ORDER_BY documentation --- docs/settings.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 080856e3..c66c42a3 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -1134,10 +1134,11 @@ Ordering content Defines how the articles (``articles_page.object_list`` in the template) are sorted. Valid options are: metadata as a string (use ``reversed-`` prefix - the reverse the sort order), special option ``'basename'`` which will use - the basename of the file (without path) or a custom function to extract the - sorting key from articles. The default value, ``'reversed-date'``, will sort - articles by date in reverse order (i.e. newest article comes first). + to reverse the sort order), special option ``'basename'`` which will use + the basename of the file (without path), or a custom function to extract the + sorting key from articles. Using a value of ``'date'`` will sort articles in + chronological order, while the default value, ``'reversed-date'``, will sort + articles by date in reverse order (i.e., newest article comes first). .. data:: PAGE_ORDER_BY = 'basename' From ce4994bec8b4124a610b947281b35edcb11babc8 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 19 Mar 2021 12:58:58 +0100 Subject: [PATCH 103/465] [Docs] Fix link to More Categories plugin --- docs/importer.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/importer.rst b/docs/importer.rst index b8313f49..7b839d30 100644 --- a/docs/importer.rst +++ b/docs/importer.rst @@ -24,8 +24,8 @@ not be converted (as Pelican also supports Markdown). Unlike Pelican, Wordpress supports multiple categories per article. These are imported as a comma-separated string. You have to resolve these - manually, or use a plugin that enables multiple categories per article - (like `more_categories`_). + manually, or use a plugin such as `More Categories`_ that enables multiple + categories per article. Dependencies ============ @@ -140,4 +140,4 @@ To test the module, one can use sample files: - for WordPress: https://www.wpbeginner.com/wp-themes/how-to-add-dummy-content-for-theme-development-in-wordpress/ - for Dotclear: http://media.dotaddict.org/tda/downloads/lorem-backup.txt -.. _more_categories: https://github.com/getpelican/pelican-plugins/tree/master/more_categories +.. _More Categories: https://github.com/pelican-plugins/more-categories From 2b08497c3237e7d98366f529d62c6b52d2a75227 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 23 Mar 2021 08:06:58 +0100 Subject: [PATCH 104/465] Relax dependency minimum versions Specifying a floor, but not a ceiling, for core dependencies should help prevent dependency resolution conflicts. Dependencies that affect functional test output are pinned more tightly. --- .github/workflows/main.yml | 6 +++--- pyproject.toml | 23 ++++++++++++----------- requirements/test.pip | 4 ++-- setup.py | 2 +- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 468f1654..53841102 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -86,7 +86,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: "3.x" - name: Set pip cache (Linux) uses: actions/cache@v1 if: startsWith(runner.os, 'Linux') @@ -110,7 +110,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: "3.x" - name: Set pip cache (Linux) uses: actions/cache@v1 if: startsWith(runner.os, 'Linux') @@ -136,7 +136,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: "3.x" - name: Check release id: check_release run: | diff --git a/pyproject.toml b/pyproject.toml index 5a1de530..0d2460b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,20 +31,21 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.6" -blinker = "^1.4" -docutils = "^0.16" -feedgenerator = "^1.9" -jinja2 = "~2.11" -pygments = "^2.6" -python-dateutil = "^2.8" -pytz = "^2020.1" -unidecode = "^1.1" -markdown = {version = "^3.2", optional = true} +blinker = ">=1.4" +docutils = ">=0.16" +feedgenerator = ">=1.9" +jinja2 = ">=2.7" +pygments = ">=2.6" +python-dateutil = ">=2.8" +pytz = ">=2020.1" +unidecode = ">=1.1" +markdown = {version = ">=3.1", optional = true} [tool.poetry.dev-dependencies] BeautifulSoup4 = "^4.9" +jinja2 = "~2.11" lxml = "^4.3" -markdown = "~3.3.3" +markdown = "~3.3.4" typogrify = "^2.0" sphinx = "^3.0" sphinx_rtd_theme = "^0.5" @@ -56,7 +57,7 @@ pytest-cov = "^2.8" pytest-pythonpath = "^0.7.3" pytest-sugar = "^0.9.4" pytest-xdist = "^2.0" -tox = "^3.13" +tox = {version = "^3.13", optional = true} flake8 = "^3.8" flake8-import-order = "^0.18.1" invoke = "^1.3" diff --git a/requirements/test.pip b/requirements/test.pip index 0ecfdc3c..2d666b25 100644 --- a/requirements/test.pip +++ b/requirements/test.pip @@ -1,11 +1,11 @@ # Tests -Pygments==2.8.0 +Pygments==2.8.1 pytest pytest-cov pytest-xdist[psutil] # Optional Packages -Markdown==3.3.3 +Markdown==3.3.4 BeautifulSoup4 lxml typogrify diff --git a/setup.py b/setup.py index 89225f21..1be77c92 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ from setuptools import find_packages, setup version = "4.5.4" -requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.11', 'pygments', +requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', 'python-dateutil'] From 1449840199febf204c81fd0357dbcfe2b80dc549 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 23 Mar 2021 08:23:07 +0100 Subject: [PATCH 105/465] Make Tox & GitHub CI jobs' Python versions match Otherwise results in InterpreterNotFound errors --- .github/workflows/main.yml | 6 +++--- tox.ini | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 53841102..7e178b57 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -86,7 +86,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: "3.x" + python-version: 3.7 - name: Set pip cache (Linux) uses: actions/cache@v1 if: startsWith(runner.os, 'Linux') @@ -110,7 +110,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: "3.x" + python-version: 3.7 - name: Set pip cache (Linux) uses: actions/cache@v1 if: startsWith(runner.os, 'Linux') @@ -136,7 +136,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: "3.x" + python-version: 3.7 - name: Check release id: check_release run: | diff --git a/tox.ini b/tox.ini index f917476b..bb45229c 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,7 @@ commands = pytest -s --cov=pelican pelican [testenv:docs] -basepython = python3.6 +basepython = python3.7 deps = -rrequirements/docs.pip changedir = docs @@ -36,7 +36,7 @@ import-order-style = cryptography max-line-length = 88 [testenv:flake8] -basepython = python3.6 +basepython = python3.7 deps = -rrequirements/style.pip commands = From 9a9dbcf523b719981aa489f7878500b07c8a9b0b Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 23 Mar 2021 09:24:07 +0100 Subject: [PATCH 106/465] Prepare release --- RELEASE.md | 6 ++++++ docs/changelog.rst | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..be31d839 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,6 @@ +Release type: minor + +* Add new URL pattern to ``PAGINATION_PATTERNS`` for the last page in the list `(#1401) `_ +* Speed up ``livereload`` Invoke task via caching `(#2847) `_ +* Ignore ``None`` return value from ``get_generators`` signal `(#2850) `_ +* Relax dependency minimum versions and remove upper bounds diff --git a/docs/changelog.rst b/docs/changelog.rst index c302d837..206554ae 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,7 +4,7 @@ Release history 4.5.4 - 2021-01-04 ================== -Replace plugin definitions in settings with string representations after registering, so they can be cached correctly (#2828). +Replace plugin definitions in settings with string representations after registering, so they can be cached correctly `(#2828) `_. 4.5.3 - 2020-12-01 ================== From 1219bcd029a9386fdc2a398b79a72e60fb1fdb08 Mon Sep 17 00:00:00 2001 From: botpub Date: Tue, 23 Mar 2021 10:31:21 +0000 Subject: [PATCH 107/465] Release Pelican 4.6.0 --- RELEASE.md | 6 ------ docs/changelog.rst | 8 ++++++++ pyproject.toml | 2 +- setup.py | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) delete mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index be31d839..00000000 --- a/RELEASE.md +++ /dev/null @@ -1,6 +0,0 @@ -Release type: minor - -* Add new URL pattern to ``PAGINATION_PATTERNS`` for the last page in the list `(#1401) `_ -* Speed up ``livereload`` Invoke task via caching `(#2847) `_ -* Ignore ``None`` return value from ``get_generators`` signal `(#2850) `_ -* Relax dependency minimum versions and remove upper bounds diff --git a/docs/changelog.rst b/docs/changelog.rst index 206554ae..fc673d90 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,14 @@ Release history ############### +4.6.0 - 2021-03-23 +================== + +* Add new URL pattern to ``PAGINATION_PATTERNS`` for the last page in the list `(#1401) `_ +* Speed up ``livereload`` Invoke task via caching `(#2847) `_ +* Ignore ``None`` return value from ``get_generators`` signal `(#2850) `_ +* Relax dependency minimum versions and remove upper bounds + 4.5.4 - 2021-01-04 ================== diff --git a/pyproject.toml b/pyproject.toml index 0d2460b3..32dd56e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pelican" -version = "4.5.4" +version = "4.6.0" description = "Static site generator supporting Markdown and reStructuredText" authors = ["Justin Mayer "] license = "AGPLv3" diff --git a/setup.py b/setup.py index 1be77c92..0f991daf 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from os.path import join, relpath from setuptools import find_packages, setup -version = "4.5.4" +version = "4.6.0" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', From cf4e8d527d6468fad5c4910d254642c3353a2f76 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Sun, 18 Apr 2021 22:20:54 -0600 Subject: [PATCH 108/465] Update tasks.py PTY is not supported on Windows. --- tasks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasks.py b/tasks.py index c7e4f42c..12116da3 100644 --- a/tasks.py +++ b/tasks.py @@ -42,7 +42,8 @@ def docserve(c): @task def tests(c): """Run the test suite""" - c.run(f"{VENV_BIN}/pytest", pty=True) + PTY = True if os.name != "nt" else False + c.run(f"{VENV_BIN}/pytest", pty=PTY) @task From 88953d45d55d55ee7e79804749a40d47aa08c412 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 19 Apr 2021 09:49:03 +0200 Subject: [PATCH 109/465] Strip HTML tags from `notmyidea` page template's title Fixes #2843 --- pelican/themes/notmyidea/templates/page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/themes/notmyidea/templates/page.html b/pelican/themes/notmyidea/templates/page.html index 7e50c1f6..428c47f1 100644 --- a/pelican/themes/notmyidea/templates/page.html +++ b/pelican/themes/notmyidea/templates/page.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% block html_lang %}{{ page.lang }}{% endblock %} -{% block title %}{{ page.title }}{% endblock %} +{% block title %}{{ page.title|striptags }}{% endblock %} {% block extra_head %} {% import 'translations.html' as translations with context %} From a00284f744450623f20e0409a98b07c2d5ccc2e6 Mon Sep 17 00:00:00 2001 From: Romain Porte Date: Mon, 19 Apr 2021 15:59:41 +0200 Subject: [PATCH 110/465] Automatically open browser when Invoke task starts web server (#2764) When the `serve` and `livereload` targets are invoked, a web browser will be automatically opened, pointing to the locally-served website. If no web browser can be found by the module, the `open()` call returns `False`, but no exception is raised. This means that it is still possible to call livereload on a remote machine and access it without any error being triggered. Signed-off-by: Romain Porte --- pelican/tools/templates/tasks.py.jinja2 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pelican/tools/templates/tasks.py.jinja2 b/pelican/tools/templates/tasks.py.jinja2 index ff8e526e..861dee37 100644 --- a/pelican/tools/templates/tasks.py.jinja2 +++ b/pelican/tools/templates/tasks.py.jinja2 @@ -13,6 +13,7 @@ from pelican import main as pelican_main from pelican.server import ComplexHTTPRequestHandler, RootedHTTPServer from pelican.settings import DEFAULT_CONFIG, get_settings_from_file +OPEN_BROWSER_ON_SERVE = True SETTINGS_FILE_BASE = 'pelicanconf.py' SETTINGS = {} SETTINGS.update(DEFAULT_CONFIG) @@ -81,6 +82,11 @@ def serve(c): (CONFIG['host'], CONFIG['port']), ComplexHTTPRequestHandler) + if OPEN_BROWSER_ON_SERVE: + # Open site in default browser + import webbrowser + webbrowser.open("http://{host}:{port}".format(**CONFIG)) + sys.stderr.write('Serving at {host}:{port} ...\n'.format(**CONFIG)) server.serve_forever() @@ -124,6 +130,12 @@ def livereload(c): for glob in watched_globs: server.watch(glob, cached_build) + + if OPEN_BROWSER_ON_SERVE: + # Open site in default browser + import webbrowser + webbrowser.open("http://{host}:{port}".format(**CONFIG)) + server.serve(host=CONFIG['host'], port=CONFIG['port'], root=CONFIG['deploy_path']) {% if cloudfiles %} From c461def10ac480647097b9ce09ffcff711f1e9b8 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 21 Apr 2021 09:57:08 +0200 Subject: [PATCH 111/465] Update to v2 of `cache` GitHub Action --- .github/workflows/main.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7e178b57..863dec4b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,16 +34,16 @@ jobs: uses: actions/setup-python@v2 with: python-version: ${{ matrix.config.python }} - - name: Set pip cache (Linux) - uses: actions/cache@v1 + - name: Set up Pip cache (Linux) + uses: actions/cache@v2 if: startsWith(runner.os, 'Linux') with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements/*') }} restore-keys: | ${{ runner.os }}-pip- - - name: Setup pip cache (macOS) - uses: actions/cache@v1 + - name: Set up Pip cache (macOS) + uses: actions/cache@v2 if: startsWith(runner.os, 'macOS') with: path: ~/Library/Caches/pip @@ -51,7 +51,7 @@ jobs: restore-keys: | ${{ runner.os }}-pip- - name: Setup pip cache (Windows) - uses: actions/cache@v1 + uses: actions/cache@v2 if: startsWith(runner.os, 'Windows') with: path: ~\AppData\Local\pip\Cache @@ -88,7 +88,7 @@ jobs: with: python-version: 3.7 - name: Set pip cache (Linux) - uses: actions/cache@v1 + uses: actions/cache@v2 if: startsWith(runner.os, 'Linux') with: path: ~/.cache/pip @@ -112,7 +112,7 @@ jobs: with: python-version: 3.7 - name: Set pip cache (Linux) - uses: actions/cache@v1 + uses: actions/cache@v2 if: startsWith(runner.os, 'Linux') with: path: ~/.cache/pip From fd3ad0c16e19b0b024bd375a1cbd0870701dda3d Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 21 Apr 2021 10:05:28 +0200 Subject: [PATCH 112/465] Update contributing docs and Poetry repository URL --- docs/contribute.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/contribute.rst b/docs/contribute.rst index a86a5031..d1ab322d 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -24,7 +24,7 @@ Please note that Python 3.6+ is required for Pelican development. *(Optional)* If you prefer to install Poetry once for use with multiple projects, you can install it via:: - curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python + curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - Point your web browser to the `Pelican repository`_ and tap the **Fork** button at top-right. Then clone the source for your fork and add the upstream project @@ -38,9 +38,9 @@ as a Git remote:: While Poetry can dynamically create and manage virtual environments, we're going to manually create and activate a virtual environment:: - mkdir ~/virtualenvs - python3 -m venv ~/virtualenvs/pelican - source ~/virtualenvs/pelican/bin/activate + mkdir ~/virtualenvs && cd ~/virtualenvs + python3 -m venv pelican + source ~/virtualenvs/pelican/*/activate Install the needed dependencies and set up the project:: From 14afc6c54a7e361bbc465fdc25511831cce29812 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 21 Apr 2021 10:16:28 +0200 Subject: [PATCH 113/465] Update Pelican feature documentation --- README.rst | 10 ++++++---- docs/index.rst | 16 +++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index 995d23b0..4e1f1812 100644 --- a/README.rst +++ b/README.rst @@ -12,16 +12,17 @@ Pelican is a static site generator, written in Python_. Features -------- -Pelican currently supports: +Pelican’s feature highlights include: * Chronological content (e.g., articles, blog posts) as well as static pages -* Integration with external services (e.g., Google Analytics and Disqus) +* Integration with external services * Site themes (created using Jinja2_ templates) * Publication of articles in multiple languages * Generation of Atom and RSS feeds -* Syntax highlighting via Pygments_ -* Importing existing content from WordPress, Dotclear, and other services +* Code syntax highlighting via Pygments_ +* Import existing content from WordPress, Dotclear, or RSS feeds * Fast rebuild times due to content caching and selective output writing +* Extensible via a rich plugin ecosystem: `Pelican Plugins`_ Check out `Pelican's documentation`_ for further information. @@ -52,6 +53,7 @@ Why the name "Pelican"? .. _Markdown: https://daringfireball.net/projects/markdown/ .. _Jinja2: https://palletsprojects.com/p/jinja/ .. _Pygments: https://pygments.org/ +.. _`Pelican Plugins`: https://github.com/pelican-plugins .. _`Pelican's documentation`: https://docs.getpelican.com/ .. _`Pelican's internals`: https://docs.getpelican.com/en/latest/internals.html .. _`hosted on GitHub`: https://github.com/getpelican/pelican diff --git a/docs/index.rst b/docs/index.rst index 4cbe41ae..e64ffd7e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,19 +23,17 @@ Ready to get started? Check out the :doc:`Quickstart` guide. Features -------- -Pelican |version| currently supports: +Pelican’s feature highlights include: * Articles (e.g., blog posts) and pages (e.g., "About", "Projects", "Contact") -* Comments, via an external service (Disqus). If you prefer to have more - control over your comment data, self-hosted comments are another option. - Check out the `Pelican Plugins`_ repository for more details. -* Theming support (themes are created using Jinja2_ templates) +* Integration with external services +* Site themes (created using Jinja2_ templates) * Publication of articles in multiple languages -* Atom/RSS feeds +* Generation of Atom and RSS feeds * Code syntax highlighting -* Import from WordPress, Dotclear, or RSS feeds -* Integration with external tools: Twitter, Google Analytics, etc. (optional) +* Import existing content from WordPress, Dotclear, or RSS feeds * Fast rebuild times thanks to content caching and selective output writing +* Extensible via a rich plugin ecosystem: `Pelican Plugins`_ Why the name "Pelican"? ----------------------- @@ -82,4 +80,4 @@ Documentation .. _Jinja2: https://palletsprojects.com/p/jinja/ .. _`Pelican documentation`: https://docs.getpelican.com/latest/ .. _`Pelican's internals`: https://docs.getpelican.com/en/latest/internals.html -.. _`Pelican plugins`: https://github.com/getpelican/pelican-plugins +.. _`Pelican Plugins`: https://github.com/pelican-plugins From 40f3d2df91e847ccbc64d015fe837650055b67bd Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 21 Apr 2021 09:51:06 +0200 Subject: [PATCH 114/465] Add progress spinner animation during generation This is a first step at enriching console output via the `rich` project. --- pelican/__init__.py | 6 +++++- pyproject.toml | 1 + setup.py | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index cb91eb59..6302fe21 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -13,6 +13,8 @@ from collections.abc import Iterable from pkgutil import extend_path __path__ = extend_path(__path__, __name__) +from rich.console import Console + # pelican.log has to be the first pelican module to be loaded # because logging.setLoggerClass has to be called before logging.getLogger from pelican.log import init as init_logging @@ -35,6 +37,7 @@ except Exception: DEFAULT_CONFIG_NAME = 'pelicanconf.py' logger = logging.getLogger(__name__) +console = Console() class Pelican: @@ -524,7 +527,8 @@ def main(argv=None): else: watcher = FileSystemWatcher(args.settings, Readers, settings) watcher.check() - pelican.run() + with console.status("Generating..."): + pelican.run() except KeyboardInterrupt: logger.warning('Keyboard interrupt received. Exiting.') except Exception as e: diff --git a/pyproject.toml b/pyproject.toml index 32dd56e3..99b1d5d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ jinja2 = ">=2.7" pygments = ">=2.6" python-dateutil = ">=2.8" pytz = ">=2020.1" +rich = ">=10.1" unidecode = ">=1.1" markdown = {version = ">=3.1", optional = true} diff --git a/setup.py b/setup.py index 0f991daf..5ffd0fdf 100755 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ version = "4.6.0" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', - 'python-dateutil'] + 'python-dateutil', 'rich'] entry_points = { 'console_scripts': [ From 487da3550bffb96ebfed9dbf206d8b3574886a21 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 29 Dec 2020 19:22:37 +0100 Subject: [PATCH 115/465] Add period_num var for period_archives template This makes it easier to create templates that are language-agnostic or need extra processing for the date period. --- docs/themes.rst | 2 ++ pelican/generators.py | 2 ++ pelican/tests/test_generators.py | 3 +++ 3 files changed, 7 insertions(+) diff --git a/docs/themes.rst b/docs/themes.rst index 86a754bc..0c053fce 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -327,6 +327,8 @@ period A tuple of the form (`year`, `month`, `day`) that given year. It contains both `year` and `month` if the time period is over years and months and so on. +period_num A tuple of the form (``year``, ``month``, ``day``), + as in ``period``, except all values are numbers. =================== =================================================== diff --git a/pelican/generators.py b/pelican/generators.py index 63e20a0a..07370264 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -511,6 +511,7 @@ class ArticlesGenerator(CachingGenerator): if key == period_date_key['year']: context["period"] = (_period,) + context["period_num"] = (_period,) else: month_name = calendar.month_name[_period[1]] if key == period_date_key['month']: @@ -520,6 +521,7 @@ class ArticlesGenerator(CachingGenerator): context["period"] = (_period[0], month_name, _period[2]) + context["period_num"] = tuple(_period) write(save_as, template, context, articles=articles, dates=archive, template_name='period_archives', diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 169765ac..be43aa0e 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -413,6 +413,7 @@ class TestArticlesGenerator(unittest.TestCase): self.assertEqual(len(dates), 1) # among other things it must have at least been called with this context["period"] = (1970,) + context["period_num"] = (1970,) write.assert_called_with("posts/1970/index.html", generator.get_template("period_archives"), context, blog=True, articles=articles, @@ -437,6 +438,7 @@ class TestArticlesGenerator(unittest.TestCase): if d.date.year == 1970 and d.date.month == 1] self.assertEqual(len(dates), 1) context["period"] = (1970, "January") + context["period_num"] = (1970, 1) # among other things it must have at least been called with this write.assert_called_with("posts/1970/Jan/index.html", generator.get_template("period_archives"), @@ -470,6 +472,7 @@ class TestArticlesGenerator(unittest.TestCase): ] self.assertEqual(len(dates), 1) context["period"] = (1970, "January", 1) + context["period_num"] = (1970, 1, 1) # among other things it must have at least been called with this write.assert_called_with("posts/1970/Jan/01/index.html", generator.get_template("period_archives"), From add3628a6474f2bd145fe2ea55c5e5255e9970ba Mon Sep 17 00:00:00 2001 From: Gio Date: Fri, 16 Apr 2021 19:07:35 -0500 Subject: [PATCH 116/465] Add support for hidden articles --- docs/content.rst | 8 +++++++ docs/themes.rst | 7 +++--- pelican/__init__.py | 8 ++++++- pelican/contents.py | 6 ++--- pelican/generators.py | 31 +++++++++++++++++++------ pelican/tests/content/article_draft.md | 5 ++++ pelican/tests/content/article_hidden.md | 5 ++++ pelican/tests/test_generators.py | 14 +++++++++++ 8 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 pelican/tests/content/article_draft.md create mode 100644 pelican/tests/content/article_hidden.md diff --git a/docs/content.rst b/docs/content.rst index 48c851ab..cd492012 100644 --- a/docs/content.rst +++ b/docs/content.rst @@ -614,6 +614,14 @@ the ``DEFAULT_METADATA``:: To publish a post when the default status is ``draft``, update the post's metadata to include ``Status: published``. +Hidden Posts +============ + +Like pages, posts can also be marked as ``hidden`` with the ``Status: hidden`` +attribute. Hidden posts will be output to ``ARTICLE_SAVE_AS`` as expected, but +are not included by default in tag or category indexes, nor in the main +article feed. This has the effect of creating an "unlisted" post. + .. _W3C ISO 8601: https://www.w3.org/TR/NOTE-datetime .. _AsciiDoc: https://www.methods.co.nz/asciidoc/ .. _pelican-plugins: https://github.com/getpelican/pelican-plugins diff --git a/docs/themes.rst b/docs/themes.rst index 0c053fce..c4d3ed7f 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -66,9 +66,9 @@ Common variables All of these settings will be available to all templates. -============= =================================================== +=============== =================================================== Variable Description -============= =================================================== +=============== =================================================== output_file The name of the file currently being generated. For instance, when Pelican is rendering the home page, output_file will be "index.html". @@ -80,6 +80,7 @@ articles The list of articles, ordered descending by date. in the `all_articles` variable. dates The same list of articles, but ordered by date, ascending. +hidden_articles The list of hidden articles drafts The list of draft articles authors A list of (author, articles) tuples, containing all the authors and corresponding articles (values) @@ -90,7 +91,7 @@ tags A list of (tag, articles) tuples, containing all pages The list of pages hidden_pages The list of hidden pages draft_pages The list of draft pages -============= =================================================== +=============== =================================================== Sorting diff --git a/pelican/__init__.py b/pelican/__init__.py index 6302fe21..735002a2 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -144,6 +144,11 @@ class Pelican: len(articles_generator.drafts_translations)), 'draft', 'drafts') + pluralized_hidden_articles = maybe_pluralize( + (len(articles_generator.hidden_articles) + + len(articles_generator.hidden_translations)), + 'hidden article', + 'hidden articles') pluralized_pages = maybe_pluralize( (len(pages_generator.pages) + len(pages_generator.translations)), @@ -160,10 +165,11 @@ class Pelican: 'draft page', 'draft pages') - print('Done: Processed {}, {}, {}, {} and {} in {:.2f} seconds.' + print('Done: Processed {}, {}, {}, {}, {} and {} in {:.2f} seconds.' .format( pluralized_articles, pluralized_drafts, + pluralized_hidden_articles, pluralized_pages, pluralized_hidden_pages, pluralized_draft_pages, diff --git a/pelican/contents.py b/pelican/contents.py index 75cedcdc..1740df88 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -181,9 +181,9 @@ class Content: if hasattr(self, 'allowed_statuses'): if self.status not in self.allowed_statuses: logger.error( - "Unknown status '%s' for file %s, skipping it.", + "Unknown status '%s' for file %s, skipping it. (Not in %s)", self.status, - self + self, self.allowed_statuses ) return False @@ -513,7 +513,7 @@ class Page(Content): class Article(Content): mandatory_properties = ('title', 'date', 'category') - allowed_statuses = ('published', 'draft') + allowed_statuses = ('published', 'hidden', 'draft') default_status = 'published' default_template = 'article' diff --git a/pelican/generators.py b/pelican/generators.py index 07370264..85bc3d95 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -285,15 +285,20 @@ class ArticlesGenerator(CachingGenerator): def __init__(self, *args, **kwargs): """initialize properties""" + # Published, listed articles self.articles = [] # only articles in default language self.translations = [] + # Published, unlisted articles + self.hidden_articles = [] + self.hidden_translations = [] + # Draft articles + self.drafts = [] # only drafts in default language + self.drafts_translations = [] self.dates = {} self.tags = defaultdict(list) self.categories = defaultdict(list) self.related_posts = [] self.authors = defaultdict(list) - self.drafts = [] # only drafts in default language - self.drafts_translations = [] super().__init__(*args, **kwargs) signals.article_generator_init.send(self) @@ -461,7 +466,10 @@ class ArticlesGenerator(CachingGenerator): def generate_articles(self, write): """Generate the articles.""" - for article in chain(self.translations, self.articles): + for article in chain( + self.translations, self.articles, + self.hidden_translations, self.hidden_articles + ): signals.article_generator_write_article.send(self, content=article) write(article.save_as, self.get_template(article.template), self.context, article=article, category=article.category, @@ -609,6 +617,7 @@ class ArticlesGenerator(CachingGenerator): all_articles = [] all_drafts = [] + hidden_articles = [] for f in self.get_files( self.settings['ARTICLE_PATHS'], exclude=self.settings['ARTICLE_EXCLUDES']): @@ -639,6 +648,9 @@ class ArticlesGenerator(CachingGenerator): all_articles.append(article) elif article.status == "draft": all_drafts.append(article) + elif article.status == "hidden": + hidden_articles.append(article) + self.add_source_path(article) self.add_static_links(article) @@ -649,13 +661,14 @@ class ArticlesGenerator(CachingGenerator): return origs, translations self.articles, self.translations = _process(all_articles) + self.hidden_articles, self.hidden_translations = _process(hidden_articles) self.drafts, self.drafts_translations = _process(all_drafts) signals.article_generator_pretaxonomy.send(self) for article in self.articles: # only main articles are listed in categories and tags - # not translations + # not translations or hidden articles self.categories[article.category].append(article) if hasattr(article, 'tags'): for tag in article.tags: @@ -677,8 +690,10 @@ class ArticlesGenerator(CachingGenerator): self.authors = list(self.authors.items()) self.authors.sort() - self._update_context(('articles', 'dates', 'tags', 'categories', - 'authors', 'related_posts', 'drafts')) + self._update_context(( + 'articles', 'drafts', 'hidden_articles', + 'dates', 'tags', 'categories', + 'authors', 'related_posts')) self.save_cache() self.readers.save_cache() signals.article_generator_finalized.send(self) @@ -692,7 +707,9 @@ class ArticlesGenerator(CachingGenerator): for e in chain(self.articles, self.translations, self.drafts, - self.drafts_translations): + self.drafts_translations, + self.hidden_articles, + self.hidden_translations): if hasattr(e, 'refresh_metadata_intersite_links'): e.refresh_metadata_intersite_links() diff --git a/pelican/tests/content/article_draft.md b/pelican/tests/content/article_draft.md new file mode 100644 index 00000000..d2235553 --- /dev/null +++ b/pelican/tests/content/article_draft.md @@ -0,0 +1,5 @@ +Title: Draft article +Date: 2012-10-31 +Status: draft + +This is some content. diff --git a/pelican/tests/content/article_hidden.md b/pelican/tests/content/article_hidden.md new file mode 100644 index 00000000..d449f701 --- /dev/null +++ b/pelican/tests/content/article_hidden.md @@ -0,0 +1,5 @@ +Title: Hidden article +Date: 2012-10-31 +Status: hidden + +This is some unlisted content. diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index be43aa0e..0a4a8fdc 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -181,6 +181,8 @@ class TestArticlesGenerator(unittest.TestCase): path=CONTENT_DIR, theme=settings['THEME'], output_path=None) cls.generator.generate_context() cls.articles = cls.distill_articles(cls.generator.articles) + cls.drafts = cls.distill_articles(cls.generator.drafts) + cls.hidden_articles = cls.distill_articles(cls.generator.hidden_articles) def setUp(self): self.temp_cache = mkdtemp(prefix='pelican_cache.') @@ -284,6 +286,18 @@ class TestArticlesGenerator(unittest.TestCase): ] self.assertEqual(sorted(articles_expected), sorted(self.articles)) + def test_articles_draft(self): + draft_articles_expected = [ + ['Draft article', 'draft', 'Default', 'article'], + ] + self.assertEqual(sorted(draft_articles_expected), sorted(self.drafts)) + + def test_articles_hidden(self): + hidden_articles_expected = [ + ['Hidden article', 'hidden', 'Default', 'article'], + ] + self.assertEqual(sorted(hidden_articles_expected), sorted(self.hidden_articles)) + def test_generate_categories(self): # test for name # categories are grouped by slug; if two categories have the same slug From c41b8abb133b85db83515bfe259e7fac5c4a7b7f Mon Sep 17 00:00:00 2001 From: jzc Date: Thu, 20 May 2021 00:11:58 -0600 Subject: [PATCH 117/465] Fix FILENAME_METADATA doc to render correctly --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index c66c42a3..a26dc1ba 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -815,7 +815,7 @@ Metadata The default metadata you want to use for all articles and pages. -.. data:: FILENAME_METADATA = r'(?P\d{4}-\d{2}-\d{2}).*' +.. data:: FILENAME_METADATA = r'(?P\\d{4}-\\d{2}-\\d{2}).*' The regexp that will be used to extract any metadata from the filename. All named groups that are matched will be set in the metadata object. The From c041bf2192dbf95cb2ac63f1ea979a0d955690b7 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 8 Jun 2021 11:40:27 -0500 Subject: [PATCH 118/465] Fix failing tests on Jinja 3+ --- pelican/utils.py | 2 +- pelican/writers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/utils.py b/pelican/utils.py index e82117d3..8c3a886d 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -18,7 +18,7 @@ from operator import attrgetter import dateutil.parser -from jinja2 import Markup +from markupsafe import Markup import pytz diff --git a/pelican/writers.py b/pelican/writers.py index 9b27a748..73f025b6 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -5,7 +5,7 @@ from urllib.parse import urljoin from feedgenerator import Atom1Feed, Rss201rev2Feed, get_tag_uri -from jinja2 import Markup +from markupsafe import Markup from pelican.paginator import Paginator from pelican.plugins import signals From 845acfe1ac7c156513d928b01e4acdad0c47f6ee Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Mon, 7 Jun 2021 21:46:15 -0600 Subject: [PATCH 119/465] Allow easy subclassing of Writer When you write a custom Writer, it gets called with `settings=None`. If you writer is simply a subclass of the built-in Writer, Pelican will through the error `CRITICAL: 'RELATIVE_URLS'`. The source of the error is from `Pelican._get_writer()` in `__init__.py`. --- pelican/writers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/writers.py b/pelican/writers.py index 73f025b6..73ee4b33 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -25,7 +25,7 @@ class Writer: self._overridden_files = set() # See Content._link_replacer for details - if self.settings['RELATIVE_URLS']: + if "RELATIVE_URLS" in self.settings and self.settings['RELATIVE_URLS']: self.urljoiner = posix_join else: self.urljoiner = lambda base, url: urljoin( From e800b23b4d9ec45663dbac6c5cb4775b0cacdb57 Mon Sep 17 00:00:00 2001 From: Tristan Miller Date: Sat, 12 Jun 2021 15:48:32 +0200 Subject: [PATCH 120/465] Update links to Jinja docs. Fixes #2883 (#2885) --- docs/settings.rst | 8 ++++---- docs/themes.rst | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index a26dc1ba..e685d55d 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -759,9 +759,9 @@ Time and Date .. [#] Default is the system locale. -.. _Jinja custom filters documentation: https://jinja.palletsprojects.com/en/master/api/#custom-filters -.. _Jinja global namespace documentation: https://jinja.palletsprojects.com/en/master/api/#the-global-namespace -.. _Jinja custom tests documentation: https://jinja.palletsprojects.com/en/master/api/#custom-tests +.. _Jinja custom filters documentation: https://jinja.palletsprojects.com/en/latest/api/#custom-filters +.. _Jinja global namespace documentation: https://jinja.palletsprojects.com/en/latest/api/#the-global-namespace +.. _Jinja custom tests documentation: https://jinja.palletsprojects.com/en/latest/api/#custom-tests .. _locales on Windows: https://www.microsoft.com/en-us/download/details.aspx?id=55979 @@ -1403,5 +1403,5 @@ Example settings :language: python -.. _Jinja Environment documentation: https://jinja.palletsprojects.com/en/master/api/#jinja2.Environment +.. _Jinja Environment documentation: https://jinja.palletsprojects.com/en/latest/api/#jinja2.Environment .. _Docutils Configuration: http://docutils.sourceforge.net/docs/user/config.html diff --git a/docs/themes.rst b/docs/themes.rst index c4d3ed7f..e21a6c79 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -105,7 +105,7 @@ that allow them to be easily sorted by name:: If you want to sort based on different criteria, `Jinja's sort command`__ has a number of options. -__ https://jinja.palletsprojects.com/en/master/templates/#sort +__ https://jinja.palletsprojects.com/en/latest/templates/#sort Date Formatting From 2cafe926fa854b5b8c84b944d77b628574a2ec61 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Tue, 15 Jun 2021 22:41:38 -0600 Subject: [PATCH 121/465] typo fix "current" only has two "r"s --- pelican/generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/generators.py b/pelican/generators.py index 85bc3d95..e18531be 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -206,7 +206,7 @@ class Generator: self.context['static_links'] |= content.get_static_links() def _update_context(self, items): - """Update the context with the given items from the currrent + """Update the context with the given items from the current processor. """ for item in items: From 80f44c494a4d6aba21bafcd908d2f1cc930e3e87 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Wed, 30 Jun 2021 22:47:32 -0600 Subject: [PATCH 122/465] Switch to rich logging --- pelican/__init__.py | 8 +-- pelican/log.py | 115 +++++--------------------------------------- 2 files changed, 17 insertions(+), 106 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 735002a2..d3969e29 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -43,7 +43,7 @@ console = Console() class Pelican: def __init__(self, settings): - """Pelican initialisation + """Pelican initialization Performs some checks on the environment before doing anything else. """ @@ -500,7 +500,7 @@ def listen(server, port, output, excqueue=None): def main(argv=None): args = parse_arguments(argv) logs_dedup_min_level = getattr(logging, args.logs_dedup_min_level) - init_logging(args.verbosity, args.fatal, + init_logging(level=args.verbosity, fatal=args.fatal, name=__name__, logs_dedup_min_level=logs_dedup_min_level) logger.debug('Pelican version: %s', __version__) @@ -538,9 +538,9 @@ def main(argv=None): except KeyboardInterrupt: logger.warning('Keyboard interrupt received. Exiting.') except Exception as e: - logger.critical('%s', e) + logger.critical("%s: %s" % (e.__class__.__name__, e)) if args.verbosity == logging.DEBUG: - raise + console.print_exception() else: sys.exit(getattr(e, 'exitcode', 1)) diff --git a/pelican/log.py b/pelican/log.py index 325ac3ea..111ce777 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -1,82 +1,13 @@ import logging -import os -import sys from collections import defaultdict -from collections.abc import Mapping + +from rich.logging import RichHandler __all__ = [ 'init' ] -class BaseFormatter(logging.Formatter): - def __init__(self, fmt=None, datefmt=None): - FORMAT = '%(customlevelname)s %(message)s' - super().__init__(fmt=FORMAT, datefmt=datefmt) - - def format(self, record): - customlevel = self._get_levelname(record.levelname) - record.__dict__['customlevelname'] = customlevel - # format multiline messages 'nicely' to make it clear they are together - record.msg = record.msg.replace('\n', '\n | ') - if not isinstance(record.args, Mapping): - record.args = tuple(arg.replace('\n', '\n | ') if - isinstance(arg, str) else - arg for arg in record.args) - return super().format(record) - - def formatException(self, ei): - ''' prefix traceback info for better representation ''' - s = super().formatException(ei) - # fancy format traceback - s = '\n'.join(' | ' + line for line in s.splitlines()) - # separate the traceback from the preceding lines - s = ' |___\n{}'.format(s) - return s - - def _get_levelname(self, name): - ''' NOOP: overridden by subclasses ''' - return name - - -class ANSIFormatter(BaseFormatter): - ANSI_CODES = { - 'red': '\033[1;31m', - 'yellow': '\033[1;33m', - 'cyan': '\033[1;36m', - 'white': '\033[1;37m', - 'bgred': '\033[1;41m', - 'bggrey': '\033[1;100m', - 'reset': '\033[0;m'} - - LEVEL_COLORS = { - 'INFO': 'cyan', - 'WARNING': 'yellow', - 'ERROR': 'red', - 'CRITICAL': 'bgred', - 'DEBUG': 'bggrey'} - - def _get_levelname(self, name): - color = self.ANSI_CODES[self.LEVEL_COLORS.get(name, 'white')] - if name == 'INFO': - fmt = '{0}->{2}' - else: - fmt = '{0}{1}{2}:' - return fmt.format(color, name, self.ANSI_CODES['reset']) - - -class TextFormatter(BaseFormatter): - """ - Convert a `logging.LogRecord' object into text. - """ - - def _get_levelname(self, name): - if name == 'INFO': - return '->' - else: - return name + ':' - - class LimitFilter(logging.Filter): """ Remove duplicates records, and limit the number of records in the same @@ -169,40 +100,20 @@ logging.setLoggerClass(FatalLogger) logging.getLogger().__class__ = FatalLogger -def supports_color(): - """ - Returns True if the running system's terminal supports color, - and False otherwise. - - from django.core.management.color - """ - plat = sys.platform - supported_platform = plat != 'Pocket PC' and \ - (plat != 'win32' or 'ANSICON' in os.environ) - - # isatty is not always implemented, #6223. - is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() - if not supported_platform or not is_a_tty: - return False - return True - - -def get_formatter(): - if supports_color(): - return ANSIFormatter() - else: - return TextFormatter() - - -def init(level=None, fatal='', handler=logging.StreamHandler(), name=None, +def init(level=None, fatal='', handler=RichHandler(), name=None, logs_dedup_min_level=None): FatalLogger.warnings_fatal = fatal.startswith('warning') FatalLogger.errors_fatal = bool(fatal) - logger = logging.getLogger(name) + LOG_FORMAT = "%(message)s" + logging.basicConfig( + level=level, + format=LOG_FORMAT, + datefmt="[%H:%M:%S]", + handlers=[handler] + ) - handler.setFormatter(get_formatter()) - logger.addHandler(handler) + logger = logging.getLogger(name) if level: logger.setLevel(level) @@ -218,9 +129,9 @@ def log_warnings(): if __name__ == '__main__': - init(level=logging.DEBUG) + init(level=logging.DEBUG, name=__name__) - root_logger = logging.getLogger() + root_logger = logging.getLogger(__name__) root_logger.debug('debug') root_logger.info('info') root_logger.warning('warning') From 4bfcedb8a543fa8ecd509446efb3c80e36841f6a Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Wed, 30 Jun 2021 23:03:22 -0600 Subject: [PATCH 123/465] Share rich handler between spinner and logging --- pelican/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index d3969e29..c4e2580b 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -14,6 +14,7 @@ from pkgutil import extend_path __path__ = extend_path(__path__, __name__) from rich.console import Console +from rich.logging import RichHandler # pelican.log has to be the first pelican module to be loaded # because logging.setLoggerClass has to be called before logging.getLogger @@ -500,7 +501,8 @@ def listen(server, port, output, excqueue=None): def main(argv=None): args = parse_arguments(argv) logs_dedup_min_level = getattr(logging, args.logs_dedup_min_level) - init_logging(level=args.verbosity, fatal=args.fatal, name=__name__, + init_logging(level=args.verbosity, fatal=args.fatal, + handler=RichHandler(console=console), name=__name__, logs_dedup_min_level=logs_dedup_min_level) logger.debug('Pelican version: %s', __version__) From 7d492bad67d78f4d08afa58abf1d1f83198c15fb Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Wed, 30 Jun 2021 23:29:20 -0600 Subject: [PATCH 124/465] Remove log format test as *rich* is now doing this --- pelican/tests/test_log.py | 50 --------------------------------------- 1 file changed, 50 deletions(-) diff --git a/pelican/tests/test_log.py b/pelican/tests/test_log.py index 3f9d7250..1f2fb83a 100644 --- a/pelican/tests/test_log.py +++ b/pelican/tests/test_log.py @@ -12,12 +12,10 @@ class TestLog(unittest.TestCase): super().setUp() self.logger = logging.getLogger(__name__) self.handler = LogCountHandler() - self.handler.setFormatter(log.get_formatter()) self.logger.addHandler(self.handler) def tearDown(self): self._reset_limit_filter() - self.logger.removeHandler(self.handler) super().tearDown() def _reset_limit_filter(self): @@ -34,54 +32,6 @@ class TestLog(unittest.TestCase): self._reset_limit_filter() self.handler.flush() - def test_log_formatter(self): - counter = self.handler.count_formatted_logs - with self.reset_logger(): - # log simple case - self.logger.warning('Log %s', 'test') - self.assertEqual( - counter('Log test', logging.WARNING), - 1) - - with self.reset_logger(): - # log multiline message - self.logger.warning('Log\n%s', 'test') - # Log - # | test - self.assertEqual( - counter('Log', logging.WARNING), - 1) - self.assertEqual( - counter(' | test', logging.WARNING), - 1) - - with self.reset_logger(): - # log multiline argument - self.logger.warning('Log %s', 'test1\ntest2') - # Log test1 - # | test2 - self.assertEqual( - counter('Log test1', logging.WARNING), - 1) - self.assertEqual( - counter(' | test2', logging.WARNING), - 1) - - with self.reset_logger(): - # log single list - self.logger.warning('Log %s', ['foo', 'bar']) - self.assertEqual( - counter(r"Log \['foo', 'bar'\]", logging.WARNING), - 1) - - with self.reset_logger(): - # log single dict - self.logger.warning('Log %s', {'foo': 1, 'bar': 2}) - self.assertEqual( - # dict order is not guaranteed - counter(r"Log {'.*': \d, '.*': \d}", logging.WARNING), - 1) - def test_log_filter(self): def do_logging(): for i in range(5): From 82098a634f24c0a73192e1c82fe8f3c54f36f472 Mon Sep 17 00:00:00 2001 From: Tristan Miller Date: Wed, 7 Jul 2021 08:48:10 +0200 Subject: [PATCH 125/465] Fix formatting of Jinja example code. Fixes #2884 (#2886) --- docs/settings.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/settings.rst b/docs/settings.rst index e685d55d..47852527 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -104,6 +104,7 @@ Basic settings should map the filtername to the filter function. Example:: + import sys sys.path.append('to/your/path') From a52922bfb597a4799cecff4641a5a2f9c40d99aa Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 8 Jul 2021 21:33:22 -0600 Subject: [PATCH 126/465] Move rich's console to log.py --- pelican/__init__.py | 11 +++-------- pelican/log.py | 5 ++++- pelican/utils.py | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index c4e2580b..e4ff92be 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -13,11 +13,9 @@ from collections.abc import Iterable from pkgutil import extend_path __path__ = extend_path(__path__, __name__) -from rich.console import Console -from rich.logging import RichHandler - # pelican.log has to be the first pelican module to be loaded # because logging.setLoggerClass has to be called before logging.getLogger +from pelican.log import console from pelican.log import init as init_logging from pelican.generators import (ArticlesGenerator, # noqa: I100 PagesGenerator, SourceFileGenerator, @@ -38,7 +36,6 @@ except Exception: DEFAULT_CONFIG_NAME = 'pelicanconf.py' logger = logging.getLogger(__name__) -console = Console() class Pelican: @@ -502,8 +499,7 @@ def main(argv=None): args = parse_arguments(argv) logs_dedup_min_level = getattr(logging, args.logs_dedup_min_level) init_logging(level=args.verbosity, fatal=args.fatal, - handler=RichHandler(console=console), name=__name__, - logs_dedup_min_level=logs_dedup_min_level) + name=__name__, logs_dedup_min_level=logs_dedup_min_level) logger.debug('Pelican version: %s', __version__) logger.debug('Python version: %s', sys.version.split()[0]) @@ -544,5 +540,4 @@ def main(argv=None): if args.verbosity == logging.DEBUG: console.print_exception() - else: - sys.exit(getattr(e, 'exitcode', 1)) + sys.exit(getattr(e, 'exitcode', 1)) diff --git a/pelican/log.py b/pelican/log.py index 111ce777..be176ea8 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -1,12 +1,15 @@ import logging from collections import defaultdict +from rich.console import Console from rich.logging import RichHandler __all__ = [ 'init' ] +console = Console() + class LimitFilter(logging.Filter): """ @@ -100,7 +103,7 @@ logging.setLoggerClass(FatalLogger) logging.getLogger().__class__ = FatalLogger -def init(level=None, fatal='', handler=RichHandler(), name=None, +def init(level=None, fatal='', handler=RichHandler(console=console), name=None, logs_dedup_min_level=None): FatalLogger.warnings_fatal = fatal.startswith('warning') FatalLogger.errors_fatal = bool(fatal) diff --git a/pelican/utils.py b/pelican/utils.py index 8c3a886d..e5c64470 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -811,7 +811,7 @@ class FileSystemWatcher: if result.get('content') is None: reader_descs = sorted( { - '%s (%s)' % (type(r).__name__, ', '.join(r.file_extensions)) + ' | %s (%s)' % (type(r).__name__, ', '.join(r.file_extensions)) for r in self.reader_class(self.settings).readers.values() if r.enabled } From 7eb730af78b0a5375e077c6f02f4440bc4578d48 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Tue, 6 Jul 2021 21:31:54 -0600 Subject: [PATCH 127/465] Nicer logging of found writer matches generator format --- pelican/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index e4ff92be..4014e971 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -222,7 +222,7 @@ class Pelican: writer = writers[0] - logger.debug("Found writer: %s", writer) + logger.debug("Found writer: %s (%s)" % (writer.__name__, writer.__module__)) return writer(self.output_path, settings=self.settings) From 58e3770b80a1e9649f29a0081041a81984e8f983 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Fri, 9 Jul 2021 08:28:15 -0600 Subject: [PATCH 128/465] Document how to add a new writer c.f. #2899 --- docs/plugins.rst | 27 +++++++++++++++++++++++++++ docs/tips.rst | 10 +++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index f661afb8..8e24b0af 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -274,6 +274,33 @@ Adding a new generator is also really easy. You might want to have a look at signals.get_generators.connect(get_generators) +Adding a new writer +------------------- + +Adding a writer will allow you to output additional file formats to disk, or +change how the existing formats are written to disk. Note that only one writer +will be active at a time, so be sure to either subclass the built-in Writer, or +completely re-implement it. + +Here is a basic example of how to set up your own writer:: + + from pelican.writers import Writer + from pelican import signals + + class MyWriter(Writer): + # define new writer functionality + pass + + + def add_writer(pelican_object): + # use pelican_instance to setup stuff if needed + return MyWriter + + + def register(): + signals.get_writer.connect(add_writer) + + .. _Pip: https://pip.pypa.io/ .. _pelican-plugins bug #314: https://github.com/getpelican/pelican-plugins/issues/314 .. _Blinker: https://pythonhosted.org/blinker/ diff --git a/docs/tips.rst b/docs/tips.rst index 18a79ef0..78013227 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -93,21 +93,21 @@ by the ``ghp-import`` command) to the ``elemoine.github.io`` repository's To publish your Pelican site as User Pages, feel free to adjust the ``github`` target of the Makefile. - + Another option for publishing to User Pages is to generate the output files in the root directory of the project. For example, your main project folder is ``.github.io`` and you can create the Pelican project in a subdirectory called ``Pelican``. Then from inside the ``Pelican`` folder you can run:: - + $ pelican content -o .. -s pelicanconf.py Now you can push the whole project ``.github.io`` to the master branch of your GitHub repository:: - + $ git push origin master - + (assuming origin is set to your remote repository). Custom 404 Pages @@ -179,4 +179,4 @@ If you are using ``develop-server.sh``, add this to the top:: and modify the ``pelican.server`` line as follows:: - $PY -m pelican.server $port --ssl --cert="$CERT" --key="$KEY" & \ No newline at end of file + $PY -m pelican.server $port --ssl --cert="$CERT" --key="$KEY" & From bc21922cf217c4e05f9c28fbc30349271735798e Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Fri, 9 Jul 2021 09:51:06 -0600 Subject: [PATCH 129/465] Don't preformat log messages as per review notes --- pelican/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 4014e971..31f98e52 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -222,7 +222,7 @@ class Pelican: writer = writers[0] - logger.debug("Found writer: %s (%s)" % (writer.__name__, writer.__module__)) + logger.debug("Found writer: %s (%s)", writer.__name__, writer.__module__) return writer(self.output_path, settings=self.settings) @@ -536,7 +536,7 @@ def main(argv=None): except KeyboardInterrupt: logger.warning('Keyboard interrupt received. Exiting.') except Exception as e: - logger.critical("%s: %s" % (e.__class__.__name__, e)) + logger.critical("%s: %s", e.__class__.__name__, e) if args.verbosity == logging.DEBUG: console.print_exception() From 1cd7dd6a28998f0c003d938d68ae8b1081f6b9ac Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Fri, 9 Jul 2021 09:55:48 -0600 Subject: [PATCH 130/465] Print to the (rich) console, rather than directly --- pelican/__init__.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 31f98e52..ebf90b79 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -163,15 +163,15 @@ class Pelican: 'draft page', 'draft pages') - print('Done: Processed {}, {}, {}, {}, {} and {} in {:.2f} seconds.' - .format( - pluralized_articles, - pluralized_drafts, - pluralized_hidden_articles, - pluralized_pages, - pluralized_hidden_pages, - pluralized_draft_pages, - time.time() - start_time)) + console.print('Done: Processed {}, {}, {}, {}, {} and {} in {:.2f} seconds.' + .format( + pluralized_articles, + pluralized_drafts, + pluralized_hidden_articles, + pluralized_pages, + pluralized_hidden_pages, + pluralized_draft_pages, + time.time() - start_time)) def _get_generator_classes(self): discovered_generators = [ @@ -426,8 +426,8 @@ def get_instance(args): def autoreload(args, excqueue=None): - print(' --- AutoReload Mode: Monitoring `content`, `theme` and' - ' `settings` for changes. ---') + console.print(' --- AutoReload Mode: Monitoring `content`, `theme` and' + ' `settings` for changes. ---') pelican, settings = get_instance(args) watcher = FileSystemWatcher(args.settings, Readers, settings) sleep = False @@ -446,8 +446,8 @@ def autoreload(args, excqueue=None): watcher.update_watchers(settings) if any(modified.values()): - print('\n-> Modified: {}. re-generating...'.format( - ', '.join(k for k, v in modified.items() if v))) + console.print('\n-> Modified: {}. re-generating...'.format( + ', '.join(k for k, v in modified.items() if v))) pelican.run() except KeyboardInterrupt: From a168470f294575655750776335da189697cec84b Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Fri, 9 Jul 2021 09:56:11 -0600 Subject: [PATCH 131/465] Use rich.console with printing settings --- pelican/__init__.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index ebf90b79..6c2c3051 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -228,26 +228,33 @@ class Pelican: class PrintSettings(argparse.Action): def __call__(self, parser, namespace, values, option_string): - instance, settings = get_instance(namespace) + init_logging(name=__name__) + + try: + instance, settings = get_instance(namespace) + except Exception as e: + logger.critical("%s: %s", e.__class__.__name__, e) + console.print_exception() + sys.exit(getattr(e, 'exitcode', 1)) if values: # One or more arguments provided, so only print those settings for setting in values: if setting in settings: # Only add newline between setting name and value if dict - if isinstance(settings[setting], dict): + if isinstance(settings[setting], (dict, tuple, list)): setting_format = '\n{}:\n{}' else: setting_format = '\n{}: {}' - print(setting_format.format( + console.print(setting_format.format( setting, pprint.pformat(settings[setting]))) else: - print('\n{} is not a recognized setting.'.format(setting)) + console.print('\n{} is not a recognized setting.'.format(setting)) break else: # No argument was given to --print-settings, so print all settings - pprint.pprint(settings) + console.print(settings) parser.exit() From 332be6e5c8005f699a708568939d31dd85e9cffb Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Tue, 13 Jul 2021 01:35:22 -0600 Subject: [PATCH 132/465] Support date format codes G, V, and u (used by ISO dates) (#2902) --- pelican/tests/test_utils.py | 3 +++ pelican/utils.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 10816199..5fbd066d 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -513,6 +513,9 @@ class TestUtils(LoggedTestCase): d = utils.SafeDatetime(2012, 8, 9) self.assertEqual(utils.strftime(d, '%-d/%-m/%y'), '9/8/12') + d = utils.SafeDatetime(2021, 1, 8) + self.assertEqual(utils.strftime(d, '%G - %-V - %u'), '2021 - 1 - 5') + # test the output of utils.strftime in a different locale # Turkish locale @unittest.skipUnless(locale_available('tr_TR.UTF-8') or diff --git a/pelican/utils.py b/pelican/utils.py index 8c3a886d..b69cbf95 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -50,7 +50,8 @@ def strftime(date, date_format): ''' def strip_zeros(x): return x.lstrip('0') or '0' - c89_directives = 'aAbBcdfHIjmMpSUwWxXyYzZ%' + # includes ISO date parameters added by Python 3.6 + c89_directives = 'aAbBcdfGHIjmMpSUuVwWxXyYzZ%' # grab candidate format options format_options = '%[-]?.' From 438938819e9c64a541e81df1d75ddfa144f92b52 Mon Sep 17 00:00:00 2001 From: Gio Date: Sun, 1 Aug 2021 17:06:34 -0500 Subject: [PATCH 133/465] Pickle.dump can raise TypeError (e.g. generators) --- pelican/cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/cache.py b/pelican/cache.py index 646f97bc..c1d4a55d 100644 --- a/pelican/cache.py +++ b/pelican/cache.py @@ -65,7 +65,7 @@ class FileDataCacher: mkdir_p(self.settings['CACHE_PATH']) with self._cache_open(self._cache_path, 'wb') as fhandle: pickle.dump(self._cache, fhandle) - except (OSError, pickle.PicklingError) as err: + except (OSError, pickle.PicklingError, TypeError) as err: logger.warning('Could not save cache %s\n ... %s', self._cache_path, err) From 991cebf95dc49098f0c9ccc1d21298fac7663eae Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Tue, 17 Aug 2021 16:26:32 +1000 Subject: [PATCH 134/465] Update notmyidea theme to scale down to smaller screens The aim here is to make the theme work respectably on mobile devices with only modest changes. Providing different layouts at multiple breakpoints is beyond the scope of this change. The changes here are: 1. `base.html`: Add a ` .hentry {border: 0; margin: 0;} position: relative; float: left; top: 0.5em; - width: 190px; + max-width: 190px; } /* About the Author */ @@ -453,12 +459,12 @@ li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} #comments-list li:nth-child(2n) blockquote {background: #F5f5f5;} /* Add a Comment */ - #add-comment label {clear: left; float: left; text-align: left; width: 150px;} + #add-comment label {clear: left; float: left; text-align: left; max-width: 150px;} #add-comment input[type='text'], #add-comment input[type='email'], - #add-comment input[type='url'] {float: left; width: 200px;} + #add-comment input[type='url'] {float: left; max-width: 200px;} - #add-comment textarea {float: left; height: 150px; width: 495px;} + #add-comment textarea {float: left; height: 150px; max-width: 495px;} #add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;} diff --git a/pelican/themes/notmyidea/templates/base.html b/pelican/themes/notmyidea/templates/base.html index 6be5f1d8..2b302899 100644 --- a/pelican/themes/notmyidea/templates/base.html +++ b/pelican/themes/notmyidea/templates/base.html @@ -3,6 +3,7 @@ {% block head %} + {% block title %}{{ SITENAME }}{%endblock%} From 62a878ded3606801b3a86961281fa5cd2a97beca Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Tue, 17 Aug 2021 21:56:18 +1000 Subject: [PATCH 135/465] Update tests to match theme changes --- .../article_with_nonconformant_meta_tags.html | 1 + .../basic/a-markdown-powered-article.html | 1 + pelican/tests/output/basic/archives.html | 1 + pelican/tests/output/basic/article-1.html | 1 + pelican/tests/output/basic/article-2.html | 1 + pelican/tests/output/basic/article-3.html | 1 + .../output/basic/author/alexis-metaireau.html | 1 + pelican/tests/output/basic/authors.html | 1 + pelican/tests/output/basic/categories.html | 1 + pelican/tests/output/basic/category/bar.html | 1 + pelican/tests/output/basic/category/cat1.html | 1 + pelican/tests/output/basic/category/misc.html | 1 + pelican/tests/output/basic/category/yeah.html | 1 + .../drafts/a-draft-article-without-date.html | 1 + .../output/basic/drafts/a-draft-article.html | 1 + .../basic/filename_metadata-example.html | 1 + pelican/tests/output/basic/index.html | 1 + pelican/tests/output/basic/oh-yeah-fr.html | 1 + pelican/tests/output/basic/oh-yeah.html | 1 + .../tests/output/basic/override/index.html | 1 + .../pages/this-is-a-test-hidden-page.html | 1 + .../basic/pages/this-is-a-test-page.html | 1 + .../tests/output/basic/second-article-fr.html | 1 + .../tests/output/basic/second-article.html | 1 + pelican/tests/output/basic/tag/bar.html | 1 + pelican/tests/output/basic/tag/baz.html | 1 + pelican/tests/output/basic/tag/foo.html | 1 + pelican/tests/output/basic/tag/foobar.html | 1 + pelican/tests/output/basic/tag/oh.html | 1 + pelican/tests/output/basic/tag/yeah.html | 1 + pelican/tests/output/basic/tags.html | 1 + pelican/tests/output/basic/theme/css/main.css | 48 +++++++++++-------- .../output/basic/this-is-a-super-article.html | 1 + pelican/tests/output/basic/unbelievable.html | 1 + .../custom/a-markdown-powered-article.html | 1 + pelican/tests/output/custom/archives.html | 1 + pelican/tests/output/custom/article-1.html | 1 + pelican/tests/output/custom/article-2.html | 1 + pelican/tests/output/custom/article-3.html | 1 + .../custom/author/alexis-metaireau.html | 1 + .../custom/author/alexis-metaireau2.html | 1 + .../custom/author/alexis-metaireau3.html | 1 + pelican/tests/output/custom/authors.html | 1 + pelican/tests/output/custom/categories.html | 1 + pelican/tests/output/custom/category/bar.html | 1 + .../tests/output/custom/category/cat1.html | 1 + .../tests/output/custom/category/misc.html | 1 + .../tests/output/custom/category/yeah.html | 1 + .../drafts/a-draft-article-without-date.html | 1 + .../output/custom/drafts/a-draft-article.html | 1 + .../custom/filename_metadata-example.html | 1 + pelican/tests/output/custom/index.html | 1 + pelican/tests/output/custom/index2.html | 1 + pelican/tests/output/custom/index3.html | 1 + .../tests/output/custom/jinja2_template.html | 1 + pelican/tests/output/custom/oh-yeah-fr.html | 1 + pelican/tests/output/custom/oh-yeah.html | 1 + .../tests/output/custom/override/index.html | 1 + .../pages/this-is-a-test-hidden-page.html | 1 + .../custom/pages/this-is-a-test-page.html | 1 + .../output/custom/second-article-fr.html | 1 + .../tests/output/custom/second-article.html | 1 + pelican/tests/output/custom/tag/bar.html | 1 + pelican/tests/output/custom/tag/baz.html | 1 + pelican/tests/output/custom/tag/foo.html | 1 + pelican/tests/output/custom/tag/foobar.html | 1 + pelican/tests/output/custom/tag/oh.html | 1 + pelican/tests/output/custom/tag/yeah.html | 1 + pelican/tests/output/custom/tags.html | 1 + .../tests/output/custom/theme/css/main.css | 48 +++++++++++-------- .../custom/this-is-a-super-article.html | 1 + pelican/tests/output/custom/unbelievable.html | 1 + .../tests/output/custom_locale/archives.html | 1 + .../author/alexis-metaireau.html | 1 + .../author/alexis-metaireau2.html | 1 + .../author/alexis-metaireau3.html | 1 + .../tests/output/custom_locale/authors.html | 1 + .../output/custom_locale/categories.html | 1 + .../output/custom_locale/category/bar.html | 1 + .../output/custom_locale/category/cat1.html | 1 + .../output/custom_locale/category/misc.html | 1 + .../output/custom_locale/category/yeah.html | 1 + .../output/custom_locale/theme/css/main.css | 48 +++++++++++-------- 83 files changed, 161 insertions(+), 63 deletions(-) diff --git a/pelican/tests/content/article_with_nonconformant_meta_tags.html b/pelican/tests/content/article_with_nonconformant_meta_tags.html index 5ed44bbd..b8ef1723 100644 --- a/pelican/tests/content/article_with_nonconformant_meta_tags.html +++ b/pelican/tests/content/article_with_nonconformant_meta_tags.html @@ -1,6 +1,7 @@ + Article with Nonconformant HTML meta tags diff --git a/pelican/tests/output/basic/a-markdown-powered-article.html b/pelican/tests/output/basic/a-markdown-powered-article.html index 894fb8c9..ca9b62eb 100644 --- a/pelican/tests/output/basic/a-markdown-powered-article.html +++ b/pelican/tests/output/basic/a-markdown-powered-article.html @@ -2,6 +2,7 @@ + A markdown powered article diff --git a/pelican/tests/output/basic/archives.html b/pelican/tests/output/basic/archives.html index 27cbb73e..93c1d5be 100644 --- a/pelican/tests/output/basic/archives.html +++ b/pelican/tests/output/basic/archives.html @@ -2,6 +2,7 @@ + A Pelican Blog diff --git a/pelican/tests/output/basic/article-1.html b/pelican/tests/output/basic/article-1.html index 5e1196b7..4dee2eb6 100644 --- a/pelican/tests/output/basic/article-1.html +++ b/pelican/tests/output/basic/article-1.html @@ -2,6 +2,7 @@ + Article 1 diff --git a/pelican/tests/output/basic/article-2.html b/pelican/tests/output/basic/article-2.html index 7dab6907..7e5995c0 100644 --- a/pelican/tests/output/basic/article-2.html +++ b/pelican/tests/output/basic/article-2.html @@ -2,6 +2,7 @@ + Article 2 diff --git a/pelican/tests/output/basic/article-3.html b/pelican/tests/output/basic/article-3.html index ba934652..b0b8ed1b 100644 --- a/pelican/tests/output/basic/article-3.html +++ b/pelican/tests/output/basic/article-3.html @@ -2,6 +2,7 @@ + Article 3 diff --git a/pelican/tests/output/basic/author/alexis-metaireau.html b/pelican/tests/output/basic/author/alexis-metaireau.html index a4d7a279..87eaa461 100644 --- a/pelican/tests/output/basic/author/alexis-metaireau.html +++ b/pelican/tests/output/basic/author/alexis-metaireau.html @@ -2,6 +2,7 @@ + A Pelican Blog - Alexis Métaireau diff --git a/pelican/tests/output/basic/authors.html b/pelican/tests/output/basic/authors.html index f062ccf6..937a3085 100644 --- a/pelican/tests/output/basic/authors.html +++ b/pelican/tests/output/basic/authors.html @@ -2,6 +2,7 @@ + A Pelican Blog - Authors diff --git a/pelican/tests/output/basic/categories.html b/pelican/tests/output/basic/categories.html index e9d981c8..930503a8 100644 --- a/pelican/tests/output/basic/categories.html +++ b/pelican/tests/output/basic/categories.html @@ -2,6 +2,7 @@ + A Pelican Blog - Categories diff --git a/pelican/tests/output/basic/category/bar.html b/pelican/tests/output/basic/category/bar.html index 7e272b78..65b7b04e 100644 --- a/pelican/tests/output/basic/category/bar.html +++ b/pelican/tests/output/basic/category/bar.html @@ -2,6 +2,7 @@ + A Pelican Blog - bar diff --git a/pelican/tests/output/basic/category/cat1.html b/pelican/tests/output/basic/category/cat1.html index eccf5587..62fb034c 100644 --- a/pelican/tests/output/basic/category/cat1.html +++ b/pelican/tests/output/basic/category/cat1.html @@ -2,6 +2,7 @@ + A Pelican Blog - cat1 diff --git a/pelican/tests/output/basic/category/misc.html b/pelican/tests/output/basic/category/misc.html index 119f5931..c7e86f19 100644 --- a/pelican/tests/output/basic/category/misc.html +++ b/pelican/tests/output/basic/category/misc.html @@ -2,6 +2,7 @@ + A Pelican Blog - misc diff --git a/pelican/tests/output/basic/category/yeah.html b/pelican/tests/output/basic/category/yeah.html index 31a95629..9d2f30a2 100644 --- a/pelican/tests/output/basic/category/yeah.html +++ b/pelican/tests/output/basic/category/yeah.html @@ -2,6 +2,7 @@ + A Pelican Blog - yeah diff --git a/pelican/tests/output/basic/drafts/a-draft-article-without-date.html b/pelican/tests/output/basic/drafts/a-draft-article-without-date.html index 039f2187..bc30dabd 100644 --- a/pelican/tests/output/basic/drafts/a-draft-article-without-date.html +++ b/pelican/tests/output/basic/drafts/a-draft-article-without-date.html @@ -2,6 +2,7 @@ + A draft article without date diff --git a/pelican/tests/output/basic/drafts/a-draft-article.html b/pelican/tests/output/basic/drafts/a-draft-article.html index 068b414a..4abd4946 100644 --- a/pelican/tests/output/basic/drafts/a-draft-article.html +++ b/pelican/tests/output/basic/drafts/a-draft-article.html @@ -2,6 +2,7 @@ + A draft article diff --git a/pelican/tests/output/basic/filename_metadata-example.html b/pelican/tests/output/basic/filename_metadata-example.html index 6a6ee389..ae19ceb1 100644 --- a/pelican/tests/output/basic/filename_metadata-example.html +++ b/pelican/tests/output/basic/filename_metadata-example.html @@ -2,6 +2,7 @@ + FILENAME_METADATA example diff --git a/pelican/tests/output/basic/index.html b/pelican/tests/output/basic/index.html index e10477db..d67b9826 100644 --- a/pelican/tests/output/basic/index.html +++ b/pelican/tests/output/basic/index.html @@ -2,6 +2,7 @@ + A Pelican Blog diff --git a/pelican/tests/output/basic/oh-yeah-fr.html b/pelican/tests/output/basic/oh-yeah-fr.html index 58b933aa..183754ff 100644 --- a/pelican/tests/output/basic/oh-yeah-fr.html +++ b/pelican/tests/output/basic/oh-yeah-fr.html @@ -2,6 +2,7 @@ + Trop bien ! diff --git a/pelican/tests/output/basic/oh-yeah.html b/pelican/tests/output/basic/oh-yeah.html index 1b8077f8..f63302d2 100644 --- a/pelican/tests/output/basic/oh-yeah.html +++ b/pelican/tests/output/basic/oh-yeah.html @@ -2,6 +2,7 @@ + Oh yeah ! diff --git a/pelican/tests/output/basic/override/index.html b/pelican/tests/output/basic/override/index.html index c3613174..1d5531d7 100644 --- a/pelican/tests/output/basic/override/index.html +++ b/pelican/tests/output/basic/override/index.html @@ -2,6 +2,7 @@ + Override url/save_as diff --git a/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html index 21fabf62..8c14e354 100644 --- a/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html @@ -2,6 +2,7 @@ + This is a test hidden page diff --git a/pelican/tests/output/basic/pages/this-is-a-test-page.html b/pelican/tests/output/basic/pages/this-is-a-test-page.html index 3970e3ff..1fdb846f 100644 --- a/pelican/tests/output/basic/pages/this-is-a-test-page.html +++ b/pelican/tests/output/basic/pages/this-is-a-test-page.html @@ -2,6 +2,7 @@ + This is a test page diff --git a/pelican/tests/output/basic/second-article-fr.html b/pelican/tests/output/basic/second-article-fr.html index 87b95cc4..b49c4517 100644 --- a/pelican/tests/output/basic/second-article-fr.html +++ b/pelican/tests/output/basic/second-article-fr.html @@ -2,6 +2,7 @@ + Deuxième article diff --git a/pelican/tests/output/basic/second-article.html b/pelican/tests/output/basic/second-article.html index 3e0c5296..2b3bca88 100644 --- a/pelican/tests/output/basic/second-article.html +++ b/pelican/tests/output/basic/second-article.html @@ -2,6 +2,7 @@ + Second article diff --git a/pelican/tests/output/basic/tag/bar.html b/pelican/tests/output/basic/tag/bar.html index ead02cd8..a78c08f9 100644 --- a/pelican/tests/output/basic/tag/bar.html +++ b/pelican/tests/output/basic/tag/bar.html @@ -2,6 +2,7 @@ + A Pelican Blog - bar diff --git a/pelican/tests/output/basic/tag/baz.html b/pelican/tests/output/basic/tag/baz.html index c891121e..0b7d04ab 100644 --- a/pelican/tests/output/basic/tag/baz.html +++ b/pelican/tests/output/basic/tag/baz.html @@ -2,6 +2,7 @@ + The baz tag diff --git a/pelican/tests/output/basic/tag/foo.html b/pelican/tests/output/basic/tag/foo.html index 3a00a6ab..6ec55bee 100644 --- a/pelican/tests/output/basic/tag/foo.html +++ b/pelican/tests/output/basic/tag/foo.html @@ -2,6 +2,7 @@ + A Pelican Blog - foo diff --git a/pelican/tests/output/basic/tag/foobar.html b/pelican/tests/output/basic/tag/foobar.html index 5418f872..def1d0c6 100644 --- a/pelican/tests/output/basic/tag/foobar.html +++ b/pelican/tests/output/basic/tag/foobar.html @@ -2,6 +2,7 @@ + A Pelican Blog - foobar diff --git a/pelican/tests/output/basic/tag/oh.html b/pelican/tests/output/basic/tag/oh.html index c2de8b5b..11b06e61 100644 --- a/pelican/tests/output/basic/tag/oh.html +++ b/pelican/tests/output/basic/tag/oh.html @@ -2,6 +2,7 @@ + Oh Oh Oh diff --git a/pelican/tests/output/basic/tag/yeah.html b/pelican/tests/output/basic/tag/yeah.html index 88660359..565a3a25 100644 --- a/pelican/tests/output/basic/tag/yeah.html +++ b/pelican/tests/output/basic/tag/yeah.html @@ -2,6 +2,7 @@ + A Pelican Blog - yeah diff --git a/pelican/tests/output/basic/tags.html b/pelican/tests/output/basic/tags.html index 3f88ce9e..6dce2632 100644 --- a/pelican/tests/output/basic/tags.html +++ b/pelican/tests/output/basic/tags.html @@ -2,6 +2,7 @@ + A Pelican Blog - Tags diff --git a/pelican/tests/output/basic/theme/css/main.css b/pelican/tests/output/basic/theme/css/main.css index aaea9b81..a4aa51a1 100644 --- a/pelican/tests/output/basic/theme/css/main.css +++ b/pelican/tests/output/basic/theme/css/main.css @@ -147,7 +147,8 @@ aside, nav, article, figure { } /***** Layout *****/ -.body {clear: both; margin: 0 auto; width: 800px;} +.body {clear: both; margin: 0 auto; max-width: 800px;} +img { max-width: 100%; } img.right, figure.right, div.figure.align-right { float: right; margin: 0 0 2em 2em; @@ -159,7 +160,7 @@ img.left, figure.left, div.figure.align-left { /* .rst support */ div.figure img, figure img { /* to fill figure exactly */ - width: 100%; + max-width: 100%; } div.figure p.caption, figure p.caption { /* margin provided by figure */ margin-top: 0; @@ -171,16 +172,21 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ *****************/ #banner { margin: 0 auto; - padding: 2.5em 0 0 0; + padding: 0.8em 0 0 0; } /* Banner */ - #banner h1 {font-size: 3.571em; line-height: 0;} + #banner h1 { + font-size: 3.571em; + line-height: 1.0; + margin-bottom: .3em; + } + #banner h1 a:link, #banner h1 a:visited { color: #000305; display: block; font-weight: bold; - margin: 0 0 .6em .2em; + margin: 0 0 0 .2em; text-decoration: none; } #banner h1 a:hover, #banner h1 a:active { @@ -195,19 +201,19 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ #banner nav { background: #000305; font-size: 1.143em; - height: 40px; + overflow: auto; line-height: 30px; margin: 0 auto 2em auto; padding: 0; text-align: center; - width: 800px; + max-width: 800px; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; } - #banner nav ul {list-style: none; margin: 0 auto; width: 800px;} + #banner nav ul {list-style: none; margin: 0 auto; max-width: 800px;} #banner nav li {float: left; display: inline; margin: 0;} #banner nav a:link, #banner nav a:visited { @@ -242,7 +248,7 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ margin-bottom: 2em; overflow: hidden; padding: 20px; - width: 760px; + max-width: 760px; border-radius: 10px; -moz-border-radius: 10px; @@ -253,7 +259,7 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ border: 2px solid #eee; float: right; margin: 0.786em 2em 0 5em; - width: 248px; + max-width: 248px; } #featured figure img {display: block; float: right;} @@ -271,7 +277,7 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ margin-bottom: 2em; overflow: hidden; padding: 20px 20px; - width: 760px; + max-width: 760px; border-radius: 10px; -moz-border-radius: 10px; @@ -305,15 +311,15 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ /* Blogroll */ #extras .blogroll { float: left; - width: 615px; + max-width: 615px; } - #extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;} + #extras .blogroll li {float: left; margin: 0 20px 0 0; max-width: 185px;} /* Social */ #extras .social { float: right; - width: 175px; + max-width: 175px; } #extras div[class='social'] a { @@ -359,20 +365,20 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ overflow: hidden; padding: 20px; text-align: left; - width: 760px; + max-width: 760px; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; } -#about .primary {float: left; width: 165px;} +#about .primary {float: left; max-width: 165px;} #about .primary strong {color: #C64350; display: block; font-size: 1.286em;} #about .photo {float: left; margin: 5px 20px;} #about .url:link, #about .url:visited {text-decoration: none;} -#about .bio {float: right; width: 500px;} +#about .bio {float: right; max-width: 500px;} /* Footer @@ -410,7 +416,7 @@ li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} position: relative; float: left; top: 0.5em; - width: 190px; + max-width: 190px; } /* About the Author */ @@ -453,12 +459,12 @@ li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} #comments-list li:nth-child(2n) blockquote {background: #F5f5f5;} /* Add a Comment */ - #add-comment label {clear: left; float: left; text-align: left; width: 150px;} + #add-comment label {clear: left; float: left; text-align: left; max-width: 150px;} #add-comment input[type='text'], #add-comment input[type='email'], - #add-comment input[type='url'] {float: left; width: 200px;} + #add-comment input[type='url'] {float: left; max-width: 200px;} - #add-comment textarea {float: left; height: 150px; width: 495px;} + #add-comment textarea {float: left; height: 150px; max-width: 495px;} #add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;} diff --git a/pelican/tests/output/basic/this-is-a-super-article.html b/pelican/tests/output/basic/this-is-a-super-article.html index bd1472e4..8681bb1b 100644 --- a/pelican/tests/output/basic/this-is-a-super-article.html +++ b/pelican/tests/output/basic/this-is-a-super-article.html @@ -2,6 +2,7 @@ + This is a super article ! diff --git a/pelican/tests/output/basic/unbelievable.html b/pelican/tests/output/basic/unbelievable.html index 33e0ad53..d623eced 100644 --- a/pelican/tests/output/basic/unbelievable.html +++ b/pelican/tests/output/basic/unbelievable.html @@ -2,6 +2,7 @@ + Unbelievable ! diff --git a/pelican/tests/output/custom/a-markdown-powered-article.html b/pelican/tests/output/custom/a-markdown-powered-article.html index 00aa18e8..36fb242f 100644 --- a/pelican/tests/output/custom/a-markdown-powered-article.html +++ b/pelican/tests/output/custom/a-markdown-powered-article.html @@ -2,6 +2,7 @@ + A markdown powered article diff --git a/pelican/tests/output/custom/archives.html b/pelican/tests/output/custom/archives.html index 1e9a8377..e1dad53d 100644 --- a/pelican/tests/output/custom/archives.html +++ b/pelican/tests/output/custom/archives.html @@ -2,6 +2,7 @@ + Alexis' log diff --git a/pelican/tests/output/custom/article-1.html b/pelican/tests/output/custom/article-1.html index cc15ba5c..6f3e0cc1 100644 --- a/pelican/tests/output/custom/article-1.html +++ b/pelican/tests/output/custom/article-1.html @@ -2,6 +2,7 @@ + Article 1 diff --git a/pelican/tests/output/custom/article-2.html b/pelican/tests/output/custom/article-2.html index cc331a4d..094471de 100644 --- a/pelican/tests/output/custom/article-2.html +++ b/pelican/tests/output/custom/article-2.html @@ -2,6 +2,7 @@ + Article 2 diff --git a/pelican/tests/output/custom/article-3.html b/pelican/tests/output/custom/article-3.html index 8b180180..3d93d807 100644 --- a/pelican/tests/output/custom/article-3.html +++ b/pelican/tests/output/custom/article-3.html @@ -2,6 +2,7 @@ + Article 3 diff --git a/pelican/tests/output/custom/author/alexis-metaireau.html b/pelican/tests/output/custom/author/alexis-metaireau.html index f768b15e..95653b66 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau.html +++ b/pelican/tests/output/custom/author/alexis-metaireau.html @@ -2,6 +2,7 @@ + Alexis' log - Alexis Métaireau diff --git a/pelican/tests/output/custom/author/alexis-metaireau2.html b/pelican/tests/output/custom/author/alexis-metaireau2.html index 96b34bff..281c7ad8 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom/author/alexis-metaireau2.html @@ -2,6 +2,7 @@ + Alexis' log - Alexis Métaireau diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index 407c1a51..b5b86c42 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -2,6 +2,7 @@ + Alexis' log - Alexis Métaireau diff --git a/pelican/tests/output/custom/authors.html b/pelican/tests/output/custom/authors.html index 6374c4a4..303e79c4 100644 --- a/pelican/tests/output/custom/authors.html +++ b/pelican/tests/output/custom/authors.html @@ -2,6 +2,7 @@ + Alexis' log - Authors diff --git a/pelican/tests/output/custom/categories.html b/pelican/tests/output/custom/categories.html index 393aef32..543eb1ee 100644 --- a/pelican/tests/output/custom/categories.html +++ b/pelican/tests/output/custom/categories.html @@ -2,6 +2,7 @@ + Alexis' log - Categories diff --git a/pelican/tests/output/custom/category/bar.html b/pelican/tests/output/custom/category/bar.html index 4511bcb6..bd277a83 100644 --- a/pelican/tests/output/custom/category/bar.html +++ b/pelican/tests/output/custom/category/bar.html @@ -2,6 +2,7 @@ + Alexis' log - bar diff --git a/pelican/tests/output/custom/category/cat1.html b/pelican/tests/output/custom/category/cat1.html index dc5c0579..73df4388 100644 --- a/pelican/tests/output/custom/category/cat1.html +++ b/pelican/tests/output/custom/category/cat1.html @@ -2,6 +2,7 @@ + Alexis' log - cat1 diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html index 053599ed..dda4c75f 100644 --- a/pelican/tests/output/custom/category/misc.html +++ b/pelican/tests/output/custom/category/misc.html @@ -2,6 +2,7 @@ + Alexis' log - misc diff --git a/pelican/tests/output/custom/category/yeah.html b/pelican/tests/output/custom/category/yeah.html index 17e8103e..8bfc1f48 100644 --- a/pelican/tests/output/custom/category/yeah.html +++ b/pelican/tests/output/custom/category/yeah.html @@ -2,6 +2,7 @@ + Alexis' log - yeah diff --git a/pelican/tests/output/custom/drafts/a-draft-article-without-date.html b/pelican/tests/output/custom/drafts/a-draft-article-without-date.html index 03aecc56..12ddfbeb 100644 --- a/pelican/tests/output/custom/drafts/a-draft-article-without-date.html +++ b/pelican/tests/output/custom/drafts/a-draft-article-without-date.html @@ -2,6 +2,7 @@ + A draft article without date diff --git a/pelican/tests/output/custom/drafts/a-draft-article.html b/pelican/tests/output/custom/drafts/a-draft-article.html index 87be8bec..77b12cf3 100644 --- a/pelican/tests/output/custom/drafts/a-draft-article.html +++ b/pelican/tests/output/custom/drafts/a-draft-article.html @@ -2,6 +2,7 @@ + A draft article diff --git a/pelican/tests/output/custom/filename_metadata-example.html b/pelican/tests/output/custom/filename_metadata-example.html index 2fcbe562..2c5e8178 100644 --- a/pelican/tests/output/custom/filename_metadata-example.html +++ b/pelican/tests/output/custom/filename_metadata-example.html @@ -2,6 +2,7 @@ + FILENAME_METADATA example diff --git a/pelican/tests/output/custom/index.html b/pelican/tests/output/custom/index.html index ff675f85..076cf519 100644 --- a/pelican/tests/output/custom/index.html +++ b/pelican/tests/output/custom/index.html @@ -2,6 +2,7 @@ + Alexis' log diff --git a/pelican/tests/output/custom/index2.html b/pelican/tests/output/custom/index2.html index 941c176e..13a857a8 100644 --- a/pelican/tests/output/custom/index2.html +++ b/pelican/tests/output/custom/index2.html @@ -2,6 +2,7 @@ + Alexis' log diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index de32dbe3..147f2ee2 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -2,6 +2,7 @@ + Alexis' log diff --git a/pelican/tests/output/custom/jinja2_template.html b/pelican/tests/output/custom/jinja2_template.html index 1ba06323..cefce74f 100644 --- a/pelican/tests/output/custom/jinja2_template.html +++ b/pelican/tests/output/custom/jinja2_template.html @@ -2,6 +2,7 @@ + Alexis' log diff --git a/pelican/tests/output/custom/oh-yeah-fr.html b/pelican/tests/output/custom/oh-yeah-fr.html index bb7eb44e..ec75b731 100644 --- a/pelican/tests/output/custom/oh-yeah-fr.html +++ b/pelican/tests/output/custom/oh-yeah-fr.html @@ -2,6 +2,7 @@ + Trop bien ! diff --git a/pelican/tests/output/custom/oh-yeah.html b/pelican/tests/output/custom/oh-yeah.html index f7afb399..109525a5 100644 --- a/pelican/tests/output/custom/oh-yeah.html +++ b/pelican/tests/output/custom/oh-yeah.html @@ -2,6 +2,7 @@ + Oh yeah ! diff --git a/pelican/tests/output/custom/override/index.html b/pelican/tests/output/custom/override/index.html index ebf07d8d..004c38fd 100644 --- a/pelican/tests/output/custom/override/index.html +++ b/pelican/tests/output/custom/override/index.html @@ -2,6 +2,7 @@ + Override url/save_as diff --git a/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html index e5fa1aaf..652fc7d0 100644 --- a/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html @@ -2,6 +2,7 @@ + This is a test hidden page diff --git a/pelican/tests/output/custom/pages/this-is-a-test-page.html b/pelican/tests/output/custom/pages/this-is-a-test-page.html index 26c6c0e9..2bcefd3d 100644 --- a/pelican/tests/output/custom/pages/this-is-a-test-page.html +++ b/pelican/tests/output/custom/pages/this-is-a-test-page.html @@ -2,6 +2,7 @@ + This is a test page diff --git a/pelican/tests/output/custom/second-article-fr.html b/pelican/tests/output/custom/second-article-fr.html index 8e360e9c..09f43888 100644 --- a/pelican/tests/output/custom/second-article-fr.html +++ b/pelican/tests/output/custom/second-article-fr.html @@ -2,6 +2,7 @@ + Deuxième article diff --git a/pelican/tests/output/custom/second-article.html b/pelican/tests/output/custom/second-article.html index 5001f0d0..f21a5495 100644 --- a/pelican/tests/output/custom/second-article.html +++ b/pelican/tests/output/custom/second-article.html @@ -2,6 +2,7 @@ + Second article diff --git a/pelican/tests/output/custom/tag/bar.html b/pelican/tests/output/custom/tag/bar.html index b2b22714..1efa16e7 100644 --- a/pelican/tests/output/custom/tag/bar.html +++ b/pelican/tests/output/custom/tag/bar.html @@ -2,6 +2,7 @@ + Alexis' log - bar diff --git a/pelican/tests/output/custom/tag/baz.html b/pelican/tests/output/custom/tag/baz.html index ef53c34c..c524db13 100644 --- a/pelican/tests/output/custom/tag/baz.html +++ b/pelican/tests/output/custom/tag/baz.html @@ -2,6 +2,7 @@ + The baz tag diff --git a/pelican/tests/output/custom/tag/foo.html b/pelican/tests/output/custom/tag/foo.html index 7684fbc5..ca3f40f2 100644 --- a/pelican/tests/output/custom/tag/foo.html +++ b/pelican/tests/output/custom/tag/foo.html @@ -2,6 +2,7 @@ + Alexis' log - foo diff --git a/pelican/tests/output/custom/tag/foobar.html b/pelican/tests/output/custom/tag/foobar.html index 5816e395..ccca5477 100644 --- a/pelican/tests/output/custom/tag/foobar.html +++ b/pelican/tests/output/custom/tag/foobar.html @@ -2,6 +2,7 @@ + Alexis' log - foobar diff --git a/pelican/tests/output/custom/tag/oh.html b/pelican/tests/output/custom/tag/oh.html index 16ea9d0d..f3c70495 100644 --- a/pelican/tests/output/custom/tag/oh.html +++ b/pelican/tests/output/custom/tag/oh.html @@ -2,6 +2,7 @@ + Oh Oh Oh diff --git a/pelican/tests/output/custom/tag/yeah.html b/pelican/tests/output/custom/tag/yeah.html index 929a48e0..96767503 100644 --- a/pelican/tests/output/custom/tag/yeah.html +++ b/pelican/tests/output/custom/tag/yeah.html @@ -2,6 +2,7 @@ + Alexis' log - yeah diff --git a/pelican/tests/output/custom/tags.html b/pelican/tests/output/custom/tags.html index 244c21e3..b23f468f 100644 --- a/pelican/tests/output/custom/tags.html +++ b/pelican/tests/output/custom/tags.html @@ -2,6 +2,7 @@ + Alexis' log - Tags diff --git a/pelican/tests/output/custom/theme/css/main.css b/pelican/tests/output/custom/theme/css/main.css index aaea9b81..a4aa51a1 100644 --- a/pelican/tests/output/custom/theme/css/main.css +++ b/pelican/tests/output/custom/theme/css/main.css @@ -147,7 +147,8 @@ aside, nav, article, figure { } /***** Layout *****/ -.body {clear: both; margin: 0 auto; width: 800px;} +.body {clear: both; margin: 0 auto; max-width: 800px;} +img { max-width: 100%; } img.right, figure.right, div.figure.align-right { float: right; margin: 0 0 2em 2em; @@ -159,7 +160,7 @@ img.left, figure.left, div.figure.align-left { /* .rst support */ div.figure img, figure img { /* to fill figure exactly */ - width: 100%; + max-width: 100%; } div.figure p.caption, figure p.caption { /* margin provided by figure */ margin-top: 0; @@ -171,16 +172,21 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ *****************/ #banner { margin: 0 auto; - padding: 2.5em 0 0 0; + padding: 0.8em 0 0 0; } /* Banner */ - #banner h1 {font-size: 3.571em; line-height: 0;} + #banner h1 { + font-size: 3.571em; + line-height: 1.0; + margin-bottom: .3em; + } + #banner h1 a:link, #banner h1 a:visited { color: #000305; display: block; font-weight: bold; - margin: 0 0 .6em .2em; + margin: 0 0 0 .2em; text-decoration: none; } #banner h1 a:hover, #banner h1 a:active { @@ -195,19 +201,19 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ #banner nav { background: #000305; font-size: 1.143em; - height: 40px; + overflow: auto; line-height: 30px; margin: 0 auto 2em auto; padding: 0; text-align: center; - width: 800px; + max-width: 800px; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; } - #banner nav ul {list-style: none; margin: 0 auto; width: 800px;} + #banner nav ul {list-style: none; margin: 0 auto; max-width: 800px;} #banner nav li {float: left; display: inline; margin: 0;} #banner nav a:link, #banner nav a:visited { @@ -242,7 +248,7 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ margin-bottom: 2em; overflow: hidden; padding: 20px; - width: 760px; + max-width: 760px; border-radius: 10px; -moz-border-radius: 10px; @@ -253,7 +259,7 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ border: 2px solid #eee; float: right; margin: 0.786em 2em 0 5em; - width: 248px; + max-width: 248px; } #featured figure img {display: block; float: right;} @@ -271,7 +277,7 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ margin-bottom: 2em; overflow: hidden; padding: 20px 20px; - width: 760px; + max-width: 760px; border-radius: 10px; -moz-border-radius: 10px; @@ -305,15 +311,15 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ /* Blogroll */ #extras .blogroll { float: left; - width: 615px; + max-width: 615px; } - #extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;} + #extras .blogroll li {float: left; margin: 0 20px 0 0; max-width: 185px;} /* Social */ #extras .social { float: right; - width: 175px; + max-width: 175px; } #extras div[class='social'] a { @@ -359,20 +365,20 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ overflow: hidden; padding: 20px; text-align: left; - width: 760px; + max-width: 760px; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; } -#about .primary {float: left; width: 165px;} +#about .primary {float: left; max-width: 165px;} #about .primary strong {color: #C64350; display: block; font-size: 1.286em;} #about .photo {float: left; margin: 5px 20px;} #about .url:link, #about .url:visited {text-decoration: none;} -#about .bio {float: right; width: 500px;} +#about .bio {float: right; max-width: 500px;} /* Footer @@ -410,7 +416,7 @@ li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} position: relative; float: left; top: 0.5em; - width: 190px; + max-width: 190px; } /* About the Author */ @@ -453,12 +459,12 @@ li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} #comments-list li:nth-child(2n) blockquote {background: #F5f5f5;} /* Add a Comment */ - #add-comment label {clear: left; float: left; text-align: left; width: 150px;} + #add-comment label {clear: left; float: left; text-align: left; max-width: 150px;} #add-comment input[type='text'], #add-comment input[type='email'], - #add-comment input[type='url'] {float: left; width: 200px;} + #add-comment input[type='url'] {float: left; max-width: 200px;} - #add-comment textarea {float: left; height: 150px; width: 495px;} + #add-comment textarea {float: left; height: 150px; max-width: 495px;} #add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;} diff --git a/pelican/tests/output/custom/this-is-a-super-article.html b/pelican/tests/output/custom/this-is-a-super-article.html index 55053cb2..eb31a851 100644 --- a/pelican/tests/output/custom/this-is-a-super-article.html +++ b/pelican/tests/output/custom/this-is-a-super-article.html @@ -2,6 +2,7 @@ + This is a super article ! diff --git a/pelican/tests/output/custom/unbelievable.html b/pelican/tests/output/custom/unbelievable.html index a103868e..3117dc28 100644 --- a/pelican/tests/output/custom/unbelievable.html +++ b/pelican/tests/output/custom/unbelievable.html @@ -2,6 +2,7 @@ + Unbelievable ! diff --git a/pelican/tests/output/custom_locale/archives.html b/pelican/tests/output/custom_locale/archives.html index 66dbe94a..df9dd241 100644 --- a/pelican/tests/output/custom_locale/archives.html +++ b/pelican/tests/output/custom_locale/archives.html @@ -2,6 +2,7 @@ + Alexis' log diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau.html b/pelican/tests/output/custom_locale/author/alexis-metaireau.html index 68054d97..6650fb9f 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau.html @@ -2,6 +2,7 @@ + Alexis' log - Alexis Métaireau diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html index 16431fb9..5fef108c 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html @@ -2,6 +2,7 @@ + Alexis' log - Alexis Métaireau diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html index ab91c56a..557676d4 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html @@ -2,6 +2,7 @@ + Alexis' log - Alexis Métaireau diff --git a/pelican/tests/output/custom_locale/authors.html b/pelican/tests/output/custom_locale/authors.html index 84bd502e..aed3bd79 100644 --- a/pelican/tests/output/custom_locale/authors.html +++ b/pelican/tests/output/custom_locale/authors.html @@ -2,6 +2,7 @@ + Alexis' log - Authors diff --git a/pelican/tests/output/custom_locale/categories.html b/pelican/tests/output/custom_locale/categories.html index 24e83abf..0d46b9a0 100644 --- a/pelican/tests/output/custom_locale/categories.html +++ b/pelican/tests/output/custom_locale/categories.html @@ -2,6 +2,7 @@ + Alexis' log - Categories diff --git a/pelican/tests/output/custom_locale/category/bar.html b/pelican/tests/output/custom_locale/category/bar.html index f9d47948..7d9abf0d 100644 --- a/pelican/tests/output/custom_locale/category/bar.html +++ b/pelican/tests/output/custom_locale/category/bar.html @@ -2,6 +2,7 @@ + Alexis' log - bar diff --git a/pelican/tests/output/custom_locale/category/cat1.html b/pelican/tests/output/custom_locale/category/cat1.html index 2bc756e6..f8b315ee 100644 --- a/pelican/tests/output/custom_locale/category/cat1.html +++ b/pelican/tests/output/custom_locale/category/cat1.html @@ -2,6 +2,7 @@ + Alexis' log - cat1 diff --git a/pelican/tests/output/custom_locale/category/misc.html b/pelican/tests/output/custom_locale/category/misc.html index eb98db2a..54e3f784 100644 --- a/pelican/tests/output/custom_locale/category/misc.html +++ b/pelican/tests/output/custom_locale/category/misc.html @@ -2,6 +2,7 @@ + Alexis' log - misc diff --git a/pelican/tests/output/custom_locale/category/yeah.html b/pelican/tests/output/custom_locale/category/yeah.html index 660c593c..1fd346ff 100644 --- a/pelican/tests/output/custom_locale/category/yeah.html +++ b/pelican/tests/output/custom_locale/category/yeah.html @@ -2,6 +2,7 @@ + Alexis' log - yeah diff --git a/pelican/tests/output/custom_locale/theme/css/main.css b/pelican/tests/output/custom_locale/theme/css/main.css index aaea9b81..a4aa51a1 100644 --- a/pelican/tests/output/custom_locale/theme/css/main.css +++ b/pelican/tests/output/custom_locale/theme/css/main.css @@ -147,7 +147,8 @@ aside, nav, article, figure { } /***** Layout *****/ -.body {clear: both; margin: 0 auto; width: 800px;} +.body {clear: both; margin: 0 auto; max-width: 800px;} +img { max-width: 100%; } img.right, figure.right, div.figure.align-right { float: right; margin: 0 0 2em 2em; @@ -159,7 +160,7 @@ img.left, figure.left, div.figure.align-left { /* .rst support */ div.figure img, figure img { /* to fill figure exactly */ - width: 100%; + max-width: 100%; } div.figure p.caption, figure p.caption { /* margin provided by figure */ margin-top: 0; @@ -171,16 +172,21 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ *****************/ #banner { margin: 0 auto; - padding: 2.5em 0 0 0; + padding: 0.8em 0 0 0; } /* Banner */ - #banner h1 {font-size: 3.571em; line-height: 0;} + #banner h1 { + font-size: 3.571em; + line-height: 1.0; + margin-bottom: .3em; + } + #banner h1 a:link, #banner h1 a:visited { color: #000305; display: block; font-weight: bold; - margin: 0 0 .6em .2em; + margin: 0 0 0 .2em; text-decoration: none; } #banner h1 a:hover, #banner h1 a:active { @@ -195,19 +201,19 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ #banner nav { background: #000305; font-size: 1.143em; - height: 40px; + overflow: auto; line-height: 30px; margin: 0 auto 2em auto; padding: 0; text-align: center; - width: 800px; + max-width: 800px; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; } - #banner nav ul {list-style: none; margin: 0 auto; width: 800px;} + #banner nav ul {list-style: none; margin: 0 auto; max-width: 800px;} #banner nav li {float: left; display: inline; margin: 0;} #banner nav a:link, #banner nav a:visited { @@ -242,7 +248,7 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ margin-bottom: 2em; overflow: hidden; padding: 20px; - width: 760px; + max-width: 760px; border-radius: 10px; -moz-border-radius: 10px; @@ -253,7 +259,7 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ border: 2px solid #eee; float: right; margin: 0.786em 2em 0 5em; - width: 248px; + max-width: 248px; } #featured figure img {display: block; float: right;} @@ -271,7 +277,7 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ margin-bottom: 2em; overflow: hidden; padding: 20px 20px; - width: 760px; + max-width: 760px; border-radius: 10px; -moz-border-radius: 10px; @@ -305,15 +311,15 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ /* Blogroll */ #extras .blogroll { float: left; - width: 615px; + max-width: 615px; } - #extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;} + #extras .blogroll li {float: left; margin: 0 20px 0 0; max-width: 185px;} /* Social */ #extras .social { float: right; - width: 175px; + max-width: 175px; } #extras div[class='social'] a { @@ -359,20 +365,20 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ overflow: hidden; padding: 20px; text-align: left; - width: 760px; + max-width: 760px; border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px; } -#about .primary {float: left; width: 165px;} +#about .primary {float: left; max-width: 165px;} #about .primary strong {color: #C64350; display: block; font-size: 1.286em;} #about .photo {float: left; margin: 5px 20px;} #about .url:link, #about .url:visited {text-decoration: none;} -#about .bio {float: right; width: 500px;} +#about .bio {float: right; max-width: 500px;} /* Footer @@ -410,7 +416,7 @@ li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} position: relative; float: left; top: 0.5em; - width: 190px; + max-width: 190px; } /* About the Author */ @@ -453,12 +459,12 @@ li:first-child .hentry, #content > .hentry {border: 0; margin: 0;} #comments-list li:nth-child(2n) blockquote {background: #F5f5f5;} /* Add a Comment */ - #add-comment label {clear: left; float: left; text-align: left; width: 150px;} + #add-comment label {clear: left; float: left; text-align: left; max-width: 150px;} #add-comment input[type='text'], #add-comment input[type='email'], - #add-comment input[type='url'] {float: left; width: 200px;} + #add-comment input[type='url'] {float: left; max-width: 200px;} - #add-comment textarea {float: left; height: 150px; width: 495px;} + #add-comment textarea {float: left; height: 150px; max-width: 495px;} #add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;} From a088f8bb9eb6c448248da159dd9264058c20b57e Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Wed, 18 Aug 2021 00:14:39 +1000 Subject: [PATCH 136/465] Update tests for other locales --- .../custom_locale/drafts/a-draft-article-without-date.html | 1 + pelican/tests/output/custom_locale/drafts/a-draft-article.html | 1 + pelican/tests/output/custom_locale/index.html | 1 + pelican/tests/output/custom_locale/index2.html | 1 + pelican/tests/output/custom_locale/index3.html | 1 + pelican/tests/output/custom_locale/jinja2_template.html | 1 + pelican/tests/output/custom_locale/oh-yeah-fr.html | 1 + pelican/tests/output/custom_locale/override/index.html | 1 + .../output/custom_locale/pages/this-is-a-test-hidden-page.html | 1 + .../tests/output/custom_locale/pages/this-is-a-test-page.html | 1 + .../posts/2010/décembre/02/this-is-a-super-article/index.html | 1 + .../custom_locale/posts/2010/octobre/15/unbelievable/index.html | 1 + .../custom_locale/posts/2010/octobre/20/oh-yeah/index.html | 1 + .../posts/2011/avril/20/a-markdown-powered-article/index.html | 1 + .../custom_locale/posts/2011/février/17/article-1/index.html | 1 + .../custom_locale/posts/2011/février/17/article-2/index.html | 1 + .../custom_locale/posts/2011/février/17/article-3/index.html | 1 + .../posts/2012/février/29/second-article/index.html | 1 + .../posts/2012/novembre/30/filename_metadata-example/index.html | 1 + pelican/tests/output/custom_locale/second-article-fr.html | 1 + pelican/tests/output/custom_locale/tag/bar.html | 1 + pelican/tests/output/custom_locale/tag/baz.html | 1 + pelican/tests/output/custom_locale/tag/foo.html | 1 + pelican/tests/output/custom_locale/tag/foobar.html | 1 + pelican/tests/output/custom_locale/tag/oh.html | 1 + pelican/tests/output/custom_locale/tag/yeah.html | 1 + pelican/tests/output/custom_locale/tags.html | 1 + 27 files changed, 27 insertions(+) diff --git a/pelican/tests/output/custom_locale/drafts/a-draft-article-without-date.html b/pelican/tests/output/custom_locale/drafts/a-draft-article-without-date.html index c289ec72..bb80bd10 100644 --- a/pelican/tests/output/custom_locale/drafts/a-draft-article-without-date.html +++ b/pelican/tests/output/custom_locale/drafts/a-draft-article-without-date.html @@ -2,6 +2,7 @@ + A draft article without date diff --git a/pelican/tests/output/custom_locale/drafts/a-draft-article.html b/pelican/tests/output/custom_locale/drafts/a-draft-article.html index a166efdf..3ce73427 100644 --- a/pelican/tests/output/custom_locale/drafts/a-draft-article.html +++ b/pelican/tests/output/custom_locale/drafts/a-draft-article.html @@ -2,6 +2,7 @@ + A draft article diff --git a/pelican/tests/output/custom_locale/index.html b/pelican/tests/output/custom_locale/index.html index 6e0d632d..e3079e41 100644 --- a/pelican/tests/output/custom_locale/index.html +++ b/pelican/tests/output/custom_locale/index.html @@ -2,6 +2,7 @@ + Alexis' log diff --git a/pelican/tests/output/custom_locale/index2.html b/pelican/tests/output/custom_locale/index2.html index 5ff21fac..8067a087 100644 --- a/pelican/tests/output/custom_locale/index2.html +++ b/pelican/tests/output/custom_locale/index2.html @@ -2,6 +2,7 @@ + Alexis' log diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html index 45ea5a89..233f7ee1 100644 --- a/pelican/tests/output/custom_locale/index3.html +++ b/pelican/tests/output/custom_locale/index3.html @@ -2,6 +2,7 @@ + Alexis' log diff --git a/pelican/tests/output/custom_locale/jinja2_template.html b/pelican/tests/output/custom_locale/jinja2_template.html index 83b6e8e0..d55744fa 100644 --- a/pelican/tests/output/custom_locale/jinja2_template.html +++ b/pelican/tests/output/custom_locale/jinja2_template.html @@ -2,6 +2,7 @@ + Alexis' log diff --git a/pelican/tests/output/custom_locale/oh-yeah-fr.html b/pelican/tests/output/custom_locale/oh-yeah-fr.html index 58859a3a..977c14e3 100644 --- a/pelican/tests/output/custom_locale/oh-yeah-fr.html +++ b/pelican/tests/output/custom_locale/oh-yeah-fr.html @@ -2,6 +2,7 @@ + Trop bien ! diff --git a/pelican/tests/output/custom_locale/override/index.html b/pelican/tests/output/custom_locale/override/index.html index 5c273ed6..fa3bcb10 100644 --- a/pelican/tests/output/custom_locale/override/index.html +++ b/pelican/tests/output/custom_locale/override/index.html @@ -2,6 +2,7 @@ + Override url/save_as diff --git a/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html index 91372822..86eb888b 100644 --- a/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html @@ -2,6 +2,7 @@ + This is a test hidden page diff --git a/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html b/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html index eb779355..7e835b50 100644 --- a/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html +++ b/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html @@ -2,6 +2,7 @@ + This is a test page diff --git a/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html index dee44f28..7b3ce9cc 100644 --- a/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html @@ -2,6 +2,7 @@ + This is a super article ! diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html index 407d457f..12806772 100644 --- a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html @@ -2,6 +2,7 @@ + Unbelievable ! diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html index 1bf74b57..0e272f86 100644 --- a/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html @@ -2,6 +2,7 @@ + Oh yeah ! diff --git a/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html index 0f35847a..ae1e27dd 100644 --- a/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html @@ -2,6 +2,7 @@ + A markdown powered article diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html index 05418036..e7d99cc7 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html @@ -2,6 +2,7 @@ + Article 1 diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html index 8cb67749..d8ba2cc6 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html @@ -2,6 +2,7 @@ + Article 2 diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html index 62423814..d2fa6936 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html @@ -2,6 +2,7 @@ + Article 3 diff --git a/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html index 615a7f30..5a718d8b 100644 --- a/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html @@ -2,6 +2,7 @@ + Second article diff --git a/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html index 2213d174..ea75a1db 100644 --- a/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html +++ b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html @@ -2,6 +2,7 @@ + FILENAME_METADATA example diff --git a/pelican/tests/output/custom_locale/second-article-fr.html b/pelican/tests/output/custom_locale/second-article-fr.html index 3c21ef75..e6989c42 100644 --- a/pelican/tests/output/custom_locale/second-article-fr.html +++ b/pelican/tests/output/custom_locale/second-article-fr.html @@ -2,6 +2,7 @@ + Deuxième article diff --git a/pelican/tests/output/custom_locale/tag/bar.html b/pelican/tests/output/custom_locale/tag/bar.html index aaa5fb0d..e6546987 100644 --- a/pelican/tests/output/custom_locale/tag/bar.html +++ b/pelican/tests/output/custom_locale/tag/bar.html @@ -2,6 +2,7 @@ + Alexis' log - bar diff --git a/pelican/tests/output/custom_locale/tag/baz.html b/pelican/tests/output/custom_locale/tag/baz.html index 9554bdd6..18976227 100644 --- a/pelican/tests/output/custom_locale/tag/baz.html +++ b/pelican/tests/output/custom_locale/tag/baz.html @@ -2,6 +2,7 @@ + The baz tag diff --git a/pelican/tests/output/custom_locale/tag/foo.html b/pelican/tests/output/custom_locale/tag/foo.html index 15523248..2c3c7622 100644 --- a/pelican/tests/output/custom_locale/tag/foo.html +++ b/pelican/tests/output/custom_locale/tag/foo.html @@ -2,6 +2,7 @@ + Alexis' log - foo diff --git a/pelican/tests/output/custom_locale/tag/foobar.html b/pelican/tests/output/custom_locale/tag/foobar.html index 1393a76e..23b21342 100644 --- a/pelican/tests/output/custom_locale/tag/foobar.html +++ b/pelican/tests/output/custom_locale/tag/foobar.html @@ -2,6 +2,7 @@ + Alexis' log - foobar diff --git a/pelican/tests/output/custom_locale/tag/oh.html b/pelican/tests/output/custom_locale/tag/oh.html index b6fe445f..096b2727 100644 --- a/pelican/tests/output/custom_locale/tag/oh.html +++ b/pelican/tests/output/custom_locale/tag/oh.html @@ -2,6 +2,7 @@ + Oh Oh Oh diff --git a/pelican/tests/output/custom_locale/tag/yeah.html b/pelican/tests/output/custom_locale/tag/yeah.html index 3953f1b7..29101c8d 100644 --- a/pelican/tests/output/custom_locale/tag/yeah.html +++ b/pelican/tests/output/custom_locale/tag/yeah.html @@ -2,6 +2,7 @@ + Alexis' log - yeah diff --git a/pelican/tests/output/custom_locale/tags.html b/pelican/tests/output/custom_locale/tags.html index cf932f97..e4683371 100644 --- a/pelican/tests/output/custom_locale/tags.html +++ b/pelican/tests/output/custom_locale/tags.html @@ -2,6 +2,7 @@ + Alexis' log - Tags From 22192c148a88b30dd942d7bad2a1ab243de59bc3 Mon Sep 17 00:00:00 2001 From: ImBearChild Date: Wed, 29 Sep 2021 14:44:47 +0800 Subject: [PATCH 137/465] Improve word count behavior when generating summary Improve _HTMLWordTruncator by using more than one unicode block in _word_regex, making word count function behave properly with CJK, Cyrillic, and more Latin characters when generating summary. --- pelican/tests/test_utils.py | 13 +++++++++++++ pelican/utils.py | 21 ++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 5fbd066d..710e14ed 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -218,6 +218,19 @@ class TestUtils(LoggedTestCase): utils.truncate_html_words('word ' * 100, 20), 'word ' * 20 + '…') + # Plain text with Unicode content. + self.assertEqual( + utils.truncate_html_words( + '我愿意这样,朋友——我独自远行,不但没有你,\ + 并且再没有别的影在黑暗里。', 12 + ), + '我愿意这样,朋友——我独自远行' + ' …') + self.assertEqual( + utils.truncate_html_words( + 'Ты мелькнула, ты предстала, Снова сердце задрожало,', 3 + ), + 'Ты мелькнула, ты' + ' …') + # Words enclosed or intervaled by HTML tags. self.assertEqual( utils.truncate_html_words('

' + 'word ' * 100 + '

', 20), diff --git a/pelican/utils.py b/pelican/utils.py index b69cbf95..5065d108 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -133,6 +133,7 @@ class memoized: (not reevaluated). """ + def __init__(self, func): self.func = func self.cache = {} @@ -408,7 +409,21 @@ def posixize_path(rel_path): class _HTMLWordTruncator(HTMLParser): - _word_regex = re.compile(r"\w[\w'-]*", re.U) + _word_regex = re.compile(r"(({SBC})({SBC}|-|')*)|{DBC}".format( + # SBC means Latin-like characters. A word contains a few characters. + # ASCII |Extended Latin | Cyrillic + SBC="[0-9a-zA-Z]|[\u00C0-\u024f]|[\u0400-\u04FF]", + # DBC means CJK-like characters. An character can stand for a word. + DBC=("([\u4E00-\u9FFF])|" # CJK Unified Ideographs + "([\u3400-\u4DBF])|" # CJK Unified Ideographs Extension A + "([\uF900-\uFAFF])|" # CJK Compatibility Ideographs + "([\U00020000-\U0002A6DF])|" # CJK Unified Ideographs Extension B + "([\U0002F800-\U0002FA1F])|" # CJK Compatibility Ideographs Supplement + "([\u3040-\u30FF])|" # Hiragana and Katakana + "([\u1100-\u11FF])|" # Hangul Jamo + "([\uAC00-\uD7FF])|" # Hangul Compatibility Jamo + "([\u3130-\u318F])" # Hangul Syllables + )), re.UNICODE) _word_prefix_regex = re.compile(r'\w', re.U) _singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area', 'hr', 'input') @@ -818,8 +833,8 @@ class FileSystemWatcher: } ) logger.warning( - 'No valid files found in content for the active readers:\n' - + '\n'.join(reader_descs)) + 'No valid files found in content for the active readers:\n' + + '\n'.join(reader_descs)) if result.get('theme') is None: logger.warning('Empty theme folder. Using `basic` theme.') From eacd6435ef4835868c9590b0cea9420e4a74c529 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 1 Oct 2021 15:04:58 +0200 Subject: [PATCH 138/465] Minor README tweaks --- README.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 4e1f1812..6d8be699 100644 --- a/README.rst +++ b/README.rst @@ -3,8 +3,8 @@ Pelican |build-status| |pypi-version| |repology| Pelican is a static site generator, written in Python_. -* Write content in reStructuredText_ or Markdown_ using your editor of choice -* Includes a simple command line tool to (re)generate site files +* Compose content in Markdown_ or reStructuredText_ using your editor of choice +* Simple command-line tool (re)generates HTML, CSS, and JS from your source content * Easy to interface with version control systems and web hooks * Completely static output is simple to host anywhere @@ -24,7 +24,7 @@ Pelican’s feature highlights include: * Fast rebuild times due to content caching and selective output writing * Extensible via a rich plugin ecosystem: `Pelican Plugins`_ -Check out `Pelican's documentation`_ for further information. +Check out the `Pelican documentation`_ for further information. How to get help, contribute, or provide feedback @@ -36,14 +36,14 @@ See our `contribution submission and feedback guidelines `_. Source code ----------- -Pelican's source code is `hosted on GitHub`_. If you feel like hacking, -take a look at `Pelican's internals`_. +Pelican’s source code is `hosted on GitHub`_. For information on how it works, +have a look at `Pelican's internals`_. -Why the name "Pelican"? +Why the name “Pelican”? ----------------------- -"Pelican" is an anagram of *calepin*, which means "notebook" in French. +“Pelican” is an anagram of *calepin*, which means “notebook” in French. .. Links @@ -54,7 +54,7 @@ Why the name "Pelican"? .. _Jinja2: https://palletsprojects.com/p/jinja/ .. _Pygments: https://pygments.org/ .. _`Pelican Plugins`: https://github.com/pelican-plugins -.. _`Pelican's documentation`: https://docs.getpelican.com/ +.. _`Pelican documentation`: https://docs.getpelican.com/ .. _`Pelican's internals`: https://docs.getpelican.com/en/latest/internals.html .. _`hosted on GitHub`: https://github.com/getpelican/pelican From 492ed61c9f4dcf3b5d11feeae332d1e351b01fe3 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 1 Oct 2021 15:30:03 +0200 Subject: [PATCH 139/465] Update changelog --- docs/changelog.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index fc673d90..b9271ddc 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,18 @@ Release history ############### +4.7.0 - 2021-10-01 +================== + +* Improve default theme rendering on mobile and other small screen devices `(#2914) `_ +* Add support for hidden articles `(#2866) `_ +* Improve word count behavior when generating summary CJK & other locales `(#2863) `_ +* Add progress spinner during generation `(#2869) `_ + and better logging `(#2897) `_, both via `Rich `_ +* Invoke tasks `serve` and `livereload` now auto-open a web browser pointing to the locally-served website `(#2764) `_ +* Support some date format codes used by ISO dates `(#2902) `_ +* Document how to add a new writer `(#2901) `_ + 4.6.0 - 2021-03-23 ================== From f9238269d76fbb02051792b0e36c4cd2d94456ba Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 1 Oct 2021 15:35:41 +0200 Subject: [PATCH 140/465] Tweak changelog --- docs/changelog.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index b9271ddc..b7418ca9 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -6,10 +6,10 @@ Release history * Improve default theme rendering on mobile and other small screen devices `(#2914) `_ * Add support for hidden articles `(#2866) `_ -* Improve word count behavior when generating summary CJK & other locales `(#2863) `_ +* Improve word count behavior when generating summary CJK & other locales `(#2864) `_ * Add progress spinner during generation `(#2869) `_ - and better logging `(#2897) `_, both via `Rich `_ -* Invoke tasks `serve` and `livereload` now auto-open a web browser pointing to the locally-served website `(#2764) `_ + and richer logging `(#2897) `_, both via `Rich `_ +* Invoke tasks ``serve`` and ``livereload`` now auto-open a web browser pointing to the locally-served web site `(#2764) `_ * Support some date format codes used by ISO dates `(#2902) `_ * Document how to add a new writer `(#2901) `_ From 7dec2660fb24dd1d50d4425751dab1ec59db608c Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 1 Oct 2021 16:04:47 +0200 Subject: [PATCH 141/465] Prepare release --- RELEASE.md | 10 ++++++++++ docs/changelog.rst | 12 ------------ 2 files changed, 10 insertions(+), 12 deletions(-) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..20087288 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,10 @@ +Release type: minor + +* Improve default theme rendering on mobile and other small screen devices `(#2914) `_ +* Add support for hidden articles `(#2866) `_ +* Improve word count behavior when generating summary CJK & other locales `(#2864) `_ +* Add progress spinner during generation `(#2869) `_ + and richer logging `(#2897) `_, both via `Rich `_ +* Invoke tasks ``serve`` and ``livereload`` now auto-open a web browser pointing to the locally-served web site `(#2764) `_ +* Support some date format codes used by ISO dates `(#2902) `_ +* Document how to add a new writer `(#2901) `_ diff --git a/docs/changelog.rst b/docs/changelog.rst index b7418ca9..fc673d90 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,18 +1,6 @@ Release history ############### -4.7.0 - 2021-10-01 -================== - -* Improve default theme rendering on mobile and other small screen devices `(#2914) `_ -* Add support for hidden articles `(#2866) `_ -* Improve word count behavior when generating summary CJK & other locales `(#2864) `_ -* Add progress spinner during generation `(#2869) `_ - and richer logging `(#2897) `_, both via `Rich `_ -* Invoke tasks ``serve`` and ``livereload`` now auto-open a web browser pointing to the locally-served web site `(#2764) `_ -* Support some date format codes used by ISO dates `(#2902) `_ -* Document how to add a new writer `(#2901) `_ - 4.6.0 - 2021-03-23 ================== From f862d64b7a492f2af24e1228790a1d75371a1112 Mon Sep 17 00:00:00 2001 From: botpub Date: Fri, 1 Oct 2021 14:10:26 +0000 Subject: [PATCH 142/465] Release Pelican 4.7.0 --- RELEASE.md | 10 ---------- docs/changelog.rst | 12 ++++++++++++ pyproject.toml | 2 +- setup.py | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) delete mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 20087288..00000000 --- a/RELEASE.md +++ /dev/null @@ -1,10 +0,0 @@ -Release type: minor - -* Improve default theme rendering on mobile and other small screen devices `(#2914) `_ -* Add support for hidden articles `(#2866) `_ -* Improve word count behavior when generating summary CJK & other locales `(#2864) `_ -* Add progress spinner during generation `(#2869) `_ - and richer logging `(#2897) `_, both via `Rich `_ -* Invoke tasks ``serve`` and ``livereload`` now auto-open a web browser pointing to the locally-served web site `(#2764) `_ -* Support some date format codes used by ISO dates `(#2902) `_ -* Document how to add a new writer `(#2901) `_ diff --git a/docs/changelog.rst b/docs/changelog.rst index fc673d90..b7418ca9 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,18 @@ Release history ############### +4.7.0 - 2021-10-01 +================== + +* Improve default theme rendering on mobile and other small screen devices `(#2914) `_ +* Add support for hidden articles `(#2866) `_ +* Improve word count behavior when generating summary CJK & other locales `(#2864) `_ +* Add progress spinner during generation `(#2869) `_ + and richer logging `(#2897) `_, both via `Rich `_ +* Invoke tasks ``serve`` and ``livereload`` now auto-open a web browser pointing to the locally-served web site `(#2764) `_ +* Support some date format codes used by ISO dates `(#2902) `_ +* Document how to add a new writer `(#2901) `_ + 4.6.0 - 2021-03-23 ================== diff --git a/pyproject.toml b/pyproject.toml index 99b1d5d4..42bd9a14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pelican" -version = "4.6.0" +version = "4.7.0" description = "Static site generator supporting Markdown and reStructuredText" authors = ["Justin Mayer "] license = "AGPLv3" diff --git a/setup.py b/setup.py index 5ffd0fdf..c59c2daa 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from os.path import join, relpath from setuptools import find_packages, setup -version = "4.6.0" +version = "4.7.0" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', From 2d97a459024db000b5ee3b20a16176e0a12ca403 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Tue, 5 Oct 2021 01:39:35 +0300 Subject: [PATCH 143/465] Adjust suffix in server to allow redirection when needed Folders without index.html has to be redirected (/foo -> /foo/) for directory listing to work properly. Skip '/' suffix if original path does not have it so that base class can return a redirect. --- pelican/server.py | 5 +++++ pelican/tests/test_server.py | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pelican/server.py b/pelican/server.py index 6ebce876..317761c9 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -75,10 +75,15 @@ class ComplexHTTPRequestHandler(server.SimpleHTTPRequestHandler): def get_path_that_exists(self, original_path): # Try to strip trailing slash + trailing_slash = original_path.endswith('/') original_path = original_path.rstrip('/') # Try to detect file by applying various suffixes tries = [] for suffix in self.SUFFIXES: + if not trailing_slash and suffix == '/': + # if original request does not have trailing slash, skip the '/' suffix + # so that base class can redirect if needed + continue path = original_path + suffix if os.path.exists(self.translate_path(path)): return path diff --git a/pelican/tests/test_server.py b/pelican/tests/test_server.py index 307a3e10..9af030f8 100644 --- a/pelican/tests/test_server.py +++ b/pelican/tests/test_server.py @@ -43,14 +43,18 @@ class TestServer(unittest.TestCase): os.mkdir(os.path.join(self.temp_output, 'baz')) for suffix in ['', '/']: + # foo.html has precedence over foo/index.html path = handler.get_path_that_exists('foo' + suffix) self.assertEqual(path, 'foo.html') + # folder with index.html should return folder/index.html path = handler.get_path_that_exists('bar' + suffix) self.assertEqual(path, 'bar/index.html') + # folder without index.html should return same as input path = handler.get_path_that_exists('baz' + suffix) - self.assertEqual(path, 'baz/') + self.assertEqual(path, 'baz' + suffix) + # not existing path should return None path = handler.get_path_that_exists('quux' + suffix) self.assertIsNone(path) From c5c74832680d5aa376a386c9519b3364087904c8 Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Tue, 5 Oct 2021 10:00:48 +1100 Subject: [PATCH 144/465] Remove shebang lines from generated pelicanconf.py and publishconf.py. These configuration files do not need a #! line as they are not intended to be executed directly and are not marked as executable. (In practise this doesn't cause any problems - it just came up in a Guix bug report because Guix transforms the #! lines.) The "coding: utf-8" lines are also no longer required now that Pelican is Python 3 only. --- pelican/tools/templates/pelicanconf.py.jinja2 | 3 --- pelican/tools/templates/publishconf.py.jinja2 | 3 --- 2 files changed, 6 deletions(-) diff --git a/pelican/tools/templates/pelicanconf.py.jinja2 b/pelican/tools/templates/pelicanconf.py.jinja2 index 4ba0208c..1112ac88 100644 --- a/pelican/tools/templates/pelicanconf.py.jinja2 +++ b/pelican/tools/templates/pelicanconf.py.jinja2 @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- # - AUTHOR = {{author}} SITENAME = {{sitename}} SITEURL = '' diff --git a/pelican/tools/templates/publishconf.py.jinja2 b/pelican/tools/templates/publishconf.py.jinja2 index bb18966b..e119222c 100755 --- a/pelican/tools/templates/publishconf.py.jinja2 +++ b/pelican/tools/templates/publishconf.py.jinja2 @@ -1,6 +1,3 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- # - # This file is only used if you use `make publish` or # explicitly specify it as your config file. From 0b9a488c26ecc139b4c64b5635833ba2f410d6c0 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Fri, 1 Oct 2021 18:05:06 -0600 Subject: [PATCH 145/465] Use rich logging for Pelican server --- pelican/__init__.py | 4 ++-- pelican/server.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 6c2c3051..5a487c9f 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -487,8 +487,8 @@ def listen(server, port, output, excqueue=None): return try: - print("\nServing site at: http://{}:{} - Tap CTRL-C to stop".format( - server, port)) + console.print("Serving site at: http://{}:{} - Tap CTRL-C to stop".format( + server, port)) httpd.serve_forever() except Exception as e: if excqueue is not None: diff --git a/pelican/server.py b/pelican/server.py index 317761c9..893cc800 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -12,6 +12,7 @@ try: except ImportError: magic_from_file = None +from pelican.log import console # noqa: F401 from pelican.log import init as init_logging logger = logging.getLogger(__name__) @@ -104,6 +105,9 @@ class ComplexHTTPRequestHandler(server.SimpleHTTPRequestHandler): return mimetype + def log_message(self, format, *args): + logger.info(format, *args) + class RootedHTTPServer(server.HTTPServer): def __init__(self, base_path, *args, **kwargs): From 2b631ab4d316b2bc50b3462e19e3ba0bcd7a419c Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Sat, 2 Oct 2021 09:55:02 -0600 Subject: [PATCH 146/465] Listen: set minimum logging level to INFO This way we can see the server requests. --- pelican/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pelican/__init__.py b/pelican/__init__.py index 5a487c9f..99aa2776 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -476,6 +476,10 @@ def autoreload(args, excqueue=None): def listen(server, port, output, excqueue=None): + # set logging level to at least "INFO" (so we can see the server requests) + if logger.level < logging.INFO: + logger.setLevel(logging.INFO) + RootedHTTPServer.allow_reuse_address = True try: httpd = RootedHTTPServer( From 76cf879414909136a77bef846b8d71eaadd8dd48 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 5 Oct 2021 07:38:24 +0200 Subject: [PATCH 147/465] Add Pelican theme repo link to docs --- docs/index.rst | 2 +- docs/themes.rst | 23 +++++++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index e64ffd7e..60591482 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -61,8 +61,8 @@ Documentation content publish settings - themes plugins + themes pelican-themes importer faq diff --git a/docs/themes.rst b/docs/themes.rst index e21a6c79..fe6337d6 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -1,7 +1,17 @@ .. _theming-pelican: -Creating themes -############### +Themes +###### + +There is a community-managed repository of `Pelican Themes`_ for people to share +and use. + +Please note that while we do our best to review and merge theme contributions, +they are submitted by the Pelican community and thus may have varying levels of +support and interoperability. + +Creating Themes +~~~~~~~~~~~~~~~ To generate its HTML output, Pelican uses the `Jinja `_ templating engine due to its flexibility and @@ -50,7 +60,7 @@ To make your own theme, you must follow the following structure:: .. _templates-variables: -Templates and variables +Templates and Variables ======================= The idea is to use a simple syntax that you can embed into your HTML pages. @@ -61,7 +71,7 @@ All templates will receive the variables defined in your settings file, as long as they are in all-caps. You can access them directly. -Common variables +Common Variables ---------------- All of these settings will be available to all templates. @@ -571,3 +581,8 @@ Download """""""" You can download this example theme :download:`here <_static/theme-basic.zip>`. + + +.. Links + +.. _`Pelican Themes`: https://github.com/getpelican/pelican-themes From 31b282f3e267a4ce536fcdf598941cc9834c479b Mon Sep 17 00:00:00 2001 From: disk0x Date: Tue, 18 Aug 2020 20:22:42 +0200 Subject: [PATCH 148/465] Add SFTP upload to Makefile Some managed web hosts do not allow uploads via scp or rsync, so the sftp command is a necessary alternative. --- pelican/tools/templates/Makefile.jinja2 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pelican/tools/templates/Makefile.jinja2 b/pelican/tools/templates/Makefile.jinja2 index df5b2643..a28d658c 100644 --- a/pelican/tools/templates/Makefile.jinja2 +++ b/pelican/tools/templates/Makefile.jinja2 @@ -72,6 +72,7 @@ help: @echo ' make devserver-global regenerate and serve on 0.0.0.0 ' {% if ssh %} @echo ' make ssh_upload upload the web site via SSH ' + @echo ' make sftp_upload upload the web site via SFTP ' @echo ' make rsync_upload upload the web site via rsync+ssh ' {% endif %} {% if dropbox %} @@ -124,6 +125,9 @@ publish: ssh_upload: publish scp -P $(SSH_PORT) -r "$(OUTPUTDIR)"/* "$(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)" +sftp_upload: publish + printf 'put -r $(OUTPUTDIR)/*' | sftp $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) + {% set upload = upload + ["rsync_upload"] %} rsync_upload: publish rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --include tags --cvs-exclude --delete "$(OUTPUTDIR)"/ "$(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)" From 5803052bb76a2648271739b6709b6ca17ae27ca7 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Sat, 2 Oct 2021 09:36:32 -0600 Subject: [PATCH 149/465] server: return proper MIME-types for webfonts --- pelican/server.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pelican/server.py b/pelican/server.py index 893cc800..7a183bce 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -43,6 +43,21 @@ def parse_arguments(): class ComplexHTTPRequestHandler(server.SimpleHTTPRequestHandler): SUFFIXES = ['.html', '/index.html', '/', ''] + extensions_map = _encodings_map_default = { + # included in Python default implementation + '.gz': 'application/gzip', + '.Z': 'application/octet-stream', + '.bz2': 'application/x-bzip2', + '.xz': 'application/x-xz', + + # web fonts + ".oft": "font/oft", + ".sfnt": "font/sfnt", + ".ttf": "font/ttf", + ".woff": "font/woff", + ".woff2": "font/woff2", + } + def translate_path(self, path): # abandon query parameters path = path.split('?', 1)[0] From 30597b70f00e39e6f93c9f5ebffd961234062b67 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 6 Oct 2021 09:38:21 +0200 Subject: [PATCH 150/465] Quote version numbers in GitHub Actions workflow Otherwise, YAML treats `python: 3.10` as a float, yielding Python 3.1. --- .github/workflows/main.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 863dec4b..ee08c25d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,17 +16,17 @@ jobs: matrix: config: - os: ubuntu - python: 3.6 + python: "3.6" - os: ubuntu - python: 3.7 + python: "3.7" - os: ubuntu - python: 3.8 + python: "3.8" - os: ubuntu - python: 3.9 + python: "3.9" - os: macos - python: 3.7 + python: "3.7" - os: windows - python: 3.7 + python: "3.7" steps: - uses: actions/checkout@v2 @@ -86,7 +86,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: "3.7" - name: Set pip cache (Linux) uses: actions/cache@v2 if: startsWith(runner.os, 'Linux') @@ -110,7 +110,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: "3.7" - name: Set pip cache (Linux) uses: actions/cache@v2 if: startsWith(runner.os, 'Linux') @@ -136,7 +136,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: "3.7" - name: Check release id: check_release run: | From 0da8659d0ee5b23550fcf0d2314bb398a00b18a1 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 6 Oct 2021 09:51:00 +0200 Subject: [PATCH 151/465] Add Python 3.10 to test matrix & classifier list --- .github/workflows/main.yml | 2 ++ setup.py | 2 ++ tox.ini | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ee08c25d..527b8db9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,6 +23,8 @@ jobs: python: "3.8" - os: ubuntu python: "3.9" + - os: ubuntu + python: "3.10" - os: macos python: "3.7" - os: windows diff --git a/setup.py b/setup.py index c59c2daa..f1ca9dc1 100755 --- a/setup.py +++ b/setup.py @@ -69,6 +69,8 @@ setup( 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Software Development :: Libraries :: Python Modules', diff --git a/tox.ini b/tox.ini index bb45229c..15575cf2 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{3.6,3.7,3.8,3.9},docs,flake8 +envlist = py{3.6,3.7,3.8,3.9,3.10},docs,flake8 [testenv] basepython = @@ -7,6 +7,7 @@ basepython = py3.7: python3.7 py3.8: python3.8 py3.9: python3.9 + py3.10: python3.10 passenv = * usedevelop=True deps = From 8849721913f4b9bc8e3c234185d5fc2bf3da68b7 Mon Sep 17 00:00:00 2001 From: Jonas Borges Date: Wed, 6 Oct 2021 09:19:17 +0100 Subject: [PATCH 152/465] Ensure _DISCARDED is not being cached. Fix #2825 (#2926) Filtration is now being applied before caching the metadata, solving the issue where _DISCARD objects from previous runs were being retrieved from cache. --- pelican/readers.py | 3 ++- .../article_with_markdown_and_empty_tags.md | 4 ++++ pelican/tests/test_generators.py | 3 +++ pelican/tests/test_readers.py | 18 ++++++++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 pelican/tests/content/article_with_markdown_and_empty_tags.md diff --git a/pelican/readers.py b/pelican/readers.py index 15d09908..b42fa4f8 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -571,8 +571,9 @@ class Readers(FileStampDataCacher): content, reader_metadata = self.get_cached_data(path, (None, None)) if content is None: content, reader_metadata = reader.read(path) + reader_metadata = _filter_discardable_metadata(reader_metadata) self.cache_data(path, (content, reader_metadata)) - metadata.update(_filter_discardable_metadata(reader_metadata)) + metadata.update(reader_metadata) if content: # find images with empty alt diff --git a/pelican/tests/content/article_with_markdown_and_empty_tags.md b/pelican/tests/content/article_with_markdown_and_empty_tags.md new file mode 100644 index 00000000..ba013fe9 --- /dev/null +++ b/pelican/tests/content/article_with_markdown_and_empty_tags.md @@ -0,0 +1,4 @@ +Title: Article with markdown and empty tags +Tags: + +This is some content. diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 0a4a8fdc..1bc8aff0 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -265,6 +265,8 @@ class TestArticlesGenerator(unittest.TestCase): ['This is a super article !', 'published', 'yeah', 'article'], ['This is a super article !', 'published', 'Default', 'article'], ['Article with an inline SVG', 'published', 'Default', 'article'], + ['Article with markdown and empty tags', 'published', 'Default', + 'article'], ['This is an article with category !', 'published', 'yeah', 'article'], ['This is an article with multiple authors!', 'published', @@ -569,6 +571,7 @@ class TestArticlesGenerator(unittest.TestCase): 'Article title', 'Article with Nonconformant HTML meta tags', 'Article with an inline SVG', + 'Article with markdown and empty tags', 'Article with markdown and nested summary metadata', 'Article with markdown and summary metadata multi', 'Article with markdown and summary metadata single', diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index ea5f3bdd..753a353d 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -18,6 +18,7 @@ class ReaderTest(unittest.TestCase): def read_file(self, path, **kwargs): # Isolate from future API changes to readers.read_file + r = readers.Readers(settings=get_settings(**kwargs)) return r.read_file(base_path=CONTENT_PATH, path=path) @@ -795,6 +796,23 @@ class MdReaderTest(ReaderTest): self.assertEqual(page.content, expected) self.assertEqual(page.title, expected_title) + def test_metadata_has_no_discarded_data(self): + md_filename = 'article_with_markdown_and_empty_tags.md' + + r = readers.Readers(cache_name='cache', settings=get_settings( + CACHE_CONTENT=True)) + page = r.read_file(base_path=CONTENT_PATH, path=md_filename) + + __, cached_metadata = r.get_cached_data( + _path(md_filename), (None, None)) + + expected = { + 'title': 'Article with markdown and empty tags' + } + self.assertEqual(cached_metadata, expected) + self.assertNotIn('tags', page.metadata) + self.assertDictHasSubset(page.metadata, expected) + class HTMLReaderTest(ReaderTest): def test_article_with_comments(self): From 98372c9869146ad00b8f37ef5775561536c81b3b Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Thu, 7 Oct 2021 14:16:27 -0600 Subject: [PATCH 153/465] server: for extension_map, refer to upstream version rather than only overwriting it --- pelican/server.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/pelican/server.py b/pelican/server.py index 7a183bce..913c3761 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -43,19 +43,16 @@ def parse_arguments(): class ComplexHTTPRequestHandler(server.SimpleHTTPRequestHandler): SUFFIXES = ['.html', '/index.html', '/', ''] - extensions_map = _encodings_map_default = { - # included in Python default implementation - '.gz': 'application/gzip', - '.Z': 'application/octet-stream', - '.bz2': 'application/x-bzip2', - '.xz': 'application/x-xz', - - # web fonts - ".oft": "font/oft", - ".sfnt": "font/sfnt", - ".ttf": "font/ttf", - ".woff": "font/woff", - ".woff2": "font/woff2", + extensions_map = { + **server.SimpleHTTPRequestHandler.extensions_map, + ** { + # web fonts + ".oft": "font/oft", + ".sfnt": "font/sfnt", + ".ttf": "font/ttf", + ".woff": "font/woff", + ".woff2": "font/woff2", + }, } def translate_path(self, path): From 793b93bd3441edf848dc4a765db18fcaf0640438 Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Fri, 8 Oct 2021 18:47:40 +1100 Subject: [PATCH 154/465] Use `--no-pager` option rather than override all environment variables Currently the `assertDirsEqual` test utility uses the `env` argument to `subprocess.Popen` to make git run non-interactively, but this overwrites all environment variables causing test failures on Guix. The `--no-pager` option is a more targeted way to achieve the same thing. --- pelican/tests/test_pelican.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index cacc65c1..389dbb3d 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -56,9 +56,8 @@ class TestPelican(LoggedTestCase): def assertDirsEqual(self, left_path, right_path): out, err = subprocess.Popen( - ['git', 'diff', '--no-ext-diff', '--exit-code', + ['git', '--no-pager', 'diff', '--no-ext-diff', '--exit-code', '-w', left_path, right_path], - env={'PAGER': ''}, stdout=subprocess.PIPE, stderr=subprocess.PIPE ).communicate() From eab67f7634af892c2b253e221c7613f5ef8c1c54 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 8 Oct 2021 08:59:35 +0200 Subject: [PATCH 155/465] PTY all the tasks --- tasks.py | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/tasks.py b/tasks.py index 12116da3..148899c7 100644 --- a/tasks.py +++ b/tasks.py @@ -8,6 +8,7 @@ PKG_NAME = "pelican" PKG_PATH = Path(PKG_NAME) DOCS_PORT = os.environ.get("DOCS_PORT", 8000) BIN_DIR = "bin" if os.name != "nt" else "Scripts" +PTY = True if os.name != "nt" else False ACTIVE_VENV = os.environ.get("VIRTUAL_ENV", None) VENV_HOME = Path(os.environ.get("WORKON_HOME", "~/virtualenvs")) VENV_PATH = Path(ACTIVE_VENV) if ACTIVE_VENV else (VENV_HOME / PKG_NAME) @@ -16,15 +17,13 @@ VENV_BIN = Path(VENV) / Path(BIN_DIR) TOOLS = ["poetry", "pre-commit", "psutil"] POETRY = which("poetry") if which("poetry") else (VENV_BIN / "poetry") -PRECOMMIT = ( - which("pre-commit") if which("pre-commit") else (VENV_BIN / "pre-commit") -) +PRECOMMIT = which("pre-commit") if which("pre-commit") else (VENV_BIN / "pre-commit") @task def docbuild(c): """Build documentation""" - c.run(f"{VENV_BIN}/sphinx-build -W docs docs/_build") + c.run(f"{VENV_BIN}/sphinx-build -W docs docs/_build", pty=PTY) @task(docbuild) @@ -42,7 +41,6 @@ def docserve(c): @task def tests(c): """Run the test suite""" - PTY = True if os.name != "nt" else False c.run(f"{VENV_BIN}/pytest", pty=PTY) @@ -54,7 +52,7 @@ def black(c, check=False, diff=False): check_flag = "--check" if diff: diff_flag = "--diff" - c.run(f"{VENV_BIN}/black {check_flag} {diff_flag} {PKG_PATH} tasks.py") + c.run(f"{VENV_BIN}/black {check_flag} {diff_flag} {PKG_PATH} tasks.py", pty=PTY) @task @@ -64,14 +62,12 @@ def isort(c, check=False, diff=False): check_flag = "-c" if diff: diff_flag = "--diff" - c.run( - f"{VENV_BIN}/isort {check_flag} {diff_flag} ." - ) + c.run(f"{VENV_BIN}/isort {check_flag} {diff_flag} .", pty=PTY) @task def flake8(c): - c.run(f"git diff HEAD | {VENV_BIN}/flake8 --diff --max-line-length=88") + c.run(f"git diff HEAD | {VENV_BIN}/flake8 --diff --max-line-length=88", pty=PTY) @task @@ -84,20 +80,20 @@ def tools(c): """Install tools in the virtual environment if not already on PATH""" for tool in TOOLS: if not which(tool): - c.run(f"{VENV_BIN}/python -m pip install {tool}") + c.run(f"{VENV_BIN}/python -m pip install {tool}", pty=PTY) @task def precommit(c): """Install pre-commit hooks to .git/hooks/pre-commit""" - c.run(f"{PRECOMMIT} install") + c.run(f"{PRECOMMIT} install", pty=PTY) @task def setup(c): - c.run(f"{VENV_BIN}/python -m pip install -U pip") + c.run(f"{VENV_BIN}/python -m pip install -U pip", pty=PTY) tools(c) - c.run(f"{POETRY} install") + c.run(f"{POETRY} install", pty=PTY) precommit(c) @@ -105,11 +101,17 @@ def setup(c): def update_functional_tests(c): """Update the generated functional test output""" c.run( - f"bash -c 'LC_ALL=en_US.utf8 pelican -o {PKG_PATH}/tests/output/custom/ -s samples/pelican.conf.py samples/content/'" + f"bash -c 'LC_ALL=en_US.utf8 pelican -o {PKG_PATH}/tests/output/custom/ \ + -s samples/pelican.conf.py samples/content/'", + pty=PTY, ) c.run( - f"bash -c 'LC_ALL=fr_FR.utf8 pelican -o {PKG_PATH}/tests/output/custom_locale/ -s samples/pelican.conf_FR.py samples/content/'" + f"bash -c 'LC_ALL=fr_FR.utf8 pelican -o {PKG_PATH}/tests/output/custom_locale/ \ + -s samples/pelican.conf_FR.py samples/content/'", + pty=PTY, ) c.run( - f"bash -c 'LC_ALL=en_US.utf8 pelican -o {PKG_PATH}/tests/output/basic/ samples/content/'" + f"bash -c 'LC_ALL=en_US.utf8 pelican -o \ + {PKG_PATH}/tests/output/basic/ samples/content/'", + pty=PTY, ) From 9bd54b7b607e66f8342399df121432d74098df64 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 8 Oct 2021 09:00:13 +0200 Subject: [PATCH 156/465] All roads lead to Rome --- pelican/tools/pelican_quickstart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 41ddea3c..e7cbfea0 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -17,7 +17,7 @@ try: import tzlocal _DEFAULT_TIMEZONE = tzlocal.get_localzone().zone except ImportError: - _DEFAULT_TIMEZONE = 'Europe/Paris' + _DEFAULT_TIMEZONE = 'Europe/Rome' from pelican import __version__ From aeec09b397314e7068020f58ce2e1e2e55b84437 Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Fri, 8 Oct 2021 15:09:59 +1100 Subject: [PATCH 157/465] Distribute sample data used to run tests --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 469f6fff..47d69d99 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include *.rst recursive-include pelican *.html *.css *png *.rst *.markdown *.md *.mkd *.xml *.py *.jinja2 include LICENSE THANKS docs/changelog.rst pyproject.toml +graft samples \ No newline at end of file From 99c935df8f3d110022ac0fed5f595acd52f36469 Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Tue, 12 Oct 2021 22:29:59 +1100 Subject: [PATCH 158/465] Omit __pycache__ and *.pyc/pyo files in packages --- MANIFEST.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 47d69d99..87d433a8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,6 @@ include *.rst recursive-include pelican *.html *.css *png *.rst *.markdown *.md *.mkd *.xml *.py *.jinja2 include LICENSE THANKS docs/changelog.rst pyproject.toml -graft samples \ No newline at end of file +graft samples +global-exclude __pycache__ +global-exclude *.py[co] \ No newline at end of file From 17089aefc93b5d44c175db218375a5f013d61111 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 12 Oct 2021 15:58:53 +0200 Subject: [PATCH 159/465] Add more contributors to THANKS --- THANKS | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/THANKS b/THANKS index fd9f030e..28b43884 100644 --- a/THANKS +++ b/THANKS @@ -1,9 +1,9 @@ -Pelican is a project originally created by Alexis Métaireau - and subsequently maintained by Justin Mayer -, but there are a large number of people that have -contributed or implemented key features over time. We do our best to keep this -list up-to-date, but you can also have a look at the nice contributor graphs -produced by GitHub: https://github.com/getpelican/pelican/graphs/contributors +Pelican is a project led by Justin Mayer +and originally created by Alexis Métaireau , but +there are a large number of people that have contributed or implemented key +features over time. We do our best to keep this list up-to-date, but you can +also have a look at the nice contributor graphs produced by GitHub: +https://github.com/getpelican/pelican/graphs/contributors If you want to contribute, check the documentation section about how to do so: @@ -26,6 +26,7 @@ Arnaud BOS asselinpaul Axel Haustant Ben Rosser (TC01) +Ben Sturmfels Benoît HERVIER Bernhard Scheirle Borgar @@ -64,6 +65,7 @@ Feth Arezki Florian Jacob Florian Preinstorfer Félix Delval +Frederik Ring Freeculture George V. Reilly Guillaume @@ -77,6 +79,7 @@ Iuri de Silvio Ivan Dyedov James King James Rowe +Jason K. Moore jawher Jered Boxman Jerome @@ -87,6 +90,7 @@ John Kristensen John Mastro Jökull Sólberg Auðunsson Jomel Imperio +Jonas Borges Joseph Reagle Joshua Adelman Julian Berman @@ -96,6 +100,7 @@ Kevin Yap Kyle Fuller Laureline Guerin Leonard Huang +Leonardo Giordani Leroy Jiang Lucas Cimon Marcel Hellkamp @@ -113,8 +118,11 @@ Michael Reneer Michael Yanovich Mike Yumatov Mikhail Korobov +Mirek Długosz m-r-r mviera +Nam Nguyen +NianQing Yao Nico Di Rocco Nicolas Duhamel Nicolas Perriault @@ -124,6 +132,8 @@ Paul Asselin Pavel Puchkin Perry Roper Peter Desmet +Peter Sabaini +Petr Viktorin Philippe Pepiot Rachid Belaid Randall Degges @@ -133,6 +143,7 @@ Rémy HUBSCHER renhbo Richard Duivenvoorde Rogdham +Romain Porte Roman Skvazh Ronny Pfannschmidt Rory McCann @@ -153,8 +164,10 @@ Tarek Ziade Thanos Lefteris Thomas Thurman Tobias +Tom Adler Tomi Pieviläinen Trae Blain +Tristan Miller Tshepang Lekhonkhobe Valentin-Costel Hăloiu Vlad Niculae From de2e9b7e4115034a0dedfb9d308850c8c5277db7 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 12 Oct 2021 15:59:59 +0200 Subject: [PATCH 160/465] Prepare release --- RELEASE.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..cabb391e --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,8 @@ +Release type: patch + +* Extend rich logging to server component `(#2927) `_ +* Fix an issue where metadata flagged to be discarded was being cached `(#2926) `_ +* Adjust suffix in server to allow redirection when needed `(#2931) `_ +* Add MIME types for web fonts `(#2929) `_ +* Distribute sample data used to run tests `(#2935) `_ +* Add Python 3.10 to test matrix From bb10d286a6ac2f283d332f7e80c5655b9997f827 Mon Sep 17 00:00:00 2001 From: botpub Date: Tue, 12 Oct 2021 14:45:30 +0000 Subject: [PATCH 161/465] Release Pelican 4.7.1 --- RELEASE.md | 8 -------- docs/changelog.rst | 10 ++++++++++ pyproject.toml | 2 +- setup.py | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) delete mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index cabb391e..00000000 --- a/RELEASE.md +++ /dev/null @@ -1,8 +0,0 @@ -Release type: patch - -* Extend rich logging to server component `(#2927) `_ -* Fix an issue where metadata flagged to be discarded was being cached `(#2926) `_ -* Adjust suffix in server to allow redirection when needed `(#2931) `_ -* Add MIME types for web fonts `(#2929) `_ -* Distribute sample data used to run tests `(#2935) `_ -* Add Python 3.10 to test matrix diff --git a/docs/changelog.rst b/docs/changelog.rst index b7418ca9..b76a4575 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,16 @@ Release history ############### +4.7.1 - 2021-10-12 +================== + +* Extend rich logging to server component `(#2927) `_ +* Fix an issue where metadata flagged to be discarded was being cached `(#2926) `_ +* Adjust suffix in server to allow redirection when needed `(#2931) `_ +* Add MIME types for web fonts `(#2929) `_ +* Distribute sample data used to run tests `(#2935) `_ +* Add Python 3.10 to test matrix + 4.7.0 - 2021-10-01 ================== diff --git a/pyproject.toml b/pyproject.toml index 42bd9a14..8d7749b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pelican" -version = "4.7.0" +version = "4.7.1" description = "Static site generator supporting Markdown and reStructuredText" authors = ["Justin Mayer "] license = "AGPLv3" diff --git a/setup.py b/setup.py index f1ca9dc1..3b538992 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from os.path import join, relpath from setuptools import find_packages, setup -version = "4.7.0" +version = "4.7.1" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', From 9ec1750709c5c2308ab5099e836d516d7478fe1e Mon Sep 17 00:00:00 2001 From: iUnknwn Date: Tue, 21 Sep 2021 17:45:20 -0700 Subject: [PATCH 162/465] Document how to inject articles with a plugin Add to the plugin documentation a recipe for injecting articles programmatically when Pelican is running. --- docs/plugins.rst | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/plugins.rst b/docs/plugins.rst index 8e24b0af..db7e00b4 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -301,6 +301,44 @@ Here is a basic example of how to set up your own writer:: signals.get_writer.connect(add_writer) +Using Plugins to Inject Content +------------------------------- + +You can programmatically inject articles or pages using plugins. This can be +useful if you plan to fetch articles from an API, for example. + +Following is a simple example of how one can build a plugin that injects a +custom article, using the ``article_generator_pretaxonomy`` signal:: + + import datetime + + from pelican import signals + from pelican.contents import Article + from pelican.readers import BaseReader + + def addArticle(articleGenerator): + settings = articleGenerator.settings + + # Author, category, and tags are objects, not strings, so they need to + # be handled using BaseReader's process_metadata() function. + baseReader = BaseReader(settings) + + content = "I am the body of an injected article!" + + newArticle = Article(content, { + "title": "Injected Article!", + "date": datetime.datetime.now(), + "category": baseReader.process_metadata("category", "fromAPI"), + "tags": baseReader.process_metadata("tags", "tagA, tagB") + }) + + articleGenerator.articles.insert(0, newArticle) + + def register(): + signals.article_generator_pretaxonomy.connect(addArticle) + + + .. _Pip: https://pip.pypa.io/ .. _pelican-plugins bug #314: https://github.com/getpelican/pelican-plugins/issues/314 .. _Blinker: https://pythonhosted.org/blinker/ From 1b87ef6a7bdc472cd85c130638b14382457cf487 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 15 Nov 2021 15:39:10 -0600 Subject: [PATCH 163/465] Add funding link --- .github/FUNDING.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 7e0d5359..8845b0a9 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,5 @@ +--- + +github: justinmayer custom: https://donate.getpelican.com liberapay: pelican From 8eb4be521fe07272bc70ab1b3840cadd5d99923a Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Thu, 25 Nov 2021 23:51:47 +0300 Subject: [PATCH 164/465] use docutils.Node.findall instead of traverse docutils.Node.traverse is being deprecated as of docutils==0.18.1 --- pelican/readers.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pelican/readers.py b/pelican/readers.py index b42fa4f8..03c3cc20 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -222,7 +222,14 @@ class RstReader(BaseReader): 'Ensure exactly one top level section', source_path) - for docinfo in document.traverse(docutils.nodes.docinfo): + try: + # docutils 0.18.1+ + nodes = document.findall(docutils.nodes.docinfo) + except AttributeError: + # docutils 0.18.0 or before + nodes = document.traverse(docutils.nodes.docinfo) + + for docinfo in nodes: for element in docinfo.children: if element.tagname == 'field': # custom fields (e.g. summary) name_elem, body_elem = element.children From fa31a7e279c3083b262237bfc88e125e110cd680 Mon Sep 17 00:00:00 2001 From: Lukas Winkler Date: Thu, 25 Nov 2021 20:19:33 +0100 Subject: [PATCH 165/465] Extend docstring We want to hint at the location of our default set of substitutions. This should allow easier reuse for plugin authors who need to access this utility as well. Closes #2845 --- pelican/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pelican/utils.py b/pelican/utils.py index 4d025657..17667078 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -230,6 +230,9 @@ def slugify(value, regex_subs=(), preserve_case=False, use_unicode=False): and converts spaces to hyphens. Took from Django sources. + + For a set of sensible default regex substitutions to pass to regex_subs + look into pelican.settings.DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS']. """ import unicodedata From 2e35bc90a6a24fc5175cb710c892a3c0ae520936 Mon Sep 17 00:00:00 2001 From: Lukas Winkler Date: Sat, 27 Nov 2021 10:31:14 +0100 Subject: [PATCH 166/465] Change spaces to tab as required by Makefile Closes #2941 --- pelican/tools/templates/Makefile.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/templates/Makefile.jinja2 b/pelican/tools/templates/Makefile.jinja2 index a28d658c..2bcb7473 100644 --- a/pelican/tools/templates/Makefile.jinja2 +++ b/pelican/tools/templates/Makefile.jinja2 @@ -72,7 +72,7 @@ help: @echo ' make devserver-global regenerate and serve on 0.0.0.0 ' {% if ssh %} @echo ' make ssh_upload upload the web site via SSH ' - @echo ' make sftp_upload upload the web site via SFTP ' + @echo ' make sftp_upload upload the web site via SFTP ' @echo ' make rsync_upload upload the web site via rsync+ssh ' {% endif %} {% if dropbox %} From 59f7f4beb85b156175b8573ab8b4da7e5bd9506c Mon Sep 17 00:00:00 2001 From: Lukas Winkler Date: Sat, 27 Nov 2021 13:56:33 +0100 Subject: [PATCH 167/465] Remove unneeded explicit close stmts Closes #2951 --- pelican/tools/pelican_quickstart.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index e7cbfea0..032b41a7 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -315,7 +315,6 @@ needed by Pelican. _template = _jinja_env.get_template('pelicanconf.py.jinja2') fd.write(_template.render(**conf_python)) - fd.close() except OSError as e: print('Error: {}'.format(e)) @@ -324,7 +323,6 @@ needed by Pelican. 'w', encoding='utf-8') as fd: _template = _jinja_env.get_template('publishconf.py.jinja2') fd.write(_template.render(**CONF)) - fd.close() except OSError as e: print('Error: {}'.format(e)) @@ -334,7 +332,6 @@ needed by Pelican. 'w', encoding='utf-8') as fd: _template = _jinja_env.get_template('tasks.py.jinja2') fd.write(_template.render(**CONF)) - fd.close() except OSError as e: print('Error: {}'.format(e)) try: @@ -343,7 +340,6 @@ needed by Pelican. py_v = 'python3' _template = _jinja_env.get_template('Makefile.jinja2') fd.write(_template.render(py_v=py_v, **CONF)) - fd.close() except OSError as e: print('Error: {}'.format(e)) From 8f6a61439d8f08d1f3897076ffec9b66678a4f8a Mon Sep 17 00:00:00 2001 From: Lukas Winkler Date: Sat, 27 Nov 2021 14:04:36 +0100 Subject: [PATCH 168/465] Add helper method for rendering jinja templates --- pelican/tools/pelican_quickstart.py | 51 ++++++++++------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 032b41a7..2d5629ef 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -3,6 +3,7 @@ import argparse import locale import os +from typing import Mapping from jinja2 import Environment, FileSystemLoader @@ -170,6 +171,16 @@ def ask_timezone(question, default, tzurl): return r +def render_jinja_template(tmpl_name: str, tmpl_vars: Mapping, target_path: str): + try: + with open(os.path.join(CONF['basedir'], target_path), + 'w', encoding='utf-8') as fd: + _template = _jinja_env.get_template(tmpl_name) + fd.write(_template.render(**tmpl_vars)) + except OSError as e: + print('Error: {}'.format(e)) + + def main(): parser = argparse.ArgumentParser( description="A kickstarter for Pelican", @@ -306,42 +317,16 @@ needed by Pelican. except OSError as e: print('Error: {}'.format(e)) - try: - with open(os.path.join(CONF['basedir'], 'pelicanconf.py'), - 'w', encoding='utf-8') as fd: - conf_python = dict() - for key, value in CONF.items(): - conf_python[key] = repr(value) + conf_python = dict() + for key, value in CONF.items(): + conf_python[key] = repr(value) + render_jinja_template('pelicanconf.py.jinja2', conf_python, 'pelicanconf.py') - _template = _jinja_env.get_template('pelicanconf.py.jinja2') - fd.write(_template.render(**conf_python)) - except OSError as e: - print('Error: {}'.format(e)) - - try: - with open(os.path.join(CONF['basedir'], 'publishconf.py'), - 'w', encoding='utf-8') as fd: - _template = _jinja_env.get_template('publishconf.py.jinja2') - fd.write(_template.render(**CONF)) - except OSError as e: - print('Error: {}'.format(e)) + render_jinja_template('publishconf.py.jinja2', CONF, 'publishconf.py') if automation: - try: - with open(os.path.join(CONF['basedir'], 'tasks.py'), - 'w', encoding='utf-8') as fd: - _template = _jinja_env.get_template('tasks.py.jinja2') - fd.write(_template.render(**CONF)) - except OSError as e: - print('Error: {}'.format(e)) - try: - with open(os.path.join(CONF['basedir'], 'Makefile'), - 'w', encoding='utf-8') as fd: - py_v = 'python3' - _template = _jinja_env.get_template('Makefile.jinja2') - fd.write(_template.render(py_v=py_v, **CONF)) - except OSError as e: - print('Error: {}'.format(e)) + render_jinja_template('tasks.py.jinja2', CONF, 'tasks.py') + render_jinja_template('Makefile.jinja2', CONF, 'Makefile') print('Done. Your new project is available at %s' % CONF['basedir']) From 0384c9bc071dd82b9dbe29fb73521587311bfc84 Mon Sep 17 00:00:00 2001 From: "Gunung P. Wibisono" <55311527+gunungpw@users.noreply.github.com> Date: Fri, 7 Jan 2022 07:26:41 +0700 Subject: [PATCH 169/465] Update Poetry install script to `install-poetry.py` (#2965) --- docs/contribute.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/contribute.rst b/docs/contribute.rst index d1ab322d..99a546a7 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -21,10 +21,10 @@ another, so you can use different packages (and package versions) for each. Please note that Python 3.6+ is required for Pelican development. -*(Optional)* If you prefer to install Poetry once for use with multiple projects, +*(Optional)* If you prefer to `install Poetry `_ once for use with multiple projects, you can install it via:: - curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - + curl -sSL https://install.python-poetry.org | python3 - Point your web browser to the `Pelican repository`_ and tap the **Fork** button at top-right. Then clone the source for your fork and add the upstream project @@ -51,7 +51,7 @@ Install the needed dependencies and set up the project:: Your local environment should now be ready to go! .. _Pip: https://pip.pypa.io/ -.. _Poetry: https://poetry.eustace.io/docs/#installation +.. _Poetry: https://python-poetry.org/ .. _Pelican repository: https://github.com/getpelican/pelican Development From fe4f1ec4eada14b4da42a48b15c83082c112306d Mon Sep 17 00:00:00 2001 From: Pierre Equoy Date: Mon, 17 Jan 2022 16:06:39 +0800 Subject: [PATCH 170/465] Uniformize headers in simple theme In the simple theme, some templates are using `h1`, others are using `h2` for the main title of the page (other than the one in the header). This commit changes that so all of the pages are using `h1`. --- pelican/themes/simple/templates/article.html | 4 ++-- pelican/themes/simple/templates/author.html | 2 +- pelican/themes/simple/templates/category.html | 2 +- pelican/themes/simple/templates/tag.html | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pelican/themes/simple/templates/article.html b/pelican/themes/simple/templates/article.html index c8c9a4f7..f85c7410 100644 --- a/pelican/themes/simple/templates/article.html +++ b/pelican/themes/simple/templates/article.html @@ -24,9 +24,9 @@ {% block content %}
-

+

{{ article.title }}

+ title="Permalink to {{ article.title|striptags }}">{{ article.title }} {% import 'translations.html' as translations with context %} {{ translations.translations_for(article) }}
diff --git a/pelican/themes/simple/templates/author.html b/pelican/themes/simple/templates/author.html index a1901946..79d22c7d 100644 --- a/pelican/themes/simple/templates/author.html +++ b/pelican/themes/simple/templates/author.html @@ -3,6 +3,6 @@ {% block title %}{{ SITENAME }} - Articles by {{ author }}{% endblock %} {% block content_title %} -

Articles by {{ author }}

+

Articles by {{ author }}

{% endblock %} diff --git a/pelican/themes/simple/templates/category.html b/pelican/themes/simple/templates/category.html index 14d7ff09..d73f6e31 100644 --- a/pelican/themes/simple/templates/category.html +++ b/pelican/themes/simple/templates/category.html @@ -3,6 +3,6 @@ {% block title %}{{ SITENAME }} - {{ category }} category{% endblock %} {% block content_title %} -

Articles in the {{ category }} category

+

Articles in the {{ category }} category

{% endblock %} diff --git a/pelican/themes/simple/templates/tag.html b/pelican/themes/simple/templates/tag.html index 9c958030..93878134 100644 --- a/pelican/themes/simple/templates/tag.html +++ b/pelican/themes/simple/templates/tag.html @@ -3,5 +3,5 @@ {% block title %}{{ SITENAME }} - {{ tag }} tag{% endblock %} {% block content_title %} -

Articles tagged with {{ tag }}

+

Articles tagged with {{ tag }}

{% endblock %} From 7b9a859e5e30180808794748e47e529485544b8d Mon Sep 17 00:00:00 2001 From: Pierre Equoy Date: Mon, 17 Jan 2022 16:11:06 +0800 Subject: [PATCH 171/465] Use
and
tags in simple theme Add a
tag to surround all the content blocks, so that it's easier to target the main part of a page (be it an article, the list of posts or the different categories) using CSS. Because of this, the
part of the article.html template is made redundant, so it is removed. Finally, the generic
is replaced by an
tag to surround the article's content. --- pelican/themes/simple/templates/article.html | 6 ++---- pelican/themes/simple/templates/base.html | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pelican/themes/simple/templates/article.html b/pelican/themes/simple/templates/article.html index f85c7410..6dd0d967 100644 --- a/pelican/themes/simple/templates/article.html +++ b/pelican/themes/simple/templates/article.html @@ -22,7 +22,6 @@ {% endblock %} {% block content %} -

{% endif %} -
+
{{ article.content }} -
-

+
{% endblock %} diff --git a/pelican/themes/simple/templates/base.html b/pelican/themes/simple/templates/base.html index 2c17dbfb..b1ea57da 100644 --- a/pelican/themes/simple/templates/base.html +++ b/pelican/themes/simple/templates/base.html @@ -51,8 +51,10 @@ {% endfor %} {% endif %} +
{% block content %} {% endblock %} +
Proudly powered by Pelican, From 4794752dd9ecd4f80eac52f84bd15abbf5bb9d4b Mon Sep 17 00:00:00 2001 From: Pierre Equoy Date: Mon, 17 Jan 2022 16:14:19 +0800 Subject: [PATCH 172/465] Add a viewport meta tag in simple theme for better mobile support See MDN article about this: https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag --- pelican/themes/simple/templates/base.html | 1 + 1 file changed, 1 insertion(+) diff --git a/pelican/themes/simple/templates/base.html b/pelican/themes/simple/templates/base.html index b1ea57da..6ac81e26 100644 --- a/pelican/themes/simple/templates/base.html +++ b/pelican/themes/simple/templates/base.html @@ -5,6 +5,7 @@ {% block title %}{{ SITENAME }}{% endblock title %} + {% if FEED_ALL_ATOM %} {% endif %} From 2f5fc106143558307651f738b904cd3dedea5a55 Mon Sep 17 00:00:00 2001 From: Paolo Melchiorre Date: Tue, 1 Feb 2022 19:07:23 +0100 Subject: [PATCH 173/465] Add categories.html template to default theme --- pelican/tests/output/basic/categories.html | 6 +++++- pelican/tests/output/custom/categories.html | 6 +++++- .../tests/output/custom_locale/categories.html | 6 +++++- .../themes/notmyidea/templates/categories.html | 16 ++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 pelican/themes/notmyidea/templates/categories.html 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 @@
  • yeah
  • -

    Categories on A Pelican Blog

    + +
    +

    Categories for A Pelican Blog

    +
    +
    - Proudly powered by Pelican, - which takes great advantage of Python. + Proudly powered by Pelican, + which takes great advantage of Python.
    From 3a6ae72333f447dececb8552800e507a619444c6 Mon Sep 17 00:00:00 2001 From: boxydog Date: Sat, 28 Oct 2023 17:01:33 -0500 Subject: [PATCH 302/465] Add a "coverage" task to generate a coverage report Add a one-liner about "invoke" in docs. --- docs/contribute.rst | 3 +++ tasks.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/docs/contribute.rst b/docs/contribute.rst index 64c144d6..cfbfe351 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -75,6 +75,9 @@ via:: invoke tests +(For more on Invoke, see ``invoke -l`` to list tasks, or +https://pyinvoke.org for documentation.) + In addition to running the test suite, it is important to also ensure that any lines you changed conform to code style guidelines. You can check that via:: diff --git a/tasks.py b/tasks.py index efdef8a4..e9f65db3 100644 --- a/tasks.py +++ b/tasks.py @@ -44,6 +44,13 @@ def tests(c): c.run(f"{VENV_BIN}/pytest", pty=PTY) +@task +def coverage(c): + """Generate code coverage of running the test suite.""" + c.run(f"{VENV_BIN}/pytest --cov=pelican", pty=PTY) + c.run(f"{VENV_BIN}/coverage html", pty=PTY) + + @task def black(c, check=False, diff=False): """Run Black auto-formatter, optionally with --check or --diff""" From fad2ff7ae3cd0ea8b974db5fe42de7383da679c1 Mon Sep 17 00:00:00 2001 From: boxydog <93335439+boxydog@users.noreply.github.com> Date: Sat, 28 Oct 2023 17:40:40 -0500 Subject: [PATCH 303/465] Add unit test utilities temporary_locale and TestCaseWithCLocale (#3224) --- pelican/tests/support.py | 13 +++ pelican/tests/test_generators.py | 30 ++----- pelican/tests/test_importer.py | 28 ++----- pelican/tests/test_utils.py | 133 ++++++++++++++++--------------- pelican/utils.py | 26 ++++-- 5 files changed, 111 insertions(+), 119 deletions(-) diff --git a/pelican/tests/support.py b/pelican/tests/support.py index 720e4d0e..3e4da785 100644 --- a/pelican/tests/support.py +++ b/pelican/tests/support.py @@ -254,3 +254,16 @@ class LoggedTestCase(unittest.TestCase): actual, count, msg='expected {} occurrences of {!r}, but found {}'.format( count, msg, actual)) + + +class TestCaseWithCLocale(unittest.TestCase): + """Set locale to C for each test case, then restore afterward. + + Use utils.temporary_locale if you want a context manager ("with" statement). + """ + def setUp(self): + self.old_locale = locale.setlocale(locale.LC_ALL) + locale.setlocale(locale.LC_ALL, 'C') + + def tearDown(self): + locale.setlocale(locale.LC_ALL, self.old_locale) diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index ac271c1c..05c37269 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -1,4 +1,3 @@ -import locale import os import sys from shutil import copy, rmtree @@ -9,26 +8,21 @@ from pelican.generators import (ArticlesGenerator, Generator, PagesGenerator, PelicanTemplateNotFound, StaticGenerator, TemplatePagesGenerator) from pelican.tests.support import (can_symlink, get_context, get_settings, - unittest) + unittest, TestCaseWithCLocale) from pelican.writers import Writer - CUR_DIR = os.path.dirname(__file__) CONTENT_DIR = os.path.join(CUR_DIR, 'content') -class TestGenerator(unittest.TestCase): +class TestGenerator(TestCaseWithCLocale): def setUp(self): - self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') + super().setUp() self.settings = get_settings() self.settings['READERS'] = {'asc': None} self.generator = Generator(self.settings.copy(), self.settings, CUR_DIR, self.settings['THEME'], None) - def tearDown(self): - locale.setlocale(locale.LC_ALL, self.old_locale) - def test_include_path(self): self.settings['IGNORE_FILES'] = {'ignored1.rst', 'ignored2.rst'} @@ -408,8 +402,6 @@ class TestArticlesGenerator(unittest.TestCase): def test_period_archives_context(self): """Test correctness of the period_archives context values.""" - old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') settings = get_settings() settings['CACHE_PATH'] = self.temp_cache @@ -532,15 +524,11 @@ class TestArticlesGenerator(unittest.TestCase): self.assertEqual(sample_archive['dates'][0].title, dates[0].title) self.assertEqual(sample_archive['dates'][0].date, dates[0].date) - locale.setlocale(locale.LC_ALL, old_locale) - def test_period_in_timeperiod_archive(self): """ Test that the context of a generated period_archive is passed 'period' : a tuple of year, month, day according to the time period """ - old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') settings = get_settings() settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html' @@ -625,7 +613,6 @@ class TestArticlesGenerator(unittest.TestCase): dates=dates, template_name='period_archives', url="posts/1970/Jan/01/", all_articles=generator.articles) - locale.setlocale(locale.LC_ALL, old_locale) def test_nonexistent_template(self): """Attempt to load a non-existent template""" @@ -926,20 +913,18 @@ class TestPageGenerator(unittest.TestCase): context['static_links']) -class TestTemplatePagesGenerator(unittest.TestCase): +class TestTemplatePagesGenerator(TestCaseWithCLocale): TEMPLATE_CONTENT = "foo: {{ foo }}" def setUp(self): + super().setUp() self.temp_content = mkdtemp(prefix='pelicantests.') self.temp_output = mkdtemp(prefix='pelicantests.') - self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') def tearDown(self): rmtree(self.temp_content) rmtree(self.temp_output) - locale.setlocale(locale.LC_ALL, self.old_locale) def test_generate_output(self): @@ -1299,18 +1284,15 @@ class TestStaticGenerator(unittest.TestCase): self.assertTrue(os.path.isfile(self.endfile)) -class TestJinja2Environment(unittest.TestCase): +class TestJinja2Environment(TestCaseWithCLocale): def setUp(self): self.temp_content = mkdtemp(prefix='pelicantests.') self.temp_output = mkdtemp(prefix='pelicantests.') - self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') def tearDown(self): rmtree(self.temp_content) rmtree(self.temp_output) - locale.setlocale(locale.LC_ALL, self.old_locale) def _test_jinja2_helper(self, additional_settings, content, expected): settings = get_settings() diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index 0d9586f0..870d3001 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -1,4 +1,3 @@ -import locale import os import re from posixpath import join as posix_join @@ -6,7 +5,7 @@ from unittest.mock import patch from pelican.settings import DEFAULT_CONFIG from pelican.tests.support import (mute, skipIfNoExecutable, temporary_folder, - unittest) + unittest, TestCaseWithCLocale) from pelican.tools.pelican_import import (blogger2fields, build_header, build_markdown_header, decode_wp_content, @@ -16,7 +15,6 @@ from pelican.tools.pelican_import import (blogger2fields, build_header, ) from pelican.utils import path_to_file_url, slugify - CUR_DIR = os.path.abspath(os.path.dirname(__file__)) BLOGGER_XML_SAMPLE = os.path.join(CUR_DIR, 'content', 'bloggerexport.xml') WORDPRESS_XML_SAMPLE = os.path.join(CUR_DIR, 'content', 'wordpressexport.xml') @@ -38,19 +36,9 @@ except ImportError: LXML = False -class TestWithOsDefaults(unittest.TestCase): - """Set locale to C and timezone to UTC for tests, then restore.""" - def setUp(self): - self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') - - def tearDown(self): - locale.setlocale(locale.LC_ALL, self.old_locale) - - @skipIfNoExecutable(['pandoc', '--version']) @unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module') -class TestBloggerXmlImporter(TestWithOsDefaults): +class TestBloggerXmlImporter(TestCaseWithCLocale): def setUp(self): super().setUp() @@ -95,7 +83,7 @@ class TestBloggerXmlImporter(TestWithOsDefaults): @skipIfNoExecutable(['pandoc', '--version']) @unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module') -class TestWordpressXmlImporter(TestWithOsDefaults): +class TestWordpressXmlImporter(TestCaseWithCLocale): def setUp(self): super().setUp() @@ -433,15 +421,11 @@ class TestBuildHeader(unittest.TestCase): @unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module') @unittest.skipUnless(LXML, 'Needs lxml module') -class TestWordpressXMLAttachements(unittest.TestCase): +class TestWordpressXMLAttachements(TestCaseWithCLocale): def setUp(self): - self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') + super().setUp() self.attachments = get_attachments(WORDPRESS_XML_SAMPLE) - def tearDown(self): - locale.setlocale(locale.LC_ALL, self.old_locale) - def test_recognise_attachments(self): self.assertTrue(self.attachments) self.assertTrue(len(self.attachments.keys()) == 3) @@ -485,7 +469,7 @@ class TestWordpressXMLAttachements(unittest.TestCase): directory) -class TestTumblrImporter(TestWithOsDefaults): +class TestTumblrImporter(TestCaseWithCLocale): @patch("pelican.tools.pelican_import._get_tumblr_posts") def test_posts(self, get): def get_posts(api_key, blogname, offset=0): diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index d8296285..40aff005 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -484,33 +484,25 @@ class TestUtils(LoggedTestCase): locale_available('Turkish'), 'Turkish locale needed') def test_strftime_locale_dependent_turkish(self): - # store current locale - old_locale = locale.setlocale(locale.LC_ALL) + temp_locale = 'Turkish' if platform == 'win32' else 'tr_TR.UTF-8' - if platform == 'win32': - locale.setlocale(locale.LC_ALL, 'Turkish') - else: - locale.setlocale(locale.LC_ALL, 'tr_TR.UTF-8') + with utils.temporary_locale(temp_locale): + d = utils.SafeDatetime(2012, 8, 29) - d = utils.SafeDatetime(2012, 8, 29) + # simple + self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 Ağustos 2012') + self.assertEqual(utils.strftime(d, '%A, %d %B %Y'), + 'Çarşamba, 29 Ağustos 2012') - # simple - self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 Ağustos 2012') - self.assertEqual(utils.strftime(d, '%A, %d %B %Y'), - 'Çarşamba, 29 Ağustos 2012') + # with text + self.assertEqual( + utils.strftime(d, 'Yayınlanma tarihi: %A, %d %B %Y'), + 'Yayınlanma tarihi: Çarşamba, 29 Ağustos 2012') - # with text - self.assertEqual( - utils.strftime(d, 'Yayınlanma tarihi: %A, %d %B %Y'), - 'Yayınlanma tarihi: Çarşamba, 29 Ağustos 2012') - - # non-ascii format candidate (someone might pass it… for some reason) - self.assertEqual( - utils.strftime(d, '%Y yılında %üretim artışı'), - '2012 yılında %üretim artışı') - - # restore locale back - locale.setlocale(locale.LC_ALL, old_locale) + # non-ascii format candidate (someone might pass it… for some reason) + self.assertEqual( + utils.strftime(d, '%Y yılında %üretim artışı'), + '2012 yılında %üretim artışı') # test the output of utils.strftime in a different locale # French locale @@ -518,34 +510,26 @@ class TestUtils(LoggedTestCase): locale_available('French'), 'French locale needed') def test_strftime_locale_dependent_french(self): - # store current locale - old_locale = locale.setlocale(locale.LC_ALL) + temp_locale = 'French' if platform == 'win32' else 'fr_FR.UTF-8' - if platform == 'win32': - locale.setlocale(locale.LC_ALL, 'French') - else: - locale.setlocale(locale.LC_ALL, 'fr_FR.UTF-8') + with utils.temporary_locale(temp_locale): + d = utils.SafeDatetime(2012, 8, 29) - d = utils.SafeDatetime(2012, 8, 29) + # simple + self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 août 2012') - # simple - self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 août 2012') + # depending on OS, the first letter is m or M + self.assertTrue(utils.strftime(d, '%A') in ('mercredi', 'Mercredi')) - # depending on OS, the first letter is m or M - self.assertTrue(utils.strftime(d, '%A') in ('mercredi', 'Mercredi')) + # with text + self.assertEqual( + utils.strftime(d, 'Écrit le %d %B %Y'), + 'Écrit le 29 août 2012') - # with text - self.assertEqual( - utils.strftime(d, 'Écrit le %d %B %Y'), - 'Écrit le 29 août 2012') - - # non-ascii format candidate (someone might pass it… for some reason) - self.assertEqual( - utils.strftime(d, '%écrits en %Y'), - '%écrits en 2012') - - # restore locale back - locale.setlocale(locale.LC_ALL, old_locale) + # non-ascii format candidate (someone might pass it… for some reason) + self.assertEqual( + utils.strftime(d, '%écrits en %Y'), + '%écrits en 2012') def test_maybe_pluralize(self): self.assertEqual( @@ -558,6 +542,23 @@ class TestUtils(LoggedTestCase): utils.maybe_pluralize(2, 'Article', 'Articles'), '2 Articles') + def test_temporary_locale(self): + # test with default LC category + orig_locale = locale.setlocale(locale.LC_ALL) + + with utils.temporary_locale('C'): + self.assertEqual(locale.setlocale(locale.LC_ALL), 'C') + + self.assertEqual(locale.setlocale(locale.LC_ALL), orig_locale) + + # test with custom LC category + orig_locale = locale.setlocale(locale.LC_TIME) + + with utils.temporary_locale('C', locale.LC_TIME): + self.assertEqual(locale.setlocale(locale.LC_TIME), 'C') + + self.assertEqual(locale.setlocale(locale.LC_TIME), orig_locale) + class TestCopy(unittest.TestCase): '''Tests the copy utility''' @@ -673,27 +674,27 @@ class TestDateFormatter(unittest.TestCase): def test_french_strftime(self): # This test tries to reproduce an issue that # occurred with python3.3 under macos10 only - if platform == 'win32': - locale.setlocale(locale.LC_ALL, 'French') - else: - locale.setlocale(locale.LC_ALL, 'fr_FR.UTF-8') - date = utils.SafeDatetime(2014, 8, 14) - # we compare the lower() dates since macos10 returns - # "Jeudi" for %A whereas linux reports "jeudi" - self.assertEqual( - 'jeudi, 14 août 2014', - utils.strftime(date, date_format="%A, %d %B %Y").lower()) - df = utils.DateFormatter() - self.assertEqual( - 'jeudi, 14 août 2014', - df(date, date_format="%A, %d %B %Y").lower()) + temp_locale = 'French' if platform == 'win32' else 'fr_FR.UTF-8' + + with utils.temporary_locale(temp_locale): + date = utils.SafeDatetime(2014, 8, 14) + # we compare the lower() dates since macos10 returns + # "Jeudi" for %A whereas linux reports "jeudi" + self.assertEqual( + 'jeudi, 14 août 2014', + utils.strftime(date, date_format="%A, %d %B %Y").lower()) + df = utils.DateFormatter() + self.assertEqual( + 'jeudi, 14 août 2014', + df(date, date_format="%A, %d %B %Y").lower()) + # Let us now set the global locale to C: - locale.setlocale(locale.LC_ALL, 'C') - # DateFormatter should still work as expected - # since it is the whole point of DateFormatter - # (This is where pre-2014/4/15 code fails on macos10) - df_date = df(date, date_format="%A, %d %B %Y").lower() - self.assertEqual('jeudi, 14 août 2014', df_date) + with utils.temporary_locale('C'): + # DateFormatter should still work as expected + # since it is the whole point of DateFormatter + # (This is where pre-2014/4/15 code fails on macos10) + df_date = df(date, date_format="%A, %d %B %Y").lower() + self.assertEqual('jeudi, 14 août 2014', df_date) @unittest.skipUnless(locale_available('fr_FR.UTF-8') or locale_available('French'), diff --git a/pelican/utils.py b/pelican/utils.py index 3c67369b..e1bed154 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -116,18 +116,14 @@ class DateFormatter: self.locale = locale.setlocale(locale.LC_TIME) def __call__(self, date, date_format): - old_lc_time = locale.setlocale(locale.LC_TIME) - old_lc_ctype = locale.setlocale(locale.LC_CTYPE) - locale.setlocale(locale.LC_TIME, self.locale) # on OSX, encoding from LC_CTYPE determines the unicode output in PY3 # make sure it's same as LC_TIME - locale.setlocale(locale.LC_CTYPE, self.locale) + with temporary_locale(self.locale, locale.LC_TIME), \ + temporary_locale(self.locale, locale.LC_CTYPE): - formatted = strftime(date, date_format) + formatted = strftime(date, date_format) - locale.setlocale(locale.LC_TIME, old_lc_time) - locale.setlocale(locale.LC_CTYPE, old_lc_ctype) return formatted @@ -872,3 +868,19 @@ def maybe_pluralize(count, singular, plural): if count == 1: selection = singular return '{} {}'.format(count, selection) + + +@contextmanager +def temporary_locale(temp_locale=None, lc_category=locale.LC_ALL): + ''' + Enable code to run in a context with a temporary locale + Resets the locale back when exiting context. + + Use tests.support.TestCaseWithCLocale if you want every unit test in a + class to use the C locale. + ''' + orig_locale = locale.setlocale(lc_category) + if temp_locale: + locale.setlocale(lc_category, temp_locale) + yield + locale.setlocale(lc_category, orig_locale) From a76a4195856f6dde3e00345999727d59bb201f07 Mon Sep 17 00:00:00 2001 From: Lioman Date: Sat, 28 Oct 2023 08:38:29 +0200 Subject: [PATCH 304/465] migrate configuration to PEP621 compatible one --- .pdm-python | 1 + pdm.lock | 1431 ++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 123 +++-- 3 files changed, 1500 insertions(+), 55 deletions(-) create mode 100644 .pdm-python create mode 100644 pdm.lock diff --git a/.pdm-python b/.pdm-python new file mode 100644 index 00000000..c740e1bd --- /dev/null +++ b/.pdm-python @@ -0,0 +1 @@ +/Users/eliaskirchgaessner/Development/FOSS/pelican/.venv/bin/python \ No newline at end of file diff --git a/pdm.lock b/pdm.lock new file mode 100644 index 00000000..0bfddb5e --- /dev/null +++ b/pdm.lock @@ -0,0 +1,1431 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default", "dev"] +cross_platform = true +static_urls = false +lock_version = "4.3" +content_hash = "sha256:0774056f38e53e29569c2888786ef845063ad0abcdaa8910c7795619996ef224" + +[[package]] +name = "alabaster" +version = "0.7.13" +requires_python = ">=3.6" +summary = "A configurable sidebar-enabled Sphinx theme" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] + +[[package]] +name = "appdirs" +version = "1.4.4" +summary = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +files = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] + +[[package]] +name = "attrs" +version = "23.1.0" +requires_python = ">=3.7" +summary = "Classes Without Boilerplate" +dependencies = [ + "importlib-metadata; python_version < \"3.8\"", +] +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[[package]] +name = "babel" +version = "2.13.0" +requires_python = ">=3.7" +summary = "Internationalization utilities" +dependencies = [ + "pytz>=2015.7; python_version < \"3.9\"", +] +files = [ + {file = "Babel-2.13.0-py3-none-any.whl", hash = "sha256:fbfcae1575ff78e26c7449136f1abbefc3c13ce542eeb13d43d50d8b047216ec"}, + {file = "Babel-2.13.0.tar.gz", hash = "sha256:04c3e2d28d2b7681644508f836be388ae49e0cfe91465095340395b60d00f210"}, +] + +[[package]] +name = "backports-zoneinfo" +version = "0.2.1" +requires_python = ">=3.6" +summary = "Backport of the standard library zoneinfo module" +files = [ + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, + {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, +] + +[[package]] +name = "beautifulsoup4" +version = "4.12.2" +requires_python = ">=3.6.0" +summary = "Screen-scraping library" +dependencies = [ + "soupsieve>1.2", +] +files = [ + {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, + {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, +] + +[[package]] +name = "black" +version = "19.10b0" +requires_python = ">=3.6" +summary = "The uncompromising code formatter." +dependencies = [ + "appdirs", + "attrs>=18.1.0", + "click>=6.5", + "pathspec<1,>=0.6", + "regex", + "toml>=0.9.4", + "typed-ast>=1.4.0", +] +files = [ + {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, +] + +[[package]] +name = "blinker" +version = "1.6.3" +requires_python = ">=3.7" +summary = "Fast, simple object-to-object and broadcast signaling" +files = [ + {file = "blinker-1.6.3-py3-none-any.whl", hash = "sha256:296320d6c28b006eb5e32d4712202dbcdcbf5dc482da298c2f44881c43884aaa"}, + {file = "blinker-1.6.3.tar.gz", hash = "sha256:152090d27c1c5c722ee7e48504b02d76502811ce02e1523553b4cf8c8b3d3a8d"}, +] + +[[package]] +name = "certifi" +version = "2023.7.22" +requires_python = ">=3.6" +summary = "Python package for providing Mozilla's CA Bundle." +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.1" +requires_python = ">=3.7.0" +summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +files = [ + {file = "charset-normalizer-3.3.1.tar.gz", hash = "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-win32.whl", hash = "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-win32.whl", hash = "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-win32.whl", hash = "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-win32.whl", hash = "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-win32.whl", hash = "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-win32.whl", hash = "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727"}, + {file = "charset_normalizer-3.3.1-py3-none-any.whl", hash = "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708"}, +] + +[[package]] +name = "click" +version = "8.1.7" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +dependencies = [ + "colorama; platform_system == \"Windows\"", + "importlib-metadata; python_version < \"3.8\"", +] +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.2.7" +requires_python = ">=3.7" +summary = "Code coverage measurement for Python" +files = [ + {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, + {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, + {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, + {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, + {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, + {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, + {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, + {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, + {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, + {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, + {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, + {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, + {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, + {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, + {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, + {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, + {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, + {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, +] + +[[package]] +name = "coverage" +version = "7.2.7" +extras = ["toml"] +requires_python = ">=3.7" +summary = "Code coverage measurement for Python" +dependencies = [ + "coverage==7.2.7", + "tomli; python_full_version <= \"3.11.0a6\"", +] +files = [ + {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, + {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, + {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, + {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, + {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, + {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, + {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, + {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, + {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, + {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, + {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, + {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, + {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, + {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, + {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, + {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, + {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, + {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, +] + +[[package]] +name = "distlib" +version = "0.3.7" +summary = "Distribution utilities" +files = [ + {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, + {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, +] + +[[package]] +name = "docutils" +version = "0.19" +requires_python = ">=3.7" +summary = "Docutils -- Python Documentation Utilities" +files = [ + {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, + {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.3" +requires_python = ">=3.7" +summary = "Backport of PEP 654 (exception groups)" +files = [ + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, +] + +[[package]] +name = "execnet" +version = "2.0.2" +requires_python = ">=3.7" +summary = "execnet: rapid multi-Python deployment" +files = [ + {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"}, + {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"}, +] + +[[package]] +name = "feedgenerator" +version = "2.1.0" +requires_python = ">=3.7" +summary = "Standalone version of django.utils.feedgenerator" +dependencies = [ + "pytz>=0a", +] +files = [ + {file = "feedgenerator-2.1.0-py3-none-any.whl", hash = "sha256:93b7ce1c5a86195cafd6a8e9baf6a2a863ebd6d9905e840ce5778f73efd9a8d5"}, + {file = "feedgenerator-2.1.0.tar.gz", hash = "sha256:f075f23f28fd227f097c36b212161c6cf012e1c6caaf7ff53d5d6bb02cd42b9d"}, +] + +[[package]] +name = "filelock" +version = "3.12.2" +requires_python = ">=3.7" +summary = "A platform independent file lock." +files = [ + {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, + {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, +] + +[[package]] +name = "flake8" +version = "3.9.2" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +summary = "the modular source code checker: pep8 pyflakes and co" +dependencies = [ + "importlib-metadata; python_version < \"3.8\"", + "mccabe<0.7.0,>=0.6.0", + "pycodestyle<2.8.0,>=2.7.0", + "pyflakes<2.4.0,>=2.3.0", +] +files = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] + +[[package]] +name = "flake8-import-order" +version = "0.18.2" +summary = "Flake8 and pylama plugin that checks the ordering of import statements." +dependencies = [ + "pycodestyle", + "setuptools", +] +files = [ + {file = "flake8-import-order-0.18.2.tar.gz", hash = "sha256:e23941f892da3e0c09d711babbb0c73bc735242e9b216b726616758a920d900e"}, + {file = "flake8_import_order-0.18.2-py2.py3-none-any.whl", hash = "sha256:82ed59f1083b629b030ee9d3928d9e06b6213eb196fe745b3a7d4af2168130df"}, +] + +[[package]] +name = "furo" +version = "2023.3.27" +requires_python = ">=3.7" +summary = "A clean customisable Sphinx documentation theme." +dependencies = [ + "beautifulsoup4", + "pygments>=2.7", + "sphinx-basic-ng", + "sphinx<7.0,>=5.0", +] +files = [ + {file = "furo-2023.3.27-py3-none-any.whl", hash = "sha256:4ab2be254a2d5e52792d0ca793a12c35582dd09897228a6dd47885dabd5c9521"}, + {file = "furo-2023.3.27.tar.gz", hash = "sha256:b99e7867a5cc833b2b34d7230631dd6558c7a29f93071fdbb5709634bb33c5a5"}, +] + +[[package]] +name = "idna" +version = "3.4" +requires_python = ">=3.5" +summary = "Internationalized Domain Names in Applications (IDNA)" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +summary = "Getting image size from png/jpeg/jpeg2000/gif file" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.7.0" +requires_python = ">=3.7" +summary = "Read metadata from Python packages" +dependencies = [ + "typing-extensions>=3.6.4; python_version < \"3.8\"", + "zipp>=0.5", +] +files = [ + {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, + {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +requires_python = ">=3.7" +summary = "brain-dead simple config-ini parsing" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "invoke" +version = "2.2.0" +requires_python = ">=3.6" +summary = "Pythonic task execution" +files = [ + {file = "invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"}, + {file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"}, +] + +[[package]] +name = "isort" +version = "5.11.5" +requires_python = ">=3.7.0" +summary = "A Python utility / library to sort Python imports." +files = [ + {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, + {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, +] + +[[package]] +name = "jinja2" +version = "3.1.2" +requires_python = ">=3.7" +summary = "A very fast and expressive template engine." +dependencies = [ + "MarkupSafe>=2.0", +] +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[[package]] +name = "livereload" +version = "2.6.3" +summary = "Python LiveReload is an awesome tool for web developers" +dependencies = [ + "six", + "tornado; python_version > \"2.7\"", +] +files = [ + {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, + {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, +] + +[[package]] +name = "lxml" +version = "4.9.3" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" +summary = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +files = [ + {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, + {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, + {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, + {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, + {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, + {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, + {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, + {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, + {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, + {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, + {file = "lxml-4.9.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4"}, + {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be"}, + {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13"}, + {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9"}, + {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5"}, + {file = "lxml-4.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, + {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a"}, + {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02"}, + {file = "lxml-4.9.3-cp37-cp37m-win32.whl", hash = "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f"}, + {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, + {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40"}, + {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7"}, + {file = "lxml-4.9.3-cp38-cp38-win32.whl", hash = "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574"}, + {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, + {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432"}, + {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69"}, + {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50"}, + {file = "lxml-4.9.3-cp39-cp39-win32.whl", hash = "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2"}, + {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, + {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, + {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, +] + +[[package]] +name = "markdown" +version = "3.4.4" +requires_python = ">=3.7" +summary = "Python implementation of John Gruber's Markdown." +dependencies = [ + "importlib-metadata>=4.4; python_version < \"3.10\"", +] +files = [ + {file = "Markdown-3.4.4-py3-none-any.whl", hash = "sha256:a4c1b65c0957b4bd9e7d86ddc7b3c9868fb9670660f6f99f6d1bca8954d5a941"}, + {file = "Markdown-3.4.4.tar.gz", hash = "sha256:225c6123522495d4119a90b3a3ba31a1e87a70369e03f14799ea9c0d7183a3d6"}, +] + +[[package]] +name = "markdown-it-py" +version = "2.2.0" +requires_python = ">=3.7" +summary = "Python port of markdown-it. Markdown parsing, done right!" +dependencies = [ + "mdurl~=0.1", + "typing-extensions>=3.7.4; python_version < \"3.8\"", +] +files = [ + {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, + {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.3" +requires_python = ">=3.7" +summary = "Safely add untrusted strings to HTML/XML markup." +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] + +[[package]] +name = "mccabe" +version = "0.6.1" +summary = "McCabe checker, plugin for flake8" +files = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +requires_python = ">=3.7" +summary = "Markdown URL utilities" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "packaging" +version = "23.2" +requires_python = ">=3.7" +summary = "Core utilities for Python packages" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pathspec" +version = "0.11.2" +requires_python = ">=3.7" +summary = "Utility library for gitignore style pattern matching of file paths." +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "platformdirs" +version = "3.11.0" +requires_python = ">=3.7" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +dependencies = [ + "typing-extensions>=4.7.1; python_version < \"3.8\"", +] +files = [ + {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, + {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, +] + +[[package]] +name = "pluggy" +version = "1.2.0" +requires_python = ">=3.7" +summary = "plugin and hook calling mechanisms for python" +dependencies = [ + "importlib-metadata>=0.12; python_version < \"3.8\"", +] +files = [ + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, +] + +[[package]] +name = "psutil" +version = "5.9.6" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +summary = "Cross-platform lib for process and system monitoring in Python." +files = [ + {file = "psutil-5.9.6-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c69596f9fc2f8acd574a12d5f8b7b1ba3765a641ea5d60fb4736bf3c08a8214a"}, + {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92e0cc43c524834af53e9d3369245e6cc3b130e78e26100d1f63cdb0abeb3d3c"}, + {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:748c9dd2583ed86347ed65d0035f45fa8c851e8d90354c122ab72319b5f366f4"}, + {file = "psutil-5.9.6-cp37-abi3-win32.whl", hash = "sha256:a6f01f03bf1843280f4ad16f4bde26b817847b4c1a0db59bf6419807bc5ce05c"}, + {file = "psutil-5.9.6-cp37-abi3-win_amd64.whl", hash = "sha256:6e5fb8dc711a514da83098bc5234264e551ad980cec5f85dabf4d38ed6f15e9a"}, + {file = "psutil-5.9.6-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:daecbcbd29b289aac14ece28eca6a3e60aa361754cf6da3dfb20d4d32b6c7f57"}, + {file = "psutil-5.9.6.tar.gz", hash = "sha256:e4b92ddcd7dd4cdd3f900180ea1e104932c7bce234fb88976e2a3b296441225a"}, +] + +[[package]] +name = "py" +version = "1.11.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +summary = "library with cross-python path, ini-parsing, io, code, log facilities" +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] + +[[package]] +name = "pycodestyle" +version = "2.7.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +summary = "Python style guide checker" +files = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] + +[[package]] +name = "pyflakes" +version = "2.3.1" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +summary = "passive checker of Python programs" +files = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] + +[[package]] +name = "pygments" +version = "2.16.1" +requires_python = ">=3.7" +summary = "Pygments is a syntax highlighting package written in Python." +files = [ + {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, + {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, +] + +[[package]] +name = "pytest" +version = "7.4.2" +requires_python = ">=3.7" +summary = "pytest: simple powerful testing with Python" +dependencies = [ + "colorama; sys_platform == \"win32\"", + "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", + "importlib-metadata>=0.12; python_version < \"3.8\"", + "iniconfig", + "packaging", + "pluggy<2.0,>=0.12", + "tomli>=1.0.0; python_version < \"3.11\"", +] +files = [ + {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, + {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, +] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +requires_python = ">=3.7" +summary = "Pytest plugin for measuring coverage." +dependencies = [ + "coverage[toml]>=5.2.1", + "pytest>=4.6", +] +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[[package]] +name = "pytest-forked" +version = "1.6.0" +requires_python = ">=3.7" +summary = "run tests in isolated forked subprocesses" +dependencies = [ + "py", + "pytest>=3.10", +] +files = [ + {file = "pytest-forked-1.6.0.tar.gz", hash = "sha256:4dafd46a9a600f65d822b8f605133ecf5b3e1941ebb3588e943b4e3eb71a5a3f"}, + {file = "pytest_forked-1.6.0-py3-none-any.whl", hash = "sha256:810958f66a91afb1a1e2ae83089d8dc1cd2437ac96b12963042fbb9fb4d16af0"}, +] + +[[package]] +name = "pytest-sugar" +version = "0.9.7" +summary = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)." +dependencies = [ + "packaging>=21.3", + "pytest>=6.2.0", + "termcolor>=2.1.0", +] +files = [ + {file = "pytest-sugar-0.9.7.tar.gz", hash = "sha256:f1e74c1abfa55f7241cf7088032b6e378566f16b938f3f08905e2cf4494edd46"}, + {file = "pytest_sugar-0.9.7-py2.py3-none-any.whl", hash = "sha256:8cb5a4e5f8bbcd834622b0235db9e50432f4cbd71fef55b467fe44e43701e062"}, +] + +[[package]] +name = "pytest-xdist" +version = "2.5.0" +requires_python = ">=3.6" +summary = "pytest xdist plugin for distributed testing and loop-on-failing modes" +dependencies = [ + "execnet>=1.1", + "pytest-forked", + "pytest>=6.2.0", +] +files = [ + {file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"}, + {file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"}, +] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "Extensions to the standard Python datetime module" +dependencies = [ + "six>=1.5", +] +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[[package]] +name = "pytz" +version = "2023.3.post1" +summary = "World timezone definitions, modern and historical" +files = [ + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, +] + +[[package]] +name = "regex" +version = "2023.10.3" +requires_python = ">=3.7" +summary = "Alternative regular expression module, to replace re." +files = [ + {file = "regex-2023.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c34d4f73ea738223a094d8e0ffd6d2c1a1b4c175da34d6b0de3d8d69bee6bcc"}, + {file = "regex-2023.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8f4e49fc3ce020f65411432183e6775f24e02dff617281094ba6ab079ef0915"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cd1bccf99d3ef1ab6ba835308ad85be040e6a11b0977ef7ea8c8005f01a3c29"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81dce2ddc9f6e8f543d94b05d56e70d03a0774d32f6cca53e978dc01e4fc75b8"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c6b4d23c04831e3ab61717a707a5d763b300213db49ca680edf8bf13ab5d91b"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c15ad0aee158a15e17e0495e1e18741573d04eb6da06d8b84af726cfc1ed02ee"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6239d4e2e0b52c8bd38c51b760cd870069f0bdf99700a62cd509d7a031749a55"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4a8bf76e3182797c6b1afa5b822d1d5802ff30284abe4599e1247be4fd6b03be"}, + {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9c727bbcf0065cbb20f39d2b4f932f8fa1631c3e01fcedc979bd4f51fe051c5"}, + {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3ccf2716add72f80714b9a63899b67fa711b654be3fcdd34fa391d2d274ce767"}, + {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:107ac60d1bfdc3edb53be75e2a52aff7481b92817cfdddd9b4519ccf0e54a6ff"}, + {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:00ba3c9818e33f1fa974693fb55d24cdc8ebafcb2e4207680669d8f8d7cca79a"}, + {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0a47efb1dbef13af9c9a54a94a0b814902e547b7f21acb29434504d18f36e3a"}, + {file = "regex-2023.10.3-cp310-cp310-win32.whl", hash = "sha256:36362386b813fa6c9146da6149a001b7bd063dabc4d49522a1f7aa65b725c7ec"}, + {file = "regex-2023.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:c65a3b5330b54103e7d21cac3f6bf3900d46f6d50138d73343d9e5b2900b2353"}, + {file = "regex-2023.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90a79bce019c442604662d17bf69df99090e24cdc6ad95b18b6725c2988a490e"}, + {file = "regex-2023.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c7964c2183c3e6cce3f497e3a9f49d182e969f2dc3aeeadfa18945ff7bdd7051"}, + {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ef80829117a8061f974b2fda8ec799717242353bff55f8a29411794d635d964"}, + {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5addc9d0209a9afca5fc070f93b726bf7003bd63a427f65ef797a931782e7edc"}, + {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c148bec483cc4b421562b4bcedb8e28a3b84fcc8f0aa4418e10898f3c2c0eb9b"}, + {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d1f21af4c1539051049796a0f50aa342f9a27cde57318f2fc41ed50b0dbc4ac"}, + {file = "regex-2023.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b9ac09853b2a3e0d0082104036579809679e7715671cfbf89d83c1cb2a30f58"}, + {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ebedc192abbc7fd13c5ee800e83a6df252bec691eb2c4bedc9f8b2e2903f5e2a"}, + {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d8a993c0a0ffd5f2d3bda23d0cd75e7086736f8f8268de8a82fbc4bd0ac6791e"}, + {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:be6b7b8d42d3090b6c80793524fa66c57ad7ee3fe9722b258aec6d0672543fd0"}, + {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4023e2efc35a30e66e938de5aef42b520c20e7eda7bb5fb12c35e5d09a4c43f6"}, + {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0d47840dc05e0ba04fe2e26f15126de7c755496d5a8aae4a08bda4dd8d646c54"}, + {file = "regex-2023.10.3-cp311-cp311-win32.whl", hash = "sha256:9145f092b5d1977ec8c0ab46e7b3381b2fd069957b9862a43bd383e5c01d18c2"}, + {file = "regex-2023.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:b6104f9a46bd8743e4f738afef69b153c4b8b592d35ae46db07fc28ae3d5fb7c"}, + {file = "regex-2023.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff507ae210371d4b1fe316d03433ac099f184d570a1a611e541923f78f05037"}, + {file = "regex-2023.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be5e22bbb67924dea15039c3282fa4cc6cdfbe0cbbd1c0515f9223186fc2ec5f"}, + {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a992f702c9be9c72fa46f01ca6e18d131906a7180950958f766c2aa294d4b41"}, + {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7434a61b158be563c1362d9071358f8ab91b8d928728cd2882af060481244c9e"}, + {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2169b2dcabf4e608416f7f9468737583ce5f0a6e8677c4efbf795ce81109d7c"}, + {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9e908ef5889cda4de038892b9accc36d33d72fb3e12c747e2799a0e806ec841"}, + {file = "regex-2023.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12bd4bc2c632742c7ce20db48e0d99afdc05e03f0b4c1af90542e05b809a03d9"}, + {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bc72c231f5449d86d6c7d9cc7cd819b6eb30134bb770b8cfdc0765e48ef9c420"}, + {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bce8814b076f0ce5766dc87d5a056b0e9437b8e0cd351b9a6c4e1134a7dfbda9"}, + {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:ba7cd6dc4d585ea544c1412019921570ebd8a597fabf475acc4528210d7c4a6f"}, + {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b0c7d2f698e83f15228ba41c135501cfe7d5740181d5903e250e47f617eb4292"}, + {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5a8f91c64f390ecee09ff793319f30a0f32492e99f5dc1c72bc361f23ccd0a9a"}, + {file = "regex-2023.10.3-cp312-cp312-win32.whl", hash = "sha256:ad08a69728ff3c79866d729b095872afe1e0557251da4abb2c5faff15a91d19a"}, + {file = "regex-2023.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:39cdf8d141d6d44e8d5a12a8569d5a227f645c87df4f92179bd06e2e2705e76b"}, + {file = "regex-2023.10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4a3ee019a9befe84fa3e917a2dd378807e423d013377a884c1970a3c2792d293"}, + {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76066d7ff61ba6bf3cb5efe2428fc82aac91802844c022d849a1f0f53820502d"}, + {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe50b61bab1b1ec260fa7cd91106fa9fece57e6beba05630afe27c71259c59b"}, + {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fd88f373cb71e6b59b7fa597e47e518282455c2734fd4306a05ca219a1991b0"}, + {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ab05a182c7937fb374f7e946f04fb23a0c0699c0450e9fb02ef567412d2fa3"}, + {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dac37cf08fcf2094159922edc7a2784cfcc5c70f8354469f79ed085f0328ebdf"}, + {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e54ddd0bb8fb626aa1f9ba7b36629564544954fff9669b15da3610c22b9a0991"}, + {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3367007ad1951fde612bf65b0dffc8fd681a4ab98ac86957d16491400d661302"}, + {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:16f8740eb6dbacc7113e3097b0a36065a02e37b47c936b551805d40340fb9971"}, + {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:f4f2ca6df64cbdd27f27b34f35adb640b5d2d77264228554e68deda54456eb11"}, + {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:39807cbcbe406efca2a233884e169d056c35aa7e9f343d4e78665246a332f597"}, + {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7eece6fbd3eae4a92d7c748ae825cbc1ee41a89bb1c3db05b5578ed3cfcfd7cb"}, + {file = "regex-2023.10.3-cp37-cp37m-win32.whl", hash = "sha256:ce615c92d90df8373d9e13acddd154152645c0dc060871abf6bd43809673d20a"}, + {file = "regex-2023.10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0f649fa32fe734c4abdfd4edbb8381c74abf5f34bc0b3271ce687b23729299ed"}, + {file = "regex-2023.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b98b7681a9437262947f41c7fac567c7e1f6eddd94b0483596d320092004533"}, + {file = "regex-2023.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:91dc1d531f80c862441d7b66c4505cd6ea9d312f01fb2f4654f40c6fdf5cc37a"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82fcc1f1cc3ff1ab8a57ba619b149b907072e750815c5ba63e7aa2e1163384a4"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7979b834ec7a33aafae34a90aad9f914c41fd6eaa8474e66953f3f6f7cbd4368"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef71561f82a89af6cfcbee47f0fabfdb6e63788a9258e913955d89fdd96902ab"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd829712de97753367153ed84f2de752b86cd1f7a88b55a3a775eb52eafe8a94"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00e871d83a45eee2f8688d7e6849609c2ca2a04a6d48fba3dff4deef35d14f07"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:706e7b739fdd17cb89e1fbf712d9dc21311fc2333f6d435eac2d4ee81985098c"}, + {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cc3f1c053b73f20c7ad88b0d1d23be7e7b3901229ce89f5000a8399746a6e039"}, + {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f85739e80d13644b981a88f529d79c5bdf646b460ba190bffcaf6d57b2a9863"}, + {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:741ba2f511cc9626b7561a440f87d658aabb3d6b744a86a3c025f866b4d19e7f"}, + {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e77c90ab5997e85901da85131fd36acd0ed2221368199b65f0d11bca44549711"}, + {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:979c24cbefaf2420c4e377ecd1f165ea08cc3d1fbb44bdc51bccbbf7c66a2cb4"}, + {file = "regex-2023.10.3-cp38-cp38-win32.whl", hash = "sha256:58837f9d221744d4c92d2cf7201c6acd19623b50c643b56992cbd2b745485d3d"}, + {file = "regex-2023.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:c55853684fe08d4897c37dfc5faeff70607a5f1806c8be148f1695be4a63414b"}, + {file = "regex-2023.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2c54e23836650bdf2c18222c87f6f840d4943944146ca479858404fedeb9f9af"}, + {file = "regex-2023.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:69c0771ca5653c7d4b65203cbfc5e66db9375f1078689459fe196fe08b7b4930"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ac965a998e1388e6ff2e9781f499ad1eaa41e962a40d11c7823c9952c77123e"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c0e8fae5b27caa34177bdfa5a960c46ff2f78ee2d45c6db15ae3f64ecadde14"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c56c3d47da04f921b73ff9415fbaa939f684d47293f071aa9cbb13c94afc17d"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ef1e014eed78ab650bef9a6a9cbe50b052c0aebe553fb2881e0453717573f52"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d29338556a59423d9ff7b6eb0cb89ead2b0875e08fe522f3e068b955c3e7b59b"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9c6d0ced3c06d0f183b73d3c5920727268d2201aa0fe6d55c60d68c792ff3588"}, + {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:994645a46c6a740ee8ce8df7911d4aee458d9b1bc5639bc968226763d07f00fa"}, + {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:66e2fe786ef28da2b28e222c89502b2af984858091675044d93cb50e6f46d7af"}, + {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:11175910f62b2b8c055f2b089e0fedd694fe2be3941b3e2633653bc51064c528"}, + {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:06e9abc0e4c9ab4779c74ad99c3fc10d3967d03114449acc2c2762ad4472b8ca"}, + {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fb02e4257376ae25c6dd95a5aec377f9b18c09be6ebdefa7ad209b9137b73d48"}, + {file = "regex-2023.10.3-cp39-cp39-win32.whl", hash = "sha256:3b2c3502603fab52d7619b882c25a6850b766ebd1b18de3df23b2f939360e1bd"}, + {file = "regex-2023.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:adbccd17dcaff65704c856bd29951c58a1bd4b2b0f8ad6b826dbd543fe740988"}, + {file = "regex-2023.10.3.tar.gz", hash = "sha256:3fef4f844d2290ee0ba57addcec17eec9e3df73f10a2748485dfd6a3a188cc0f"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +requires_python = ">=3.7" +summary = "Python HTTP for Humans." +dependencies = [ + "certifi>=2017.4.17", + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1", +] +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[[package]] +name = "rich" +version = "13.6.0" +requires_python = ">=3.7.0" +summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +dependencies = [ + "markdown-it-py>=2.2.0", + "pygments<3.0.0,>=2.13.0", + "typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"", +] +files = [ + {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, + {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, +] + +[[package]] +name = "setuptools" +version = "68.0.0" +requires_python = ">=3.7" +summary = "Easily download, build, install, upgrade, and uninstall Python packages" +files = [ + {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, + {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, +] + +[[package]] +name = "six" +version = "1.16.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python 2 and 3 compatibility utilities" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "smartypants" +version = "2.0.1" +summary = "Python with the SmartyPants" +files = [ + {file = "smartypants-2.0.1-py2.py3-none-any.whl", hash = "sha256:8db97f7cbdf08d15b158a86037cd9e116b4cf37703d24e0419a0d64ca5808f0d"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +summary = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.4.1" +requires_python = ">=3.7" +summary = "A modern CSS selector implementation for Beautiful Soup." +files = [ + {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, + {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, +] + +[[package]] +name = "sphinx" +version = "5.3.0" +requires_python = ">=3.6" +summary = "Python documentation generator" +dependencies = [ + "Jinja2>=3.0", + "Pygments>=2.12", + "alabaster<0.8,>=0.7", + "babel>=2.9", + "colorama>=0.4.5; sys_platform == \"win32\"", + "docutils<0.20,>=0.14", + "imagesize>=1.3", + "importlib-metadata>=4.8; python_version < \"3.10\"", + "packaging>=21.0", + "requests>=2.5.0", + "snowballstemmer>=2.0", + "sphinxcontrib-applehelp", + "sphinxcontrib-devhelp", + "sphinxcontrib-htmlhelp>=2.0.0", + "sphinxcontrib-jsmath", + "sphinxcontrib-qthelp", + "sphinxcontrib-serializinghtml>=1.1.5", +] +files = [ + {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, + {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, +] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b2" +requires_python = ">=3.7" +summary = "A modern skeleton for Sphinx themes." +dependencies = [ + "sphinx>=4.0", +] +files = [ + {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, + {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.2" +requires_python = ">=3.5" +summary = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +files = [ + {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, + {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +requires_python = ">=3.5" +summary = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +files = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.0" +requires_python = ">=3.6" +summary = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +files = [ + {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, + {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +requires_python = ">=3.5" +summary = "A sphinx extension which renders display math in HTML via JavaScript" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +requires_python = ">=3.5" +summary = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +files = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +requires_python = ">=3.5" +summary = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +files = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] + +[[package]] +name = "termcolor" +version = "2.3.0" +requires_python = ">=3.7" +summary = "ANSI color formatting for output in terminal" +files = [ + {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, + {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, +] + +[[package]] +name = "toml" +version = "0.10.2" +requires_python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python Library for Tom's Obvious, Minimal Language" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +requires_python = ">=3.7" +summary = "A lil' TOML parser" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tornado" +version = "6.2" +requires_python = ">= 3.7" +summary = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +files = [ + {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, + {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"}, + {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"}, + {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, + {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, +] + +[[package]] +name = "tox" +version = "3.28.0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +summary = "tox is a generic virtualenv management and test command line tool" +dependencies = [ + "colorama>=0.4.1; platform_system == \"Windows\"", + "filelock>=3.0.0", + "importlib-metadata>=0.12; python_version < \"3.8\"", + "packaging>=14", + "pluggy>=0.12.0", + "py>=1.4.17", + "six>=1.14.0", + "tomli>=2.0.1; python_version >= \"3.7\" and python_version < \"3.11\"", + "virtualenv!=20.0.0,!=20.0.1,!=20.0.2,!=20.0.3,!=20.0.4,!=20.0.5,!=20.0.6,!=20.0.7,>=16.0.0", +] +files = [ + {file = "tox-3.28.0-py2.py3-none-any.whl", hash = "sha256:57b5ab7e8bb3074edc3c0c0b4b192a4f3799d3723b2c5b76f1fa9f2d40316eea"}, + {file = "tox-3.28.0.tar.gz", hash = "sha256:d0d28f3fe6d6d7195c27f8b054c3e99d5451952b54abdae673b71609a581f640"}, +] + +[[package]] +name = "typed-ast" +version = "1.5.5" +requires_python = ">=3.6" +summary = "a fork of Python 2 and 3 ast modules with type comment support" +files = [ + {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, + {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, + {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, + {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, + {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, + {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, + {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, + {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, + {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, + {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, + {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, + {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, + {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, + {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, + {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, + {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, + {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, + {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, + {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, + {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, + {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, + {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, + {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, + {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, + {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, + {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, + {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, + {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, + {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, + {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, + {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, + {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, + {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, + {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, + {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, +] + +[[package]] +name = "typing-extensions" +version = "4.7.1" +requires_python = ">=3.7" +summary = "Backported and Experimental Type Hints for Python 3.7+" +files = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "typogrify" +version = "2.0.7" +summary = "Filters to enhance web typography, including support for Django & Jinja templates" +dependencies = [ + "smartypants>=1.8.3", +] +files = [ + {file = "typogrify-2.0.7.tar.gz", hash = "sha256:8be4668cda434163ce229d87ca273a11922cb1614cb359970b7dc96eed13cb38"}, +] + +[[package]] +name = "unidecode" +version = "1.3.7" +requires_python = ">=3.5" +summary = "ASCII transliterations of Unicode text" +files = [ + {file = "Unidecode-1.3.7-py3-none-any.whl", hash = "sha256:663a537f506834ed836af26a81b210d90cbde044c47bfbdc0fbbc9f94c86a6e4"}, + {file = "Unidecode-1.3.7.tar.gz", hash = "sha256:3c90b4662aa0de0cb591884b934ead8d2225f1800d8da675a7750cbc3bd94610"}, +] + +[[package]] +name = "urllib3" +version = "2.0.7" +requires_python = ">=3.7" +summary = "HTTP library with thread-safe connection pooling, file post, and more." +files = [ + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, +] + +[[package]] +name = "virtualenv" +version = "20.24.6" +requires_python = ">=3.7" +summary = "Virtual Python Environment builder" +dependencies = [ + "distlib<1,>=0.3.7", + "filelock<4,>=3.12.2", + "importlib-metadata>=6.6; python_version < \"3.8\"", + "platformdirs<4,>=3.9.1", +] +files = [ + {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"}, + {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"}, +] + +[[package]] +name = "zipp" +version = "3.15.0" +requires_python = ">=3.7" +summary = "Backport of pathlib-compatible object wrapper for zip files" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] diff --git a/pyproject.toml b/pyproject.toml index 02d1160e..9383029b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,17 +1,30 @@ -[tool.poetry] +[project] +authors = [{ name = "Justin Mayer", email = "authors@getpelican.com" }] +license = { text = "AGPLv3" } +requires-python = ">=3.8,<4.0" +dependencies = [ + "blinker>=1.4", + "docutils>=0.16", + "feedgenerator>=1.9", + "jinja2>=2.7", + "pygments>=2.6", + "python-dateutil>=2.8", + "rich>=10.1", + "unidecode>=1.1", + "backports-zoneinfo<1.0.0,>=0.2.1; python_version < \"3.9\"", +] name = "pelican" version = "4.8.0" description = "Static site generator supporting Markdown and reStructuredText" -authors = ["Justin Mayer "] -license = "AGPLv3" readme = "README.rst" keywords = ["static site generator", "static sites", "ssg"] - -homepage = "https://getpelican.com" -repository = "https://github.com/getpelican/pelican" -documentation = "https://docs.getpelican.com" - classifiers = [ + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Development Status :: 5 - Production/Stable", "Environment :: Console", "Framework :: Pelican", @@ -25,56 +38,12 @@ classifiers = [ "Topic :: Text Processing :: Markup :: reStructuredText", ] -[tool.poetry.urls] -"Funding" = "https://donate.getpelican.com/" -"Tracker" = "https://github.com/getpelican/pelican/issues" - -[tool.poetry.dependencies] -python = ">=3.7,<4.0" -blinker = ">=1.4" -docutils = ">=0.16" -feedgenerator = ">=1.9" -jinja2 = ">=2.7" -pygments = ">=2.6" -python-dateutil = ">=2.8" -rich = ">=10.1" -unidecode = ">=1.1" -markdown = {version = ">=3.1", optional = true} -backports-zoneinfo = {version = "^0.2.1", python = "<3.9"} -watchfiles = "^0.19.0" - -[tool.poetry.dev-dependencies] -BeautifulSoup4 = "^4.9" -jinja2 = "~3.1.2" -lxml = "^4.3" -markdown = "~3.4.3" -typogrify = "^2.0" -sphinx = "^5.1" -furo = "2023.03.27" -livereload = "^2.6" -psutil = {version = "^5.7", optional = true} -pygments = "~2.15" -pytest = "^7.1" -pytest-cov = "^4.0" -pytest-sugar = "^0.9.5" -pytest-xdist = "^2.0" -ruff = "^0.1.3" -tox = {version = "^3.13", optional = true} -flake8 = "^3.8" -flake8-import-order = "^0.18.1" -invoke = "^2.0" -isort = "^5.2" -black = {version = "^19.10b0", allow-prereleases = true} - -[tool.poetry.extras] -markdown = ["markdown"] - -[tool.poetry.scripts] +[project.scripts] pelican = "pelican.__main__:main" pelican-import = "pelican.tools.pelican_import:main" -pelican-plugins = "pelican.plugins._utils:list_plugins" pelican-quickstart = "pelican.tools.pelican_quickstart:main" pelican-themes = "pelican.tools.pelican_themes:main" +pelican-plugins = "pelican.plugins._utils:list_plugins" [tool.autopub] project-name = "Pelican" @@ -86,5 +55,49 @@ version-header = "=" version-strings = ["setup.py"] build-system = "setuptools" +[tool.pdm] + +[tool.pdm.scripts] +docbuild = "invoke docbuild" +docserve = "invoke docserve" + +[tool.pdm.dev-dependencies] +dev = [ + "BeautifulSoup4<5.0,>=4.9", + "jinja2~=3.1.2", + "lxml<5.0,>=4.3", + "markdown~=3.4.3", + "typogrify<3.0,>=2.0", + "sphinx<6.0,>=5.1", + "furo==2023.03.27", + "livereload<3.0,>=2.6", + "psutil<6.0,>=5.7", + "pygments~=2.15", + "pytest<8.0,>=7.1", + "pytest-cov<5.0,>=4.0", + "pytest-sugar<1.0.0,>=0.9.5", + "pytest-xdist<3.0,>=2.0", + "tox<4.0,>=3.13", + "flake8<4.0,>=3.8", + "flake8-import-order<1.0.0,>=0.18.1", + "invoke<3.0,>=2.0", + "isort<6.0,>=5.2", + "black<20.0,>=19.10b0", +] + +[tool.pdm.build] +includes = [] + +[project.urls] +Funding = "https://donate.getpelican.com/" +Tracker = "https://github.com/getpelican/pelican/issues" +Homepage = "https://getpelican.com" +Repository = "https://github.com/getpelican/pelican" +Documentation = "https://docs.getpelican.com" + +[project.optional-dependencies] +markdown = ["markdown>=3.1"] + [build-system] -requires = ["setuptools >= 40.6.0", "wheel"] +requires = ["pdm-backend"] +build-backend = "pdm.backend" From c18f1a7308d528743746986d9aaebf29b04af645 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sat, 28 Oct 2023 12:17:57 +0200 Subject: [PATCH 305/465] Re-order pyproject items, with other small fixes --- pyproject.toml | 74 ++++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9383029b..da9deec1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,33 @@ [project] +name = "pelican" authors = [{ name = "Justin Mayer", email = "authors@getpelican.com" }] +description = "Static site generator supporting Markdown and reStructuredText" +version = "4.8.0" license = { text = "AGPLv3" } -requires-python = ">=3.8,<4.0" +readme = "README.rst" +keywords = ["static site generator", "static sites", "ssg"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Framework :: Pelican", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System", + "Topic :: Internet :: WWW/HTTP :: Site Management", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Text Processing :: Markup :: Markdown", + "Topic :: Text Processing :: Markup :: HTML", + "Topic :: Text Processing :: Markup :: reStructuredText", +] +requires-python = ">=3.8.1,<4.0" dependencies = [ "blinker>=1.4", "docutils>=0.16", @@ -11,33 +37,19 @@ dependencies = [ "python-dateutil>=2.8", "rich>=10.1", "unidecode>=1.1", - "backports-zoneinfo<1.0.0,>=0.2.1; python_version < \"3.9\"", -] -name = "pelican" -version = "4.8.0" -description = "Static site generator supporting Markdown and reStructuredText" -readme = "README.rst" -keywords = ["static site generator", "static sites", "ssg"] -classifiers = [ - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "Framework :: Pelican", - "Operating System :: OS Independent", - "Programming Language :: Python :: Implementation :: CPython", - "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System", - "Topic :: Internet :: WWW/HTTP :: Site Management", - "Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Text Processing :: Markup :: Markdown", - "Topic :: Text Processing :: Markup :: HTML", - "Topic :: Text Processing :: Markup :: reStructuredText", + "backports-zoneinfo<1.0.0,>=0.2.1; python_version<3.9", ] +[project.optional-dependencies] +markdown = ["markdown>=3.1"] + +[project.urls] +Homepage = "https://getpelican.com" +Funding = "https://donate.getpelican.com/" +"Issue Tracker" = "https://github.com/getpelican/pelican/issues" +Repository = "https://github.com/getpelican/pelican" +Documentation = "https://docs.getpelican.com" + [project.scripts] pelican = "pelican.__main__:main" pelican-import = "pelican.tools.pelican_import:main" @@ -88,16 +100,6 @@ dev = [ [tool.pdm.build] includes = [] -[project.urls] -Funding = "https://donate.getpelican.com/" -Tracker = "https://github.com/getpelican/pelican/issues" -Homepage = "https://getpelican.com" -Repository = "https://github.com/getpelican/pelican" -Documentation = "https://docs.getpelican.com" - -[project.optional-dependencies] -markdown = ["markdown>=3.1"] - [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" From 8b6d2159345ee9260a2fda0911decab3248f112f Mon Sep 17 00:00:00 2001 From: Lioman Date: Sat, 28 Oct 2023 17:43:16 +0200 Subject: [PATCH 306/465] migrate configuration to PEP621 compatible config - adapt documentation - add wheel tests to check wheel contents. - adapt pipeline to use pdm - adapt autopub config - add scripts as shortcuts to invoke tasks --- .github/workflows/main.yml | 17 +- .gitignore | 3 +- .pdm-python | 1 - docs/conf.py | 82 +- docs/contribute.rst | 12 +- docs/install.rst | 2 +- docs/quickstart.rst | 2 +- pdm.lock | 1431 ------------------------ pelican/tests/build_test/conftest.py | 7 + pelican/tests/build_test/test_wheel.py | 28 + pyproject.toml | 14 +- requirements/docs.pip | 1 + tasks.py | 6 +- 13 files changed, 112 insertions(+), 1494 deletions(-) delete mode 100644 .pdm-python delete mode 100644 pdm.lock create mode 100644 pelican/tests/build_test/conftest.py create mode 100644 pelican/tests/build_test/test_wheel.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d867122f..ff1c15b5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -51,23 +51,18 @@ jobs: lint: name: Lint runs-on: ubuntu-latest - steps: - uses: actions/checkout@v3 - - name: Install Poetry - run: pipx install poetry - - name: Set up Python - uses: actions/setup-python@v4 + - uses: pdm-project/setup-pdm@v3 with: - python-version: "3.9" - cache: "poetry" - cache-dependency-path: "pyproject.toml" + python-version: 3.9 + cache: true + cache-dependency-path: ./pyproject.toml - name: Install dependencies run: | - poetry env use "3.9" - poetry install --no-interaction --no-root + pdm install - name: Run linters - run: poetry run invoke lint --diff + run: pdm lint --diff docs: name: Build docs diff --git a/.gitignore b/.gitignore index b94526d6..b27f3eb9 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ htmlcov venv samples/output *.pem -poetry.lock +*.lock +.pdm-python diff --git a/.pdm-python b/.pdm-python deleted file mode 100644 index c740e1bd..00000000 --- a/.pdm-python +++ /dev/null @@ -1 +0,0 @@ -/Users/eliaskirchgaessner/Development/FOSS/pelican/.venv/bin/python \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index f00ed3c2..8d8078a2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,48 +2,58 @@ import datetime import os import sys -from pelican import __version__ +if sys.version_info >= (3, 11): + import tomllib +else: + import tomli as tomllib + sys.path.append(os.path.abspath(os.pardir)) + +with open("../pyproject.toml", "rb") as f: + project_data = tomllib.load(f).get("project") + if project_data is None: + raise KeyError("project data is not found") + + # -- General configuration ---------------------------------------------------- -templates_path = ['_templates'] +templates_path = ["_templates"] extensions = [ "sphinx.ext.autodoc", "sphinx.ext.ifconfig", "sphinx.ext.extlinks", "sphinxext.opengraph", ] -source_suffix = '.rst' -master_doc = 'index' -project = 'Pelican' +source_suffix = ".rst" +master_doc = "index" +project = project_data.get("name").upper() year = datetime.datetime.now().date().year -copyright = f'2010–{year}' -exclude_patterns = ['_build'] -release = __version__ -version = '.'.join(release.split('.')[:1]) -last_stable = __version__ -rst_prolog = ''' -.. |last_stable| replace:: :pelican-doc:`{}` -'''.format(last_stable) +copyright = f"2010–{year}" +exclude_patterns = ["_build"] +release = project_data.get("version") +version = ".".join(release.split(".")[:1]) +last_stable = project_data.get("version") +rst_prolog = f""" +.. |last_stable| replace:: :pelican-doc:`{last_stable}` +.. |min_python| replace:: {project_data.get('requires-python').split(",")[0]} +""" -extlinks = { - 'pelican-doc': ('https://docs.getpelican.com/en/latest/%s.html', '%s') -} +extlinks = {"pelican-doc": ("https://docs.getpelican.com/en/latest/%s.html", "%s")} # -- Options for HTML output -------------------------------------------------- -html_theme = 'furo' -html_title = f'{project} {release}' -html_static_path = ['_static'] +html_theme = "furo" +html_title = f"{project} {release}" +html_static_path = ["_static"] html_theme_options = { - 'light_logo': 'pelican-logo.svg', - 'dark_logo': 'pelican-logo.svg', - 'navigation_with_keys': True, + "light_logo": "pelican-logo.svg", + "dark_logo": "pelican-logo.svg", + "navigation_with_keys": True, } # Output file base name for HTML help builder. -htmlhelp_basename = 'Pelicandoc' +htmlhelp_basename = "Pelicandoc" html_use_smartypants = True @@ -59,21 +69,29 @@ html_show_sourcelink = False def setup(app): # overrides for wide tables in RTD theme - app.add_css_file('theme_overrides.css') # path relative to _static + app.add_css_file("theme_overrides.css") # path relative to _static # -- Options for LaTeX output ------------------------------------------------- latex_documents = [ - ('index', 'Pelican.tex', 'Pelican Documentation', 'Justin Mayer', - 'manual'), + ("index", "Pelican.tex", "Pelican Documentation", "Justin Mayer", "manual"), ] # -- Options for manual page output ------------------------------------------- man_pages = [ - ('index', 'pelican', 'pelican documentation', - ['Justin Mayer'], 1), - ('pelican-themes', 'pelican-themes', 'A theme manager for Pelican', - ['Mickaël Raybaud'], 1), - ('themes', 'pelican-theming', 'How to create themes for Pelican', - ['The Pelican contributors'], 1) + ("index", "pelican", "pelican documentation", ["Justin Mayer"], 1), + ( + "pelican-themes", + "pelican-themes", + "A theme manager for Pelican", + ["Mickaël Raybaud"], + 1, + ), + ( + "themes", + "pelican-theming", + "How to create themes for Pelican", + ["The Pelican contributors"], + 1, + ), ] diff --git a/docs/contribute.rst b/docs/contribute.rst index cfbfe351..a5292dd5 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -15,16 +15,16 @@ Setting up the development environment ====================================== While there are many ways to set up one's development environment, the following -instructions will utilize Pip_ and Poetry_. These tools facilitate managing +instructions will utilize Pip_ and pdm_. These tools facilitate managing virtual environments for separate Python projects that are isolated from one another, so you can use different packages (and package versions) for each. -Please note that Python 3.7+ is required for Pelican development. +Please note that Python |min_python| is required for Pelican development. -*(Optional)* If you prefer to `install Poetry `_ once for use with multiple projects, +*(Optional)* If you prefer to `install pdm `_ once for use with multiple projects, you can install it via:: - curl -sSL https://install.python-poetry.org | python3 - + curl -sSL https://pdm.fming.dev/install-pdm.py | python3 - Point your web browser to the `Pelican repository`_ and tap the **Fork** button at top-right. Then clone the source for your fork and add the upstream project @@ -35,7 +35,7 @@ as a Git remote:: cd ~/projects/pelican git remote add upstream https://github.com/getpelican/pelican.git -While Poetry can dynamically create and manage virtual environments, we're going +While pdm can dynamically create and manage virtual environments, we're going to manually create and activate a virtual environment:: mkdir ~/virtualenvs && cd ~/virtualenvs @@ -51,7 +51,7 @@ Install the needed dependencies and set up the project:: Your local environment should now be ready to go! .. _Pip: https://pip.pypa.io/ -.. _Poetry: https://python-poetry.org/ +.. _pdm: https://pdm.fming.dev/latest/ .. _Pelican repository: https://github.com/getpelican/pelican Development diff --git a/docs/install.rst b/docs/install.rst index ea47311f..aa3c92d0 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -1,7 +1,7 @@ Installing Pelican ################## -Pelican currently runs best on 3.7+; earlier versions of Python are not supported. +Pelican currently runs best on |min_python|; earlier versions of Python are not supported. You can install Pelican via several different methods. The simplest is via Pip_:: diff --git a/docs/quickstart.rst b/docs/quickstart.rst index f1198b94..686b822f 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -8,7 +8,7 @@ Installation ------------ Install Pelican (and optionally Markdown if you intend to use it) on Python -3.7+ by running the following command in your preferred terminal, prefixing +|min_python| by running the following command in your preferred terminal, prefixing with ``sudo`` if permissions warrant:: python -m pip install "pelican[markdown]" diff --git a/pdm.lock b/pdm.lock deleted file mode 100644 index 0bfddb5e..00000000 --- a/pdm.lock +++ /dev/null @@ -1,1431 +0,0 @@ -# This file is @generated by PDM. -# It is not intended for manual editing. - -[metadata] -groups = ["default", "dev"] -cross_platform = true -static_urls = false -lock_version = "4.3" -content_hash = "sha256:0774056f38e53e29569c2888786ef845063ad0abcdaa8910c7795619996ef224" - -[[package]] -name = "alabaster" -version = "0.7.13" -requires_python = ">=3.6" -summary = "A configurable sidebar-enabled Sphinx theme" -files = [ - {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, - {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, -] - -[[package]] -name = "appdirs" -version = "1.4.4" -summary = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -files = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] - -[[package]] -name = "attrs" -version = "23.1.0" -requires_python = ">=3.7" -summary = "Classes Without Boilerplate" -dependencies = [ - "importlib-metadata; python_version < \"3.8\"", -] -files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, -] - -[[package]] -name = "babel" -version = "2.13.0" -requires_python = ">=3.7" -summary = "Internationalization utilities" -dependencies = [ - "pytz>=2015.7; python_version < \"3.9\"", -] -files = [ - {file = "Babel-2.13.0-py3-none-any.whl", hash = "sha256:fbfcae1575ff78e26c7449136f1abbefc3c13ce542eeb13d43d50d8b047216ec"}, - {file = "Babel-2.13.0.tar.gz", hash = "sha256:04c3e2d28d2b7681644508f836be388ae49e0cfe91465095340395b60d00f210"}, -] - -[[package]] -name = "backports-zoneinfo" -version = "0.2.1" -requires_python = ">=3.6" -summary = "Backport of the standard library zoneinfo module" -files = [ - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, - {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, - {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, - {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, -] - -[[package]] -name = "beautifulsoup4" -version = "4.12.2" -requires_python = ">=3.6.0" -summary = "Screen-scraping library" -dependencies = [ - "soupsieve>1.2", -] -files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, -] - -[[package]] -name = "black" -version = "19.10b0" -requires_python = ">=3.6" -summary = "The uncompromising code formatter." -dependencies = [ - "appdirs", - "attrs>=18.1.0", - "click>=6.5", - "pathspec<1,>=0.6", - "regex", - "toml>=0.9.4", - "typed-ast>=1.4.0", -] -files = [ - {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, -] - -[[package]] -name = "blinker" -version = "1.6.3" -requires_python = ">=3.7" -summary = "Fast, simple object-to-object and broadcast signaling" -files = [ - {file = "blinker-1.6.3-py3-none-any.whl", hash = "sha256:296320d6c28b006eb5e32d4712202dbcdcbf5dc482da298c2f44881c43884aaa"}, - {file = "blinker-1.6.3.tar.gz", hash = "sha256:152090d27c1c5c722ee7e48504b02d76502811ce02e1523553b4cf8c8b3d3a8d"}, -] - -[[package]] -name = "certifi" -version = "2023.7.22" -requires_python = ">=3.6" -summary = "Python package for providing Mozilla's CA Bundle." -files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.1" -requires_python = ">=3.7.0" -summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -files = [ - {file = "charset-normalizer-3.3.1.tar.gz", hash = "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-win32.whl", hash = "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f"}, - {file = "charset_normalizer-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-win32.whl", hash = "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8"}, - {file = "charset_normalizer-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-win32.whl", hash = "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61"}, - {file = "charset_normalizer-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-win32.whl", hash = "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9"}, - {file = "charset_normalizer-3.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-win32.whl", hash = "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb"}, - {file = "charset_normalizer-3.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-win32.whl", hash = "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4"}, - {file = "charset_normalizer-3.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727"}, - {file = "charset_normalizer-3.3.1-py3-none-any.whl", hash = "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708"}, -] - -[[package]] -name = "click" -version = "8.1.7" -requires_python = ">=3.7" -summary = "Composable command line interface toolkit" -dependencies = [ - "colorama; platform_system == \"Windows\"", - "importlib-metadata; python_version < \"3.8\"", -] -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -summary = "Cross-platform colored terminal text." -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.2.7" -requires_python = ">=3.7" -summary = "Code coverage measurement for Python" -files = [ - {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, - {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, - {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, - {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, - {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, - {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, - {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, - {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, - {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, - {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, - {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, - {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, - {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, - {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, - {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, - {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, - {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, - {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, -] - -[[package]] -name = "coverage" -version = "7.2.7" -extras = ["toml"] -requires_python = ">=3.7" -summary = "Code coverage measurement for Python" -dependencies = [ - "coverage==7.2.7", - "tomli; python_full_version <= \"3.11.0a6\"", -] -files = [ - {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, - {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, - {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, - {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, - {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, - {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, - {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, - {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, - {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, - {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, - {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, - {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, - {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, - {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, - {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, - {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, - {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, - {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, -] - -[[package]] -name = "distlib" -version = "0.3.7" -summary = "Distribution utilities" -files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, -] - -[[package]] -name = "docutils" -version = "0.19" -requires_python = ">=3.7" -summary = "Docutils -- Python Documentation Utilities" -files = [ - {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, - {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.1.3" -requires_python = ">=3.7" -summary = "Backport of PEP 654 (exception groups)" -files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, -] - -[[package]] -name = "execnet" -version = "2.0.2" -requires_python = ">=3.7" -summary = "execnet: rapid multi-Python deployment" -files = [ - {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"}, - {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"}, -] - -[[package]] -name = "feedgenerator" -version = "2.1.0" -requires_python = ">=3.7" -summary = "Standalone version of django.utils.feedgenerator" -dependencies = [ - "pytz>=0a", -] -files = [ - {file = "feedgenerator-2.1.0-py3-none-any.whl", hash = "sha256:93b7ce1c5a86195cafd6a8e9baf6a2a863ebd6d9905e840ce5778f73efd9a8d5"}, - {file = "feedgenerator-2.1.0.tar.gz", hash = "sha256:f075f23f28fd227f097c36b212161c6cf012e1c6caaf7ff53d5d6bb02cd42b9d"}, -] - -[[package]] -name = "filelock" -version = "3.12.2" -requires_python = ">=3.7" -summary = "A platform independent file lock." -files = [ - {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, - {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, -] - -[[package]] -name = "flake8" -version = "3.9.2" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -summary = "the modular source code checker: pep8 pyflakes and co" -dependencies = [ - "importlib-metadata; python_version < \"3.8\"", - "mccabe<0.7.0,>=0.6.0", - "pycodestyle<2.8.0,>=2.7.0", - "pyflakes<2.4.0,>=2.3.0", -] -files = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, -] - -[[package]] -name = "flake8-import-order" -version = "0.18.2" -summary = "Flake8 and pylama plugin that checks the ordering of import statements." -dependencies = [ - "pycodestyle", - "setuptools", -] -files = [ - {file = "flake8-import-order-0.18.2.tar.gz", hash = "sha256:e23941f892da3e0c09d711babbb0c73bc735242e9b216b726616758a920d900e"}, - {file = "flake8_import_order-0.18.2-py2.py3-none-any.whl", hash = "sha256:82ed59f1083b629b030ee9d3928d9e06b6213eb196fe745b3a7d4af2168130df"}, -] - -[[package]] -name = "furo" -version = "2023.3.27" -requires_python = ">=3.7" -summary = "A clean customisable Sphinx documentation theme." -dependencies = [ - "beautifulsoup4", - "pygments>=2.7", - "sphinx-basic-ng", - "sphinx<7.0,>=5.0", -] -files = [ - {file = "furo-2023.3.27-py3-none-any.whl", hash = "sha256:4ab2be254a2d5e52792d0ca793a12c35582dd09897228a6dd47885dabd5c9521"}, - {file = "furo-2023.3.27.tar.gz", hash = "sha256:b99e7867a5cc833b2b34d7230631dd6558c7a29f93071fdbb5709634bb33c5a5"}, -] - -[[package]] -name = "idna" -version = "3.4" -requires_python = ">=3.5" -summary = "Internationalized Domain Names in Applications (IDNA)" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "imagesize" -version = "1.4.1" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -summary = "Getting image size from png/jpeg/jpeg2000/gif file" -files = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, -] - -[[package]] -name = "importlib-metadata" -version = "6.7.0" -requires_python = ">=3.7" -summary = "Read metadata from Python packages" -dependencies = [ - "typing-extensions>=3.6.4; python_version < \"3.8\"", - "zipp>=0.5", -] -files = [ - {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, - {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -requires_python = ">=3.7" -summary = "brain-dead simple config-ini parsing" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "invoke" -version = "2.2.0" -requires_python = ">=3.6" -summary = "Pythonic task execution" -files = [ - {file = "invoke-2.2.0-py3-none-any.whl", hash = "sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820"}, - {file = "invoke-2.2.0.tar.gz", hash = "sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5"}, -] - -[[package]] -name = "isort" -version = "5.11.5" -requires_python = ">=3.7.0" -summary = "A Python utility / library to sort Python imports." -files = [ - {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, - {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, -] - -[[package]] -name = "jinja2" -version = "3.1.2" -requires_python = ">=3.7" -summary = "A very fast and expressive template engine." -dependencies = [ - "MarkupSafe>=2.0", -] -files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] - -[[package]] -name = "livereload" -version = "2.6.3" -summary = "Python LiveReload is an awesome tool for web developers" -dependencies = [ - "six", - "tornado; python_version > \"2.7\"", -] -files = [ - {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, - {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, -] - -[[package]] -name = "lxml" -version = "4.9.3" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -summary = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -files = [ - {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, - {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, - {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, - {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, - {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, - {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, - {file = "lxml-4.9.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5"}, - {file = "lxml-4.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02"}, - {file = "lxml-4.9.3-cp37-cp37m-win32.whl", hash = "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f"}, - {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7"}, - {file = "lxml-4.9.3-cp38-cp38-win32.whl", hash = "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574"}, - {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, - {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50"}, - {file = "lxml-4.9.3-cp39-cp39-win32.whl", hash = "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2"}, - {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, - {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, - {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, -] - -[[package]] -name = "markdown" -version = "3.4.4" -requires_python = ">=3.7" -summary = "Python implementation of John Gruber's Markdown." -dependencies = [ - "importlib-metadata>=4.4; python_version < \"3.10\"", -] -files = [ - {file = "Markdown-3.4.4-py3-none-any.whl", hash = "sha256:a4c1b65c0957b4bd9e7d86ddc7b3c9868fb9670660f6f99f6d1bca8954d5a941"}, - {file = "Markdown-3.4.4.tar.gz", hash = "sha256:225c6123522495d4119a90b3a3ba31a1e87a70369e03f14799ea9c0d7183a3d6"}, -] - -[[package]] -name = "markdown-it-py" -version = "2.2.0" -requires_python = ">=3.7" -summary = "Python port of markdown-it. Markdown parsing, done right!" -dependencies = [ - "mdurl~=0.1", - "typing-extensions>=3.7.4; python_version < \"3.8\"", -] -files = [ - {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, - {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, -] - -[[package]] -name = "markupsafe" -version = "2.1.3" -requires_python = ">=3.7" -summary = "Safely add untrusted strings to HTML/XML markup." -files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, -] - -[[package]] -name = "mccabe" -version = "0.6.1" -summary = "McCabe checker, plugin for flake8" -files = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -requires_python = ">=3.7" -summary = "Markdown URL utilities" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "packaging" -version = "23.2" -requires_python = ">=3.7" -summary = "Core utilities for Python packages" -files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, -] - -[[package]] -name = "pathspec" -version = "0.11.2" -requires_python = ">=3.7" -summary = "Utility library for gitignore style pattern matching of file paths." -files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, -] - -[[package]] -name = "platformdirs" -version = "3.11.0" -requires_python = ">=3.7" -summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -dependencies = [ - "typing-extensions>=4.7.1; python_version < \"3.8\"", -] -files = [ - {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, - {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, -] - -[[package]] -name = "pluggy" -version = "1.2.0" -requires_python = ">=3.7" -summary = "plugin and hook calling mechanisms for python" -dependencies = [ - "importlib-metadata>=0.12; python_version < \"3.8\"", -] -files = [ - {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, - {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, -] - -[[package]] -name = "psutil" -version = "5.9.6" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -summary = "Cross-platform lib for process and system monitoring in Python." -files = [ - {file = "psutil-5.9.6-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c69596f9fc2f8acd574a12d5f8b7b1ba3765a641ea5d60fb4736bf3c08a8214a"}, - {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92e0cc43c524834af53e9d3369245e6cc3b130e78e26100d1f63cdb0abeb3d3c"}, - {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:748c9dd2583ed86347ed65d0035f45fa8c851e8d90354c122ab72319b5f366f4"}, - {file = "psutil-5.9.6-cp37-abi3-win32.whl", hash = "sha256:a6f01f03bf1843280f4ad16f4bde26b817847b4c1a0db59bf6419807bc5ce05c"}, - {file = "psutil-5.9.6-cp37-abi3-win_amd64.whl", hash = "sha256:6e5fb8dc711a514da83098bc5234264e551ad980cec5f85dabf4d38ed6f15e9a"}, - {file = "psutil-5.9.6-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:daecbcbd29b289aac14ece28eca6a3e60aa361754cf6da3dfb20d4d32b6c7f57"}, - {file = "psutil-5.9.6.tar.gz", hash = "sha256:e4b92ddcd7dd4cdd3f900180ea1e104932c7bce234fb88976e2a3b296441225a"}, -] - -[[package]] -name = "py" -version = "1.11.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -summary = "library with cross-python path, ini-parsing, io, code, log facilities" -files = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] - -[[package]] -name = "pycodestyle" -version = "2.7.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -summary = "Python style guide checker" -files = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, -] - -[[package]] -name = "pyflakes" -version = "2.3.1" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -summary = "passive checker of Python programs" -files = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, -] - -[[package]] -name = "pygments" -version = "2.16.1" -requires_python = ">=3.7" -summary = "Pygments is a syntax highlighting package written in Python." -files = [ - {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, - {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, -] - -[[package]] -name = "pytest" -version = "7.4.2" -requires_python = ">=3.7" -summary = "pytest: simple powerful testing with Python" -dependencies = [ - "colorama; sys_platform == \"win32\"", - "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", - "importlib-metadata>=0.12; python_version < \"3.8\"", - "iniconfig", - "packaging", - "pluggy<2.0,>=0.12", - "tomli>=1.0.0; python_version < \"3.11\"", -] -files = [ - {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, - {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, -] - -[[package]] -name = "pytest-cov" -version = "4.1.0" -requires_python = ">=3.7" -summary = "Pytest plugin for measuring coverage." -dependencies = [ - "coverage[toml]>=5.2.1", - "pytest>=4.6", -] -files = [ - {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, - {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, -] - -[[package]] -name = "pytest-forked" -version = "1.6.0" -requires_python = ">=3.7" -summary = "run tests in isolated forked subprocesses" -dependencies = [ - "py", - "pytest>=3.10", -] -files = [ - {file = "pytest-forked-1.6.0.tar.gz", hash = "sha256:4dafd46a9a600f65d822b8f605133ecf5b3e1941ebb3588e943b4e3eb71a5a3f"}, - {file = "pytest_forked-1.6.0-py3-none-any.whl", hash = "sha256:810958f66a91afb1a1e2ae83089d8dc1cd2437ac96b12963042fbb9fb4d16af0"}, -] - -[[package]] -name = "pytest-sugar" -version = "0.9.7" -summary = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)." -dependencies = [ - "packaging>=21.3", - "pytest>=6.2.0", - "termcolor>=2.1.0", -] -files = [ - {file = "pytest-sugar-0.9.7.tar.gz", hash = "sha256:f1e74c1abfa55f7241cf7088032b6e378566f16b938f3f08905e2cf4494edd46"}, - {file = "pytest_sugar-0.9.7-py2.py3-none-any.whl", hash = "sha256:8cb5a4e5f8bbcd834622b0235db9e50432f4cbd71fef55b467fe44e43701e062"}, -] - -[[package]] -name = "pytest-xdist" -version = "2.5.0" -requires_python = ">=3.6" -summary = "pytest xdist plugin for distributed testing and loop-on-failing modes" -dependencies = [ - "execnet>=1.1", - "pytest-forked", - "pytest>=6.2.0", -] -files = [ - {file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"}, - {file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"}, -] - -[[package]] -name = "python-dateutil" -version = "2.8.2" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -summary = "Extensions to the standard Python datetime module" -dependencies = [ - "six>=1.5", -] -files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] - -[[package]] -name = "pytz" -version = "2023.3.post1" -summary = "World timezone definitions, modern and historical" -files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, -] - -[[package]] -name = "regex" -version = "2023.10.3" -requires_python = ">=3.7" -summary = "Alternative regular expression module, to replace re." -files = [ - {file = "regex-2023.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c34d4f73ea738223a094d8e0ffd6d2c1a1b4c175da34d6b0de3d8d69bee6bcc"}, - {file = "regex-2023.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8f4e49fc3ce020f65411432183e6775f24e02dff617281094ba6ab079ef0915"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cd1bccf99d3ef1ab6ba835308ad85be040e6a11b0977ef7ea8c8005f01a3c29"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81dce2ddc9f6e8f543d94b05d56e70d03a0774d32f6cca53e978dc01e4fc75b8"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c6b4d23c04831e3ab61717a707a5d763b300213db49ca680edf8bf13ab5d91b"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c15ad0aee158a15e17e0495e1e18741573d04eb6da06d8b84af726cfc1ed02ee"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6239d4e2e0b52c8bd38c51b760cd870069f0bdf99700a62cd509d7a031749a55"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4a8bf76e3182797c6b1afa5b822d1d5802ff30284abe4599e1247be4fd6b03be"}, - {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9c727bbcf0065cbb20f39d2b4f932f8fa1631c3e01fcedc979bd4f51fe051c5"}, - {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3ccf2716add72f80714b9a63899b67fa711b654be3fcdd34fa391d2d274ce767"}, - {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:107ac60d1bfdc3edb53be75e2a52aff7481b92817cfdddd9b4519ccf0e54a6ff"}, - {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:00ba3c9818e33f1fa974693fb55d24cdc8ebafcb2e4207680669d8f8d7cca79a"}, - {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0a47efb1dbef13af9c9a54a94a0b814902e547b7f21acb29434504d18f36e3a"}, - {file = "regex-2023.10.3-cp310-cp310-win32.whl", hash = "sha256:36362386b813fa6c9146da6149a001b7bd063dabc4d49522a1f7aa65b725c7ec"}, - {file = "regex-2023.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:c65a3b5330b54103e7d21cac3f6bf3900d46f6d50138d73343d9e5b2900b2353"}, - {file = "regex-2023.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90a79bce019c442604662d17bf69df99090e24cdc6ad95b18b6725c2988a490e"}, - {file = "regex-2023.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c7964c2183c3e6cce3f497e3a9f49d182e969f2dc3aeeadfa18945ff7bdd7051"}, - {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ef80829117a8061f974b2fda8ec799717242353bff55f8a29411794d635d964"}, - {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5addc9d0209a9afca5fc070f93b726bf7003bd63a427f65ef797a931782e7edc"}, - {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c148bec483cc4b421562b4bcedb8e28a3b84fcc8f0aa4418e10898f3c2c0eb9b"}, - {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d1f21af4c1539051049796a0f50aa342f9a27cde57318f2fc41ed50b0dbc4ac"}, - {file = "regex-2023.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b9ac09853b2a3e0d0082104036579809679e7715671cfbf89d83c1cb2a30f58"}, - {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ebedc192abbc7fd13c5ee800e83a6df252bec691eb2c4bedc9f8b2e2903f5e2a"}, - {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d8a993c0a0ffd5f2d3bda23d0cd75e7086736f8f8268de8a82fbc4bd0ac6791e"}, - {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:be6b7b8d42d3090b6c80793524fa66c57ad7ee3fe9722b258aec6d0672543fd0"}, - {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4023e2efc35a30e66e938de5aef42b520c20e7eda7bb5fb12c35e5d09a4c43f6"}, - {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0d47840dc05e0ba04fe2e26f15126de7c755496d5a8aae4a08bda4dd8d646c54"}, - {file = "regex-2023.10.3-cp311-cp311-win32.whl", hash = "sha256:9145f092b5d1977ec8c0ab46e7b3381b2fd069957b9862a43bd383e5c01d18c2"}, - {file = "regex-2023.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:b6104f9a46bd8743e4f738afef69b153c4b8b592d35ae46db07fc28ae3d5fb7c"}, - {file = "regex-2023.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff507ae210371d4b1fe316d03433ac099f184d570a1a611e541923f78f05037"}, - {file = "regex-2023.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be5e22bbb67924dea15039c3282fa4cc6cdfbe0cbbd1c0515f9223186fc2ec5f"}, - {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a992f702c9be9c72fa46f01ca6e18d131906a7180950958f766c2aa294d4b41"}, - {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7434a61b158be563c1362d9071358f8ab91b8d928728cd2882af060481244c9e"}, - {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2169b2dcabf4e608416f7f9468737583ce5f0a6e8677c4efbf795ce81109d7c"}, - {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9e908ef5889cda4de038892b9accc36d33d72fb3e12c747e2799a0e806ec841"}, - {file = "regex-2023.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12bd4bc2c632742c7ce20db48e0d99afdc05e03f0b4c1af90542e05b809a03d9"}, - {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bc72c231f5449d86d6c7d9cc7cd819b6eb30134bb770b8cfdc0765e48ef9c420"}, - {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bce8814b076f0ce5766dc87d5a056b0e9437b8e0cd351b9a6c4e1134a7dfbda9"}, - {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:ba7cd6dc4d585ea544c1412019921570ebd8a597fabf475acc4528210d7c4a6f"}, - {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b0c7d2f698e83f15228ba41c135501cfe7d5740181d5903e250e47f617eb4292"}, - {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5a8f91c64f390ecee09ff793319f30a0f32492e99f5dc1c72bc361f23ccd0a9a"}, - {file = "regex-2023.10.3-cp312-cp312-win32.whl", hash = "sha256:ad08a69728ff3c79866d729b095872afe1e0557251da4abb2c5faff15a91d19a"}, - {file = "regex-2023.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:39cdf8d141d6d44e8d5a12a8569d5a227f645c87df4f92179bd06e2e2705e76b"}, - {file = "regex-2023.10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4a3ee019a9befe84fa3e917a2dd378807e423d013377a884c1970a3c2792d293"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76066d7ff61ba6bf3cb5efe2428fc82aac91802844c022d849a1f0f53820502d"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe50b61bab1b1ec260fa7cd91106fa9fece57e6beba05630afe27c71259c59b"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fd88f373cb71e6b59b7fa597e47e518282455c2734fd4306a05ca219a1991b0"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ab05a182c7937fb374f7e946f04fb23a0c0699c0450e9fb02ef567412d2fa3"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dac37cf08fcf2094159922edc7a2784cfcc5c70f8354469f79ed085f0328ebdf"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e54ddd0bb8fb626aa1f9ba7b36629564544954fff9669b15da3610c22b9a0991"}, - {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3367007ad1951fde612bf65b0dffc8fd681a4ab98ac86957d16491400d661302"}, - {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:16f8740eb6dbacc7113e3097b0a36065a02e37b47c936b551805d40340fb9971"}, - {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:f4f2ca6df64cbdd27f27b34f35adb640b5d2d77264228554e68deda54456eb11"}, - {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:39807cbcbe406efca2a233884e169d056c35aa7e9f343d4e78665246a332f597"}, - {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7eece6fbd3eae4a92d7c748ae825cbc1ee41a89bb1c3db05b5578ed3cfcfd7cb"}, - {file = "regex-2023.10.3-cp37-cp37m-win32.whl", hash = "sha256:ce615c92d90df8373d9e13acddd154152645c0dc060871abf6bd43809673d20a"}, - {file = "regex-2023.10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0f649fa32fe734c4abdfd4edbb8381c74abf5f34bc0b3271ce687b23729299ed"}, - {file = "regex-2023.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b98b7681a9437262947f41c7fac567c7e1f6eddd94b0483596d320092004533"}, - {file = "regex-2023.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:91dc1d531f80c862441d7b66c4505cd6ea9d312f01fb2f4654f40c6fdf5cc37a"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82fcc1f1cc3ff1ab8a57ba619b149b907072e750815c5ba63e7aa2e1163384a4"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7979b834ec7a33aafae34a90aad9f914c41fd6eaa8474e66953f3f6f7cbd4368"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef71561f82a89af6cfcbee47f0fabfdb6e63788a9258e913955d89fdd96902ab"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd829712de97753367153ed84f2de752b86cd1f7a88b55a3a775eb52eafe8a94"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00e871d83a45eee2f8688d7e6849609c2ca2a04a6d48fba3dff4deef35d14f07"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:706e7b739fdd17cb89e1fbf712d9dc21311fc2333f6d435eac2d4ee81985098c"}, - {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cc3f1c053b73f20c7ad88b0d1d23be7e7b3901229ce89f5000a8399746a6e039"}, - {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f85739e80d13644b981a88f529d79c5bdf646b460ba190bffcaf6d57b2a9863"}, - {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:741ba2f511cc9626b7561a440f87d658aabb3d6b744a86a3c025f866b4d19e7f"}, - {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e77c90ab5997e85901da85131fd36acd0ed2221368199b65f0d11bca44549711"}, - {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:979c24cbefaf2420c4e377ecd1f165ea08cc3d1fbb44bdc51bccbbf7c66a2cb4"}, - {file = "regex-2023.10.3-cp38-cp38-win32.whl", hash = "sha256:58837f9d221744d4c92d2cf7201c6acd19623b50c643b56992cbd2b745485d3d"}, - {file = "regex-2023.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:c55853684fe08d4897c37dfc5faeff70607a5f1806c8be148f1695be4a63414b"}, - {file = "regex-2023.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2c54e23836650bdf2c18222c87f6f840d4943944146ca479858404fedeb9f9af"}, - {file = "regex-2023.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:69c0771ca5653c7d4b65203cbfc5e66db9375f1078689459fe196fe08b7b4930"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ac965a998e1388e6ff2e9781f499ad1eaa41e962a40d11c7823c9952c77123e"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c0e8fae5b27caa34177bdfa5a960c46ff2f78ee2d45c6db15ae3f64ecadde14"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c56c3d47da04f921b73ff9415fbaa939f684d47293f071aa9cbb13c94afc17d"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ef1e014eed78ab650bef9a6a9cbe50b052c0aebe553fb2881e0453717573f52"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d29338556a59423d9ff7b6eb0cb89ead2b0875e08fe522f3e068b955c3e7b59b"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9c6d0ced3c06d0f183b73d3c5920727268d2201aa0fe6d55c60d68c792ff3588"}, - {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:994645a46c6a740ee8ce8df7911d4aee458d9b1bc5639bc968226763d07f00fa"}, - {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:66e2fe786ef28da2b28e222c89502b2af984858091675044d93cb50e6f46d7af"}, - {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:11175910f62b2b8c055f2b089e0fedd694fe2be3941b3e2633653bc51064c528"}, - {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:06e9abc0e4c9ab4779c74ad99c3fc10d3967d03114449acc2c2762ad4472b8ca"}, - {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fb02e4257376ae25c6dd95a5aec377f9b18c09be6ebdefa7ad209b9137b73d48"}, - {file = "regex-2023.10.3-cp39-cp39-win32.whl", hash = "sha256:3b2c3502603fab52d7619b882c25a6850b766ebd1b18de3df23b2f939360e1bd"}, - {file = "regex-2023.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:adbccd17dcaff65704c856bd29951c58a1bd4b2b0f8ad6b826dbd543fe740988"}, - {file = "regex-2023.10.3.tar.gz", hash = "sha256:3fef4f844d2290ee0ba57addcec17eec9e3df73f10a2748485dfd6a3a188cc0f"}, -] - -[[package]] -name = "requests" -version = "2.31.0" -requires_python = ">=3.7" -summary = "Python HTTP for Humans." -dependencies = [ - "certifi>=2017.4.17", - "charset-normalizer<4,>=2", - "idna<4,>=2.5", - "urllib3<3,>=1.21.1", -] -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[[package]] -name = "rich" -version = "13.6.0" -requires_python = ">=3.7.0" -summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -dependencies = [ - "markdown-it-py>=2.2.0", - "pygments<3.0.0,>=2.13.0", - "typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"", -] -files = [ - {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, - {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, -] - -[[package]] -name = "setuptools" -version = "68.0.0" -requires_python = ">=3.7" -summary = "Easily download, build, install, upgrade, and uninstall Python packages" -files = [ - {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, - {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, -] - -[[package]] -name = "six" -version = "1.16.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -summary = "Python 2 and 3 compatibility utilities" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "smartypants" -version = "2.0.1" -summary = "Python with the SmartyPants" -files = [ - {file = "smartypants-2.0.1-py2.py3-none-any.whl", hash = "sha256:8db97f7cbdf08d15b158a86037cd9e116b4cf37703d24e0419a0d64ca5808f0d"}, -] - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -summary = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] - -[[package]] -name = "soupsieve" -version = "2.4.1" -requires_python = ">=3.7" -summary = "A modern CSS selector implementation for Beautiful Soup." -files = [ - {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, - {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, -] - -[[package]] -name = "sphinx" -version = "5.3.0" -requires_python = ">=3.6" -summary = "Python documentation generator" -dependencies = [ - "Jinja2>=3.0", - "Pygments>=2.12", - "alabaster<0.8,>=0.7", - "babel>=2.9", - "colorama>=0.4.5; sys_platform == \"win32\"", - "docutils<0.20,>=0.14", - "imagesize>=1.3", - "importlib-metadata>=4.8; python_version < \"3.10\"", - "packaging>=21.0", - "requests>=2.5.0", - "snowballstemmer>=2.0", - "sphinxcontrib-applehelp", - "sphinxcontrib-devhelp", - "sphinxcontrib-htmlhelp>=2.0.0", - "sphinxcontrib-jsmath", - "sphinxcontrib-qthelp", - "sphinxcontrib-serializinghtml>=1.1.5", -] -files = [ - {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, - {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, -] - -[[package]] -name = "sphinx-basic-ng" -version = "1.0.0b2" -requires_python = ">=3.7" -summary = "A modern skeleton for Sphinx themes." -dependencies = [ - "sphinx>=4.0", -] -files = [ - {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, - {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, -] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "1.0.2" -requires_python = ">=3.5" -summary = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" -files = [ - {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, - {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, -] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "1.0.2" -requires_python = ">=3.5" -summary = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -files = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.0.0" -requires_python = ">=3.6" -summary = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -files = [ - {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, - {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, -] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -requires_python = ">=3.5" -summary = "A sphinx extension which renders display math in HTML via JavaScript" -files = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "1.0.3" -requires_python = ">=3.5" -summary = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -files = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "1.1.5" -requires_python = ">=3.5" -summary = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -files = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] - -[[package]] -name = "termcolor" -version = "2.3.0" -requires_python = ">=3.7" -summary = "ANSI color formatting for output in terminal" -files = [ - {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, - {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, -] - -[[package]] -name = "toml" -version = "0.10.2" -requires_python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -summary = "Python Library for Tom's Obvious, Minimal Language" -files = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -requires_python = ">=3.7" -summary = "A lil' TOML parser" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "tornado" -version = "6.2" -requires_python = ">= 3.7" -summary = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -files = [ - {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, - {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"}, - {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"}, - {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, - {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, -] - -[[package]] -name = "tox" -version = "3.28.0" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -summary = "tox is a generic virtualenv management and test command line tool" -dependencies = [ - "colorama>=0.4.1; platform_system == \"Windows\"", - "filelock>=3.0.0", - "importlib-metadata>=0.12; python_version < \"3.8\"", - "packaging>=14", - "pluggy>=0.12.0", - "py>=1.4.17", - "six>=1.14.0", - "tomli>=2.0.1; python_version >= \"3.7\" and python_version < \"3.11\"", - "virtualenv!=20.0.0,!=20.0.1,!=20.0.2,!=20.0.3,!=20.0.4,!=20.0.5,!=20.0.6,!=20.0.7,>=16.0.0", -] -files = [ - {file = "tox-3.28.0-py2.py3-none-any.whl", hash = "sha256:57b5ab7e8bb3074edc3c0c0b4b192a4f3799d3723b2c5b76f1fa9f2d40316eea"}, - {file = "tox-3.28.0.tar.gz", hash = "sha256:d0d28f3fe6d6d7195c27f8b054c3e99d5451952b54abdae673b71609a581f640"}, -] - -[[package]] -name = "typed-ast" -version = "1.5.5" -requires_python = ">=3.6" -summary = "a fork of Python 2 and 3 ast modules with type comment support" -files = [ - {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, - {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, - {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, - {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, - {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, - {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, - {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, - {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, - {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, -] - -[[package]] -name = "typing-extensions" -version = "4.7.1" -requires_python = ">=3.7" -summary = "Backported and Experimental Type Hints for Python 3.7+" -files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, -] - -[[package]] -name = "typogrify" -version = "2.0.7" -summary = "Filters to enhance web typography, including support for Django & Jinja templates" -dependencies = [ - "smartypants>=1.8.3", -] -files = [ - {file = "typogrify-2.0.7.tar.gz", hash = "sha256:8be4668cda434163ce229d87ca273a11922cb1614cb359970b7dc96eed13cb38"}, -] - -[[package]] -name = "unidecode" -version = "1.3.7" -requires_python = ">=3.5" -summary = "ASCII transliterations of Unicode text" -files = [ - {file = "Unidecode-1.3.7-py3-none-any.whl", hash = "sha256:663a537f506834ed836af26a81b210d90cbde044c47bfbdc0fbbc9f94c86a6e4"}, - {file = "Unidecode-1.3.7.tar.gz", hash = "sha256:3c90b4662aa0de0cb591884b934ead8d2225f1800d8da675a7750cbc3bd94610"}, -] - -[[package]] -name = "urllib3" -version = "2.0.7" -requires_python = ">=3.7" -summary = "HTTP library with thread-safe connection pooling, file post, and more." -files = [ - {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, - {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, -] - -[[package]] -name = "virtualenv" -version = "20.24.6" -requires_python = ">=3.7" -summary = "Virtual Python Environment builder" -dependencies = [ - "distlib<1,>=0.3.7", - "filelock<4,>=3.12.2", - "importlib-metadata>=6.6; python_version < \"3.8\"", - "platformdirs<4,>=3.9.1", -] -files = [ - {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"}, - {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"}, -] - -[[package]] -name = "zipp" -version = "3.15.0" -requires_python = ">=3.7" -summary = "Backport of pathlib-compatible object wrapper for zip files" -files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, -] diff --git a/pelican/tests/build_test/conftest.py b/pelican/tests/build_test/conftest.py new file mode 100644 index 00000000..548f7970 --- /dev/null +++ b/pelican/tests/build_test/conftest.py @@ -0,0 +1,7 @@ +def pytest_addoption(parser): + parser.addoption( + "--check-wheel", + action="store", + default=False, + help="Check wheel contents.", + ) diff --git a/pelican/tests/build_test/test_wheel.py b/pelican/tests/build_test/test_wheel.py new file mode 100644 index 00000000..a4635481 --- /dev/null +++ b/pelican/tests/build_test/test_wheel.py @@ -0,0 +1,28 @@ +from pathlib import Path +import pytest +from zipfile import ZipFile + + +@pytest.mark.skipif( + "not config.getoption('--check-wheel')", + reason="Only run when --check-wheel is given", +) +def test_wheel_contents(pytestconfig): + """ + This test, should test the contents of the wheel to make sure, + that everything that is needed is included in the final build + """ + wheel_file = pytestconfig.getoption("--check-wheel") + assert wheel_file.endswith(".whl") + files_list = ZipFile(wheel_file).namelist() + ## Check is theme files are copiedto wheel + simple_theme = Path("./pelican/themes/simple/templates") + for x in simple_theme.iterdir(): + assert str(x) in files_list + + ## Check is tool templatesare copiedto wheel + tools = Path("./pelican/tools/templates") + for x in tools.iterdir(): + assert str(x) in files_list + + assert "pelican/tools/templates/tasks.py.jinja2" in files_list diff --git a/pyproject.toml b/pyproject.toml index da9deec1..9d439680 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,8 @@ dependencies = [ "python-dateutil>=2.8", "rich>=10.1", "unidecode>=1.1", - "backports-zoneinfo<1.0.0,>=0.2.1; python_version<3.9", + "backports-zoneinfo<1.0.0,>=0.2.1;python_version<'3.9'", + "watchfiles", ] [project.optional-dependencies] @@ -53,9 +54,9 @@ Documentation = "https://docs.getpelican.com" [project.scripts] pelican = "pelican.__main__:main" pelican-import = "pelican.tools.pelican_import:main" +pelican-plugins = "pelican.plugins._utils:list_plugins" pelican-quickstart = "pelican.tools.pelican_quickstart:main" pelican-themes = "pelican.tools.pelican_themes:main" -pelican-plugins = "pelican.plugins._utils:list_plugins" [tool.autopub] project-name = "Pelican" @@ -64,14 +65,14 @@ git-email = "52496925+botpub@users.noreply.github.com" changelog-file = "docs/changelog.rst" changelog-header = "###############" version-header = "=" -version-strings = ["setup.py"] -build-system = "setuptools" [tool.pdm] [tool.pdm.scripts] docbuild = "invoke docbuild" docserve = "invoke docserve" +lint = "invoke lint" +test = "invoke tests" [tool.pdm.dev-dependencies] dev = [ @@ -95,11 +96,10 @@ dev = [ "invoke<3.0,>=2.0", "isort<6.0,>=5.2", "black<20.0,>=19.10b0", + "ruff>=0.1.3,<1.0.0", + "tomli;python_version<'3.11'", ] -[tool.pdm.build] -includes = [] - [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" diff --git a/requirements/docs.pip b/requirements/docs.pip index 6db7c6c8..961a6473 100644 --- a/requirements/docs.pip +++ b/requirements/docs.pip @@ -2,3 +2,4 @@ sphinx<6.0 sphinxext-opengraph furo livereload +tomli;python_version<"3.11" diff --git a/tasks.py b/tasks.py index e9f65db3..56461679 100644 --- a/tasks.py +++ b/tasks.py @@ -15,8 +15,8 @@ VENV_PATH = Path(ACTIVE_VENV) if ACTIVE_VENV else (VENV_HOME / PKG_NAME) VENV = str(VENV_PATH.expanduser()) VENV_BIN = Path(VENV) / Path(BIN_DIR) -TOOLS = ["poetry", "pre-commit", "psutil"] -POETRY = which("poetry") or VENV_BIN / "poetry" +TOOLS = ["pdm", "pre-commit", "psutil"] +PDM = which("pdm") or VENV_BIN / "pdm" PRECOMMIT = which("pre-commit") or VENV_BIN / "pre-commit" @@ -107,7 +107,7 @@ def precommit(c): def setup(c): c.run(f"{VENV_BIN}/python -m pip install -U pip", pty=PTY) tools(c) - c.run(f"{POETRY} install", pty=PTY) + c.run(f"{PDM} install", pty=PTY) precommit(c) From 00d26fc0686acc6b8405cbc9c9e014b9671eaccd Mon Sep 17 00:00:00 2001 From: Lioman Date: Sun, 29 Oct 2023 11:56:28 +0100 Subject: [PATCH 307/465] remove old setup files --- MANIFEST.in | 6 ---- setup.cfg | 2 -- setup.py | 96 ----------------------------------------------------- 3 files changed, 104 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 setup.cfg delete mode 100755 setup.py diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 87d433a8..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include *.rst -recursive-include pelican *.html *.css *png *.rst *.markdown *.md *.mkd *.xml *.py *.jinja2 -include LICENSE THANKS docs/changelog.rst pyproject.toml -graft samples -global-exclude __pycache__ -global-exclude *.py[co] \ No newline at end of file diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 2a9acf13..00000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -universal = 1 diff --git a/setup.py b/setup.py deleted file mode 100755 index 4ffee0cb..00000000 --- a/setup.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python - -from os import walk -from os.path import join, relpath - -from setuptools import find_packages, setup - - -version = "4.8.0" - -requires = [ - 'feedgenerator >= 1.9', - 'jinja2 >= 2.7', - 'pygments', - 'docutils>=0.15', - 'blinker', - 'unidecode', - 'python-dateutil', - 'rich', - 'backports-zoneinfo[tzdata] >= 0.2; python_version<"3.9"', - 'watchfiles' -] - -entry_points = { - 'console_scripts': [ - 'pelican = pelican.__main__:main', - 'pelican-import = pelican.tools.pelican_import:main', - 'pelican-quickstart = pelican.tools.pelican_quickstart:main', - 'pelican-themes = pelican.tools.pelican_themes:main', - 'pelican-plugins = pelican.plugins._utils:list_plugins' - ] -} - -README = open('README.rst', encoding='utf-8').read() -CHANGELOG = open('docs/changelog.rst', encoding='utf-8').read() - -# Relative links in the README must be converted to absolute URL's -# so that they render correctly on PyPI. -README = README.replace( - "", - "", -) - -description = '\n'.join([README, CHANGELOG]) - -setup( - name='pelican', - version=version, - url='https://getpelican.com/', - author='Justin Mayer', - author_email='authors@getpelican.com', - description="Static site generator supporting reStructuredText and " - "Markdown source content.", - project_urls={ - 'Documentation': 'https://docs.getpelican.com/', - 'Funding': 'https://donate.getpelican.com/', - 'Source': 'https://github.com/getpelican/pelican', - 'Tracker': 'https://github.com/getpelican/pelican/issues', - }, - keywords='static web site generator SSG reStructuredText Markdown', - license='AGPLv3', - long_description=description, - long_description_content_type='text/x-rst', - packages=find_packages(), - include_package_data=True, # includes all in MANIFEST.in if in package - # NOTE : This will collect any files that happen to be in the themes - # directory, even though they may not be checked into version control. - package_data={ # pelican/themes is not a package, so include manually - 'pelican': [relpath(join(root, name), 'pelican') - for root, _, names in walk(join('pelican', 'themes')) - for name in names], - }, - install_requires=requires, - extras_require={ - 'Markdown': ['markdown~=3.1.1'] - }, - entry_points=entry_points, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Framework :: Pelican', - 'License :: OSI Approved :: GNU Affero General Public License v3', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: Implementation :: CPython', - 'Topic :: Internet :: WWW/HTTP', - 'Topic :: Software Development :: Libraries :: Python Modules', - ], - test_suite='pelican.tests', -) From eb052cae096c27dba40ccf3a467b197a7bc91e1b Mon Sep 17 00:00:00 2001 From: Lioman Date: Sun, 29 Oct 2023 12:41:55 +0100 Subject: [PATCH 308/465] Capitalize PDM in docs --- docs/contribute.rst | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/contribute.rst b/docs/contribute.rst index a5292dd5..33a62064 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -15,13 +15,13 @@ Setting up the development environment ====================================== While there are many ways to set up one's development environment, the following -instructions will utilize Pip_ and pdm_. These tools facilitate managing +instructions will utilize Pip_ and PDM_. These tools facilitate managing virtual environments for separate Python projects that are isolated from one another, so you can use different packages (and package versions) for each. Please note that Python |min_python| is required for Pelican development. -*(Optional)* If you prefer to `install pdm `_ once for use with multiple projects, +*(Optional)* If you prefer to `install PDM `_ once for use with multiple projects, you can install it via:: curl -sSL https://pdm.fming.dev/install-pdm.py | python3 - @@ -35,7 +35,7 @@ as a Git remote:: cd ~/projects/pelican git remote add upstream https://github.com/getpelican/pelican.git -While pdm can dynamically create and manage virtual environments, we're going +While PDM can dynamically create and manage virtual environments, we're going to manually create and activate a virtual environment:: mkdir ~/virtualenvs && cd ~/virtualenvs @@ -51,7 +51,7 @@ Install the needed dependencies and set up the project:: Your local environment should now be ready to go! .. _Pip: https://pip.pypa.io/ -.. _pdm: https://pdm.fming.dev/latest/ +.. _PDM: https://pdm.fming.dev/latest/ .. _Pelican repository: https://github.com/getpelican/pelican Development diff --git a/pyproject.toml b/pyproject.toml index 9d439680..ac4a73df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ dependencies = [ "rich>=10.1", "unidecode>=1.1", "backports-zoneinfo<1.0.0,>=0.2.1;python_version<'3.9'", - "watchfiles", + "watchfiles>=0.21.0", ] [project.optional-dependencies] From 8a0f335e2bfb8cb3bba0a1e6b9ec0468e5bcd8c9 Mon Sep 17 00:00:00 2001 From: Lioman Date: Sun, 29 Oct 2023 15:23:14 +0100 Subject: [PATCH 309/465] only install dev dependencies during lint step --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ff1c15b5..0127982e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -60,7 +60,7 @@ jobs: cache-dependency-path: ./pyproject.toml - name: Install dependencies run: | - pdm install + pdm install --no-default --dev - name: Run linters run: pdm lint --diff From cce15701359f028bddb3f4115c614b006efc560b Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 29 Oct 2023 15:53:11 +0100 Subject: [PATCH 310/465] Fix some comments in wheel-related test --- pelican/tests/build_test/test_wheel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/tests/build_test/test_wheel.py b/pelican/tests/build_test/test_wheel.py index a4635481..8e643981 100644 --- a/pelican/tests/build_test/test_wheel.py +++ b/pelican/tests/build_test/test_wheel.py @@ -15,12 +15,12 @@ def test_wheel_contents(pytestconfig): wheel_file = pytestconfig.getoption("--check-wheel") assert wheel_file.endswith(".whl") files_list = ZipFile(wheel_file).namelist() - ## Check is theme files are copiedto wheel + # Check if theme files are copied to wheel simple_theme = Path("./pelican/themes/simple/templates") for x in simple_theme.iterdir(): assert str(x) in files_list - ## Check is tool templatesare copiedto wheel + # Check if tool templates are copied to wheel tools = Path("./pelican/tools/templates") for x in tools.iterdir(): assert str(x) in files_list From 9437de63419080a61733b26444517ac81a5e6b65 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 29 Oct 2023 15:51:24 +0100 Subject: [PATCH 311/465] Include more files in PDM sdist builds This was previously the job of directives in MANIFEST.in, which should be covered by this PDM-specific configuration. --- pyproject.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index ac4a73df..58fda86b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -100,6 +100,14 @@ dev = [ "tomli;python_version<'3.11'", ] +[tool.pdm.build] +source-includes = [ + "CONTRIBUTING.rst", + "THANKS", + "docs/changelog.rst", + "samples/", +] + [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" From 6f1605edf9b434fd311a4e801b5d441a5f04985d Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Sun, 29 Oct 2023 18:30:25 +0300 Subject: [PATCH 312/465] Extend GHA documentation to specify requirements file --- .github/workflows/github_pages.yml | 3 +-- docs/tips.rst | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github_pages.yml b/.github/workflows/github_pages.yml index 481dd118..ccf172b4 100644 --- a/.github/workflows/github_pages.yml +++ b/.github/workflows/github_pages.yml @@ -9,7 +9,7 @@ on: requirements: required: false default: "pelican" - description: "The Python requirements to install, for example to enable markdown and typogrify use: 'pelican[markdown] typogrify'" + description: "The Python requirements to install, for example to enable markdown and typogrify use: 'pelican[markdown] typogrify' or if you have a requirements file use: '-r requirements.txt'" type: string output-path: required: false @@ -58,4 +58,3 @@ jobs: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v2 - diff --git a/docs/tips.rst b/docs/tips.rst index abd46c8a..904e5ee7 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -187,6 +187,8 @@ the workflow: | | | install, for example to enable | | | | | | markdown and typogrify use: | | | | | | ``"pelican[markdown] typogrify"`` | | | +| | | or if you have a requirements | | | +| | | file: ``"-r requirements.txt"`` | | | +--------------+----------+-----------------------------------+--------+---------------+ | output-path | No | Where to output the generated | string | ``"output/"`` | | | | files (``pelican``'s ``--output`` | | | From dbe0b1125f573721dd25211bc19523baf1082425 Mon Sep 17 00:00:00 2001 From: boxydog Date: Sun, 29 Oct 2023 12:55:37 -0500 Subject: [PATCH 313/465] Don't copy file ownership, permissions and metadata --- pelican/utils.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pelican/utils.py b/pelican/utils.py index e1bed154..09ffcfe6 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -303,7 +303,7 @@ def copy(source, destination, ignores=None): logger.info('Creating directory %s', dst_dir) os.makedirs(dst_dir) logger.info('Copying %s to %s', source_, destination_) - copy_file_metadata(source_, destination_) + copy_file(source_, destination_) elif os.path.isdir(source_): if not os.path.exists(destination_): @@ -333,20 +333,17 @@ def copy(source, destination, ignores=None): dst_path = os.path.join(dst_dir, o) if os.path.isfile(src_path): logger.info('Copying %s to %s', src_path, dst_path) - copy_file_metadata(src_path, dst_path) + copy_file(src_path, dst_path) else: logger.warning('Skipped copy %s (not a file or ' 'directory) to %s', src_path, dst_path) -def copy_file_metadata(source, destination): - '''Copy a file and its metadata (perm bits, access times, ...)''' - - # This function is a workaround for Android python copystat - # bug ([issue28141]) https://bugs.python.org/issue28141 +def copy_file(source, destination): + '''Copy a file''' try: - shutil.copy2(source, destination) + shutil.copyfile(source, destination) except OSError as e: logger.warning("A problem occurred copying file %s to %s; %s", source, destination, e) From 8ea27b82f6c0c6fccd4362246ae1d2ff76b46d05 Mon Sep 17 00:00:00 2001 From: Chris Rose Date: Sun, 29 Oct 2023 09:50:01 -0700 Subject: [PATCH 314/465] Bump all of the dev dependencies - remove upper version caps - updated the minimum version of most of Pelican's runtime deps - replaced black with ruff as a formatter for pelican - added a cache step to the docs CI task so that the docs can be downloaded and inspected. --- .github/workflows/main.yml | 5 +++ pyproject.toml | 62 +++++++++++++++++++------------------- tasks.py | 19 ++++-------- 3 files changed, 42 insertions(+), 44 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0127982e..59a22862 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -80,6 +80,11 @@ jobs: run: python -m pip install -U pip tox - name: Check run: tox -e docs + - name: cache the docs for inspection + uses: actions/upload-artifact@v3 + with: + name: docs + path: docs/_build/html/ deploy: name: Deploy diff --git a/pyproject.toml b/pyproject.toml index 58fda86b..4e3e712a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,15 +29,15 @@ classifiers = [ ] requires-python = ">=3.8.1,<4.0" dependencies = [ - "blinker>=1.4", - "docutils>=0.16", - "feedgenerator>=1.9", - "jinja2>=2.7", - "pygments>=2.6", - "python-dateutil>=2.8", - "rich>=10.1", - "unidecode>=1.1", - "backports-zoneinfo<1.0.0,>=0.2.1;python_version<'3.9'", + "blinker>=1.6.3", + "docutils>=0.20.1", + "feedgenerator>=2.1.0", + "jinja2>=3.1.2", + "pygments>=2.16.1", + "python-dateutil>=2.8.2", + "rich>=13.6.0", + "unidecode>=1.3.7", + "backports-zoneinfo>=0.2.1; python_version < \"3.9\"", "watchfiles>=0.21.0", ] @@ -76,28 +76,28 @@ test = "invoke tests" [tool.pdm.dev-dependencies] dev = [ - "BeautifulSoup4<5.0,>=4.9", - "jinja2~=3.1.2", - "lxml<5.0,>=4.3", - "markdown~=3.4.3", - "typogrify<3.0,>=2.0", - "sphinx<6.0,>=5.1", - "furo==2023.03.27", - "livereload<3.0,>=2.6", - "psutil<6.0,>=5.7", - "pygments~=2.15", - "pytest<8.0,>=7.1", - "pytest-cov<5.0,>=4.0", - "pytest-sugar<1.0.0,>=0.9.5", - "pytest-xdist<3.0,>=2.0", - "tox<4.0,>=3.13", - "flake8<4.0,>=3.8", - "flake8-import-order<1.0.0,>=0.18.1", - "invoke<3.0,>=2.0", - "isort<6.0,>=5.2", - "black<20.0,>=19.10b0", - "ruff>=0.1.3,<1.0.0", - "tomli;python_version<'3.11'", + "BeautifulSoup4>=4.12.2", + "jinja2>=3.1.2", + "lxml>=4.9.3", + "markdown>=3.5", + "typogrify>=2.0.7", + "sphinx>=7.1.2", + "furo>=2023.9.10", + "livereload>=2.6.3", + "psutil>=5.9.6", + "pygments>=2.16.1", + "pytest>=7.4.3", + "pytest-cov>=4.1.0", + "pytest-sugar>=0.9.7", + "pytest-xdist>=3.3.1", + "tox>=4.11.3", + "flake8>=6.1.0", + "flake8-import-order>=0.18.2", + "invoke>=2.2.0", + "isort>=5.12.0", + "black>=23.10.1", + "ruff>=0.1.3", + "tomli>=2.0.1; python_version < \"3.11\"", ] [tool.pdm.build] diff --git a/tasks.py b/tasks.py index 56461679..64409e20 100644 --- a/tasks.py +++ b/tasks.py @@ -52,24 +52,16 @@ def coverage(c): @task -def black(c, check=False, diff=False): - """Run Black auto-formatter, optionally with --check or --diff""" +def format(c, check=False, diff=False): + """Run Ruff's auto-formatter, optionally with --check or --diff""" check_flag, diff_flag = "", "" if check: check_flag = "--check" if diff: diff_flag = "--diff" - c.run(f"{VENV_BIN}/black {check_flag} {diff_flag} {PKG_PATH} tasks.py", pty=PTY) - - -@task -def isort(c, check=False, diff=False): - check_flag, diff_flag = "", "" - if check: - check_flag = "-c" - if diff: - diff_flag = "--diff" - c.run(f"{VENV_BIN}/isort {check_flag} {diff_flag} .", pty=PTY) + c.run( + f"{VENV_BIN}/ruff format {check_flag} {diff_flag} {PKG_PATH} tasks.py", pty=PTY + ) @task @@ -87,6 +79,7 @@ def ruff(c, fix=False, diff=False): def lint(c, fix=False, diff=False): """Check code style via linting tools.""" ruff(c, fix=fix, diff=diff) + format(c, check=not fix, diff=diff) @task From cabdb26cee66e1173cf16cb31d3fe5f9fa4392e7 Mon Sep 17 00:00:00 2001 From: Chris Rose Date: Sun, 29 Oct 2023 22:18:29 +0100 Subject: [PATCH 315/465] Apply code style to project via: ruff format . --- pelican/__init__.py | 530 +++--- pelican/__main__.py | 2 +- pelican/cache.py | 52 +- pelican/contents.py | 323 ++-- pelican/generators.py | 796 +++++---- pelican/log.py | 45 +- pelican/paginator.py | 47 +- pelican/plugins/_utils.py | 36 +- pelican/plugins/signals.py | 56 +- pelican/readers.py | 416 ++--- pelican/rstdirectives.py | 60 +- pelican/server.py | 110 +- pelican/settings.py | 819 +++++---- pelican/signals.py | 4 +- pelican/tests/default_conf.py | 44 +- .../pelican/plugins/ns_plugin/__init__.py | 2 +- pelican/tests/support.py | 80 +- pelican/tests/test_cache.py | 235 ++- pelican/tests/test_cli.py | 77 +- pelican/tests/test_contents.py | 870 +++++----- pelican/tests/test_generators.py | 1500 ++++++++++------- pelican/tests/test_importer.py | 638 ++++--- pelican/tests/test_log.py | 45 +- pelican/tests/test_paginator.py | 111 +- pelican/tests/test_pelican.py | 250 +-- pelican/tests/test_plugins.py | 118 +- pelican/tests/test_readers.py | 853 +++++----- pelican/tests/test_rstdirectives.py | 12 +- pelican/tests/test_server.py | 36 +- pelican/tests/test_settings.py | 292 ++-- pelican/tests/test_testsuite.py | 3 +- pelican/tests/test_urlwrappers.py | 83 +- pelican/tests/test_utils.py | 920 +++++----- pelican/tools/pelican_import.py | 963 ++++++----- pelican/tools/pelican_quickstart.py | 367 ++-- pelican/tools/pelican_themes.py | 181 +- pelican/urlwrappers.py | 42 +- pelican/utils.py | 323 ++-- pelican/writers.py | 185 +- samples/pelican.conf.py | 52 +- samples/pelican.conf_FR.py | 54 +- 41 files changed, 6487 insertions(+), 5145 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index fcdda8a4..a0ff4989 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -9,19 +9,25 @@ import sys import time import traceback from collections.abc import Iterable + # Combines all paths to `pelican` package accessible from `sys.path` # Makes it possible to install `pelican` and namespace plugins into different # locations in the file system (e.g. pip with `-e` or `--user`) from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) # pelican.log has to be the first pelican module to be loaded # because logging.setLoggerClass has to be called before logging.getLogger from pelican.log import console from pelican.log import init as init_logging -from pelican.generators import (ArticlesGenerator, # noqa: I100 - PagesGenerator, SourceFileGenerator, - StaticGenerator, TemplatePagesGenerator) +from pelican.generators import ( + ArticlesGenerator, # noqa: I100 + PagesGenerator, + SourceFileGenerator, + StaticGenerator, + TemplatePagesGenerator, +) from pelican.plugins import signals from pelican.plugins._utils import get_plugin_name, load_plugins from pelican.readers import Readers @@ -35,12 +41,11 @@ try: except Exception: __version__ = "unknown" -DEFAULT_CONFIG_NAME = 'pelicanconf.py' +DEFAULT_CONFIG_NAME = "pelicanconf.py" logger = logging.getLogger(__name__) class Pelican: - def __init__(self, settings): """Pelican initialization @@ -50,35 +55,34 @@ class Pelican: # define the default settings self.settings = settings - self.path = settings['PATH'] - self.theme = settings['THEME'] - self.output_path = settings['OUTPUT_PATH'] - self.ignore_files = settings['IGNORE_FILES'] - self.delete_outputdir = settings['DELETE_OUTPUT_DIRECTORY'] - self.output_retention = settings['OUTPUT_RETENTION'] + self.path = settings["PATH"] + self.theme = settings["THEME"] + self.output_path = settings["OUTPUT_PATH"] + self.ignore_files = settings["IGNORE_FILES"] + self.delete_outputdir = settings["DELETE_OUTPUT_DIRECTORY"] + self.output_retention = settings["OUTPUT_RETENTION"] self.init_path() self.init_plugins() signals.initialized.send(self) def init_path(self): - if not any(p in sys.path for p in ['', os.curdir]): + if not any(p in sys.path for p in ["", os.curdir]): logger.debug("Adding current directory to system path") - sys.path.insert(0, '') + sys.path.insert(0, "") def init_plugins(self): self.plugins = [] for plugin in load_plugins(self.settings): name = get_plugin_name(plugin) - logger.debug('Registering plugin `%s`', name) + logger.debug("Registering plugin `%s`", name) try: plugin.register() self.plugins.append(plugin) except Exception as e: - logger.error('Cannot register plugin `%s`\n%s', - name, e) + logger.error("Cannot register plugin `%s`\n%s", name, e) - self.settings['PLUGINS'] = [get_plugin_name(p) for p in self.plugins] + self.settings["PLUGINS"] = [get_plugin_name(p) for p in self.plugins] def run(self): """Run the generators and return""" @@ -87,10 +91,10 @@ class Pelican: context = self.settings.copy() # Share these among all the generators and content objects # They map source paths to Content objects or None - context['generated_content'] = {} - context['static_links'] = set() - context['static_content'] = {} - context['localsiteurl'] = self.settings['SITEURL'] + context["generated_content"] = {} + context["static_links"] = set() + context["static_content"] = {} + context["localsiteurl"] = self.settings["SITEURL"] generators = [ cls( @@ -99,23 +103,25 @@ class Pelican: path=self.path, theme=self.theme, output_path=self.output_path, - ) for cls in self._get_generator_classes() + ) + for cls in self._get_generator_classes() ] # Delete the output directory if (1) the appropriate setting is True # and (2) that directory is not the parent of the source directory - if (self.delete_outputdir - and os.path.commonpath([os.path.realpath(self.output_path)]) != - os.path.commonpath([os.path.realpath(self.output_path), - os.path.realpath(self.path)])): + if self.delete_outputdir and os.path.commonpath( + [os.path.realpath(self.output_path)] + ) != os.path.commonpath( + [os.path.realpath(self.output_path), os.path.realpath(self.path)] + ): clean_output_dir(self.output_path, self.output_retention) for p in generators: - if hasattr(p, 'generate_context'): + if hasattr(p, "generate_context"): p.generate_context() for p in generators: - if hasattr(p, 'refresh_metadata_intersite_links'): + if hasattr(p, "refresh_metadata_intersite_links"): p.refresh_metadata_intersite_links() signals.all_generators_finalized.send(generators) @@ -123,61 +129,75 @@ class Pelican: writer = self._get_writer() for p in generators: - if hasattr(p, 'generate_output'): + if hasattr(p, "generate_output"): p.generate_output(writer) signals.finalized.send(self) - articles_generator = next(g for g in generators - if isinstance(g, ArticlesGenerator)) - pages_generator = next(g for g in generators - if isinstance(g, PagesGenerator)) + articles_generator = next( + g for g in generators if isinstance(g, ArticlesGenerator) + ) + pages_generator = next(g for g in generators if isinstance(g, PagesGenerator)) pluralized_articles = maybe_pluralize( - (len(articles_generator.articles) + - len(articles_generator.translations)), - 'article', - 'articles') + (len(articles_generator.articles) + len(articles_generator.translations)), + "article", + "articles", + ) pluralized_drafts = maybe_pluralize( - (len(articles_generator.drafts) + - len(articles_generator.drafts_translations)), - 'draft', - 'drafts') + ( + len(articles_generator.drafts) + + len(articles_generator.drafts_translations) + ), + "draft", + "drafts", + ) pluralized_hidden_articles = maybe_pluralize( - (len(articles_generator.hidden_articles) + - len(articles_generator.hidden_translations)), - 'hidden article', - 'hidden articles') + ( + len(articles_generator.hidden_articles) + + len(articles_generator.hidden_translations) + ), + "hidden article", + "hidden articles", + ) pluralized_pages = maybe_pluralize( - (len(pages_generator.pages) + - len(pages_generator.translations)), - 'page', - 'pages') + (len(pages_generator.pages) + len(pages_generator.translations)), + "page", + "pages", + ) pluralized_hidden_pages = maybe_pluralize( - (len(pages_generator.hidden_pages) + - len(pages_generator.hidden_translations)), - 'hidden page', - 'hidden pages') + ( + len(pages_generator.hidden_pages) + + len(pages_generator.hidden_translations) + ), + "hidden page", + "hidden pages", + ) pluralized_draft_pages = maybe_pluralize( - (len(pages_generator.draft_pages) + - len(pages_generator.draft_translations)), - 'draft page', - 'draft pages') + ( + len(pages_generator.draft_pages) + + len(pages_generator.draft_translations) + ), + "draft page", + "draft pages", + ) - console.print('Done: Processed {}, {}, {}, {}, {} and {} in {:.2f} seconds.' - .format( - pluralized_articles, - pluralized_drafts, - pluralized_hidden_articles, - pluralized_pages, - pluralized_hidden_pages, - pluralized_draft_pages, - time.time() - start_time)) + console.print( + "Done: Processed {}, {}, {}, {}, {} and {} in {:.2f} seconds.".format( + pluralized_articles, + pluralized_drafts, + pluralized_hidden_articles, + pluralized_pages, + pluralized_hidden_pages, + pluralized_draft_pages, + time.time() - start_time, + ) + ) def _get_generator_classes(self): discovered_generators = [ (ArticlesGenerator, "internal"), - (PagesGenerator, "internal") + (PagesGenerator, "internal"), ] if self.settings["TEMPLATE_PAGES"]: @@ -236,7 +256,7 @@ class PrintSettings(argparse.Action): except Exception as e: logger.critical("%s: %s", e.__class__.__name__, e) console.print_exception() - sys.exit(getattr(e, 'exitcode', 1)) + sys.exit(getattr(e, "exitcode", 1)) if values: # One or more arguments provided, so only print those settings @@ -244,14 +264,16 @@ class PrintSettings(argparse.Action): if setting in settings: # Only add newline between setting name and value if dict if isinstance(settings[setting], (dict, tuple, list)): - setting_format = '\n{}:\n{}' + setting_format = "\n{}:\n{}" else: - setting_format = '\n{}: {}' - console.print(setting_format.format( - setting, - pprint.pformat(settings[setting]))) + setting_format = "\n{}: {}" + console.print( + setting_format.format( + setting, pprint.pformat(settings[setting]) + ) + ) else: - console.print('\n{} is not a recognized setting.'.format(setting)) + console.print("\n{} is not a recognized setting.".format(setting)) break else: # No argument was given to --print-settings, so print all settings @@ -268,170 +290,258 @@ class ParseOverrides(argparse.Action): k, v = item.split("=", 1) except ValueError: raise ValueError( - 'Extra settings must be specified as KEY=VALUE pairs ' - f'but you specified {item}' + "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).' + 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): parser = argparse.ArgumentParser( - description='A tool to generate a static blog, ' - ' with restructured text input files.', - formatter_class=argparse.ArgumentDefaultsHelpFormatter + description="A tool to generate a static blog, " + " with restructured text input files.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) - parser.add_argument(dest='path', nargs='?', - help='Path where to find the content files.', - default=None) + parser.add_argument( + dest="path", + nargs="?", + help="Path where to find the content files.", + default=None, + ) - parser.add_argument('-t', '--theme-path', dest='theme', - help='Path where to find the theme templates. If not ' - 'specified, it will use the default one included with ' - 'pelican.') + parser.add_argument( + "-t", + "--theme-path", + dest="theme", + help="Path where to find the theme templates. If not " + "specified, it will use the default one included with " + "pelican.", + ) - parser.add_argument('-o', '--output', dest='output', - help='Where to output the generated files. If not ' - 'specified, a directory will be created, named ' - '"output" in the current path.') + parser.add_argument( + "-o", + "--output", + dest="output", + help="Where to output the generated files. If not " + "specified, a directory will be created, named " + '"output" in the current path.', + ) - parser.add_argument('-s', '--settings', dest='settings', - help='The settings of the application, this is ' - 'automatically set to {} if a file exists with this ' - 'name.'.format(DEFAULT_CONFIG_NAME)) + parser.add_argument( + "-s", + "--settings", + dest="settings", + help="The settings of the application, this is " + "automatically set to {} if a file exists with this " + "name.".format(DEFAULT_CONFIG_NAME), + ) - parser.add_argument('-d', '--delete-output-directory', - dest='delete_outputdir', action='store_true', - default=None, help='Delete the output directory.') + parser.add_argument( + "-d", + "--delete-output-directory", + dest="delete_outputdir", + action="store_true", + default=None, + help="Delete the output directory.", + ) - parser.add_argument('-v', '--verbose', action='store_const', - const=logging.INFO, dest='verbosity', - help='Show all messages.') + parser.add_argument( + "-v", + "--verbose", + action="store_const", + const=logging.INFO, + dest="verbosity", + help="Show all messages.", + ) - parser.add_argument('-q', '--quiet', action='store_const', - const=logging.CRITICAL, dest='verbosity', - help='Show only critical errors.') + parser.add_argument( + "-q", + "--quiet", + action="store_const", + const=logging.CRITICAL, + dest="verbosity", + help="Show only critical errors.", + ) - parser.add_argument('-D', '--debug', action='store_const', - const=logging.DEBUG, dest='verbosity', - help='Show all messages, including debug messages.') + parser.add_argument( + "-D", + "--debug", + action="store_const", + const=logging.DEBUG, + dest="verbosity", + help="Show all messages, including debug messages.", + ) - parser.add_argument('--version', action='version', version=__version__, - help='Print the pelican version and exit.') + parser.add_argument( + "--version", + action="version", + version=__version__, + help="Print the pelican version and exit.", + ) - parser.add_argument('-r', '--autoreload', dest='autoreload', - action='store_true', - help='Relaunch pelican each time a modification occurs' - ' on the content files.') + parser.add_argument( + "-r", + "--autoreload", + dest="autoreload", + action="store_true", + help="Relaunch pelican each time a modification occurs" + " on the content files.", + ) - parser.add_argument('--print-settings', dest='print_settings', nargs='*', - action=PrintSettings, metavar='SETTING_NAME', - help='Print current configuration settings and exit. ' - 'Append one or more setting name arguments to see the ' - 'values for specific settings only.') + parser.add_argument( + "--print-settings", + dest="print_settings", + nargs="*", + action=PrintSettings, + metavar="SETTING_NAME", + help="Print current configuration settings and exit. " + "Append one or more setting name arguments to see the " + "values for specific settings only.", + ) - parser.add_argument('--relative-urls', dest='relative_paths', - action='store_true', - help='Use relative urls in output, ' - 'useful for site development') + parser.add_argument( + "--relative-urls", + dest="relative_paths", + action="store_true", + help="Use relative urls in output, " "useful for site development", + ) - parser.add_argument('--cache-path', dest='cache_path', - help=('Directory in which to store cache files. ' - 'If not specified, defaults to "cache".')) + parser.add_argument( + "--cache-path", + dest="cache_path", + help=( + "Directory in which to store cache files. " + 'If not specified, defaults to "cache".' + ), + ) - parser.add_argument('--ignore-cache', action='store_true', - dest='ignore_cache', help='Ignore content cache ' - 'from previous runs by not loading cache files.') + parser.add_argument( + "--ignore-cache", + action="store_true", + dest="ignore_cache", + help="Ignore content cache " "from previous runs by not loading cache files.", + ) - parser.add_argument('-w', '--write-selected', type=str, - dest='selected_paths', default=None, - help='Comma separated list of selected paths to write') + parser.add_argument( + "-w", + "--write-selected", + type=str, + dest="selected_paths", + default=None, + help="Comma separated list of selected paths to write", + ) - parser.add_argument('--fatal', metavar='errors|warnings', - choices=('errors', 'warnings'), default='', - help=('Exit the program with non-zero status if any ' - 'errors/warnings encountered.')) + parser.add_argument( + "--fatal", + metavar="errors|warnings", + choices=("errors", "warnings"), + default="", + help=( + "Exit the program with non-zero status if any " + "errors/warnings encountered." + ), + ) - parser.add_argument('--logs-dedup-min-level', default='WARNING', - choices=('DEBUG', 'INFO', 'WARNING', 'ERROR'), - help=('Only enable log de-duplication for levels equal' - ' to or above the specified value')) + parser.add_argument( + "--logs-dedup-min-level", + default="WARNING", + choices=("DEBUG", "INFO", "WARNING", "ERROR"), + help=( + "Only enable log de-duplication for levels equal" + " to or above the specified value" + ), + ) - parser.add_argument('-l', '--listen', dest='listen', action='store_true', - help='Serve content files via HTTP and port 8000.') + parser.add_argument( + "-l", + "--listen", + dest="listen", + action="store_true", + help="Serve content files via HTTP and port 8000.", + ) - parser.add_argument('-p', '--port', dest='port', type=int, - help='Port to serve HTTP files at. (default: 8000)') + parser.add_argument( + "-p", + "--port", + dest="port", + type=int, + help="Port to serve HTTP files at. (default: 8000)", + ) - parser.add_argument('-b', '--bind', dest='bind', - help='IP to bind to when serving files via HTTP ' - '(default: 127.0.0.1)') + parser.add_argument( + "-b", + "--bind", + dest="bind", + help="IP to bind to when serving files via HTTP " "(default: 127.0.0.1)", + ) - parser.add_argument('-e', '--extra-settings', dest='overrides', - help='Specify one or more SETTING=VALUE pairs to ' - '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=ParseOverrides, - default={}) + parser.add_argument( + "-e", + "--extra-settings", + dest="overrides", + help="Specify one or more SETTING=VALUE pairs to " + "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=ParseOverrides, + default={}, + ) args = parser.parse_args(argv) if args.port is not None and not args.listen: - logger.warning('--port without --listen has no effect') + logger.warning("--port without --listen has no effect") if args.bind is not None and not args.listen: - logger.warning('--bind without --listen has no effect') + logger.warning("--bind without --listen has no effect") return args def get_config(args): - """Builds a config dictionary based on supplied `args`. - """ + """Builds a config dictionary based on supplied `args`.""" config = {} if args.path: - config['PATH'] = os.path.abspath(os.path.expanduser(args.path)) + config["PATH"] = os.path.abspath(os.path.expanduser(args.path)) if args.output: - config['OUTPUT_PATH'] = \ - os.path.abspath(os.path.expanduser(args.output)) + config["OUTPUT_PATH"] = os.path.abspath(os.path.expanduser(args.output)) if args.theme: abstheme = os.path.abspath(os.path.expanduser(args.theme)) - config['THEME'] = abstheme if os.path.exists(abstheme) else args.theme + config["THEME"] = abstheme if os.path.exists(abstheme) else args.theme if args.delete_outputdir is not None: - config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir + config["DELETE_OUTPUT_DIRECTORY"] = args.delete_outputdir if args.ignore_cache: - config['LOAD_CONTENT_CACHE'] = False + config["LOAD_CONTENT_CACHE"] = False if args.cache_path: - config['CACHE_PATH'] = args.cache_path + config["CACHE_PATH"] = args.cache_path if args.selected_paths: - config['WRITE_SELECTED'] = args.selected_paths.split(',') + config["WRITE_SELECTED"] = args.selected_paths.split(",") if args.relative_paths: - config['RELATIVE_URLS'] = args.relative_paths + config["RELATIVE_URLS"] = args.relative_paths if args.port is not None: - config['PORT'] = args.port + config["PORT"] = args.port if args.bind is not None: - config['BIND'] = args.bind - config['DEBUG'] = args.verbosity == logging.DEBUG + config["BIND"] = args.bind + config["DEBUG"] = args.verbosity == logging.DEBUG config.update(args.overrides) return config def get_instance(args): - config_file = args.settings if config_file is None and os.path.isfile(DEFAULT_CONFIG_NAME): config_file = DEFAULT_CONFIG_NAME @@ -439,9 +549,9 @@ def get_instance(args): settings = read_settings(config_file, override=get_config(args)) - cls = settings['PELICAN_CLASS'] + cls = settings["PELICAN_CLASS"] if isinstance(cls, str): - module, cls_name = cls.rsplit('.', 1) + module, cls_name = cls.rsplit(".", 1) module = __import__(module) cls = getattr(module, cls_name) @@ -449,8 +559,10 @@ def get_instance(args): def autoreload(args, excqueue=None): - console.print(' --- AutoReload Mode: Monitoring `content`, `theme` and' - ' `settings` for changes. ---') + console.print( + " --- AutoReload Mode: Monitoring `content`, `theme` and" + " `settings` for changes. ---" + ) pelican, settings = get_instance(args) settings_file = os.path.abspath(args.settings) while True: @@ -463,8 +575,9 @@ def autoreload(args, excqueue=None): if settings_file in changed_files: pelican, settings = get_instance(args) - console.print('\n-> Modified: {}. re-generating...'.format( - ', '.join(changed_files))) + console.print( + "\n-> Modified: {}. re-generating...".format(", ".join(changed_files)) + ) except KeyboardInterrupt: if excqueue is not None: @@ -473,15 +586,14 @@ def autoreload(args, excqueue=None): raise except Exception as e: - if (args.verbosity == logging.DEBUG): + if args.verbosity == logging.DEBUG: if excqueue is not None: - excqueue.put( - traceback.format_exception_only(type(e), e)[-1]) + excqueue.put(traceback.format_exception_only(type(e), e)[-1]) else: raise logger.warning( - 'Caught exception:\n"%s".', e, - exc_info=settings.get('DEBUG', False)) + 'Caught exception:\n"%s".', e, exc_info=settings.get("DEBUG", False) + ) def listen(server, port, output, excqueue=None): @@ -491,8 +603,7 @@ def listen(server, port, output, excqueue=None): RootedHTTPServer.allow_reuse_address = True try: - httpd = RootedHTTPServer( - output, (server, port), ComplexHTTPRequestHandler) + httpd = RootedHTTPServer(output, (server, port), ComplexHTTPRequestHandler) except OSError as e: logging.error("Could not listen on port %s, server %s.", port, server) if excqueue is not None: @@ -500,8 +611,9 @@ def listen(server, port, output, excqueue=None): return try: - console.print("Serving site at: http://{}:{} - Tap CTRL-C to stop".format( - server, port)) + console.print( + "Serving site at: http://{}:{} - Tap CTRL-C to stop".format(server, port) + ) httpd.serve_forever() except Exception as e: if excqueue is not None: @@ -518,24 +630,31 @@ def listen(server, port, output, excqueue=None): def main(argv=None): args = parse_arguments(argv) logs_dedup_min_level = getattr(logging, args.logs_dedup_min_level) - init_logging(level=args.verbosity, fatal=args.fatal, - name=__name__, logs_dedup_min_level=logs_dedup_min_level) + init_logging( + level=args.verbosity, + fatal=args.fatal, + name=__name__, + logs_dedup_min_level=logs_dedup_min_level, + ) - logger.debug('Pelican version: %s', __version__) - logger.debug('Python version: %s', sys.version.split()[0]) + logger.debug("Pelican version: %s", __version__) + logger.debug("Python version: %s", sys.version.split()[0]) try: pelican, settings = get_instance(args) if args.autoreload and args.listen: excqueue = multiprocessing.Queue() - p1 = multiprocessing.Process( - target=autoreload, - args=(args, excqueue)) + p1 = multiprocessing.Process(target=autoreload, args=(args, excqueue)) p2 = multiprocessing.Process( target=listen, - args=(settings.get('BIND'), settings.get('PORT'), - settings.get("OUTPUT_PATH"), excqueue)) + args=( + settings.get("BIND"), + settings.get("PORT"), + settings.get("OUTPUT_PATH"), + excqueue, + ), + ) try: p1.start() p2.start() @@ -548,16 +667,17 @@ def main(argv=None): elif args.autoreload: autoreload(args) elif args.listen: - listen(settings.get('BIND'), settings.get('PORT'), - settings.get("OUTPUT_PATH")) + listen( + settings.get("BIND"), settings.get("PORT"), settings.get("OUTPUT_PATH") + ) else: with console.status("Generating..."): pelican.run() except KeyboardInterrupt: - logger.warning('Keyboard interrupt received. Exiting.') + logger.warning("Keyboard interrupt received. Exiting.") except Exception as e: logger.critical("%s: %s", e.__class__.__name__, e) if args.verbosity == logging.DEBUG: console.print_exception() - sys.exit(getattr(e, 'exitcode', 1)) + sys.exit(getattr(e, "exitcode", 1)) diff --git a/pelican/__main__.py b/pelican/__main__.py index 69a5b95d..17aead3b 100644 --- a/pelican/__main__.py +++ b/pelican/__main__.py @@ -5,5 +5,5 @@ python -m pelican module entry point to run via python -m from . import main -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/pelican/cache.py b/pelican/cache.py index d2665691..d1f8550e 100644 --- a/pelican/cache.py +++ b/pelican/cache.py @@ -19,29 +19,35 @@ class FileDataCacher: Sets caching policy according to *caching_policy*. """ self.settings = settings - self._cache_path = os.path.join(self.settings['CACHE_PATH'], - cache_name) + self._cache_path = os.path.join(self.settings["CACHE_PATH"], cache_name) self._cache_data_policy = caching_policy - if self.settings['GZIP_CACHE']: + if self.settings["GZIP_CACHE"]: import gzip + self._cache_open = gzip.open else: self._cache_open = open if load_policy: try: - with self._cache_open(self._cache_path, 'rb') as fhandle: + with self._cache_open(self._cache_path, "rb") as fhandle: self._cache = pickle.load(fhandle) except (OSError, UnicodeDecodeError) as err: - logger.debug('Cannot load cache %s (this is normal on first ' - 'run). Proceeding with empty cache.\n%s', - self._cache_path, err) + logger.debug( + "Cannot load cache %s (this is normal on first " + "run). Proceeding with empty cache.\n%s", + self._cache_path, + err, + ) self._cache = {} except pickle.PickleError as err: - logger.warning('Cannot unpickle cache %s, cache may be using ' - 'an incompatible protocol (see pelican ' - 'caching docs). ' - 'Proceeding with empty cache.\n%s', - self._cache_path, err) + logger.warning( + "Cannot unpickle cache %s, cache may be using " + "an incompatible protocol (see pelican " + "caching docs). " + "Proceeding with empty cache.\n%s", + self._cache_path, + err, + ) self._cache = {} else: self._cache = {} @@ -62,12 +68,13 @@ class FileDataCacher: """Save the updated cache""" if self._cache_data_policy: try: - mkdir_p(self.settings['CACHE_PATH']) - with self._cache_open(self._cache_path, 'wb') as fhandle: + mkdir_p(self.settings["CACHE_PATH"]) + with self._cache_open(self._cache_path, "wb") as fhandle: pickle.dump(self._cache, fhandle) except (OSError, pickle.PicklingError, TypeError) as err: - logger.warning('Could not save cache %s\n ... %s', - self._cache_path, err) + logger.warning( + "Could not save cache %s\n ... %s", self._cache_path, err + ) class FileStampDataCacher(FileDataCacher): @@ -80,8 +87,8 @@ class FileStampDataCacher(FileDataCacher): super().__init__(settings, cache_name, caching_policy, load_policy) - method = self.settings['CHECK_MODIFIED_METHOD'] - if method == 'mtime': + method = self.settings["CHECK_MODIFIED_METHOD"] + if method == "mtime": self._filestamp_func = os.path.getmtime else: try: @@ -89,12 +96,12 @@ class FileStampDataCacher(FileDataCacher): def filestamp_func(filename): """return hash of file contents""" - with open(filename, 'rb') as fhandle: + with open(filename, "rb") as fhandle: return hash_func(fhandle.read()).digest() self._filestamp_func = filestamp_func except AttributeError as err: - logger.warning('Could not get hashing function\n\t%s', err) + logger.warning("Could not get hashing function\n\t%s", err) self._filestamp_func = None def cache_data(self, filename, data): @@ -115,9 +122,8 @@ class FileStampDataCacher(FileDataCacher): try: return self._filestamp_func(filename) except (OSError, TypeError) as err: - logger.warning('Cannot get modification stamp for %s\n\t%s', - filename, err) - return '' + logger.warning("Cannot get modification stamp for %s\n\t%s", filename, err) + return "" def get_cached_data(self, filename, default=None): """Get the cached data for the given filename diff --git a/pelican/contents.py b/pelican/contents.py index c347a999..f99e6426 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -16,12 +16,19 @@ except ModuleNotFoundError: from pelican.plugins import signals from pelican.settings import DEFAULT_CONFIG -from pelican.utils import (deprecated_attribute, memoized, path_to_url, - posixize_path, sanitised_join, set_date_tzinfo, - slugify, truncate_html_words) +from pelican.utils import ( + deprecated_attribute, + memoized, + path_to_url, + posixize_path, + sanitised_join, + set_date_tzinfo, + slugify, + truncate_html_words, +) # Import these so that they're available when you import from pelican.contents. -from pelican.urlwrappers import (Author, Category, Tag, URLWrapper) # NOQA +from pelican.urlwrappers import Author, Category, Tag, URLWrapper # NOQA logger = logging.getLogger(__name__) @@ -36,12 +43,14 @@ class Content: :param context: The shared context between generators. """ - @deprecated_attribute(old='filename', new='source_path', since=(3, 2, 0)) + + @deprecated_attribute(old="filename", new="source_path", since=(3, 2, 0)) def filename(): return None - def __init__(self, content, metadata=None, settings=None, - source_path=None, context=None): + def __init__( + self, content, metadata=None, settings=None, source_path=None, context=None + ): if metadata is None: metadata = {} if settings is None: @@ -59,8 +68,8 @@ class Content: # set metadata as attributes for key, value in local_metadata.items(): - if key in ('save_as', 'url'): - key = 'override_' + key + if key in ("save_as", "url"): + key = "override_" + key setattr(self, key.lower(), value) # also keep track of the metadata attributes available @@ -71,53 +80,52 @@ class Content: # First, read the authors from "authors", if not, fallback to "author" # and if not use the settings defined one, if any. - if not hasattr(self, 'author'): - if hasattr(self, 'authors'): + if not hasattr(self, "author"): + if hasattr(self, "authors"): self.author = self.authors[0] - elif 'AUTHOR' in settings: - self.author = Author(settings['AUTHOR'], settings) + elif "AUTHOR" in settings: + self.author = Author(settings["AUTHOR"], settings) - if not hasattr(self, 'authors') and hasattr(self, 'author'): + if not hasattr(self, "authors") and hasattr(self, "author"): self.authors = [self.author] # XXX Split all the following code into pieces, there is too much here. # manage languages self.in_default_lang = True - if 'DEFAULT_LANG' in settings: - default_lang = settings['DEFAULT_LANG'].lower() - if not hasattr(self, 'lang'): + if "DEFAULT_LANG" in settings: + default_lang = settings["DEFAULT_LANG"].lower() + if not hasattr(self, "lang"): self.lang = default_lang - self.in_default_lang = (self.lang == default_lang) + self.in_default_lang = self.lang == default_lang # create the slug if not existing, generate slug according to # setting of SLUG_ATTRIBUTE - if not hasattr(self, 'slug'): - if (settings['SLUGIFY_SOURCE'] == 'title' and - hasattr(self, 'title')): + if not hasattr(self, "slug"): + if settings["SLUGIFY_SOURCE"] == "title" and hasattr(self, "title"): value = self.title - elif (settings['SLUGIFY_SOURCE'] == 'basename' and - source_path is not None): + elif settings["SLUGIFY_SOURCE"] == "basename" and source_path is not None: value = os.path.basename(os.path.splitext(source_path)[0]) else: value = None if value is not None: self.slug = slugify( value, - regex_subs=settings.get('SLUG_REGEX_SUBSTITUTIONS', []), - preserve_case=settings.get('SLUGIFY_PRESERVE_CASE', False), - use_unicode=settings.get('SLUGIFY_USE_UNICODE', False)) + regex_subs=settings.get("SLUG_REGEX_SUBSTITUTIONS", []), + preserve_case=settings.get("SLUGIFY_PRESERVE_CASE", False), + use_unicode=settings.get("SLUGIFY_USE_UNICODE", False), + ) self.source_path = source_path self.relative_source_path = self.get_relative_source_path() # manage the date format - if not hasattr(self, 'date_format'): - if hasattr(self, 'lang') and self.lang in settings['DATE_FORMATS']: - self.date_format = settings['DATE_FORMATS'][self.lang] + if not hasattr(self, "date_format"): + if hasattr(self, "lang") and self.lang in settings["DATE_FORMATS"]: + self.date_format = settings["DATE_FORMATS"][self.lang] else: - self.date_format = settings['DEFAULT_DATE_FORMAT'] + self.date_format = settings["DEFAULT_DATE_FORMAT"] if isinstance(self.date_format, tuple): locale_string = self.date_format[0] @@ -129,22 +137,22 @@ class Content: timezone = getattr(self, "timezone", default_timezone) self.timezone = ZoneInfo(timezone) - if hasattr(self, 'date'): + if hasattr(self, "date"): self.date = set_date_tzinfo(self.date, timezone) self.locale_date = self.date.strftime(self.date_format) - if hasattr(self, 'modified'): + if hasattr(self, "modified"): self.modified = set_date_tzinfo(self.modified, timezone) self.locale_modified = self.modified.strftime(self.date_format) # manage status - if not hasattr(self, 'status'): + if not hasattr(self, "status"): # Previous default of None broke comment plugins and perhaps others - self.status = getattr(self, 'default_status', '') + self.status = getattr(self, "default_status", "") # store the summary metadata if it is set - if 'summary' in metadata: - self._summary = metadata['summary'] + if "summary" in metadata: + self._summary = metadata["summary"] signals.content_object_init.send(self) @@ -156,8 +164,8 @@ class Content: for prop in self.mandatory_properties: if not hasattr(self, prop): logger.error( - "Skipping %s: could not find information about '%s'", - self, prop) + "Skipping %s: could not find information about '%s'", self, prop + ) return False return True @@ -183,12 +191,13 @@ class Content: return True def _has_valid_status(self): - if hasattr(self, 'allowed_statuses'): + if hasattr(self, "allowed_statuses"): if self.status not in self.allowed_statuses: logger.error( "Unknown status '%s' for file %s, skipping it. (Not in %s)", self.status, - self, self.allowed_statuses + self, + self.allowed_statuses, ) return False @@ -198,42 +207,48 @@ class Content: def is_valid(self): """Validate Content""" # Use all() to not short circuit and get results of all validations - return all([self._has_valid_mandatory_properties(), - self._has_valid_save_as(), - self._has_valid_status()]) + return all( + [ + self._has_valid_mandatory_properties(), + self._has_valid_save_as(), + self._has_valid_status(), + ] + ) @property def url_format(self): """Returns the URL, formatted with the proper values""" metadata = copy.copy(self.metadata) - path = self.metadata.get('path', self.get_relative_source_path()) - metadata.update({ - 'path': path_to_url(path), - 'slug': getattr(self, 'slug', ''), - 'lang': getattr(self, 'lang', 'en'), - 'date': getattr(self, 'date', datetime.datetime.now()), - 'author': self.author.slug if hasattr(self, 'author') else '', - 'category': self.category.slug if hasattr(self, 'category') else '' - }) + path = self.metadata.get("path", self.get_relative_source_path()) + metadata.update( + { + "path": path_to_url(path), + "slug": getattr(self, "slug", ""), + "lang": getattr(self, "lang", "en"), + "date": getattr(self, "date", datetime.datetime.now()), + "author": self.author.slug if hasattr(self, "author") else "", + "category": self.category.slug if hasattr(self, "category") else "", + } + ) return metadata def _expand_settings(self, key, klass=None): if not klass: klass = self.__class__.__name__ - fq_key = ('{}_{}'.format(klass, key)).upper() + fq_key = ("{}_{}".format(klass, key)).upper() return str(self.settings[fq_key]).format(**self.url_format) def get_url_setting(self, key): - if hasattr(self, 'override_' + key): - return getattr(self, 'override_' + key) - key = key if self.in_default_lang else 'lang_%s' % key + if hasattr(self, "override_" + key): + return getattr(self, "override_" + key) + key = key if self.in_default_lang else "lang_%s" % key return self._expand_settings(key) def _link_replacer(self, siteurl, m): - what = m.group('what') - value = urlparse(m.group('value')) + what = m.group("what") + value = urlparse(m.group("value")) path = value.path - origin = m.group('path') + origin = m.group("path") # urllib.parse.urljoin() produces `a.html` for urljoin("..", "a.html") # so if RELATIVE_URLS are enabled, we fall back to os.path.join() to @@ -241,7 +256,7 @@ class Content: # `baz/http://foo/bar.html` for join("baz", "http://foo/bar.html") # instead of correct "http://foo/bar.html", so one has to pick a side # as there is no silver bullet. - if self.settings['RELATIVE_URLS']: + if self.settings["RELATIVE_URLS"]: joiner = os.path.join else: joiner = urljoin @@ -251,16 +266,17 @@ class Content: # os.path.join()), so in order to get a correct answer one needs to # append a trailing slash to siteurl in that case. This also makes # the new behavior fully compatible with Pelican 3.7.1. - if not siteurl.endswith('/'): - siteurl += '/' + if not siteurl.endswith("/"): + siteurl += "/" # XXX Put this in a different location. - if what in {'filename', 'static', 'attach'}: + if what in {"filename", "static", "attach"}: + def _get_linked_content(key, url): nonlocal value def _find_path(path): - if path.startswith('/'): + if path.startswith("/"): path = path[1:] else: # relative to the source path of this content @@ -287,59 +303,64 @@ class Content: return result # check if a static file is linked with {filename} - if what == 'filename' and key == 'generated_content': - linked_content = _get_linked_content('static_content', value) + if what == "filename" and key == "generated_content": + linked_content = _get_linked_content("static_content", value) if linked_content: logger.warning( - '{filename} used for linking to static' - ' content %s in %s. Use {static} instead', + "{filename} used for linking to static" + " content %s in %s. Use {static} instead", value.path, - self.get_relative_source_path()) + self.get_relative_source_path(), + ) return linked_content return None - if what == 'filename': - key = 'generated_content' + if what == "filename": + key = "generated_content" else: - key = 'static_content' + key = "static_content" linked_content = _get_linked_content(key, value) if linked_content: - if what == 'attach': + if what == "attach": linked_content.attach_to(self) origin = joiner(siteurl, linked_content.url) - origin = origin.replace('\\', '/') # for Windows paths. + origin = origin.replace("\\", "/") # for Windows paths. else: logger.warning( "Unable to find '%s', skipping url replacement.", - value.geturl(), extra={ - 'limit_msg': ("Other resources were not found " - "and their urls not replaced")}) - elif what == 'category': + value.geturl(), + extra={ + "limit_msg": ( + "Other resources were not found " + "and their urls not replaced" + ) + }, + ) + elif what == "category": origin = joiner(siteurl, Category(path, self.settings).url) - elif what == 'tag': + elif what == "tag": origin = joiner(siteurl, Tag(path, self.settings).url) - elif what == 'index': - origin = joiner(siteurl, self.settings['INDEX_SAVE_AS']) - elif what == 'author': + elif what == "index": + origin = joiner(siteurl, self.settings["INDEX_SAVE_AS"]) + elif what == "author": origin = joiner(siteurl, Author(path, self.settings).url) else: logger.warning( - "Replacement Indicator '%s' not recognized, " - "skipping replacement", - what) + "Replacement Indicator '%s' not recognized, " "skipping replacement", + what, + ) # keep all other parts, such as query, fragment, etc. parts = list(value) parts[2] = origin origin = urlunparse(parts) - return ''.join((m.group('markup'), m.group('quote'), origin, - m.group('quote'))) + return "".join((m.group("markup"), m.group("quote"), origin, m.group("quote"))) def _get_intrasite_link_regex(self): - intrasite_link_regex = self.settings['INTRASITE_LINK_REGEX'] + intrasite_link_regex = self.settings["INTRASITE_LINK_REGEX"] regex = r""" (?P<[^\>]+ # match tag with all url-value attributes (?:href|src|poster|data|cite|formaction|action|content)\s*=\s*) @@ -369,28 +390,28 @@ class Content: static_links = set() hrefs = self._get_intrasite_link_regex() for m in hrefs.finditer(self._content): - what = m.group('what') - value = urlparse(m.group('value')) + what = m.group("what") + value = urlparse(m.group("value")) path = value.path - if what not in {'static', 'attach'}: + if what not in {"static", "attach"}: continue - if path.startswith('/'): + if path.startswith("/"): path = path[1:] else: # relative to the source path of this content path = self.get_relative_source_path( os.path.join(self.relative_dir, path) ) - path = path.replace('%20', ' ') + path = path.replace("%20", " ") static_links.add(path) return static_links def get_siteurl(self): - return self._context.get('localsiteurl', '') + return self._context.get("localsiteurl", "") @memoized def get_content(self, siteurl): - if hasattr(self, '_get_content'): + if hasattr(self, "_get_content"): content = self._get_content() else: content = self._content @@ -407,15 +428,17 @@ class Content: This is based on the summary metadata if set, otherwise truncate the content. """ - if 'summary' in self.metadata: - return self.metadata['summary'] + if "summary" in self.metadata: + return self.metadata["summary"] - if self.settings['SUMMARY_MAX_LENGTH'] is None: + if self.settings["SUMMARY_MAX_LENGTH"] is None: return self.content - return truncate_html_words(self.content, - self.settings['SUMMARY_MAX_LENGTH'], - self.settings['SUMMARY_END_SUFFIX']) + return truncate_html_words( + self.content, + self.settings["SUMMARY_MAX_LENGTH"], + self.settings["SUMMARY_END_SUFFIX"], + ) @property def summary(self): @@ -424,8 +447,10 @@ class Content: def _get_summary(self): """deprecated function to access summary""" - logger.warning('_get_summary() has been deprecated since 3.6.4. ' - 'Use the summary decorator instead') + logger.warning( + "_get_summary() has been deprecated since 3.6.4. " + "Use the summary decorator instead" + ) return self.summary @summary.setter @@ -444,14 +469,14 @@ class Content: @property def url(self): - return self.get_url_setting('url') + return self.get_url_setting("url") @property def save_as(self): - return self.get_url_setting('save_as') + return self.get_url_setting("save_as") def _get_template(self): - if hasattr(self, 'template') and self.template is not None: + if hasattr(self, "template") and self.template is not None: return self.template else: return self.default_template @@ -470,11 +495,10 @@ class Content: return posixize_path( os.path.relpath( - os.path.abspath(os.path.join( - self.settings['PATH'], - source_path)), - os.path.abspath(self.settings['PATH']) - )) + os.path.abspath(os.path.join(self.settings["PATH"], source_path)), + os.path.abspath(self.settings["PATH"]), + ) + ) @property def relative_dir(self): @@ -482,85 +506,84 @@ class Content: os.path.dirname( os.path.relpath( os.path.abspath(self.source_path), - os.path.abspath(self.settings['PATH'])))) + os.path.abspath(self.settings["PATH"]), + ) + ) + ) def refresh_metadata_intersite_links(self): - for key in self.settings['FORMATTED_FIELDS']: - if key in self.metadata and key != 'summary': - value = self._update_content( - self.metadata[key], - self.get_siteurl() - ) + for key in self.settings["FORMATTED_FIELDS"]: + if key in self.metadata and key != "summary": + value = self._update_content(self.metadata[key], self.get_siteurl()) self.metadata[key] = value setattr(self, key.lower(), value) # _summary is an internal variable that some plugins may be writing to, # so ensure changes to it are picked up - if ('summary' in self.settings['FORMATTED_FIELDS'] and - 'summary' in self.metadata): - self._summary = self._update_content( - self._summary, - self.get_siteurl() - ) - self.metadata['summary'] = self._summary + if ( + "summary" in self.settings["FORMATTED_FIELDS"] + and "summary" in self.metadata + ): + self._summary = self._update_content(self._summary, self.get_siteurl()) + self.metadata["summary"] = self._summary class Page(Content): - mandatory_properties = ('title',) - allowed_statuses = ('published', 'hidden', 'draft') - default_status = 'published' - default_template = 'page' + mandatory_properties = ("title",) + allowed_statuses = ("published", "hidden", "draft") + default_status = "published" + default_template = "page" def _expand_settings(self, key): - klass = 'draft_page' if self.status == 'draft' else None + klass = "draft_page" if self.status == "draft" else None return super()._expand_settings(key, klass) class Article(Content): - mandatory_properties = ('title', 'date', 'category') - allowed_statuses = ('published', 'hidden', 'draft') - default_status = 'published' - default_template = 'article' + mandatory_properties = ("title", "date", "category") + allowed_statuses = ("published", "hidden", "draft") + default_status = "published" + default_template = "article" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # handle WITH_FUTURE_DATES (designate article to draft based on date) - if not self.settings['WITH_FUTURE_DATES'] and hasattr(self, 'date'): + if not self.settings["WITH_FUTURE_DATES"] and hasattr(self, "date"): if self.date.tzinfo is None: now = datetime.datetime.now() else: now = datetime.datetime.utcnow().replace(tzinfo=timezone.utc) if self.date > now: - self.status = 'draft' + self.status = "draft" # if we are a draft and there is no date provided, set max datetime - if not hasattr(self, 'date') and self.status == 'draft': + if not hasattr(self, "date") and self.status == "draft": self.date = datetime.datetime.max.replace(tzinfo=self.timezone) def _expand_settings(self, key): - klass = 'draft' if self.status == 'draft' else 'article' + klass = "draft" if self.status == "draft" else "article" return super()._expand_settings(key, klass) class Static(Content): - mandatory_properties = ('title',) - default_status = 'published' + mandatory_properties = ("title",) + default_status = "published" default_template = None def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._output_location_referenced = False - @deprecated_attribute(old='filepath', new='source_path', since=(3, 2, 0)) + @deprecated_attribute(old="filepath", new="source_path", since=(3, 2, 0)) def filepath(): return None - @deprecated_attribute(old='src', new='source_path', since=(3, 2, 0)) + @deprecated_attribute(old="src", new="source_path", since=(3, 2, 0)) def src(): return None - @deprecated_attribute(old='dst', new='save_as', since=(3, 2, 0)) + @deprecated_attribute(old="dst", new="save_as", since=(3, 2, 0)) def dst(): return None @@ -577,8 +600,7 @@ class Static(Content): return super().save_as def attach_to(self, content): - """Override our output directory with that of the given content object. - """ + """Override our output directory with that of the given content object.""" # Determine our file's new output path relative to the linking # document. If it currently lives beneath the linking @@ -589,8 +611,7 @@ class Static(Content): tail_path = os.path.relpath(self.source_path, linking_source_dir) if tail_path.startswith(os.pardir + os.sep): tail_path = os.path.basename(tail_path) - new_save_as = os.path.join( - os.path.dirname(content.save_as), tail_path) + new_save_as = os.path.join(os.path.dirname(content.save_as), tail_path) # We do not build our new url by joining tail_path with the linking # document's url, because we cannot know just by looking at the latter @@ -609,12 +630,14 @@ class Static(Content): "%s because %s. Falling back to " "{filename} link behavior instead.", content.get_relative_source_path(), - self.get_relative_source_path(), reason, - extra={'limit_msg': "More {attach} warnings silenced."}) + self.get_relative_source_path(), + reason, + extra={"limit_msg": "More {attach} warnings silenced."}, + ) # We never override an override, because we don't want to interfere # with user-defined overrides that might be in EXTRA_PATH_METADATA. - if hasattr(self, 'override_save_as') or hasattr(self, 'override_url'): + if hasattr(self, "override_save_as") or hasattr(self, "override_url"): if new_save_as != self.save_as or new_url != self.url: _log_reason("its output location was already overridden") return diff --git a/pelican/generators.py b/pelican/generators.py index b9063304..0bbb7268 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -8,15 +8,27 @@ from functools import partial from itertools import chain, groupby from operator import attrgetter -from jinja2 import (BaseLoader, ChoiceLoader, Environment, FileSystemLoader, - PrefixLoader, TemplateNotFound) +from jinja2 import ( + BaseLoader, + ChoiceLoader, + Environment, + FileSystemLoader, + PrefixLoader, + TemplateNotFound, +) from pelican.cache import FileStampDataCacher from pelican.contents import Article, Page, Static from pelican.plugins import signals from pelican.readers import Readers -from pelican.utils import (DateFormatter, copy, mkdir_p, order_content, - posixize_path, process_translations) +from pelican.utils import ( + DateFormatter, + copy, + mkdir_p, + order_content, + posixize_path, + process_translations, +) logger = logging.getLogger(__name__) @@ -28,8 +40,16 @@ class PelicanTemplateNotFound(Exception): class Generator: """Baseclass generator""" - def __init__(self, context, settings, path, theme, output_path, - readers_cache_name='', **kwargs): + def __init__( + self, + context, + settings, + path, + theme, + output_path, + readers_cache_name="", + **kwargs, + ): self.context = context self.settings = settings self.path = path @@ -43,44 +63,45 @@ class Generator: # templates cache self._templates = {} - self._templates_path = list(self.settings['THEME_TEMPLATES_OVERRIDES']) + self._templates_path = list(self.settings["THEME_TEMPLATES_OVERRIDES"]) - theme_templates_path = os.path.expanduser( - os.path.join(self.theme, 'templates')) + theme_templates_path = os.path.expanduser(os.path.join(self.theme, "templates")) self._templates_path.append(theme_templates_path) theme_loader = FileSystemLoader(theme_templates_path) simple_theme_path = os.path.dirname(os.path.abspath(__file__)) simple_loader = FileSystemLoader( - os.path.join(simple_theme_path, "themes", "simple", "templates")) - - self.env = Environment( - loader=ChoiceLoader([ - FileSystemLoader(self._templates_path), - simple_loader, # implicit inheritance - PrefixLoader({ - '!simple': simple_loader, - '!theme': theme_loader - }) # explicit ones - ]), - **self.settings['JINJA_ENVIRONMENT'] + os.path.join(simple_theme_path, "themes", "simple", "templates") ) - logger.debug('Template list: %s', self.env.list_templates()) + self.env = Environment( + loader=ChoiceLoader( + [ + FileSystemLoader(self._templates_path), + simple_loader, # implicit inheritance + PrefixLoader( + {"!simple": simple_loader, "!theme": theme_loader} + ), # explicit ones + ] + ), + **self.settings["JINJA_ENVIRONMENT"], + ) + + logger.debug("Template list: %s", self.env.list_templates()) # provide utils.strftime as a jinja filter - self.env.filters.update({'strftime': DateFormatter()}) + self.env.filters.update({"strftime": DateFormatter()}) # get custom Jinja filters from user settings - custom_filters = self.settings['JINJA_FILTERS'] + custom_filters = self.settings["JINJA_FILTERS"] self.env.filters.update(custom_filters) # get custom Jinja globals from user settings - custom_globals = self.settings['JINJA_GLOBALS'] + custom_globals = self.settings["JINJA_GLOBALS"] self.env.globals.update(custom_globals) # get custom Jinja tests from user settings - custom_tests = self.settings['JINJA_TESTS'] + custom_tests = self.settings["JINJA_TESTS"] self.env.tests.update(custom_tests) signals.generator_init.send(self) @@ -91,7 +112,7 @@ class Generator: templates ready to use with Jinja2. """ if name not in self._templates: - for ext in self.settings['TEMPLATE_EXTENSIONS']: + for ext in self.settings["TEMPLATE_EXTENSIONS"]: try: self._templates[name] = self.env.get_template(name + ext) break @@ -100,9 +121,12 @@ class Generator: if name not in self._templates: raise PelicanTemplateNotFound( - '[templates] unable to load {}[{}] from {}'.format( - name, ', '.join(self.settings['TEMPLATE_EXTENSIONS']), - self._templates_path)) + "[templates] unable to load {}[{}] from {}".format( + name, + ", ".join(self.settings["TEMPLATE_EXTENSIONS"]), + self._templates_path, + ) + ) return self._templates[name] @@ -118,7 +142,7 @@ class Generator: basename = os.path.basename(path) # check IGNORE_FILES - ignores = self.settings['IGNORE_FILES'] + ignores = self.settings["IGNORE_FILES"] if any(fnmatch.fnmatch(basename, ignore) for ignore in ignores): return False @@ -147,20 +171,21 @@ class Generator: exclusions_by_dirpath.setdefault(parent_path, set()).add(subdir) files = set() - ignores = self.settings['IGNORE_FILES'] + ignores = self.settings["IGNORE_FILES"] for path in paths: # careful: os.path.join() will add a slash when path == ''. root = os.path.join(self.path, path) if path else self.path if os.path.isdir(root): for dirpath, dirs, temp_files in os.walk( - root, topdown=True, followlinks=True): + root, topdown=True, followlinks=True + ): excl = exclusions_by_dirpath.get(dirpath, ()) # We copy the `dirs` list as we will modify it in the loop: for d in list(dirs): - if (d in excl or - any(fnmatch.fnmatch(d, ignore) - for ignore in ignores)): + if d in excl or any( + fnmatch.fnmatch(d, ignore) for ignore in ignores + ): if d in dirs: dirs.remove(d) @@ -178,7 +203,7 @@ class Generator: Store a reference to its Content object, for url lookups later. """ location = content.get_relative_source_path() - key = 'static_content' if static else 'generated_content' + key = "static_content" if static else "generated_content" self.context[key][location] = content def _add_failed_source_path(self, path, static=False): @@ -186,7 +211,7 @@ class Generator: (For example, one that was missing mandatory metadata.) The path argument is expected to be relative to self.path. """ - key = 'static_content' if static else 'generated_content' + key = "static_content" if static else "generated_content" self.context[key][posixize_path(os.path.normpath(path))] = None def _is_potential_source_path(self, path, static=False): @@ -195,14 +220,14 @@ class Generator: before this method is called, even if they failed to process.) The path argument is expected to be relative to self.path. """ - key = 'static_content' if static else 'generated_content' - return (posixize_path(os.path.normpath(path)) in self.context[key]) + key = "static_content" if static else "generated_content" + return posixize_path(os.path.normpath(path)) in self.context[key] def add_static_links(self, content): """Add file links in content to context to be processed as Static content. """ - self.context['static_links'] |= content.get_static_links() + self.context["static_links"] |= content.get_static_links() def _update_context(self, items): """Update the context with the given items from the current processor. @@ -211,7 +236,7 @@ class Generator: """ for item in items: value = getattr(self, item) - if hasattr(value, 'items'): + if hasattr(value, "items"): value = list(value.items()) # py3k safeguard for iterators self.context[item] = value @@ -221,37 +246,35 @@ class Generator: class CachingGenerator(Generator, FileStampDataCacher): - '''Subclass of Generator and FileStampDataCacher classes + """Subclass of Generator and FileStampDataCacher classes enables content caching, either at the generator or reader level - ''' + """ def __init__(self, *args, **kwargs): - '''Initialize the generator, then set up caching + """Initialize the generator, then set up caching note the multiple inheritance structure - ''' + """ cls_name = self.__class__.__name__ - Generator.__init__(self, *args, - readers_cache_name=(cls_name + '-Readers'), - **kwargs) + Generator.__init__( + self, *args, readers_cache_name=(cls_name + "-Readers"), **kwargs + ) - cache_this_level = \ - self.settings['CONTENT_CACHING_LAYER'] == 'generator' - caching_policy = cache_this_level and self.settings['CACHE_CONTENT'] - load_policy = cache_this_level and self.settings['LOAD_CONTENT_CACHE'] - FileStampDataCacher.__init__(self, self.settings, cls_name, - caching_policy, load_policy - ) + cache_this_level = self.settings["CONTENT_CACHING_LAYER"] == "generator" + caching_policy = cache_this_level and self.settings["CACHE_CONTENT"] + load_policy = cache_this_level and self.settings["LOAD_CONTENT_CACHE"] + FileStampDataCacher.__init__( + self, self.settings, cls_name, caching_policy, load_policy + ) def _get_file_stamp(self, filename): - '''Get filestamp for path relative to generator.path''' + """Get filestamp for path relative to generator.path""" filename = os.path.join(self.path, filename) return super()._get_file_stamp(filename) class _FileLoader(BaseLoader): - def __init__(self, path, basedir): self.path = path self.fullpath = os.path.join(basedir, path) @@ -260,22 +283,21 @@ class _FileLoader(BaseLoader): if template != self.path or not os.path.exists(self.fullpath): raise TemplateNotFound(template) mtime = os.path.getmtime(self.fullpath) - with open(self.fullpath, encoding='utf-8') as f: + with open(self.fullpath, encoding="utf-8") as f: source = f.read() - return (source, self.fullpath, - lambda: mtime == os.path.getmtime(self.fullpath)) + return (source, self.fullpath, lambda: mtime == os.path.getmtime(self.fullpath)) class TemplatePagesGenerator(Generator): - def generate_output(self, writer): - for source, dest in self.settings['TEMPLATE_PAGES'].items(): + for source, dest in self.settings["TEMPLATE_PAGES"].items(): self.env.loader.loaders.insert(0, _FileLoader(source, self.path)) try: template = self.env.get_template(source) - rurls = self.settings['RELATIVE_URLS'] - writer.write_file(dest, template, self.context, rurls, - override_output=True, url='') + rurls = self.settings["RELATIVE_URLS"] + writer.write_file( + dest, template, self.context, rurls, override_output=True, url="" + ) finally: del self.env.loader.loaders[0] @@ -286,13 +308,13 @@ class ArticlesGenerator(CachingGenerator): def __init__(self, *args, **kwargs): """initialize properties""" # Published, listed articles - self.articles = [] # only articles in default language + self.articles = [] # only articles in default language self.translations = [] # Published, unlisted articles self.hidden_articles = [] self.hidden_translations = [] # Draft articles - self.drafts = [] # only drafts in default language + self.drafts = [] # only drafts in default language self.drafts_translations = [] self.dates = {} self.period_archives = defaultdict(list) @@ -306,263 +328,304 @@ class ArticlesGenerator(CachingGenerator): def generate_feeds(self, writer): """Generate the feeds from the current context, and output files.""" - if self.settings.get('FEED_ATOM'): + if self.settings.get("FEED_ATOM"): writer.write_feed( self.articles, self.context, - self.settings['FEED_ATOM'], - self.settings.get('FEED_ATOM_URL', self.settings['FEED_ATOM']) - ) + self.settings["FEED_ATOM"], + self.settings.get("FEED_ATOM_URL", self.settings["FEED_ATOM"]), + ) - if self.settings.get('FEED_RSS'): + if self.settings.get("FEED_RSS"): writer.write_feed( self.articles, self.context, - self.settings['FEED_RSS'], - self.settings.get('FEED_RSS_URL', self.settings['FEED_RSS']), - feed_type='rss' - ) + self.settings["FEED_RSS"], + self.settings.get("FEED_RSS_URL", self.settings["FEED_RSS"]), + feed_type="rss", + ) - if (self.settings.get('FEED_ALL_ATOM') or - self.settings.get('FEED_ALL_RSS')): + if self.settings.get("FEED_ALL_ATOM") or self.settings.get("FEED_ALL_RSS"): all_articles = list(self.articles) for article in self.articles: all_articles.extend(article.translations) - order_content( - all_articles, order_by=self.settings['ARTICLE_ORDER_BY'] - ) + order_content(all_articles, order_by=self.settings["ARTICLE_ORDER_BY"]) - if self.settings.get('FEED_ALL_ATOM'): + if self.settings.get("FEED_ALL_ATOM"): writer.write_feed( all_articles, self.context, - self.settings['FEED_ALL_ATOM'], - self.settings.get('FEED_ALL_ATOM_URL', - self.settings['FEED_ALL_ATOM']) - ) + self.settings["FEED_ALL_ATOM"], + self.settings.get( + "FEED_ALL_ATOM_URL", self.settings["FEED_ALL_ATOM"] + ), + ) - if self.settings.get('FEED_ALL_RSS'): + if self.settings.get("FEED_ALL_RSS"): writer.write_feed( all_articles, self.context, - self.settings['FEED_ALL_RSS'], - self.settings.get('FEED_ALL_RSS_URL', - self.settings['FEED_ALL_RSS']), - feed_type='rss' - ) + self.settings["FEED_ALL_RSS"], + self.settings.get( + "FEED_ALL_RSS_URL", self.settings["FEED_ALL_RSS"] + ), + feed_type="rss", + ) for cat, arts in self.categories: - if self.settings.get('CATEGORY_FEED_ATOM'): + if self.settings.get("CATEGORY_FEED_ATOM"): writer.write_feed( arts, self.context, - str(self.settings['CATEGORY_FEED_ATOM']).format(slug=cat.slug), + str(self.settings["CATEGORY_FEED_ATOM"]).format(slug=cat.slug), self.settings.get( - 'CATEGORY_FEED_ATOM_URL', - str(self.settings['CATEGORY_FEED_ATOM']).format( - slug=cat.slug - )), - feed_title=cat.name - ) - - if self.settings.get('CATEGORY_FEED_RSS'): - writer.write_feed( - arts, - self.context, - str(self.settings['CATEGORY_FEED_RSS']).format(slug=cat.slug), - self.settings.get( - 'CATEGORY_FEED_RSS_URL', - str(self.settings['CATEGORY_FEED_RSS']).format( - slug=cat.slug - )), + "CATEGORY_FEED_ATOM_URL", + str(self.settings["CATEGORY_FEED_ATOM"]).format(slug=cat.slug), + ), feed_title=cat.name, - feed_type='rss' - ) + ) + + if self.settings.get("CATEGORY_FEED_RSS"): + writer.write_feed( + arts, + self.context, + str(self.settings["CATEGORY_FEED_RSS"]).format(slug=cat.slug), + self.settings.get( + "CATEGORY_FEED_RSS_URL", + str(self.settings["CATEGORY_FEED_RSS"]).format(slug=cat.slug), + ), + feed_title=cat.name, + feed_type="rss", + ) for auth, arts in self.authors: - if self.settings.get('AUTHOR_FEED_ATOM'): + if self.settings.get("AUTHOR_FEED_ATOM"): writer.write_feed( arts, self.context, - str(self.settings['AUTHOR_FEED_ATOM']).format(slug=auth.slug), + str(self.settings["AUTHOR_FEED_ATOM"]).format(slug=auth.slug), self.settings.get( - 'AUTHOR_FEED_ATOM_URL', - str(self.settings['AUTHOR_FEED_ATOM']).format( - slug=auth.slug - )), - feed_title=auth.name - ) - - if self.settings.get('AUTHOR_FEED_RSS'): - writer.write_feed( - arts, - self.context, - str(self.settings['AUTHOR_FEED_RSS']).format(slug=auth.slug), - self.settings.get( - 'AUTHOR_FEED_RSS_URL', - str(self.settings['AUTHOR_FEED_RSS']).format( - slug=auth.slug - )), + "AUTHOR_FEED_ATOM_URL", + str(self.settings["AUTHOR_FEED_ATOM"]).format(slug=auth.slug), + ), feed_title=auth.name, - feed_type='rss' + ) + + if self.settings.get("AUTHOR_FEED_RSS"): + writer.write_feed( + arts, + self.context, + str(self.settings["AUTHOR_FEED_RSS"]).format(slug=auth.slug), + self.settings.get( + "AUTHOR_FEED_RSS_URL", + str(self.settings["AUTHOR_FEED_RSS"]).format(slug=auth.slug), + ), + feed_title=auth.name, + feed_type="rss", + ) + + if self.settings.get("TAG_FEED_ATOM") or self.settings.get("TAG_FEED_RSS"): + for tag, arts in self.tags.items(): + if self.settings.get("TAG_FEED_ATOM"): + writer.write_feed( + arts, + self.context, + str(self.settings["TAG_FEED_ATOM"]).format(slug=tag.slug), + self.settings.get( + "TAG_FEED_ATOM_URL", + str(self.settings["TAG_FEED_ATOM"]).format(slug=tag.slug), + ), + feed_title=tag.name, ) - if (self.settings.get('TAG_FEED_ATOM') or - self.settings.get('TAG_FEED_RSS')): - for tag, arts in self.tags.items(): - if self.settings.get('TAG_FEED_ATOM'): + if self.settings.get("TAG_FEED_RSS"): writer.write_feed( arts, self.context, - str(self.settings['TAG_FEED_ATOM']).format(slug=tag.slug), + str(self.settings["TAG_FEED_RSS"]).format(slug=tag.slug), self.settings.get( - 'TAG_FEED_ATOM_URL', - str(self.settings['TAG_FEED_ATOM']).format( - slug=tag.slug - )), - feed_title=tag.name - ) - - if self.settings.get('TAG_FEED_RSS'): - writer.write_feed( - arts, - self.context, - str(self.settings['TAG_FEED_RSS']).format(slug=tag.slug), - self.settings.get( - 'TAG_FEED_RSS_URL', - str(self.settings['TAG_FEED_RSS']).format( - slug=tag.slug - )), + "TAG_FEED_RSS_URL", + str(self.settings["TAG_FEED_RSS"]).format(slug=tag.slug), + ), feed_title=tag.name, - feed_type='rss' - ) + feed_type="rss", + ) - if (self.settings.get('TRANSLATION_FEED_ATOM') or - self.settings.get('TRANSLATION_FEED_RSS')): + if self.settings.get("TRANSLATION_FEED_ATOM") or self.settings.get( + "TRANSLATION_FEED_RSS" + ): translations_feeds = defaultdict(list) for article in chain(self.articles, self.translations): translations_feeds[article.lang].append(article) for lang, items in translations_feeds.items(): - items = order_content( - items, order_by=self.settings['ARTICLE_ORDER_BY']) - if self.settings.get('TRANSLATION_FEED_ATOM'): + items = order_content(items, order_by=self.settings["ARTICLE_ORDER_BY"]) + if self.settings.get("TRANSLATION_FEED_ATOM"): writer.write_feed( items, self.context, - str( - self.settings['TRANSLATION_FEED_ATOM'] - ).format(lang=lang), + str(self.settings["TRANSLATION_FEED_ATOM"]).format(lang=lang), self.settings.get( - 'TRANSLATION_FEED_ATOM_URL', - str( - self.settings['TRANSLATION_FEED_ATOM'] - ).format(lang=lang), - ) - ) - if self.settings.get('TRANSLATION_FEED_RSS'): - writer.write_feed( - items, - self.context, - str( - self.settings['TRANSLATION_FEED_RSS'] - ).format(lang=lang), - self.settings.get( - 'TRANSLATION_FEED_RSS_URL', - str(self.settings['TRANSLATION_FEED_RSS'])).format( + "TRANSLATION_FEED_ATOM_URL", + str(self.settings["TRANSLATION_FEED_ATOM"]).format( lang=lang ), - feed_type='rss' + ), + ) + if self.settings.get("TRANSLATION_FEED_RSS"): + writer.write_feed( + items, + self.context, + str(self.settings["TRANSLATION_FEED_RSS"]).format(lang=lang), + self.settings.get( + "TRANSLATION_FEED_RSS_URL", + str(self.settings["TRANSLATION_FEED_RSS"]), + ).format(lang=lang), + feed_type="rss", ) def generate_articles(self, write): """Generate the articles.""" for article in chain( - self.translations, self.articles, - self.hidden_translations, self.hidden_articles + self.translations, + self.articles, + self.hidden_translations, + self.hidden_articles, ): signals.article_generator_write_article.send(self, content=article) - write(article.save_as, self.get_template(article.template), - self.context, article=article, category=article.category, - override_output=hasattr(article, 'override_save_as'), - url=article.url, blog=True) + write( + article.save_as, + self.get_template(article.template), + self.context, + article=article, + category=article.category, + override_output=hasattr(article, "override_save_as"), + url=article.url, + blog=True, + ) def generate_period_archives(self, write): """Generate per-year, per-month, and per-day archives.""" try: - template = self.get_template('period_archives') + template = self.get_template("period_archives") except PelicanTemplateNotFound: - template = self.get_template('archives') + template = self.get_template("archives") for granularity in self.period_archives: for period in self.period_archives[granularity]: - context = self.context.copy() - context['period'] = period['period'] - context['period_num'] = period['period_num'] + context["period"] = period["period"] + context["period_num"] = period["period_num"] - write(period['save_as'], template, context, - articles=period['articles'], dates=period['dates'], - template_name='period_archives', blog=True, - url=period['url'], all_articles=self.articles) + write( + period["save_as"], + template, + context, + articles=period["articles"], + dates=period["dates"], + template_name="period_archives", + blog=True, + url=period["url"], + all_articles=self.articles, + ) def generate_direct_templates(self, write): """Generate direct templates pages""" - for template in self.settings['DIRECT_TEMPLATES']: - save_as = self.settings.get("%s_SAVE_AS" % template.upper(), - '%s.html' % template) - url = self.settings.get("%s_URL" % template.upper(), - '%s.html' % template) + for template in self.settings["DIRECT_TEMPLATES"]: + save_as = self.settings.get( + "%s_SAVE_AS" % template.upper(), "%s.html" % template + ) + url = self.settings.get("%s_URL" % template.upper(), "%s.html" % template) if not save_as: continue - write(save_as, self.get_template(template), self.context, - articles=self.articles, dates=self.dates, blog=True, - template_name=template, - page_name=os.path.splitext(save_as)[0], url=url) + write( + save_as, + self.get_template(template), + self.context, + articles=self.articles, + dates=self.dates, + blog=True, + template_name=template, + page_name=os.path.splitext(save_as)[0], + url=url, + ) def generate_tags(self, write): """Generate Tags pages.""" - tag_template = self.get_template('tag') + tag_template = self.get_template("tag") for tag, articles in self.tags.items(): dates = [article for article in self.dates if article in articles] - write(tag.save_as, tag_template, self.context, tag=tag, - url=tag.url, articles=articles, dates=dates, - template_name='tag', blog=True, page_name=tag.page_name, - all_articles=self.articles) + write( + tag.save_as, + tag_template, + self.context, + tag=tag, + url=tag.url, + articles=articles, + dates=dates, + template_name="tag", + blog=True, + page_name=tag.page_name, + all_articles=self.articles, + ) def generate_categories(self, write): """Generate category pages.""" - category_template = self.get_template('category') + category_template = self.get_template("category") for cat, articles in self.categories: dates = [article for article in self.dates if article in articles] - write(cat.save_as, category_template, self.context, url=cat.url, - category=cat, articles=articles, dates=dates, - template_name='category', blog=True, page_name=cat.page_name, - all_articles=self.articles) + write( + cat.save_as, + category_template, + self.context, + url=cat.url, + category=cat, + articles=articles, + dates=dates, + template_name="category", + blog=True, + page_name=cat.page_name, + all_articles=self.articles, + ) def generate_authors(self, write): """Generate Author pages.""" - author_template = self.get_template('author') + author_template = self.get_template("author") for aut, articles in self.authors: dates = [article for article in self.dates if article in articles] - write(aut.save_as, author_template, self.context, - url=aut.url, author=aut, articles=articles, dates=dates, - template_name='author', blog=True, - page_name=aut.page_name, all_articles=self.articles) + write( + aut.save_as, + author_template, + self.context, + url=aut.url, + author=aut, + articles=articles, + dates=dates, + template_name="author", + blog=True, + page_name=aut.page_name, + all_articles=self.articles, + ) def generate_drafts(self, write): """Generate drafts pages.""" for draft in chain(self.drafts_translations, self.drafts): - write(draft.save_as, self.get_template(draft.template), - self.context, article=draft, category=draft.category, - override_output=hasattr(draft, 'override_save_as'), - blog=True, all_articles=self.articles, url=draft.url) + write( + draft.save_as, + self.get_template(draft.template), + self.context, + article=draft, + category=draft.category, + override_output=hasattr(draft, "override_save_as"), + blog=True, + all_articles=self.articles, + url=draft.url, + ) def generate_pages(self, writer): """Generate the pages on the disk""" - write = partial(writer.write_file, - relative_urls=self.settings['RELATIVE_URLS']) + write = partial(writer.write_file, relative_urls=self.settings["RELATIVE_URLS"]) # to minimize the number of relative path stuff modification # in writer, articles pass first @@ -583,22 +646,28 @@ class ArticlesGenerator(CachingGenerator): all_drafts = [] hidden_articles = [] for f in self.get_files( - self.settings['ARTICLE_PATHS'], - exclude=self.settings['ARTICLE_EXCLUDES']): + self.settings["ARTICLE_PATHS"], exclude=self.settings["ARTICLE_EXCLUDES"] + ): article = self.get_cached_data(f, None) if article is None: try: article = self.readers.read_file( - base_path=self.path, path=f, content_class=Article, + base_path=self.path, + path=f, + content_class=Article, context=self.context, preread_signal=signals.article_generator_preread, preread_sender=self, context_signal=signals.article_generator_context, - context_sender=self) + context_sender=self, + ) except Exception as e: logger.error( - 'Could not process %s\n%s', f, e, - exc_info=self.settings.get('DEBUG', False)) + "Could not process %s\n%s", + f, + e, + exc_info=self.settings.get("DEBUG", False), + ) self._add_failed_source_path(f) continue @@ -620,8 +689,9 @@ class ArticlesGenerator(CachingGenerator): def _process(arts): origs, translations = process_translations( - arts, translation_id=self.settings['ARTICLE_TRANSLATION_ID']) - origs = order_content(origs, self.settings['ARTICLE_ORDER_BY']) + arts, translation_id=self.settings["ARTICLE_TRANSLATION_ID"] + ) + origs = order_content(origs, self.settings["ARTICLE_ORDER_BY"]) return origs, translations self.articles, self.translations = _process(all_articles) @@ -634,36 +704,45 @@ class ArticlesGenerator(CachingGenerator): # only main articles are listed in categories and tags # not translations or hidden articles self.categories[article.category].append(article) - if hasattr(article, 'tags'): + if hasattr(article, "tags"): for tag in article.tags: self.tags[tag].append(article) - for author in getattr(article, 'authors', []): + for author in getattr(article, "authors", []): self.authors[author].append(article) self.dates = list(self.articles) - self.dates.sort(key=attrgetter('date'), - reverse=self.context['NEWEST_FIRST_ARCHIVES']) + self.dates.sort( + key=attrgetter("date"), reverse=self.context["NEWEST_FIRST_ARCHIVES"] + ) self.period_archives = self._build_period_archives( - self.dates, self.articles, self.settings) + self.dates, self.articles, self.settings + ) # and generate the output :) # order the categories per name self.categories = list(self.categories.items()) - self.categories.sort( - reverse=self.settings['REVERSE_CATEGORY_ORDER']) + self.categories.sort(reverse=self.settings["REVERSE_CATEGORY_ORDER"]) self.authors = list(self.authors.items()) self.authors.sort() - self._update_context(( - 'articles', 'drafts', 'hidden_articles', - 'dates', 'tags', 'categories', - 'authors', 'related_posts')) + self._update_context( + ( + "articles", + "drafts", + "hidden_articles", + "dates", + "tags", + "categories", + "authors", + "related_posts", + ) + ) # _update_context flattens dicts, which should not happen to # period_archives, so we update the context directly for it: - self.context['period_archives'] = self.period_archives + self.context["period_archives"] = self.period_archives self.save_cache() self.readers.save_cache() signals.article_generator_finalized.send(self) @@ -677,29 +756,29 @@ class ArticlesGenerator(CachingGenerator): period_archives = defaultdict(list) period_archives_settings = { - 'year': { - 'save_as': settings['YEAR_ARCHIVE_SAVE_AS'], - 'url': settings['YEAR_ARCHIVE_URL'], + "year": { + "save_as": settings["YEAR_ARCHIVE_SAVE_AS"], + "url": settings["YEAR_ARCHIVE_URL"], }, - 'month': { - 'save_as': settings['MONTH_ARCHIVE_SAVE_AS'], - 'url': settings['MONTH_ARCHIVE_URL'], + "month": { + "save_as": settings["MONTH_ARCHIVE_SAVE_AS"], + "url": settings["MONTH_ARCHIVE_URL"], }, - 'day': { - 'save_as': settings['DAY_ARCHIVE_SAVE_AS'], - 'url': settings['DAY_ARCHIVE_URL'], + "day": { + "save_as": settings["DAY_ARCHIVE_SAVE_AS"], + "url": settings["DAY_ARCHIVE_URL"], }, } granularity_key_func = { - 'year': attrgetter('date.year'), - 'month': attrgetter('date.year', 'date.month'), - 'day': attrgetter('date.year', 'date.month', 'date.day'), + "year": attrgetter("date.year"), + "month": attrgetter("date.year", "date.month"), + "day": attrgetter("date.year", "date.month", "date.day"), } - for granularity in 'year', 'month', 'day': - save_as_fmt = period_archives_settings[granularity]['save_as'] - url_fmt = period_archives_settings[granularity]['url'] + for granularity in "year", "month", "day": + save_as_fmt = period_archives_settings[granularity]["save_as"] + url_fmt = period_archives_settings[granularity]["url"] key_func = granularity_key_func[granularity] if not save_as_fmt: @@ -710,26 +789,26 @@ class ArticlesGenerator(CachingGenerator): archive = {} dates = list(group) - archive['dates'] = dates - archive['articles'] = [a for a in articles if a in dates] + archive["dates"] = dates + archive["articles"] = [a for a in articles if a in dates] # use the first date to specify the period archive URL # and save_as; the specific date used does not matter as # they all belong to the same period d = dates[0].date - archive['save_as'] = save_as_fmt.format(date=d) - archive['url'] = url_fmt.format(date=d) + archive["save_as"] = save_as_fmt.format(date=d) + archive["url"] = url_fmt.format(date=d) - if granularity == 'year': - archive['period'] = (period,) - archive['period_num'] = (period,) + if granularity == "year": + archive["period"] = (period,) + archive["period_num"] = (period,) else: month_name = calendar.month_name[period[1]] - if granularity == 'month': - archive['period'] = (period[0], month_name) + if granularity == "month": + archive["period"] = (period[0], month_name) else: - archive['period'] = (period[0], month_name, period[2]) - archive['period_num'] = tuple(period) + archive["period"] = (period[0], month_name, period[2]) + archive["period_num"] = tuple(period) period_archives[granularity].append(archive) @@ -741,13 +820,15 @@ class ArticlesGenerator(CachingGenerator): signals.article_writer_finalized.send(self, writer=writer) def refresh_metadata_intersite_links(self): - for e in chain(self.articles, - self.translations, - self.drafts, - self.drafts_translations, - self.hidden_articles, - self.hidden_translations): - if hasattr(e, 'refresh_metadata_intersite_links'): + for e in chain( + self.articles, + self.translations, + self.drafts, + self.drafts_translations, + self.hidden_articles, + self.hidden_translations, + ): + if hasattr(e, "refresh_metadata_intersite_links"): e.refresh_metadata_intersite_links() @@ -769,22 +850,28 @@ class PagesGenerator(CachingGenerator): hidden_pages = [] draft_pages = [] for f in self.get_files( - self.settings['PAGE_PATHS'], - exclude=self.settings['PAGE_EXCLUDES']): + self.settings["PAGE_PATHS"], exclude=self.settings["PAGE_EXCLUDES"] + ): page = self.get_cached_data(f, None) if page is None: try: page = self.readers.read_file( - base_path=self.path, path=f, content_class=Page, + base_path=self.path, + path=f, + content_class=Page, context=self.context, preread_signal=signals.page_generator_preread, preread_sender=self, context_signal=signals.page_generator_context, - context_sender=self) + context_sender=self, + ) except Exception as e: logger.error( - 'Could not process %s\n%s', f, e, - exc_info=self.settings.get('DEBUG', False)) + "Could not process %s\n%s", + f, + e, + exc_info=self.settings.get("DEBUG", False), + ) self._add_failed_source_path(f) continue @@ -805,40 +892,51 @@ class PagesGenerator(CachingGenerator): def _process(pages): origs, translations = process_translations( - pages, translation_id=self.settings['PAGE_TRANSLATION_ID']) - origs = order_content(origs, self.settings['PAGE_ORDER_BY']) + pages, translation_id=self.settings["PAGE_TRANSLATION_ID"] + ) + origs = order_content(origs, self.settings["PAGE_ORDER_BY"]) return origs, translations self.pages, self.translations = _process(all_pages) self.hidden_pages, self.hidden_translations = _process(hidden_pages) self.draft_pages, self.draft_translations = _process(draft_pages) - self._update_context(('pages', 'hidden_pages', 'draft_pages')) + self._update_context(("pages", "hidden_pages", "draft_pages")) self.save_cache() self.readers.save_cache() signals.page_generator_finalized.send(self) def generate_output(self, writer): - for page in chain(self.translations, self.pages, - self.hidden_translations, self.hidden_pages, - self.draft_translations, self.draft_pages): + for page in chain( + self.translations, + self.pages, + self.hidden_translations, + self.hidden_pages, + self.draft_translations, + self.draft_pages, + ): signals.page_generator_write_page.send(self, content=page) writer.write_file( - page.save_as, self.get_template(page.template), - self.context, page=page, - relative_urls=self.settings['RELATIVE_URLS'], - override_output=hasattr(page, 'override_save_as'), - url=page.url) + page.save_as, + self.get_template(page.template), + self.context, + page=page, + relative_urls=self.settings["RELATIVE_URLS"], + override_output=hasattr(page, "override_save_as"), + url=page.url, + ) signals.page_writer_finalized.send(self, writer=writer) def refresh_metadata_intersite_links(self): - for e in chain(self.pages, - self.hidden_pages, - self.hidden_translations, - self.draft_pages, - self.draft_translations): - if hasattr(e, 'refresh_metadata_intersite_links'): + for e in chain( + self.pages, + self.hidden_pages, + self.hidden_translations, + self.draft_pages, + self.draft_translations, + ): + if hasattr(e, "refresh_metadata_intersite_links"): e.refresh_metadata_intersite_links() @@ -853,71 +951,82 @@ class StaticGenerator(Generator): def generate_context(self): self.staticfiles = [] - linked_files = set(self.context['static_links']) - found_files = self.get_files(self.settings['STATIC_PATHS'], - exclude=self.settings['STATIC_EXCLUDES'], - extensions=False) + linked_files = set(self.context["static_links"]) + found_files = self.get_files( + self.settings["STATIC_PATHS"], + exclude=self.settings["STATIC_EXCLUDES"], + extensions=False, + ) for f in linked_files | found_files: - # skip content source files unless the user explicitly wants them - if self.settings['STATIC_EXCLUDE_SOURCES']: + if self.settings["STATIC_EXCLUDE_SOURCES"]: if self._is_potential_source_path(f): continue static = self.readers.read_file( - base_path=self.path, path=f, content_class=Static, - fmt='static', context=self.context, + base_path=self.path, + path=f, + content_class=Static, + fmt="static", + context=self.context, preread_signal=signals.static_generator_preread, preread_sender=self, context_signal=signals.static_generator_context, - context_sender=self) + context_sender=self, + ) self.staticfiles.append(static) self.add_source_path(static, static=True) - self._update_context(('staticfiles',)) + self._update_context(("staticfiles",)) signals.static_generator_finalized.send(self) def generate_output(self, writer): - self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme, - self.settings['THEME_STATIC_DIR'], self.output_path, - os.curdir) - for sc in self.context['staticfiles']: + self._copy_paths( + self.settings["THEME_STATIC_PATHS"], + self.theme, + self.settings["THEME_STATIC_DIR"], + self.output_path, + os.curdir, + ) + for sc in self.context["staticfiles"]: if self._file_update_required(sc): self._link_or_copy_staticfile(sc) else: - logger.debug('%s is up to date, not copying', sc.source_path) + logger.debug("%s is up to date, not copying", sc.source_path) - def _copy_paths(self, paths, source, destination, output_path, - final_path=None): + def _copy_paths(self, paths, source, destination, output_path, final_path=None): """Copy all the paths from source to destination""" for path in paths: source_path = os.path.join(source, path) if final_path: if os.path.isfile(source_path): - destination_path = os.path.join(output_path, destination, - final_path, - os.path.basename(path)) + destination_path = os.path.join( + output_path, destination, final_path, os.path.basename(path) + ) else: - destination_path = os.path.join(output_path, destination, - final_path) + destination_path = os.path.join( + output_path, destination, final_path + ) else: destination_path = os.path.join(output_path, destination, path) - copy(source_path, destination_path, - self.settings['IGNORE_FILES']) + copy(source_path, destination_path, self.settings["IGNORE_FILES"]) def _file_update_required(self, staticfile): source_path = os.path.join(self.path, staticfile.source_path) save_as = os.path.join(self.output_path, staticfile.save_as) if not os.path.exists(save_as): return True - elif (self.settings['STATIC_CREATE_LINKS'] and - os.path.samefile(source_path, save_as)): + elif self.settings["STATIC_CREATE_LINKS"] and os.path.samefile( + source_path, save_as + ): return False - elif (self.settings['STATIC_CREATE_LINKS'] and - os.path.realpath(save_as) == source_path): + elif ( + self.settings["STATIC_CREATE_LINKS"] + and os.path.realpath(save_as) == source_path + ): return False - elif not self.settings['STATIC_CHECK_IF_MODIFIED']: + elif not self.settings["STATIC_CHECK_IF_MODIFIED"]: return True else: return self._source_is_newer(staticfile) @@ -930,7 +1039,7 @@ class StaticGenerator(Generator): return s_mtime - d_mtime > 0.000001 def _link_or_copy_staticfile(self, sc): - if self.settings['STATIC_CREATE_LINKS']: + if self.settings["STATIC_CREATE_LINKS"]: self._link_staticfile(sc) else: self._copy_staticfile(sc) @@ -940,7 +1049,7 @@ class StaticGenerator(Generator): save_as = os.path.join(self.output_path, sc.save_as) self._mkdir(os.path.dirname(save_as)) copy(source_path, save_as) - logger.info('Copying %s to %s', sc.source_path, sc.save_as) + logger.info("Copying %s to %s", sc.source_path, sc.save_as) def _link_staticfile(self, sc): source_path = os.path.join(self.path, sc.source_path) @@ -949,7 +1058,7 @@ class StaticGenerator(Generator): try: if os.path.lexists(save_as): os.unlink(save_as) - logger.info('Linking %s and %s', sc.source_path, sc.save_as) + logger.info("Linking %s and %s", sc.source_path, sc.save_as) if self.fallback_to_symlinks: os.symlink(source_path, save_as) else: @@ -957,9 +1066,8 @@ class StaticGenerator(Generator): except OSError as err: if err.errno == errno.EXDEV: # 18: Invalid cross-device link logger.debug( - "Cross-device links not valid. " - "Creating symbolic links instead." - ) + "Cross-device links not valid. " "Creating symbolic links instead." + ) self.fallback_to_symlinks = True self._link_staticfile(sc) else: @@ -972,19 +1080,17 @@ class StaticGenerator(Generator): class SourceFileGenerator(Generator): - def generate_context(self): - self.output_extension = self.settings['OUTPUT_SOURCES_EXTENSION'] + self.output_extension = self.settings["OUTPUT_SOURCES_EXTENSION"] def _create_source(self, obj): output_path, _ = os.path.splitext(obj.save_as) - dest = os.path.join(self.output_path, - output_path + self.output_extension) + dest = os.path.join(self.output_path, output_path + self.output_extension) copy(obj.source_path, dest) def generate_output(self, writer=None): - logger.info('Generating source files...') - for obj in chain(self.context['articles'], self.context['pages']): + logger.info("Generating source files...") + for obj in chain(self.context["articles"], self.context["pages"]): self._create_source(obj) for obj_trans in obj.translations: self._create_source(obj_trans) diff --git a/pelican/log.py b/pelican/log.py index be176ea8..0d2b6a3f 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -4,9 +4,7 @@ from collections import defaultdict from rich.console import Console from rich.logging import RichHandler -__all__ = [ - 'init' -] +__all__ = ["init"] console = Console() @@ -34,8 +32,8 @@ class LimitFilter(logging.Filter): return True # extract group - group = record.__dict__.get('limit_msg', None) - group_args = record.__dict__.get('limit_args', ()) + group = record.__dict__.get("limit_msg", None) + group_args = record.__dict__.get("limit_args", ()) # ignore record if it was already raised message_key = (record.levelno, record.getMessage()) @@ -50,7 +48,7 @@ class LimitFilter(logging.Filter): if logger_level > logging.DEBUG: template_key = (record.levelno, record.msg) message_key = (record.levelno, record.getMessage()) - if (template_key in self._ignore or message_key in self._ignore): + if template_key in self._ignore or message_key in self._ignore: return False # check if we went over threshold @@ -90,12 +88,12 @@ class FatalLogger(LimitLogger): def warning(self, *args, **kwargs): super().warning(*args, **kwargs) if FatalLogger.warnings_fatal: - raise RuntimeError('Warning encountered') + raise RuntimeError("Warning encountered") def error(self, *args, **kwargs): super().error(*args, **kwargs) if FatalLogger.errors_fatal: - raise RuntimeError('Error encountered') + raise RuntimeError("Error encountered") logging.setLoggerClass(FatalLogger) @@ -103,17 +101,19 @@ logging.setLoggerClass(FatalLogger) logging.getLogger().__class__ = FatalLogger -def init(level=None, fatal='', handler=RichHandler(console=console), name=None, - logs_dedup_min_level=None): - FatalLogger.warnings_fatal = fatal.startswith('warning') +def init( + level=None, + fatal="", + handler=RichHandler(console=console), + name=None, + logs_dedup_min_level=None, +): + FatalLogger.warnings_fatal = fatal.startswith("warning") FatalLogger.errors_fatal = bool(fatal) LOG_FORMAT = "%(message)s" logging.basicConfig( - level=level, - format=LOG_FORMAT, - datefmt="[%H:%M:%S]", - handlers=[handler] + level=level, format=LOG_FORMAT, datefmt="[%H:%M:%S]", handlers=[handler] ) logger = logging.getLogger(name) @@ -126,17 +126,18 @@ def init(level=None, fatal='', handler=RichHandler(console=console), name=None, def log_warnings(): import warnings + logging.captureWarnings(True) warnings.simplefilter("default", DeprecationWarning) - init(logging.DEBUG, name='py.warnings') + init(logging.DEBUG, name="py.warnings") -if __name__ == '__main__': +if __name__ == "__main__": init(level=logging.DEBUG, name=__name__) root_logger = logging.getLogger(__name__) - root_logger.debug('debug') - root_logger.info('info') - root_logger.warning('warning') - root_logger.error('error') - root_logger.critical('critical') + root_logger.debug("debug") + root_logger.info("info") + root_logger.warning("warning") + root_logger.error("error") + root_logger.critical("critical") diff --git a/pelican/paginator.py b/pelican/paginator.py index 4231e67b..930c915b 100644 --- a/pelican/paginator.py +++ b/pelican/paginator.py @@ -6,8 +6,8 @@ from math import ceil logger = logging.getLogger(__name__) PaginationRule = namedtuple( - 'PaginationRule', - 'min_page URL SAVE_AS', + "PaginationRule", + "min_page URL SAVE_AS", ) @@ -19,7 +19,7 @@ class Paginator: self.settings = settings if per_page: self.per_page = per_page - self.orphans = settings['DEFAULT_ORPHANS'] + self.orphans = settings["DEFAULT_ORPHANS"] else: self.per_page = len(object_list) self.orphans = 0 @@ -32,14 +32,21 @@ class Paginator: top = bottom + self.per_page if top + self.orphans >= self.count: top = self.count - return Page(self.name, self.url, self.object_list[bottom:top], number, - self, self.settings) + return Page( + self.name, + self.url, + self.object_list[bottom:top], + number, + self, + self.settings, + ) def _get_count(self): "Returns the total number of objects, across all pages." if self._count is None: self._count = len(self.object_list) return self._count + count = property(_get_count) def _get_num_pages(self): @@ -48,6 +55,7 @@ class Paginator: hits = max(1, self.count - self.orphans) self._num_pages = int(ceil(hits / (float(self.per_page) or 1))) return self._num_pages + num_pages = property(_get_num_pages) def _get_page_range(self): @@ -56,6 +64,7 @@ class Paginator: a template for loop. """ return list(range(1, self.num_pages + 1)) + page_range = property(_get_page_range) @@ -64,7 +73,7 @@ class Page: self.full_name = name self.name, self.extension = os.path.splitext(name) dn, fn = os.path.split(name) - self.base_name = dn if fn in ('index.htm', 'index.html') else self.name + self.base_name = dn if fn in ("index.htm", "index.html") else self.name self.base_url = url self.object_list = object_list self.number = number @@ -72,7 +81,7 @@ class Page: self.settings = settings def __repr__(self): - return ''.format(self.number, self.paginator.num_pages) + return "".format(self.number, self.paginator.num_pages) def has_next(self): return self.number < self.paginator.num_pages @@ -117,7 +126,7 @@ class Page: rule = None # find the last matching pagination rule - for p in self.settings['PAGINATION_PATTERNS']: + for p in self.settings["PAGINATION_PATTERNS"]: if p.min_page == -1: if not self.has_next(): rule = p @@ -127,22 +136,22 @@ class Page: rule = p if not rule: - return '' + return "" prop_value = getattr(rule, key) if not isinstance(prop_value, str): - logger.warning('%s is set to %s', key, prop_value) + logger.warning("%s is set to %s", key, prop_value) return prop_value # URL or SAVE_AS is a string, format it with a controlled context context = { - 'save_as': self.full_name, - 'url': self.base_url, - 'name': self.name, - 'base_name': self.base_name, - 'extension': self.extension, - 'number': self.number, + "save_as": self.full_name, + "url": self.base_url, + "name": self.name, + "base_name": self.base_name, + "extension": self.extension, + "number": self.number, } ret = prop_value.format(**context) @@ -155,9 +164,9 @@ class Page: # changed to lstrip() because that would remove all leading slashes and # thus make the workaround impossible. See # test_custom_pagination_pattern() for a verification of this. - if ret.startswith('/'): + if ret.startswith("/"): ret = ret[1:] return ret - url = property(functools.partial(_from_settings, key='URL')) - save_as = property(functools.partial(_from_settings, key='SAVE_AS')) + url = property(functools.partial(_from_settings, key="URL")) + save_as = property(functools.partial(_from_settings, key="SAVE_AS")) diff --git a/pelican/plugins/_utils.py b/pelican/plugins/_utils.py index 87877b08..f0c18f5c 100644 --- a/pelican/plugins/_utils.py +++ b/pelican/plugins/_utils.py @@ -24,26 +24,26 @@ def get_namespace_plugins(ns_pkg=None): return { name: importlib.import_module(name) - for finder, name, ispkg - in iter_namespace(ns_pkg) + for finder, name, ispkg in iter_namespace(ns_pkg) if ispkg } def list_plugins(ns_pkg=None): from pelican.log import init as init_logging + init_logging(logging.INFO) ns_plugins = get_namespace_plugins(ns_pkg) if ns_plugins: - logger.info('Plugins found:\n' + '\n'.join(ns_plugins)) + logger.info("Plugins found:\n" + "\n".join(ns_plugins)) else: - logger.info('No plugins are installed') + logger.info("No plugins are installed") def load_legacy_plugin(plugin, plugin_paths): - if '.' in plugin: + if "." in plugin: # it is in a package, try to resolve package first - package, _, _ = plugin.rpartition('.') + package, _, _ = plugin.rpartition(".") load_legacy_plugin(package, plugin_paths) # Try to find plugin in PLUGIN_PATHS @@ -52,7 +52,7 @@ def load_legacy_plugin(plugin, plugin_paths): # If failed, try to find it in normal importable locations spec = importlib.util.find_spec(plugin) if spec is None: - raise ImportError('Cannot import plugin `{}`'.format(plugin)) + raise ImportError("Cannot import plugin `{}`".format(plugin)) else: # Avoid loading the same plugin twice if spec.name in sys.modules: @@ -78,30 +78,28 @@ def load_legacy_plugin(plugin, plugin_paths): def load_plugins(settings): - logger.debug('Finding namespace plugins') + logger.debug("Finding namespace plugins") namespace_plugins = get_namespace_plugins() if namespace_plugins: - logger.debug('Namespace plugins found:\n' + - '\n'.join(namespace_plugins)) + logger.debug("Namespace plugins found:\n" + "\n".join(namespace_plugins)) plugins = [] - if settings.get('PLUGINS') is not None: - for plugin in settings['PLUGINS']: + if settings.get("PLUGINS") is not None: + for plugin in settings["PLUGINS"]: if isinstance(plugin, str): - logger.debug('Loading plugin `%s`', plugin) + logger.debug("Loading plugin `%s`", plugin) # try to find in namespace plugins if plugin in namespace_plugins: plugin = namespace_plugins[plugin] - elif 'pelican.plugins.{}'.format(plugin) in namespace_plugins: - plugin = namespace_plugins['pelican.plugins.{}'.format( - plugin)] + elif "pelican.plugins.{}".format(plugin) in namespace_plugins: + plugin = namespace_plugins["pelican.plugins.{}".format(plugin)] # try to import it else: try: plugin = load_legacy_plugin( - plugin, - settings.get('PLUGIN_PATHS', [])) + plugin, settings.get("PLUGIN_PATHS", []) + ) except ImportError as e: - logger.error('Cannot load plugin `%s`\n%s', plugin, e) + logger.error("Cannot load plugin `%s`\n%s", plugin, e) continue plugins.append(plugin) else: diff --git a/pelican/plugins/signals.py b/pelican/plugins/signals.py index 4013360f..ff129cb4 100644 --- a/pelican/plugins/signals.py +++ b/pelican/plugins/signals.py @@ -2,48 +2,48 @@ from blinker import signal # Run-level signals: -initialized = signal('pelican_initialized') -get_generators = signal('get_generators') -all_generators_finalized = signal('all_generators_finalized') -get_writer = signal('get_writer') -finalized = signal('pelican_finalized') +initialized = signal("pelican_initialized") +get_generators = signal("get_generators") +all_generators_finalized = signal("all_generators_finalized") +get_writer = signal("get_writer") +finalized = signal("pelican_finalized") # Reader-level signals -readers_init = signal('readers_init') +readers_init = signal("readers_init") # Generator-level signals -generator_init = signal('generator_init') +generator_init = signal("generator_init") -article_generator_init = signal('article_generator_init') -article_generator_pretaxonomy = signal('article_generator_pretaxonomy') -article_generator_finalized = signal('article_generator_finalized') -article_generator_write_article = signal('article_generator_write_article') -article_writer_finalized = signal('article_writer_finalized') +article_generator_init = signal("article_generator_init") +article_generator_pretaxonomy = signal("article_generator_pretaxonomy") +article_generator_finalized = signal("article_generator_finalized") +article_generator_write_article = signal("article_generator_write_article") +article_writer_finalized = signal("article_writer_finalized") -page_generator_init = signal('page_generator_init') -page_generator_finalized = signal('page_generator_finalized') -page_generator_write_page = signal('page_generator_write_page') -page_writer_finalized = signal('page_writer_finalized') +page_generator_init = signal("page_generator_init") +page_generator_finalized = signal("page_generator_finalized") +page_generator_write_page = signal("page_generator_write_page") +page_writer_finalized = signal("page_writer_finalized") -static_generator_init = signal('static_generator_init') -static_generator_finalized = signal('static_generator_finalized') +static_generator_init = signal("static_generator_init") +static_generator_finalized = signal("static_generator_finalized") # Page-level signals -article_generator_preread = signal('article_generator_preread') -article_generator_context = signal('article_generator_context') +article_generator_preread = signal("article_generator_preread") +article_generator_context = signal("article_generator_context") -page_generator_preread = signal('page_generator_preread') -page_generator_context = signal('page_generator_context') +page_generator_preread = signal("page_generator_preread") +page_generator_context = signal("page_generator_context") -static_generator_preread = signal('static_generator_preread') -static_generator_context = signal('static_generator_context') +static_generator_preread = signal("static_generator_preread") +static_generator_context = signal("static_generator_context") -content_object_init = signal('content_object_init') +content_object_init = signal("content_object_init") # Writers signals -content_written = signal('content_written') -feed_generated = signal('feed_generated') -feed_written = signal('feed_written') +content_written = signal("content_written") +feed_generated = signal("feed_generated") +feed_written = signal("feed_written") diff --git a/pelican/readers.py b/pelican/readers.py index 03c3cc20..5033c0bd 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -31,33 +31,29 @@ except ImportError: _DISCARD = object() DUPLICATES_DEFINITIONS_ALLOWED = { - 'tags': False, - 'date': False, - 'modified': False, - 'status': False, - 'category': False, - 'author': False, - 'save_as': False, - 'url': False, - 'authors': False, - 'slug': False + "tags": False, + "date": False, + "modified": False, + "status": False, + "category": False, + "author": False, + "save_as": False, + "url": False, + "authors": False, + "slug": False, } METADATA_PROCESSORS = { - 'tags': lambda x, y: ([ - Tag(tag, y) - for tag in ensure_metadata_list(x) - ] or _DISCARD), - 'date': lambda x, y: get_date(x.replace('_', ' ')), - 'modified': lambda x, y: get_date(x), - 'status': lambda x, y: x.strip() or _DISCARD, - 'category': lambda x, y: _process_if_nonempty(Category, x, y), - 'author': lambda x, y: _process_if_nonempty(Author, x, y), - 'authors': lambda x, y: ([ - Author(author, y) - for author in ensure_metadata_list(x) - ] or _DISCARD), - 'slug': lambda x, y: x.strip() or _DISCARD, + "tags": lambda x, y: ([Tag(tag, y) for tag in ensure_metadata_list(x)] or _DISCARD), + "date": lambda x, y: get_date(x.replace("_", " ")), + "modified": lambda x, y: get_date(x), + "status": lambda x, y: x.strip() or _DISCARD, + "category": lambda x, y: _process_if_nonempty(Category, x, y), + "author": lambda x, y: _process_if_nonempty(Author, x, y), + "authors": lambda x, y: ( + [Author(author, y) for author in ensure_metadata_list(x)] or _DISCARD + ), + "slug": lambda x, y: x.strip() or _DISCARD, } logger = logging.getLogger(__name__) @@ -65,25 +61,23 @@ logger = logging.getLogger(__name__) def ensure_metadata_list(text): """Canonicalize the format of a list of authors or tags. This works - the same way as Docutils' "authors" field: if it's already a list, - those boundaries are preserved; otherwise, it must be a string; - if the string contains semicolons, it is split on semicolons; - otherwise, it is split on commas. This allows you to write - author lists in either "Jane Doe, John Doe" or "Doe, Jane; Doe, John" - format. + the same way as Docutils' "authors" field: if it's already a list, + those boundaries are preserved; otherwise, it must be a string; + if the string contains semicolons, it is split on semicolons; + otherwise, it is split on commas. This allows you to write + author lists in either "Jane Doe, John Doe" or "Doe, Jane; Doe, John" + format. - Regardless, all list items undergo .strip() before returning, and - empty items are discarded. + Regardless, all list items undergo .strip() before returning, and + empty items are discarded. """ if isinstance(text, str): - if ';' in text: - text = text.split(';') + if ";" in text: + text = text.split(";") else: - text = text.split(',') + text = text.split(",") - return list(OrderedDict.fromkeys( - [v for v in (w.strip() for w in text) if v] - )) + return list(OrderedDict.fromkeys([v for v in (w.strip() for w in text) if v])) def _process_if_nonempty(processor, name, settings): @@ -112,8 +106,9 @@ class BaseReader: Markdown). """ + enabled = True - file_extensions = ['static'] + file_extensions = ["static"] extensions = None def __init__(self, settings): @@ -132,13 +127,12 @@ class BaseReader: class _FieldBodyTranslator(HTMLTranslator): - def __init__(self, document): super().__init__(document) self.compact_p = None def astext(self): - return ''.join(self.body) + return "".join(self.body) def visit_field_body(self, node): pass @@ -154,27 +148,25 @@ def render_node_to_html(document, node, field_body_translator_class): class PelicanHTMLWriter(Writer): - def __init__(self): super().__init__() self.translator_class = PelicanHTMLTranslator class PelicanHTMLTranslator(HTMLTranslator): - def visit_abbreviation(self, node): attrs = {} - if node.hasattr('explanation'): - attrs['title'] = node['explanation'] - self.body.append(self.starttag(node, 'abbr', '', **attrs)) + if node.hasattr("explanation"): + attrs["title"] = node["explanation"] + self.body.append(self.starttag(node, "abbr", "", **attrs)) def depart_abbreviation(self, node): - self.body.append('') + self.body.append("") def visit_image(self, node): # set an empty alt if alt is not specified # avoids that alt is taken from src - node['alt'] = node.get('alt', '') + node["alt"] = node.get("alt", "") return HTMLTranslator.visit_image(self, node) @@ -194,7 +186,7 @@ class RstReader(BaseReader): """ enabled = bool(docutils) - file_extensions = ['rst'] + file_extensions = ["rst"] writer_class = PelicanHTMLWriter field_body_translator_class = _FieldBodyTranslator @@ -202,25 +194,28 @@ class RstReader(BaseReader): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - lang_code = self.settings.get('DEFAULT_LANG', 'en') + lang_code = self.settings.get("DEFAULT_LANG", "en") if get_docutils_lang(lang_code): self._language_code = lang_code else: - logger.warning("Docutils has no localization for '%s'." - " Using 'en' instead.", lang_code) - self._language_code = 'en' + logger.warning( + "Docutils has no localization for '%s'." " Using 'en' instead.", + lang_code, + ) + self._language_code = "en" def _parse_metadata(self, document, source_path): """Return the dict containing document metadata""" - formatted_fields = self.settings['FORMATTED_FIELDS'] + formatted_fields = self.settings["FORMATTED_FIELDS"] output = {} if document.first_child_matching_class(docutils.nodes.title) is None: logger.warning( - 'Document title missing in file %s: ' - 'Ensure exactly one top level section', - source_path) + "Document title missing in file %s: " + "Ensure exactly one top level section", + source_path, + ) try: # docutils 0.18.1+ @@ -231,16 +226,16 @@ class RstReader(BaseReader): for docinfo in nodes: for element in docinfo.children: - if element.tagname == 'field': # custom fields (e.g. summary) + if element.tagname == "field": # custom fields (e.g. summary) name_elem, body_elem = element.children name = name_elem.astext() if name.lower() in formatted_fields: value = render_node_to_html( - document, body_elem, - self.field_body_translator_class) + document, body_elem, self.field_body_translator_class + ) else: value = body_elem.astext() - elif element.tagname == 'authors': # author list + elif element.tagname == "authors": # author list name = element.tagname value = [element.astext() for element in element.children] else: # standard fields (e.g. address) @@ -252,22 +247,24 @@ class RstReader(BaseReader): return output def _get_publisher(self, source_path): - extra_params = {'initial_header_level': '2', - 'syntax_highlight': 'short', - 'input_encoding': 'utf-8', - 'language_code': self._language_code, - 'halt_level': 2, - 'traceback': True, - 'warning_stream': StringIO(), - 'embed_stylesheet': False} - user_params = self.settings.get('DOCUTILS_SETTINGS') + extra_params = { + "initial_header_level": "2", + "syntax_highlight": "short", + "input_encoding": "utf-8", + "language_code": self._language_code, + "halt_level": 2, + "traceback": True, + "warning_stream": StringIO(), + "embed_stylesheet": False, + } + user_params = self.settings.get("DOCUTILS_SETTINGS") if user_params: extra_params.update(user_params) pub = docutils.core.Publisher( - writer=self.writer_class(), - destination_class=docutils.io.StringOutput) - pub.set_components('standalone', 'restructuredtext', 'html') + writer=self.writer_class(), destination_class=docutils.io.StringOutput + ) + pub.set_components("standalone", "restructuredtext", "html") pub.process_programmatic_settings(None, extra_params, None) pub.set_source(source_path=source_path) pub.publish() @@ -277,10 +274,10 @@ class RstReader(BaseReader): """Parses restructured text""" pub = self._get_publisher(source_path) parts = pub.writer.parts - content = parts.get('body') + content = parts.get("body") metadata = self._parse_metadata(pub.document, source_path) - metadata.setdefault('title', parts.get('title')) + metadata.setdefault("title", parts.get("title")) return content, metadata @@ -289,26 +286,26 @@ class MarkdownReader(BaseReader): """Reader for Markdown files""" enabled = bool(Markdown) - file_extensions = ['md', 'markdown', 'mkd', 'mdown'] + file_extensions = ["md", "markdown", "mkd", "mdown"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - settings = self.settings['MARKDOWN'] - settings.setdefault('extension_configs', {}) - settings.setdefault('extensions', []) - for extension in settings['extension_configs'].keys(): - if extension not in settings['extensions']: - settings['extensions'].append(extension) - if 'markdown.extensions.meta' not in settings['extensions']: - settings['extensions'].append('markdown.extensions.meta') + settings = self.settings["MARKDOWN"] + settings.setdefault("extension_configs", {}) + settings.setdefault("extensions", []) + for extension in settings["extension_configs"].keys(): + if extension not in settings["extensions"]: + settings["extensions"].append(extension) + if "markdown.extensions.meta" not in settings["extensions"]: + settings["extensions"].append("markdown.extensions.meta") self._source_path = None def _parse_metadata(self, meta): """Return the dict containing document metadata""" - formatted_fields = self.settings['FORMATTED_FIELDS'] + formatted_fields = self.settings["FORMATTED_FIELDS"] # prevent metadata extraction in fields - self._md.preprocessors.deregister('meta') + self._md.preprocessors.deregister("meta") output = {} for name, value in meta.items(): @@ -323,9 +320,10 @@ class MarkdownReader(BaseReader): elif not DUPLICATES_DEFINITIONS_ALLOWED.get(name, True): if len(value) > 1: logger.warning( - 'Duplicate definition of `%s` ' - 'for %s. Using first one.', - name, self._source_path) + "Duplicate definition of `%s` " "for %s. Using first one.", + name, + self._source_path, + ) output[name] = self.process_metadata(name, value[0]) elif len(value) > 1: # handle list metadata as list of string @@ -339,11 +337,11 @@ class MarkdownReader(BaseReader): """Parse content and metadata of markdown files""" self._source_path = source_path - self._md = Markdown(**self.settings['MARKDOWN']) + self._md = Markdown(**self.settings["MARKDOWN"]) with pelican_open(source_path) as text: content = self._md.convert(text) - if hasattr(self._md, 'Meta'): + if hasattr(self._md, "Meta"): metadata = self._parse_metadata(self._md.Meta) else: metadata = {} @@ -353,17 +351,17 @@ class MarkdownReader(BaseReader): class HTMLReader(BaseReader): """Parses HTML files as input, looking for meta, title, and body tags""" - file_extensions = ['htm', 'html'] + file_extensions = ["htm", "html"] enabled = True class _HTMLParser(HTMLParser): def __init__(self, settings, filename): super().__init__(convert_charrefs=False) - self.body = '' + self.body = "" self.metadata = {} self.settings = settings - self._data_buffer = '' + self._data_buffer = "" self._filename = filename @@ -374,59 +372,59 @@ class HTMLReader(BaseReader): self._in_tags = False def handle_starttag(self, tag, attrs): - if tag == 'head' and self._in_top_level: + if tag == "head" and self._in_top_level: self._in_top_level = False self._in_head = True - elif tag == 'title' and self._in_head: + elif tag == "title" and self._in_head: self._in_title = True - self._data_buffer = '' - elif tag == 'body' and self._in_top_level: + self._data_buffer = "" + elif tag == "body" and self._in_top_level: self._in_top_level = False self._in_body = True - self._data_buffer = '' - elif tag == 'meta' and self._in_head: + self._data_buffer = "" + elif tag == "meta" and self._in_head: self._handle_meta_tag(attrs) elif self._in_body: self._data_buffer += self.build_tag(tag, attrs, False) def handle_endtag(self, tag): - if tag == 'head': + if tag == "head": if self._in_head: self._in_head = False self._in_top_level = True - elif self._in_head and tag == 'title': + elif self._in_head and tag == "title": self._in_title = False - self.metadata['title'] = self._data_buffer - elif tag == 'body': + self.metadata["title"] = self._data_buffer + elif tag == "body": self.body = self._data_buffer self._in_body = False self._in_top_level = True elif self._in_body: - self._data_buffer += ''.format(escape(tag)) + self._data_buffer += "".format(escape(tag)) def handle_startendtag(self, tag, attrs): - if tag == 'meta' and self._in_head: + if tag == "meta" and self._in_head: self._handle_meta_tag(attrs) if self._in_body: self._data_buffer += self.build_tag(tag, attrs, True) def handle_comment(self, data): - self._data_buffer += ''.format(data) + self._data_buffer += "".format(data) def handle_data(self, data): self._data_buffer += data def handle_entityref(self, data): - self._data_buffer += '&{};'.format(data) + self._data_buffer += "&{};".format(data) def handle_charref(self, data): - self._data_buffer += '&#{};'.format(data) + self._data_buffer += "&#{};".format(data) def build_tag(self, tag, attrs, close_tag): - result = '<{}'.format(escape(tag)) + result = "<{}".format(escape(tag)) for k, v in attrs: - result += ' ' + escape(k) + result += " " + escape(k) if v is not None: # If the attribute value contains a double quote, surround # with single quotes, otherwise use double quotes. @@ -435,33 +433,39 @@ class HTMLReader(BaseReader): else: result += '="{}"'.format(escape(v, quote=False)) if close_tag: - return result + ' />' - return result + '>' + return result + " />" + return result + ">" def _handle_meta_tag(self, attrs): - name = self._attr_value(attrs, 'name') + name = self._attr_value(attrs, "name") if name is None: attr_list = ['{}="{}"'.format(k, v) for k, v in attrs] - attr_serialized = ', '.join(attr_list) - logger.warning("Meta tag in file %s does not have a 'name' " - "attribute, skipping. Attributes: %s", - self._filename, attr_serialized) + attr_serialized = ", ".join(attr_list) + logger.warning( + "Meta tag in file %s does not have a 'name' " + "attribute, skipping. Attributes: %s", + self._filename, + attr_serialized, + ) return name = name.lower() - contents = self._attr_value(attrs, 'content', '') + contents = self._attr_value(attrs, "content", "") if not contents: - contents = self._attr_value(attrs, 'contents', '') + contents = self._attr_value(attrs, "contents", "") if contents: logger.warning( "Meta tag attribute 'contents' used in file %s, should" " be changed to 'content'", self._filename, - extra={'limit_msg': "Other files have meta tag " - "attribute 'contents' that should " - "be changed to 'content'"}) + extra={ + "limit_msg": "Other files have meta tag " + "attribute 'contents' that should " + "be changed to 'content'" + }, + ) - if name == 'keywords': - name = 'tags' + if name == "keywords": + name = "tags" if name in self.metadata: # if this metadata already exists (i.e. a previous tag with the @@ -501,22 +505,23 @@ class Readers(FileStampDataCacher): """ - def __init__(self, settings=None, cache_name=''): + def __init__(self, settings=None, cache_name=""): self.settings = settings or {} self.readers = {} self.reader_classes = {} for cls in [BaseReader] + BaseReader.__subclasses__(): if not cls.enabled: - logger.debug('Missing dependencies for %s', - ', '.join(cls.file_extensions)) + logger.debug( + "Missing dependencies for %s", ", ".join(cls.file_extensions) + ) continue for ext in cls.file_extensions: self.reader_classes[ext] = cls - if self.settings['READERS']: - self.reader_classes.update(self.settings['READERS']) + if self.settings["READERS"]: + self.reader_classes.update(self.settings["READERS"]) signals.readers_init.send(self) @@ -527,53 +532,67 @@ class Readers(FileStampDataCacher): self.readers[fmt] = reader_class(self.settings) # set up caching - cache_this_level = (cache_name != '' and - self.settings['CONTENT_CACHING_LAYER'] == 'reader') - caching_policy = cache_this_level and self.settings['CACHE_CONTENT'] - load_policy = cache_this_level and self.settings['LOAD_CONTENT_CACHE'] + cache_this_level = ( + cache_name != "" and self.settings["CONTENT_CACHING_LAYER"] == "reader" + ) + caching_policy = cache_this_level and self.settings["CACHE_CONTENT"] + load_policy = cache_this_level and self.settings["LOAD_CONTENT_CACHE"] super().__init__(settings, cache_name, caching_policy, load_policy) @property def extensions(self): return self.readers.keys() - def read_file(self, base_path, path, content_class=Page, fmt=None, - context=None, preread_signal=None, preread_sender=None, - context_signal=None, context_sender=None): + def read_file( + self, + base_path, + path, + content_class=Page, + fmt=None, + context=None, + preread_signal=None, + preread_sender=None, + context_signal=None, + context_sender=None, + ): """Return a content object parsed with the given format.""" path = os.path.abspath(os.path.join(base_path, path)) source_path = posixize_path(os.path.relpath(path, base_path)) - logger.debug( - 'Read file %s -> %s', - source_path, content_class.__name__) + logger.debug("Read file %s -> %s", source_path, content_class.__name__) if not fmt: _, ext = os.path.splitext(os.path.basename(path)) fmt = ext[1:] if fmt not in self.readers: - raise TypeError( - 'Pelican does not know how to parse %s', path) + raise TypeError("Pelican does not know how to parse %s", path) if preread_signal: - logger.debug( - 'Signal %s.send(%s)', - preread_signal.name, preread_sender) + logger.debug("Signal %s.send(%s)", preread_signal.name, preread_sender) preread_signal.send(preread_sender) reader = self.readers[fmt] - metadata = _filter_discardable_metadata(default_metadata( - settings=self.settings, process=reader.process_metadata)) - metadata.update(path_metadata( - full_path=path, source_path=source_path, - settings=self.settings)) - metadata.update(_filter_discardable_metadata(parse_path_metadata( - source_path=source_path, settings=self.settings, - process=reader.process_metadata))) + metadata = _filter_discardable_metadata( + default_metadata(settings=self.settings, process=reader.process_metadata) + ) + metadata.update( + path_metadata( + full_path=path, source_path=source_path, settings=self.settings + ) + ) + metadata.update( + _filter_discardable_metadata( + parse_path_metadata( + source_path=source_path, + settings=self.settings, + process=reader.process_metadata, + ) + ) + ) reader_name = reader.__class__.__name__ - metadata['reader'] = reader_name.replace('Reader', '').lower() + metadata["reader"] = reader_name.replace("Reader", "").lower() content, reader_metadata = self.get_cached_data(path, (None, None)) if content is None: @@ -587,14 +606,14 @@ class Readers(FileStampDataCacher): find_empty_alt(content, path) # eventually filter the content with typogrify if asked so - if self.settings['TYPOGRIFY']: + if self.settings["TYPOGRIFY"]: from typogrify.filters import typogrify import smartypants - typogrify_dashes = self.settings['TYPOGRIFY_DASHES'] - if typogrify_dashes == 'oldschool': + typogrify_dashes = self.settings["TYPOGRIFY_DASHES"] + if typogrify_dashes == "oldschool": smartypants.Attr.default = smartypants.Attr.set2 - elif typogrify_dashes == 'oldschool_inverted': + elif typogrify_dashes == "oldschool_inverted": smartypants.Attr.default = smartypants.Attr.set3 else: smartypants.Attr.default = smartypants.Attr.set1 @@ -608,31 +627,32 @@ class Readers(FileStampDataCacher): def typogrify_wrapper(text): """Ensures ignore_tags feature is backward compatible""" try: - return typogrify( - text, - self.settings['TYPOGRIFY_IGNORE_TAGS']) + return typogrify(text, self.settings["TYPOGRIFY_IGNORE_TAGS"]) except TypeError: return typogrify(text) if content: content = typogrify_wrapper(content) - if 'title' in metadata: - metadata['title'] = typogrify_wrapper(metadata['title']) + if "title" in metadata: + metadata["title"] = typogrify_wrapper(metadata["title"]) - if 'summary' in metadata: - metadata['summary'] = typogrify_wrapper(metadata['summary']) + if "summary" in metadata: + metadata["summary"] = typogrify_wrapper(metadata["summary"]) if context_signal: logger.debug( - 'Signal %s.send(%s, )', - context_signal.name, - context_sender) + "Signal %s.send(%s, )", context_signal.name, context_sender + ) context_signal.send(context_sender, metadata=metadata) - return content_class(content=content, metadata=metadata, - settings=self.settings, source_path=path, - context=context) + return content_class( + content=content, + metadata=metadata, + settings=self.settings, + source_path=path, + context=context, + ) def find_empty_alt(content, path): @@ -642,7 +662,8 @@ def find_empty_alt(content, path): as they are really likely to be accessibility flaws. """ - imgs = re.compile(r""" + imgs = re.compile( + r""" (?: # src before alt ]* src=(['"])(.*?)\5 ) - """, re.X) + """, + re.X, + ) for match in re.findall(imgs, content): logger.warning( - 'Empty alt attribute for image %s in %s', - os.path.basename(match[1] + match[5]), path, - extra={'limit_msg': 'Other images have empty alt attributes'}) + "Empty alt attribute for image %s in %s", + os.path.basename(match[1] + match[5]), + path, + extra={"limit_msg": "Other images have empty alt attributes"}, + ) def default_metadata(settings=None, process=None): metadata = {} if settings: - for name, value in dict(settings.get('DEFAULT_METADATA', {})).items(): + for name, value in dict(settings.get("DEFAULT_METADATA", {})).items(): if process: value = process(name, value) metadata[name] = value - if 'DEFAULT_CATEGORY' in settings: - value = settings['DEFAULT_CATEGORY'] + if "DEFAULT_CATEGORY" in settings: + value = settings["DEFAULT_CATEGORY"] if process: - value = process('category', value) - metadata['category'] = value - if settings.get('DEFAULT_DATE', None) and \ - settings['DEFAULT_DATE'] != 'fs': - if isinstance(settings['DEFAULT_DATE'], str): - metadata['date'] = get_date(settings['DEFAULT_DATE']) + value = process("category", value) + metadata["category"] = value + if settings.get("DEFAULT_DATE", None) and settings["DEFAULT_DATE"] != "fs": + if isinstance(settings["DEFAULT_DATE"], str): + metadata["date"] = get_date(settings["DEFAULT_DATE"]) else: - metadata['date'] = datetime.datetime(*settings['DEFAULT_DATE']) + metadata["date"] = datetime.datetime(*settings["DEFAULT_DATE"]) return metadata def path_metadata(full_path, source_path, settings=None): metadata = {} if settings: - if settings.get('DEFAULT_DATE', None) == 'fs': - metadata['date'] = datetime.datetime.fromtimestamp( - os.stat(full_path).st_mtime) - metadata['modified'] = metadata['date'] + if settings.get("DEFAULT_DATE", None) == "fs": + metadata["date"] = datetime.datetime.fromtimestamp( + os.stat(full_path).st_mtime + ) + metadata["modified"] = metadata["date"] # Apply EXTRA_PATH_METADATA for the source path and the paths of any # parent directories. Sorting EPM first ensures that the most specific # path wins conflicts. - epm = settings.get('EXTRA_PATH_METADATA', {}) + epm = settings.get("EXTRA_PATH_METADATA", {}) for path, meta in sorted(epm.items()): # Enforce a trailing slash when checking for parent directories. # This prevents false positives when one file or directory's name # is a prefix of another's. - dirpath = posixize_path(os.path.join(path, '')) + dirpath = posixize_path(os.path.join(path, "")) if source_path == path or source_path.startswith(dirpath): metadata.update(meta) @@ -736,11 +761,10 @@ def parse_path_metadata(source_path, settings=None, process=None): subdir = os.path.basename(dirname) if settings: checks = [] - for key, data in [('FILENAME_METADATA', base), - ('PATH_METADATA', source_path)]: + for key, data in [("FILENAME_METADATA", base), ("PATH_METADATA", source_path)]: checks.append((settings.get(key, None), data)) - if settings.get('USE_FOLDER_AS_CATEGORY', None): - checks.append(('(?P.*)', subdir)) + if settings.get("USE_FOLDER_AS_CATEGORY", None): + checks.append(("(?P.*)", subdir)) for regexp, data in checks: if regexp and data: match = re.match(regexp, data) diff --git a/pelican/rstdirectives.py b/pelican/rstdirectives.py index 500c8578..0a549424 100644 --- a/pelican/rstdirectives.py +++ b/pelican/rstdirectives.py @@ -11,26 +11,26 @@ import pelican.settings as pys class Pygments(Directive): - """ Source code syntax highlighting. - """ + """Source code syntax highlighting.""" + required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True option_spec = { - 'anchorlinenos': directives.flag, - 'classprefix': directives.unchanged, - 'hl_lines': directives.unchanged, - 'lineanchors': directives.unchanged, - 'linenos': directives.unchanged, - 'linenospecial': directives.nonnegative_int, - 'linenostart': directives.nonnegative_int, - 'linenostep': directives.nonnegative_int, - 'lineseparator': directives.unchanged, - 'linespans': directives.unchanged, - 'nobackground': directives.flag, - 'nowrap': directives.flag, - 'tagsfile': directives.unchanged, - 'tagurlformat': directives.unchanged, + "anchorlinenos": directives.flag, + "classprefix": directives.unchanged, + "hl_lines": directives.unchanged, + "lineanchors": directives.unchanged, + "linenos": directives.unchanged, + "linenospecial": directives.nonnegative_int, + "linenostart": directives.nonnegative_int, + "linenostep": directives.nonnegative_int, + "lineseparator": directives.unchanged, + "linespans": directives.unchanged, + "nobackground": directives.flag, + "nowrap": directives.flag, + "tagsfile": directives.unchanged, + "tagurlformat": directives.unchanged, } has_content = True @@ -49,28 +49,30 @@ class Pygments(Directive): if k not in self.options: self.options[k] = v - if ('linenos' in self.options and - self.options['linenos'] not in ('table', 'inline')): - if self.options['linenos'] == 'none': - self.options.pop('linenos') + if "linenos" in self.options and self.options["linenos"] not in ( + "table", + "inline", + ): + if self.options["linenos"] == "none": + self.options.pop("linenos") else: - self.options['linenos'] = 'table' + self.options["linenos"] = "table" - for flag in ('nowrap', 'nobackground', 'anchorlinenos'): + for flag in ("nowrap", "nobackground", "anchorlinenos"): if flag in self.options: self.options[flag] = True # noclasses should already default to False, but just in case... formatter = HtmlFormatter(noclasses=False, **self.options) - parsed = highlight('\n'.join(self.content), lexer, formatter) - return [nodes.raw('', parsed, format='html')] + parsed = highlight("\n".join(self.content), lexer, formatter) + return [nodes.raw("", parsed, format="html")] -directives.register_directive('code-block', Pygments) -directives.register_directive('sourcecode', Pygments) +directives.register_directive("code-block", Pygments) +directives.register_directive("sourcecode", Pygments) -_abbr_re = re.compile(r'\((.*)\)$', re.DOTALL) +_abbr_re = re.compile(r"\((.*)\)$", re.DOTALL) class abbreviation(nodes.Inline, nodes.TextElement): @@ -82,9 +84,9 @@ def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): m = _abbr_re.search(text) if m is None: return [abbreviation(text, text)], [] - abbr = text[:m.start()].strip() + abbr = text[: m.start()].strip() expl = m.group(1) return [abbreviation(abbr, abbr, explanation=expl)], [] -roles.register_local_role('abbr', abbr_role) +roles.register_local_role("abbr", abbr_role) diff --git a/pelican/server.py b/pelican/server.py index 913c3761..61729bf1 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -14,38 +14,47 @@ except ImportError: from pelican.log import console # noqa: F401 from pelican.log import init as init_logging + logger = logging.getLogger(__name__) def parse_arguments(): parser = argparse.ArgumentParser( - description='Pelican Development Server', - formatter_class=argparse.ArgumentDefaultsHelpFormatter + description="Pelican Development Server", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "port", default=8000, type=int, nargs="?", help="Port to Listen On" + ) + parser.add_argument("server", default="", nargs="?", help="Interface to Listen On") + parser.add_argument("--ssl", action="store_true", help="Activate SSL listener") + parser.add_argument( + "--cert", + default="./cert.pem", + nargs="?", + help="Path to certificate file. " + "Relative to current directory", + ) + parser.add_argument( + "--key", + default="./key.pem", + nargs="?", + help="Path to certificate key file. " + "Relative to current directory", + ) + parser.add_argument( + "--path", + default=".", + help="Path to pelican source directory to serve. " + + "Relative to current directory", ) - parser.add_argument("port", default=8000, type=int, nargs="?", - help="Port to Listen On") - parser.add_argument("server", default="", nargs="?", - help="Interface to Listen On") - parser.add_argument('--ssl', action="store_true", - help='Activate SSL listener') - parser.add_argument('--cert', default="./cert.pem", nargs="?", - help='Path to certificate file. ' + - 'Relative to current directory') - parser.add_argument('--key', default="./key.pem", nargs="?", - help='Path to certificate key file. ' + - 'Relative to current directory') - parser.add_argument('--path', default=".", - help='Path to pelican source directory to serve. ' + - 'Relative to current directory') return parser.parse_args() class ComplexHTTPRequestHandler(server.SimpleHTTPRequestHandler): - SUFFIXES = ['.html', '/index.html', '/', ''] + SUFFIXES = [".html", "/index.html", "/", ""] extensions_map = { **server.SimpleHTTPRequestHandler.extensions_map, - ** { + **{ # web fonts ".oft": "font/oft", ".sfnt": "font/sfnt", @@ -57,13 +66,13 @@ class ComplexHTTPRequestHandler(server.SimpleHTTPRequestHandler): def translate_path(self, path): # abandon query parameters - path = path.split('?', 1)[0] - path = path.split('#', 1)[0] + path = path.split("?", 1)[0] + path = path.split("#", 1)[0] # Don't forget explicit trailing slash when normalizing. Issue17324 - trailing_slash = path.rstrip().endswith('/') + trailing_slash = path.rstrip().endswith("/") path = urllib.parse.unquote(path) path = posixpath.normpath(path) - words = path.split('/') + words = path.split("/") words = filter(None, words) path = self.base_path for word in words: @@ -72,12 +81,12 @@ class ComplexHTTPRequestHandler(server.SimpleHTTPRequestHandler): continue path = os.path.join(path, word) if trailing_slash: - path += '/' + path += "/" return path def do_GET(self): # cut off a query string - original_path = self.path.split('?', 1)[0] + original_path = self.path.split("?", 1)[0] # try to find file self.path = self.get_path_that_exists(original_path) @@ -88,12 +97,12 @@ class ComplexHTTPRequestHandler(server.SimpleHTTPRequestHandler): def get_path_that_exists(self, original_path): # Try to strip trailing slash - trailing_slash = original_path.endswith('/') - original_path = original_path.rstrip('/') + trailing_slash = original_path.endswith("/") + original_path = original_path.rstrip("/") # Try to detect file by applying various suffixes tries = [] for suffix in self.SUFFIXES: - if not trailing_slash and suffix == '/': + if not trailing_slash and suffix == "/": # if original request does not have trailing slash, skip the '/' suffix # so that base class can redirect if needed continue @@ -101,18 +110,17 @@ class ComplexHTTPRequestHandler(server.SimpleHTTPRequestHandler): if os.path.exists(self.translate_path(path)): return path tries.append(path) - logger.warning("Unable to find `%s` or variations:\n%s", - original_path, - '\n'.join(tries)) + logger.warning( + "Unable to find `%s` or variations:\n%s", original_path, "\n".join(tries) + ) return None def guess_type(self, path): - """Guess at the mime type for the specified file. - """ + """Guess at the mime type for the specified file.""" mimetype = server.SimpleHTTPRequestHandler.guess_type(self, path) # If the default guess is too generic, try the python-magic library - if mimetype == 'application/octet-stream' and magic_from_file: + if mimetype == "application/octet-stream" and magic_from_file: mimetype = magic_from_file(path, mime=True) return mimetype @@ -127,31 +135,33 @@ class RootedHTTPServer(server.HTTPServer): self.RequestHandlerClass.base_path = base_path -if __name__ == '__main__': +if __name__ == "__main__": init_logging(level=logging.INFO) - logger.warning("'python -m pelican.server' is deprecated.\nThe " - "Pelican development server should be run via " - "'pelican --listen' or 'pelican -l'.\nThis can be combined " - "with regeneration as 'pelican -lr'.\nRerun 'pelican-" - "quickstart' to get new Makefile and tasks.py files.") + logger.warning( + "'python -m pelican.server' is deprecated.\nThe " + "Pelican development server should be run via " + "'pelican --listen' or 'pelican -l'.\nThis can be combined " + "with regeneration as 'pelican -lr'.\nRerun 'pelican-" + "quickstart' to get new Makefile and tasks.py files." + ) args = parse_arguments() RootedHTTPServer.allow_reuse_address = True try: httpd = RootedHTTPServer( - args.path, (args.server, args.port), ComplexHTTPRequestHandler) + args.path, (args.server, args.port), ComplexHTTPRequestHandler + ) if args.ssl: httpd.socket = ssl.wrap_socket( - httpd.socket, keyfile=args.key, - certfile=args.cert, server_side=True) + httpd.socket, keyfile=args.key, certfile=args.cert, server_side=True + ) except ssl.SSLError as e: - logger.error("Couldn't open certificate file %s or key file %s", - args.cert, args.key) - logger.error("Could not listen on port %s, server %s.", - args.port, args.server) - sys.exit(getattr(e, 'exitcode', 1)) + logger.error( + "Couldn't open certificate file %s or key file %s", args.cert, args.key + ) + logger.error("Could not listen on port %s, server %s.", args.port, args.server) + sys.exit(getattr(e, "exitcode", 1)) - logger.info("Serving at port %s, server %s.", - args.port, args.server) + logger.info("Serving at port %s, server %s.", args.port, args.server) try: httpd.serve_forever() except KeyboardInterrupt: diff --git a/pelican/settings.py b/pelican/settings.py index 9a54b2a6..2c84b6f0 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -22,150 +22,157 @@ def load_source(name, path): logger = logging.getLogger(__name__) -DEFAULT_THEME = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'themes', 'notmyidea') +DEFAULT_THEME = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "themes", "notmyidea" +) DEFAULT_CONFIG = { - 'PATH': os.curdir, - 'ARTICLE_PATHS': [''], - 'ARTICLE_EXCLUDES': [], - 'PAGE_PATHS': ['pages'], - 'PAGE_EXCLUDES': [], - 'THEME': DEFAULT_THEME, - 'OUTPUT_PATH': 'output', - 'READERS': {}, - 'STATIC_PATHS': ['images'], - 'STATIC_EXCLUDES': [], - 'STATIC_EXCLUDE_SOURCES': True, - 'THEME_STATIC_DIR': 'theme', - 'THEME_STATIC_PATHS': ['static', ], - 'FEED_ALL_ATOM': 'feeds/all.atom.xml', - 'CATEGORY_FEED_ATOM': 'feeds/{slug}.atom.xml', - 'AUTHOR_FEED_ATOM': 'feeds/{slug}.atom.xml', - 'AUTHOR_FEED_RSS': 'feeds/{slug}.rss.xml', - 'TRANSLATION_FEED_ATOM': 'feeds/all-{lang}.atom.xml', - 'FEED_MAX_ITEMS': 100, - 'RSS_FEED_SUMMARY_ONLY': True, - 'SITEURL': '', - 'SITENAME': 'A Pelican Blog', - 'DISPLAY_PAGES_ON_MENU': True, - 'DISPLAY_CATEGORIES_ON_MENU': True, - 'DOCUTILS_SETTINGS': {}, - 'OUTPUT_SOURCES': False, - 'OUTPUT_SOURCES_EXTENSION': '.text', - 'USE_FOLDER_AS_CATEGORY': True, - 'DEFAULT_CATEGORY': 'misc', - 'WITH_FUTURE_DATES': True, - 'CSS_FILE': 'main.css', - 'NEWEST_FIRST_ARCHIVES': True, - 'REVERSE_CATEGORY_ORDER': False, - 'DELETE_OUTPUT_DIRECTORY': False, - 'OUTPUT_RETENTION': [], - 'INDEX_SAVE_AS': 'index.html', - 'ARTICLE_URL': '{slug}.html', - 'ARTICLE_SAVE_AS': '{slug}.html', - 'ARTICLE_ORDER_BY': 'reversed-date', - 'ARTICLE_LANG_URL': '{slug}-{lang}.html', - 'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html', - 'DRAFT_URL': 'drafts/{slug}.html', - 'DRAFT_SAVE_AS': 'drafts/{slug}.html', - 'DRAFT_LANG_URL': 'drafts/{slug}-{lang}.html', - 'DRAFT_LANG_SAVE_AS': 'drafts/{slug}-{lang}.html', - 'PAGE_URL': 'pages/{slug}.html', - 'PAGE_SAVE_AS': 'pages/{slug}.html', - 'PAGE_ORDER_BY': 'basename', - 'PAGE_LANG_URL': 'pages/{slug}-{lang}.html', - 'PAGE_LANG_SAVE_AS': 'pages/{slug}-{lang}.html', - 'DRAFT_PAGE_URL': 'drafts/pages/{slug}.html', - 'DRAFT_PAGE_SAVE_AS': 'drafts/pages/{slug}.html', - 'DRAFT_PAGE_LANG_URL': 'drafts/pages/{slug}-{lang}.html', - 'DRAFT_PAGE_LANG_SAVE_AS': 'drafts/pages/{slug}-{lang}.html', - 'STATIC_URL': '{path}', - 'STATIC_SAVE_AS': '{path}', - 'STATIC_CREATE_LINKS': False, - 'STATIC_CHECK_IF_MODIFIED': False, - 'CATEGORY_URL': 'category/{slug}.html', - 'CATEGORY_SAVE_AS': 'category/{slug}.html', - 'TAG_URL': 'tag/{slug}.html', - 'TAG_SAVE_AS': 'tag/{slug}.html', - 'AUTHOR_URL': 'author/{slug}.html', - 'AUTHOR_SAVE_AS': 'author/{slug}.html', - 'PAGINATION_PATTERNS': [ - (1, '{name}{extension}', '{name}{extension}'), - (2, '{name}{number}{extension}', '{name}{number}{extension}'), + "PATH": os.curdir, + "ARTICLE_PATHS": [""], + "ARTICLE_EXCLUDES": [], + "PAGE_PATHS": ["pages"], + "PAGE_EXCLUDES": [], + "THEME": DEFAULT_THEME, + "OUTPUT_PATH": "output", + "READERS": {}, + "STATIC_PATHS": ["images"], + "STATIC_EXCLUDES": [], + "STATIC_EXCLUDE_SOURCES": True, + "THEME_STATIC_DIR": "theme", + "THEME_STATIC_PATHS": [ + "static", ], - 'YEAR_ARCHIVE_URL': '', - 'YEAR_ARCHIVE_SAVE_AS': '', - 'MONTH_ARCHIVE_URL': '', - 'MONTH_ARCHIVE_SAVE_AS': '', - 'DAY_ARCHIVE_URL': '', - 'DAY_ARCHIVE_SAVE_AS': '', - 'RELATIVE_URLS': False, - 'DEFAULT_LANG': 'en', - 'ARTICLE_TRANSLATION_ID': 'slug', - 'PAGE_TRANSLATION_ID': 'slug', - 'DIRECT_TEMPLATES': ['index', 'tags', 'categories', 'authors', 'archives'], - 'THEME_TEMPLATES_OVERRIDES': [], - 'PAGINATED_TEMPLATES': {'index': None, 'tag': None, 'category': None, - 'author': None}, - 'PELICAN_CLASS': 'pelican.Pelican', - 'DEFAULT_DATE_FORMAT': '%a %d %B %Y', - 'DATE_FORMATS': {}, - 'MARKDOWN': { - 'extension_configs': { - 'markdown.extensions.codehilite': {'css_class': 'highlight'}, - 'markdown.extensions.extra': {}, - 'markdown.extensions.meta': {}, + "FEED_ALL_ATOM": "feeds/all.atom.xml", + "CATEGORY_FEED_ATOM": "feeds/{slug}.atom.xml", + "AUTHOR_FEED_ATOM": "feeds/{slug}.atom.xml", + "AUTHOR_FEED_RSS": "feeds/{slug}.rss.xml", + "TRANSLATION_FEED_ATOM": "feeds/all-{lang}.atom.xml", + "FEED_MAX_ITEMS": 100, + "RSS_FEED_SUMMARY_ONLY": True, + "SITEURL": "", + "SITENAME": "A Pelican Blog", + "DISPLAY_PAGES_ON_MENU": True, + "DISPLAY_CATEGORIES_ON_MENU": True, + "DOCUTILS_SETTINGS": {}, + "OUTPUT_SOURCES": False, + "OUTPUT_SOURCES_EXTENSION": ".text", + "USE_FOLDER_AS_CATEGORY": True, + "DEFAULT_CATEGORY": "misc", + "WITH_FUTURE_DATES": True, + "CSS_FILE": "main.css", + "NEWEST_FIRST_ARCHIVES": True, + "REVERSE_CATEGORY_ORDER": False, + "DELETE_OUTPUT_DIRECTORY": False, + "OUTPUT_RETENTION": [], + "INDEX_SAVE_AS": "index.html", + "ARTICLE_URL": "{slug}.html", + "ARTICLE_SAVE_AS": "{slug}.html", + "ARTICLE_ORDER_BY": "reversed-date", + "ARTICLE_LANG_URL": "{slug}-{lang}.html", + "ARTICLE_LANG_SAVE_AS": "{slug}-{lang}.html", + "DRAFT_URL": "drafts/{slug}.html", + "DRAFT_SAVE_AS": "drafts/{slug}.html", + "DRAFT_LANG_URL": "drafts/{slug}-{lang}.html", + "DRAFT_LANG_SAVE_AS": "drafts/{slug}-{lang}.html", + "PAGE_URL": "pages/{slug}.html", + "PAGE_SAVE_AS": "pages/{slug}.html", + "PAGE_ORDER_BY": "basename", + "PAGE_LANG_URL": "pages/{slug}-{lang}.html", + "PAGE_LANG_SAVE_AS": "pages/{slug}-{lang}.html", + "DRAFT_PAGE_URL": "drafts/pages/{slug}.html", + "DRAFT_PAGE_SAVE_AS": "drafts/pages/{slug}.html", + "DRAFT_PAGE_LANG_URL": "drafts/pages/{slug}-{lang}.html", + "DRAFT_PAGE_LANG_SAVE_AS": "drafts/pages/{slug}-{lang}.html", + "STATIC_URL": "{path}", + "STATIC_SAVE_AS": "{path}", + "STATIC_CREATE_LINKS": False, + "STATIC_CHECK_IF_MODIFIED": False, + "CATEGORY_URL": "category/{slug}.html", + "CATEGORY_SAVE_AS": "category/{slug}.html", + "TAG_URL": "tag/{slug}.html", + "TAG_SAVE_AS": "tag/{slug}.html", + "AUTHOR_URL": "author/{slug}.html", + "AUTHOR_SAVE_AS": "author/{slug}.html", + "PAGINATION_PATTERNS": [ + (1, "{name}{extension}", "{name}{extension}"), + (2, "{name}{number}{extension}", "{name}{number}{extension}"), + ], + "YEAR_ARCHIVE_URL": "", + "YEAR_ARCHIVE_SAVE_AS": "", + "MONTH_ARCHIVE_URL": "", + "MONTH_ARCHIVE_SAVE_AS": "", + "DAY_ARCHIVE_URL": "", + "DAY_ARCHIVE_SAVE_AS": "", + "RELATIVE_URLS": False, + "DEFAULT_LANG": "en", + "ARTICLE_TRANSLATION_ID": "slug", + "PAGE_TRANSLATION_ID": "slug", + "DIRECT_TEMPLATES": ["index", "tags", "categories", "authors", "archives"], + "THEME_TEMPLATES_OVERRIDES": [], + "PAGINATED_TEMPLATES": { + "index": None, + "tag": None, + "category": None, + "author": None, + }, + "PELICAN_CLASS": "pelican.Pelican", + "DEFAULT_DATE_FORMAT": "%a %d %B %Y", + "DATE_FORMATS": {}, + "MARKDOWN": { + "extension_configs": { + "markdown.extensions.codehilite": {"css_class": "highlight"}, + "markdown.extensions.extra": {}, + "markdown.extensions.meta": {}, }, - 'output_format': 'html5', + "output_format": "html5", }, - 'JINJA_FILTERS': {}, - 'JINJA_GLOBALS': {}, - 'JINJA_TESTS': {}, - 'JINJA_ENVIRONMENT': { - 'trim_blocks': True, - 'lstrip_blocks': True, - 'extensions': [], + "JINJA_FILTERS": {}, + "JINJA_GLOBALS": {}, + "JINJA_TESTS": {}, + "JINJA_ENVIRONMENT": { + "trim_blocks": True, + "lstrip_blocks": True, + "extensions": [], }, - 'LOG_FILTER': [], - 'LOCALE': [''], # defaults to user locale - 'DEFAULT_PAGINATION': False, - 'DEFAULT_ORPHANS': 0, - 'DEFAULT_METADATA': {}, - 'FILENAME_METADATA': r'(?P\d{4}-\d{2}-\d{2}).*', - 'PATH_METADATA': '', - 'EXTRA_PATH_METADATA': {}, - 'ARTICLE_PERMALINK_STRUCTURE': '', - 'TYPOGRIFY': False, - 'TYPOGRIFY_IGNORE_TAGS': [], - 'TYPOGRIFY_DASHES': 'default', - 'SUMMARY_END_SUFFIX': '…', - 'SUMMARY_MAX_LENGTH': 50, - 'PLUGIN_PATHS': [], - 'PLUGINS': None, - 'PYGMENTS_RST_OPTIONS': {}, - 'TEMPLATE_PAGES': {}, - 'TEMPLATE_EXTENSIONS': ['.html'], - 'IGNORE_FILES': ['.#*'], - 'SLUG_REGEX_SUBSTITUTIONS': [ - (r'[^\w\s-]', ''), # remove non-alphabetical/whitespace/'-' chars - (r'(?u)\A\s*', ''), # strip leading whitespace - (r'(?u)\s*\Z', ''), # strip trailing whitespace - (r'[-\s]+', '-'), # reduce multiple whitespace or '-' to single '-' + "LOG_FILTER": [], + "LOCALE": [""], # defaults to user locale + "DEFAULT_PAGINATION": False, + "DEFAULT_ORPHANS": 0, + "DEFAULT_METADATA": {}, + "FILENAME_METADATA": r"(?P\d{4}-\d{2}-\d{2}).*", + "PATH_METADATA": "", + "EXTRA_PATH_METADATA": {}, + "ARTICLE_PERMALINK_STRUCTURE": "", + "TYPOGRIFY": False, + "TYPOGRIFY_IGNORE_TAGS": [], + "TYPOGRIFY_DASHES": "default", + "SUMMARY_END_SUFFIX": "…", + "SUMMARY_MAX_LENGTH": 50, + "PLUGIN_PATHS": [], + "PLUGINS": None, + "PYGMENTS_RST_OPTIONS": {}, + "TEMPLATE_PAGES": {}, + "TEMPLATE_EXTENSIONS": [".html"], + "IGNORE_FILES": [".#*"], + "SLUG_REGEX_SUBSTITUTIONS": [ + (r"[^\w\s-]", ""), # remove non-alphabetical/whitespace/'-' chars + (r"(?u)\A\s*", ""), # strip leading whitespace + (r"(?u)\s*\Z", ""), # strip trailing whitespace + (r"[-\s]+", "-"), # reduce multiple whitespace or '-' to single '-' ], - 'INTRASITE_LINK_REGEX': '[{|](?P.*?)[|}]', - 'SLUGIFY_SOURCE': 'title', - 'SLUGIFY_USE_UNICODE': False, - 'SLUGIFY_PRESERVE_CASE': False, - 'CACHE_CONTENT': False, - 'CONTENT_CACHING_LAYER': 'reader', - 'CACHE_PATH': 'cache', - 'GZIP_CACHE': True, - 'CHECK_MODIFIED_METHOD': 'mtime', - 'LOAD_CONTENT_CACHE': False, - 'WRITE_SELECTED': [], - 'FORMATTED_FIELDS': ['summary'], - 'PORT': 8000, - 'BIND': '127.0.0.1', + "INTRASITE_LINK_REGEX": "[{|](?P.*?)[|}]", + "SLUGIFY_SOURCE": "title", + "SLUGIFY_USE_UNICODE": False, + "SLUGIFY_PRESERVE_CASE": False, + "CACHE_CONTENT": False, + "CONTENT_CACHING_LAYER": "reader", + "CACHE_PATH": "cache", + "GZIP_CACHE": True, + "CHECK_MODIFIED_METHOD": "mtime", + "LOAD_CONTENT_CACHE": False, + "WRITE_SELECTED": [], + "FORMATTED_FIELDS": ["summary"], + "PORT": 8000, + "BIND": "127.0.0.1", } PYGMENTS_RST_OPTIONS = None @@ -185,20 +192,23 @@ def read_settings(path=None, override=None): def getabs(maybe_relative, base_path=path): if isabs(maybe_relative): return maybe_relative - return os.path.abspath(os.path.normpath(os.path.join( - os.path.dirname(base_path), maybe_relative))) + return os.path.abspath( + os.path.normpath( + os.path.join(os.path.dirname(base_path), maybe_relative) + ) + ) - for p in ['PATH', 'OUTPUT_PATH', 'THEME', 'CACHE_PATH']: + for p in ["PATH", "OUTPUT_PATH", "THEME", "CACHE_PATH"]: if settings.get(p) is not None: absp = getabs(settings[p]) # THEME may be a name rather than a path - if p != 'THEME' or os.path.exists(absp): + if p != "THEME" or os.path.exists(absp): settings[p] = absp - if settings.get('PLUGIN_PATHS') is not None: - settings['PLUGIN_PATHS'] = [getabs(pluginpath) - for pluginpath - in settings['PLUGIN_PATHS']] + if settings.get("PLUGIN_PATHS") is not None: + settings["PLUGIN_PATHS"] = [ + getabs(pluginpath) for pluginpath in settings["PLUGIN_PATHS"] + ] settings = dict(copy.deepcopy(DEFAULT_CONFIG), **settings) settings = configure_settings(settings) @@ -208,7 +218,7 @@ def read_settings(path=None, override=None): # variable here that we'll import from within Pygments.run (see # rstdirectives.py) to see what the user defaults were. global PYGMENTS_RST_OPTIONS - PYGMENTS_RST_OPTIONS = settings.get('PYGMENTS_RST_OPTIONS', None) + PYGMENTS_RST_OPTIONS = settings.get("PYGMENTS_RST_OPTIONS", None) return settings @@ -217,8 +227,7 @@ def get_settings_from_module(module=None): context = {} if module is not None: - context.update( - (k, v) for k, v in inspect.getmembers(module) if k.isupper()) + context.update((k, v) for k, v in inspect.getmembers(module) if k.isupper()) return context @@ -233,11 +242,12 @@ def get_settings_from_file(path): def get_jinja_environment(settings): """Sets the environment for Jinja""" - jinja_env = settings.setdefault('JINJA_ENVIRONMENT', - DEFAULT_CONFIG['JINJA_ENVIRONMENT']) + jinja_env = settings.setdefault( + "JINJA_ENVIRONMENT", DEFAULT_CONFIG["JINJA_ENVIRONMENT"] + ) # Make sure we include the defaults if the user has set env variables - for key, value in DEFAULT_CONFIG['JINJA_ENVIRONMENT'].items(): + for key, value in DEFAULT_CONFIG["JINJA_ENVIRONMENT"].items(): if key not in jinja_env: jinja_env[key] = value @@ -248,14 +258,14 @@ def _printf_s_to_format_field(printf_string, format_field): """Tries to replace %s with {format_field} in the provided printf_string. Raises ValueError in case of failure. """ - TEST_STRING = 'PELICAN_PRINTF_S_DEPRECATION' + TEST_STRING = "PELICAN_PRINTF_S_DEPRECATION" expected = printf_string % TEST_STRING - result = printf_string.replace('{', '{{').replace('}', '}}') \ - % '{{{}}}'.format(format_field) + result = printf_string.replace("{", "{{").replace("}", "}}") % "{{{}}}".format( + format_field + ) if result.format(**{format_field: TEST_STRING}) != expected: - raise ValueError('Failed to safely replace %s with {{{}}}'.format( - format_field)) + raise ValueError("Failed to safely replace %s with {{{}}}".format(format_field)) return result @@ -266,115 +276,140 @@ def handle_deprecated_settings(settings): """ # PLUGIN_PATH -> PLUGIN_PATHS - if 'PLUGIN_PATH' in settings: - logger.warning('PLUGIN_PATH setting has been replaced by ' - 'PLUGIN_PATHS, moving it to the new setting name.') - settings['PLUGIN_PATHS'] = settings['PLUGIN_PATH'] - del settings['PLUGIN_PATH'] + if "PLUGIN_PATH" in settings: + logger.warning( + "PLUGIN_PATH setting has been replaced by " + "PLUGIN_PATHS, moving it to the new setting name." + ) + settings["PLUGIN_PATHS"] = settings["PLUGIN_PATH"] + del settings["PLUGIN_PATH"] # PLUGIN_PATHS: str -> [str] - if isinstance(settings.get('PLUGIN_PATHS'), str): - logger.warning("Defining PLUGIN_PATHS setting as string " - "has been deprecated (should be a list)") - settings['PLUGIN_PATHS'] = [settings['PLUGIN_PATHS']] + if isinstance(settings.get("PLUGIN_PATHS"), str): + logger.warning( + "Defining PLUGIN_PATHS setting as string " + "has been deprecated (should be a list)" + ) + settings["PLUGIN_PATHS"] = [settings["PLUGIN_PATHS"]] # JINJA_EXTENSIONS -> JINJA_ENVIRONMENT > extensions - if 'JINJA_EXTENSIONS' in settings: - logger.warning('JINJA_EXTENSIONS setting has been deprecated, ' - 'moving it to JINJA_ENVIRONMENT setting.') - settings['JINJA_ENVIRONMENT']['extensions'] = \ - settings['JINJA_EXTENSIONS'] - del settings['JINJA_EXTENSIONS'] + if "JINJA_EXTENSIONS" in settings: + logger.warning( + "JINJA_EXTENSIONS setting has been deprecated, " + "moving it to JINJA_ENVIRONMENT setting." + ) + settings["JINJA_ENVIRONMENT"]["extensions"] = settings["JINJA_EXTENSIONS"] + del settings["JINJA_EXTENSIONS"] # {ARTICLE,PAGE}_DIR -> {ARTICLE,PAGE}_PATHS - for key in ['ARTICLE', 'PAGE']: - old_key = key + '_DIR' - new_key = key + '_PATHS' + for key in ["ARTICLE", "PAGE"]: + old_key = key + "_DIR" + new_key = key + "_PATHS" if old_key in settings: logger.warning( - 'Deprecated setting %s, moving it to %s list', - old_key, new_key) - settings[new_key] = [settings[old_key]] # also make a list + "Deprecated setting %s, moving it to %s list", old_key, new_key + ) + settings[new_key] = [settings[old_key]] # also make a list del settings[old_key] # EXTRA_TEMPLATES_PATHS -> THEME_TEMPLATES_OVERRIDES - if 'EXTRA_TEMPLATES_PATHS' in settings: - logger.warning('EXTRA_TEMPLATES_PATHS is deprecated use ' - 'THEME_TEMPLATES_OVERRIDES instead.') - if ('THEME_TEMPLATES_OVERRIDES' in settings and - settings['THEME_TEMPLATES_OVERRIDES']): + if "EXTRA_TEMPLATES_PATHS" in settings: + logger.warning( + "EXTRA_TEMPLATES_PATHS is deprecated use " + "THEME_TEMPLATES_OVERRIDES instead." + ) + if ( + "THEME_TEMPLATES_OVERRIDES" in settings + and settings["THEME_TEMPLATES_OVERRIDES"] + ): raise Exception( - 'Setting both EXTRA_TEMPLATES_PATHS and ' - 'THEME_TEMPLATES_OVERRIDES is not permitted. Please move to ' - 'only setting THEME_TEMPLATES_OVERRIDES.') - settings['THEME_TEMPLATES_OVERRIDES'] = \ - settings['EXTRA_TEMPLATES_PATHS'] - del settings['EXTRA_TEMPLATES_PATHS'] + "Setting both EXTRA_TEMPLATES_PATHS and " + "THEME_TEMPLATES_OVERRIDES is not permitted. Please move to " + "only setting THEME_TEMPLATES_OVERRIDES." + ) + settings["THEME_TEMPLATES_OVERRIDES"] = settings["EXTRA_TEMPLATES_PATHS"] + del settings["EXTRA_TEMPLATES_PATHS"] # MD_EXTENSIONS -> MARKDOWN - if 'MD_EXTENSIONS' in settings: - logger.warning('MD_EXTENSIONS is deprecated use MARKDOWN ' - 'instead. Falling back to the default.') - settings['MARKDOWN'] = DEFAULT_CONFIG['MARKDOWN'] + if "MD_EXTENSIONS" in settings: + logger.warning( + "MD_EXTENSIONS is deprecated use MARKDOWN " + "instead. Falling back to the default." + ) + settings["MARKDOWN"] = DEFAULT_CONFIG["MARKDOWN"] # LESS_GENERATOR -> Webassets plugin # FILES_TO_COPY -> STATIC_PATHS, EXTRA_PATH_METADATA for old, new, doc in [ - ('LESS_GENERATOR', 'the Webassets plugin', None), - ('FILES_TO_COPY', 'STATIC_PATHS and EXTRA_PATH_METADATA', - 'https://github.com/getpelican/pelican/' - 'blob/master/docs/settings.rst#path-metadata'), + ("LESS_GENERATOR", "the Webassets plugin", None), + ( + "FILES_TO_COPY", + "STATIC_PATHS and EXTRA_PATH_METADATA", + "https://github.com/getpelican/pelican/" + "blob/master/docs/settings.rst#path-metadata", + ), ]: if old in settings: - message = 'The {} setting has been removed in favor of {}'.format( - old, new) + message = "The {} setting has been removed in favor of {}".format(old, new) if doc: - message += ', see {} for details'.format(doc) + message += ", see {} for details".format(doc) logger.warning(message) # PAGINATED_DIRECT_TEMPLATES -> PAGINATED_TEMPLATES - if 'PAGINATED_DIRECT_TEMPLATES' in settings: - message = 'The {} setting has been removed in favor of {}'.format( - 'PAGINATED_DIRECT_TEMPLATES', 'PAGINATED_TEMPLATES') + if "PAGINATED_DIRECT_TEMPLATES" in settings: + message = "The {} setting has been removed in favor of {}".format( + "PAGINATED_DIRECT_TEMPLATES", "PAGINATED_TEMPLATES" + ) logger.warning(message) # set PAGINATED_TEMPLATES - if 'PAGINATED_TEMPLATES' not in settings: - settings['PAGINATED_TEMPLATES'] = { - 'tag': None, 'category': None, 'author': None} + if "PAGINATED_TEMPLATES" not in settings: + settings["PAGINATED_TEMPLATES"] = { + "tag": None, + "category": None, + "author": None, + } - for t in settings['PAGINATED_DIRECT_TEMPLATES']: - if t not in settings['PAGINATED_TEMPLATES']: - settings['PAGINATED_TEMPLATES'][t] = None - del settings['PAGINATED_DIRECT_TEMPLATES'] + for t in settings["PAGINATED_DIRECT_TEMPLATES"]: + if t not in settings["PAGINATED_TEMPLATES"]: + settings["PAGINATED_TEMPLATES"][t] = None + del settings["PAGINATED_DIRECT_TEMPLATES"] # {SLUG,CATEGORY,TAG,AUTHOR}_SUBSTITUTIONS -> # {SLUG,CATEGORY,TAG,AUTHOR}_REGEX_SUBSTITUTIONS - url_settings_url = \ - 'http://docs.getpelican.com/en/latest/settings.html#url-settings' - flavours = {'SLUG', 'CATEGORY', 'TAG', 'AUTHOR'} - old_values = {f: settings[f + '_SUBSTITUTIONS'] - for f in flavours if f + '_SUBSTITUTIONS' in settings} - new_values = {f: settings[f + '_REGEX_SUBSTITUTIONS'] - for f in flavours if f + '_REGEX_SUBSTITUTIONS' in settings} + url_settings_url = "http://docs.getpelican.com/en/latest/settings.html#url-settings" + flavours = {"SLUG", "CATEGORY", "TAG", "AUTHOR"} + old_values = { + f: settings[f + "_SUBSTITUTIONS"] + for f in flavours + if f + "_SUBSTITUTIONS" in settings + } + new_values = { + f: settings[f + "_REGEX_SUBSTITUTIONS"] + for f in flavours + if f + "_REGEX_SUBSTITUTIONS" in settings + } if old_values and new_values: raise Exception( - 'Setting both {new_key} and {old_key} (or variants thereof) is ' - 'not permitted. Please move to only setting {new_key}.' - .format(old_key='SLUG_SUBSTITUTIONS', - new_key='SLUG_REGEX_SUBSTITUTIONS')) + "Setting both {new_key} and {old_key} (or variants thereof) is " + "not permitted. Please move to only setting {new_key}.".format( + old_key="SLUG_SUBSTITUTIONS", new_key="SLUG_REGEX_SUBSTITUTIONS" + ) + ) if old_values: - message = ('{} and variants thereof are deprecated and will be ' - 'removed in the future. Please use {} and variants thereof ' - 'instead. Check {}.' - .format('SLUG_SUBSTITUTIONS', 'SLUG_REGEX_SUBSTITUTIONS', - url_settings_url)) + message = ( + "{} and variants thereof are deprecated and will be " + "removed in the future. Please use {} and variants thereof " + "instead. Check {}.".format( + "SLUG_SUBSTITUTIONS", "SLUG_REGEX_SUBSTITUTIONS", url_settings_url + ) + ) logger.warning(message) - if old_values.get('SLUG'): - for f in {'CATEGORY', 'TAG'}: + if old_values.get("SLUG"): + for f in {"CATEGORY", "TAG"}: if old_values.get(f): - old_values[f] = old_values['SLUG'] + old_values[f] - old_values['AUTHOR'] = old_values.get('AUTHOR', []) + old_values[f] = old_values["SLUG"] + old_values[f] + old_values["AUTHOR"] = old_values.get("AUTHOR", []) for f in flavours: if old_values.get(f) is not None: regex_subs = [] @@ -387,120 +422,138 @@ def handle_deprecated_settings(settings): replace = False except ValueError: src, dst = tpl - regex_subs.append( - (re.escape(src), dst.replace('\\', r'\\'))) + regex_subs.append((re.escape(src), dst.replace("\\", r"\\"))) if replace: regex_subs += [ - (r'[^\w\s-]', ''), - (r'(?u)\A\s*', ''), - (r'(?u)\s*\Z', ''), - (r'[-\s]+', '-'), + (r"[^\w\s-]", ""), + (r"(?u)\A\s*", ""), + (r"(?u)\s*\Z", ""), + (r"[-\s]+", "-"), ] else: regex_subs += [ - (r'(?u)\A\s*', ''), - (r'(?u)\s*\Z', ''), + (r"(?u)\A\s*", ""), + (r"(?u)\s*\Z", ""), ] - settings[f + '_REGEX_SUBSTITUTIONS'] = regex_subs - settings.pop(f + '_SUBSTITUTIONS', None) + settings[f + "_REGEX_SUBSTITUTIONS"] = regex_subs + settings.pop(f + "_SUBSTITUTIONS", None) # `%s` -> '{slug}` or `{lang}` in FEED settings - for key in ['TRANSLATION_FEED_ATOM', - 'TRANSLATION_FEED_RSS' - ]: + for key in ["TRANSLATION_FEED_ATOM", "TRANSLATION_FEED_RSS"]: if ( - settings.get(key) and not isinstance(settings[key], Path) - and '%s' in settings[key] + settings.get(key) + and not isinstance(settings[key], Path) + and "%s" in settings[key] ): - logger.warning('%%s usage in %s is deprecated, use {lang} ' - 'instead.', key) + logger.warning("%%s usage in %s is deprecated, use {lang} " "instead.", key) try: - settings[key] = _printf_s_to_format_field( - settings[key], 'lang') + settings[key] = _printf_s_to_format_field(settings[key], "lang") except ValueError: - logger.warning('Failed to convert %%s to {lang} for %s. ' - 'Falling back to default.', key) + logger.warning( + "Failed to convert %%s to {lang} for %s. " + "Falling back to default.", + key, + ) settings[key] = DEFAULT_CONFIG[key] - for key in ['AUTHOR_FEED_ATOM', - 'AUTHOR_FEED_RSS', - 'CATEGORY_FEED_ATOM', - 'CATEGORY_FEED_RSS', - 'TAG_FEED_ATOM', - 'TAG_FEED_RSS', - ]: + for key in [ + "AUTHOR_FEED_ATOM", + "AUTHOR_FEED_RSS", + "CATEGORY_FEED_ATOM", + "CATEGORY_FEED_RSS", + "TAG_FEED_ATOM", + "TAG_FEED_RSS", + ]: if ( - settings.get(key) and not isinstance(settings[key], Path) - and '%s' in settings[key] + settings.get(key) + and not isinstance(settings[key], Path) + and "%s" in settings[key] ): - logger.warning('%%s usage in %s is deprecated, use {slug} ' - 'instead.', key) + logger.warning("%%s usage in %s is deprecated, use {slug} " "instead.", key) try: - settings[key] = _printf_s_to_format_field( - settings[key], 'slug') + settings[key] = _printf_s_to_format_field(settings[key], "slug") except ValueError: - logger.warning('Failed to convert %%s to {slug} for %s. ' - 'Falling back to default.', key) + logger.warning( + "Failed to convert %%s to {slug} for %s. " + "Falling back to default.", + key, + ) settings[key] = DEFAULT_CONFIG[key] # CLEAN_URLS - if settings.get('CLEAN_URLS', False): - logger.warning('Found deprecated `CLEAN_URLS` in settings.' - ' Modifying the following settings for the' - ' same behaviour.') + if settings.get("CLEAN_URLS", False): + logger.warning( + "Found deprecated `CLEAN_URLS` in settings." + " Modifying the following settings for the" + " same behaviour." + ) - settings['ARTICLE_URL'] = '{slug}/' - settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/' - settings['PAGE_URL'] = 'pages/{slug}/' - settings['PAGE_LANG_URL'] = 'pages/{slug}-{lang}/' + settings["ARTICLE_URL"] = "{slug}/" + settings["ARTICLE_LANG_URL"] = "{slug}-{lang}/" + settings["PAGE_URL"] = "pages/{slug}/" + settings["PAGE_LANG_URL"] = "pages/{slug}-{lang}/" - for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL', - 'PAGE_LANG_URL'): + for setting in ("ARTICLE_URL", "ARTICLE_LANG_URL", "PAGE_URL", "PAGE_LANG_URL"): logger.warning("%s = '%s'", setting, settings[setting]) # AUTORELOAD_IGNORE_CACHE -> --ignore-cache - if settings.get('AUTORELOAD_IGNORE_CACHE'): - logger.warning('Found deprecated `AUTORELOAD_IGNORE_CACHE` in ' - 'settings. Use --ignore-cache instead.') - settings.pop('AUTORELOAD_IGNORE_CACHE') + if settings.get("AUTORELOAD_IGNORE_CACHE"): + logger.warning( + "Found deprecated `AUTORELOAD_IGNORE_CACHE` in " + "settings. Use --ignore-cache instead." + ) + settings.pop("AUTORELOAD_IGNORE_CACHE") # ARTICLE_PERMALINK_STRUCTURE - if settings.get('ARTICLE_PERMALINK_STRUCTURE', False): - logger.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in' - ' settings. Modifying the following settings for' - ' the same behaviour.') + if settings.get("ARTICLE_PERMALINK_STRUCTURE", False): + logger.warning( + "Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in" + " settings. Modifying the following settings for" + " the same behaviour." + ) - structure = settings['ARTICLE_PERMALINK_STRUCTURE'] + structure = settings["ARTICLE_PERMALINK_STRUCTURE"] # Convert %(variable) into {variable}. - structure = re.sub(r'%\((\w+)\)s', r'{\g<1>}', structure) + structure = re.sub(r"%\((\w+)\)s", r"{\g<1>}", structure) # Convert %x into {date:%x} for strftime - structure = re.sub(r'(%[A-z])', r'{date:\g<1>}', structure) + structure = re.sub(r"(%[A-z])", r"{date:\g<1>}", structure) # Strip a / prefix - structure = re.sub('^/', '', structure) + structure = re.sub("^/", "", structure) - for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL', - 'PAGE_LANG_URL', 'DRAFT_URL', 'DRAFT_LANG_URL', - 'ARTICLE_SAVE_AS', 'ARTICLE_LANG_SAVE_AS', - 'DRAFT_SAVE_AS', 'DRAFT_LANG_SAVE_AS', - 'PAGE_SAVE_AS', 'PAGE_LANG_SAVE_AS'): - settings[setting] = os.path.join(structure, - settings[setting]) + for setting in ( + "ARTICLE_URL", + "ARTICLE_LANG_URL", + "PAGE_URL", + "PAGE_LANG_URL", + "DRAFT_URL", + "DRAFT_LANG_URL", + "ARTICLE_SAVE_AS", + "ARTICLE_LANG_SAVE_AS", + "DRAFT_SAVE_AS", + "DRAFT_LANG_SAVE_AS", + "PAGE_SAVE_AS", + "PAGE_LANG_SAVE_AS", + ): + settings[setting] = os.path.join(structure, settings[setting]) logger.warning("%s = '%s'", setting, settings[setting]) # {,TAG,CATEGORY,TRANSLATION}_FEED -> {,TAG,CATEGORY,TRANSLATION}_FEED_ATOM - for new, old in [('FEED', 'FEED_ATOM'), ('TAG_FEED', 'TAG_FEED_ATOM'), - ('CATEGORY_FEED', 'CATEGORY_FEED_ATOM'), - ('TRANSLATION_FEED', 'TRANSLATION_FEED_ATOM')]: + for new, old in [ + ("FEED", "FEED_ATOM"), + ("TAG_FEED", "TAG_FEED_ATOM"), + ("CATEGORY_FEED", "CATEGORY_FEED_ATOM"), + ("TRANSLATION_FEED", "TRANSLATION_FEED_ATOM"), + ]: if settings.get(new, False): logger.warning( - 'Found deprecated `%(new)s` in settings. Modify %(new)s ' - 'to %(old)s in your settings and theme for the same ' - 'behavior. Temporarily setting %(old)s for backwards ' - 'compatibility.', - {'new': new, 'old': old} + "Found deprecated `%(new)s` in settings. Modify %(new)s " + "to %(old)s in your settings and theme for the same " + "behavior. Temporarily setting %(old)s for backwards " + "compatibility.", + {"new": new, "old": old}, ) settings[old] = settings[new] @@ -512,34 +565,34 @@ def configure_settings(settings): settings. Also, specify the log messages to be ignored. """ - if 'PATH' not in settings or not os.path.isdir(settings['PATH']): - raise Exception('You need to specify a path containing the content' - ' (see pelican --help for more information)') + if "PATH" not in settings or not os.path.isdir(settings["PATH"]): + raise Exception( + "You need to specify a path containing the content" + " (see pelican --help for more information)" + ) # specify the log messages to be ignored - log_filter = settings.get('LOG_FILTER', DEFAULT_CONFIG['LOG_FILTER']) + log_filter = settings.get("LOG_FILTER", DEFAULT_CONFIG["LOG_FILTER"]) LimitFilter._ignore.update(set(log_filter)) # lookup the theme in "pelican/themes" if the given one doesn't exist - if not os.path.isdir(settings['THEME']): + if not os.path.isdir(settings["THEME"]): theme_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - 'themes', - settings['THEME']) + os.path.dirname(os.path.abspath(__file__)), "themes", settings["THEME"] + ) if os.path.exists(theme_path): - settings['THEME'] = theme_path + settings["THEME"] = theme_path else: - raise Exception("Could not find the theme %s" - % settings['THEME']) + raise Exception("Could not find the theme %s" % settings["THEME"]) # make paths selected for writing absolute if necessary - settings['WRITE_SELECTED'] = [ - os.path.abspath(path) for path in - settings.get('WRITE_SELECTED', DEFAULT_CONFIG['WRITE_SELECTED']) + settings["WRITE_SELECTED"] = [ + os.path.abspath(path) + for path in settings.get("WRITE_SELECTED", DEFAULT_CONFIG["WRITE_SELECTED"]) ] # standardize strings to lowercase strings - for key in ['DEFAULT_LANG']: + for key in ["DEFAULT_LANG"]: if key in settings: settings[key] = settings[key].lower() @@ -547,24 +600,26 @@ def configure_settings(settings): settings = get_jinja_environment(settings) # standardize strings to lists - for key in ['LOCALE']: + for key in ["LOCALE"]: if key in settings and isinstance(settings[key], str): settings[key] = [settings[key]] # check settings that must be a particular type for key, types in [ - ('OUTPUT_SOURCES_EXTENSION', str), - ('FILENAME_METADATA', str), + ("OUTPUT_SOURCES_EXTENSION", str), + ("FILENAME_METADATA", str), ]: if key in settings and not isinstance(settings[key], types): value = settings.pop(key) logger.warn( - 'Detected misconfigured %s (%s), ' - 'falling back to the default (%s)', - key, value, DEFAULT_CONFIG[key]) + "Detected misconfigured %s (%s), " "falling back to the default (%s)", + key, + value, + DEFAULT_CONFIG[key], + ) # try to set the different locales, fallback on the default. - locales = settings.get('LOCALE', DEFAULT_CONFIG['LOCALE']) + locales = settings.get("LOCALE", DEFAULT_CONFIG["LOCALE"]) for locale_ in locales: try: @@ -575,95 +630,111 @@ def configure_settings(settings): else: logger.warning( "Locale could not be set. Check the LOCALE setting, ensuring it " - "is valid and available on your system.") + "is valid and available on your system." + ) - if ('SITEURL' in settings): + if "SITEURL" in settings: # If SITEURL has a trailing slash, remove it and provide a warning - siteurl = settings['SITEURL'] - if (siteurl.endswith('/')): - settings['SITEURL'] = siteurl[:-1] + siteurl = settings["SITEURL"] + if siteurl.endswith("/"): + settings["SITEURL"] = siteurl[:-1] logger.warning("Removed extraneous trailing slash from SITEURL.") # If SITEURL is defined but FEED_DOMAIN isn't, # set FEED_DOMAIN to SITEURL - if 'FEED_DOMAIN' not in settings: - settings['FEED_DOMAIN'] = settings['SITEURL'] + if "FEED_DOMAIN" not in settings: + settings["FEED_DOMAIN"] = settings["SITEURL"] # check content caching layer and warn of incompatibilities - if settings.get('CACHE_CONTENT', False) and \ - settings.get('CONTENT_CACHING_LAYER', '') == 'generator' and \ - not settings.get('WITH_FUTURE_DATES', True): + if ( + settings.get("CACHE_CONTENT", False) + and settings.get("CONTENT_CACHING_LAYER", "") == "generator" + and not settings.get("WITH_FUTURE_DATES", True) + ): logger.warning( "WITH_FUTURE_DATES conflicts with CONTENT_CACHING_LAYER " - "set to 'generator', use 'reader' layer instead") + "set to 'generator', use 'reader' layer instead" + ) # Warn if feeds are generated with both SITEURL & FEED_DOMAIN undefined feed_keys = [ - 'FEED_ATOM', 'FEED_RSS', - 'FEED_ALL_ATOM', 'FEED_ALL_RSS', - 'CATEGORY_FEED_ATOM', 'CATEGORY_FEED_RSS', - 'AUTHOR_FEED_ATOM', 'AUTHOR_FEED_RSS', - 'TAG_FEED_ATOM', 'TAG_FEED_RSS', - 'TRANSLATION_FEED_ATOM', 'TRANSLATION_FEED_RSS', + "FEED_ATOM", + "FEED_RSS", + "FEED_ALL_ATOM", + "FEED_ALL_RSS", + "CATEGORY_FEED_ATOM", + "CATEGORY_FEED_RSS", + "AUTHOR_FEED_ATOM", + "AUTHOR_FEED_RSS", + "TAG_FEED_ATOM", + "TAG_FEED_RSS", + "TRANSLATION_FEED_ATOM", + "TRANSLATION_FEED_RSS", ] if any(settings.get(k) for k in feed_keys): - if not settings.get('SITEURL'): - logger.warning('Feeds generated without SITEURL set properly may' - ' not be valid') + if not settings.get("SITEURL"): + logger.warning( + "Feeds generated without SITEURL set properly may" " not be valid" + ) - if 'TIMEZONE' not in settings: + if "TIMEZONE" not in settings: logger.warning( - 'No timezone information specified in the settings. Assuming' - ' your timezone is UTC for feed generation. Check ' - 'https://docs.getpelican.com/en/latest/settings.html#TIMEZONE ' - 'for more information') + "No timezone information specified in the settings. Assuming" + " your timezone is UTC for feed generation. Check " + "https://docs.getpelican.com/en/latest/settings.html#TIMEZONE " + "for more information" + ) # fix up pagination rules from pelican.paginator import PaginationRule + pagination_rules = [ - PaginationRule(*r) for r in settings.get( - 'PAGINATION_PATTERNS', - DEFAULT_CONFIG['PAGINATION_PATTERNS'], + PaginationRule(*r) + for r in settings.get( + "PAGINATION_PATTERNS", + DEFAULT_CONFIG["PAGINATION_PATTERNS"], ) ] - settings['PAGINATION_PATTERNS'] = sorted( + settings["PAGINATION_PATTERNS"] = sorted( pagination_rules, key=lambda r: r[0], ) # Save people from accidentally setting a string rather than a list path_keys = ( - 'ARTICLE_EXCLUDES', - 'DEFAULT_METADATA', - 'DIRECT_TEMPLATES', - 'THEME_TEMPLATES_OVERRIDES', - 'FILES_TO_COPY', - 'IGNORE_FILES', - 'PAGINATED_DIRECT_TEMPLATES', - 'PLUGINS', - 'STATIC_EXCLUDES', - 'STATIC_PATHS', - 'THEME_STATIC_PATHS', - 'ARTICLE_PATHS', - 'PAGE_PATHS', + "ARTICLE_EXCLUDES", + "DEFAULT_METADATA", + "DIRECT_TEMPLATES", + "THEME_TEMPLATES_OVERRIDES", + "FILES_TO_COPY", + "IGNORE_FILES", + "PAGINATED_DIRECT_TEMPLATES", + "PLUGINS", + "STATIC_EXCLUDES", + "STATIC_PATHS", + "THEME_STATIC_PATHS", + "ARTICLE_PATHS", + "PAGE_PATHS", ) for PATH_KEY in filter(lambda k: k in settings, path_keys): if isinstance(settings[PATH_KEY], str): - logger.warning("Detected misconfiguration with %s setting " - "(must be a list), falling back to the default", - PATH_KEY) + logger.warning( + "Detected misconfiguration with %s setting " + "(must be a list), falling back to the default", + PATH_KEY, + ) settings[PATH_KEY] = DEFAULT_CONFIG[PATH_KEY] # Add {PAGE,ARTICLE}_PATHS to {ARTICLE,PAGE}_EXCLUDES - mutually_exclusive = ('ARTICLE', 'PAGE') + mutually_exclusive = ("ARTICLE", "PAGE") for type_1, type_2 in [mutually_exclusive, mutually_exclusive[::-1]]: try: - includes = settings[type_1 + '_PATHS'] - excludes = settings[type_2 + '_EXCLUDES'] + includes = settings[type_1 + "_PATHS"] + excludes = settings[type_2 + "_EXCLUDES"] for path in includes: if path not in excludes: excludes.append(path) except KeyError: - continue # setting not specified, nothing to do + continue # setting not specified, nothing to do return settings diff --git a/pelican/signals.py b/pelican/signals.py index 9b84a92a..4d232e34 100644 --- a/pelican/signals.py +++ b/pelican/signals.py @@ -1,4 +1,4 @@ raise ImportError( - 'Importing from `pelican.signals` is deprecated. ' - 'Use `from pelican import signals` or `import pelican.plugins.signals` instead.' + "Importing from `pelican.signals` is deprecated. " + "Use `from pelican import signals` or `import pelican.plugins.signals` instead." ) diff --git a/pelican/tests/default_conf.py b/pelican/tests/default_conf.py index 99f3b6cf..583c3253 100644 --- a/pelican/tests/default_conf.py +++ b/pelican/tests/default_conf.py @@ -1,43 +1,47 @@ -AUTHOR = 'Alexis Métaireau' +AUTHOR = "Alexis Métaireau" SITENAME = "Alexis' log" -SITEURL = 'http://blog.notmyidea.org' -TIMEZONE = 'UTC' +SITEURL = "http://blog.notmyidea.org" +TIMEZONE = "UTC" -GITHUB_URL = 'http://github.com/ametaireau/' +GITHUB_URL = "http://github.com/ametaireau/" DISQUS_SITENAME = "blog-notmyidea" PDF_GENERATOR = False REVERSE_CATEGORY_ORDER = True DEFAULT_PAGINATION = 2 -FEED_RSS = 'feeds/all.rss.xml' -CATEGORY_FEED_RSS = 'feeds/{slug}.rss.xml' +FEED_RSS = "feeds/all.rss.xml" +CATEGORY_FEED_RSS = "feeds/{slug}.rss.xml" -LINKS = (('Biologeek', 'http://biologeek.org'), - ('Filyb', "http://filyb.info/"), - ('Libert-fr', "http://www.libert-fr.com"), - ('N1k0', "http://prendreuncafe.com/blog/"), - ('Tarek Ziadé', "http://ziade.org/blog"), - ('Zubin Mithra', "http://zubin71.wordpress.com/"),) +LINKS = ( + ("Biologeek", "http://biologeek.org"), + ("Filyb", "http://filyb.info/"), + ("Libert-fr", "http://www.libert-fr.com"), + ("N1k0", "http://prendreuncafe.com/blog/"), + ("Tarek Ziadé", "http://ziade.org/blog"), + ("Zubin Mithra", "http://zubin71.wordpress.com/"), +) -SOCIAL = (('twitter', 'http://twitter.com/ametaireau'), - ('lastfm', 'http://lastfm.com/user/akounet'), - ('github', 'http://github.com/ametaireau'),) +SOCIAL = ( + ("twitter", "http://twitter.com/ametaireau"), + ("lastfm", "http://lastfm.com/user/akounet"), + ("github", "http://github.com/ametaireau"), +) # global metadata to all the contents -DEFAULT_METADATA = {'yeah': 'it is'} +DEFAULT_METADATA = {"yeah": "it is"} # path-specific metadata EXTRA_PATH_METADATA = { - 'extra/robots.txt': {'path': 'robots.txt'}, + "extra/robots.txt": {"path": "robots.txt"}, } # static paths will be copied without parsing their contents STATIC_PATHS = [ - 'pictures', - 'extra/robots.txt', + "pictures", + "extra/robots.txt", ] -FORMATTED_FIELDS = ['summary', 'custom_formatted_field'] +FORMATTED_FIELDS = ["summary", "custom_formatted_field"] # foobar will not be used, because it's not in caps. All configuration keys # have to be in caps diff --git a/pelican/tests/dummy_plugins/namespace_plugin/pelican/plugins/ns_plugin/__init__.py b/pelican/tests/dummy_plugins/namespace_plugin/pelican/plugins/ns_plugin/__init__.py index c514861d..1979cf09 100644 --- a/pelican/tests/dummy_plugins/namespace_plugin/pelican/plugins/ns_plugin/__init__.py +++ b/pelican/tests/dummy_plugins/namespace_plugin/pelican/plugins/ns_plugin/__init__.py @@ -1,4 +1,4 @@ -NAME = 'namespace plugin' +NAME = "namespace plugin" def register(): diff --git a/pelican/tests/support.py b/pelican/tests/support.py index 3e4da785..31b12ce8 100644 --- a/pelican/tests/support.py +++ b/pelican/tests/support.py @@ -16,7 +16,10 @@ from pelican.contents import Article from pelican.readers import default_metadata from pelican.settings import DEFAULT_CONFIG -__all__ = ['get_article', 'unittest', ] +__all__ = [ + "get_article", + "unittest", +] @contextmanager @@ -51,7 +54,7 @@ def isplit(s, sep=None): True """ - sep, hardsep = r'\s+' if sep is None else re.escape(sep), sep is not None + sep, hardsep = r"\s+" if sep is None else re.escape(sep), sep is not None exp, pos, length = re.compile(sep), 0, len(s) while True: m = exp.search(s, pos) @@ -89,10 +92,8 @@ def mute(returns_output=False): """ def decorator(func): - @wraps(func) def wrapper(*args, **kwargs): - saved_stdout = sys.stdout sys.stdout = StringIO() @@ -112,7 +113,7 @@ def mute(returns_output=False): def get_article(title, content, **extra_metadata): metadata = default_metadata(settings=DEFAULT_CONFIG) - metadata['title'] = title + metadata["title"] = title if extra_metadata: metadata.update(extra_metadata) return Article(content, metadata=metadata) @@ -125,14 +126,14 @@ def skipIfNoExecutable(executable): and skips the tests if not found (if subprocess raises a `OSError`). """ - with open(os.devnull, 'w') as fnull: + with open(os.devnull, "w") as fnull: try: res = subprocess.call(executable, stdout=fnull, stderr=fnull) except OSError: res = None if res is None: - return unittest.skip('{} executable not found'.format(executable)) + return unittest.skip("{} executable not found".format(executable)) return lambda func: func @@ -164,10 +165,7 @@ def can_symlink(): res = True try: with temporary_folder() as f: - os.symlink( - f, - os.path.join(f, 'symlink') - ) + os.symlink(f, os.path.join(f, "symlink")) except OSError: res = False return res @@ -186,9 +184,9 @@ def get_settings(**kwargs): def get_context(settings=None, **kwargs): context = settings.copy() if settings else {} - context['generated_content'] = {} - context['static_links'] = set() - context['static_content'] = {} + context["generated_content"] = {} + context["static_links"] = set() + context["static_content"] = {} context.update(kwargs) return context @@ -200,22 +198,24 @@ class LogCountHandler(BufferingHandler): super().__init__(capacity) def count_logs(self, msg=None, level=None): - return len([ - rec - for rec - in self.buffer - if (msg is None or re.match(msg, rec.getMessage())) and - (level is None or rec.levelno == level) - ]) + return len( + [ + rec + for rec in self.buffer + if (msg is None or re.match(msg, rec.getMessage())) + and (level is None or rec.levelno == level) + ] + ) def count_formatted_logs(self, msg=None, level=None): - return len([ - rec - for rec - in self.buffer - if (msg is None or re.search(msg, self.format(rec))) and - (level is None or rec.levelno == level) - ]) + return len( + [ + rec + for rec in self.buffer + if (msg is None or re.search(msg, self.format(rec))) + and (level is None or rec.levelno == level) + ] + ) def diff_subproc(first, second): @@ -228,8 +228,16 @@ def diff_subproc(first, second): >>> didCheckFail = proc.returnCode != 0 """ return subprocess.Popen( - ['git', '--no-pager', 'diff', '--no-ext-diff', '--exit-code', - '-w', first, second], + [ + "git", + "--no-pager", + "diff", + "--no-ext-diff", + "--exit-code", + "-w", + first, + second, + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, @@ -251,9 +259,12 @@ class LoggedTestCase(unittest.TestCase): def assertLogCountEqual(self, count=None, msg=None, **kwargs): actual = self._logcount_handler.count_logs(msg=msg, **kwargs) self.assertEqual( - actual, count, - msg='expected {} occurrences of {!r}, but found {}'.format( - count, msg, actual)) + actual, + count, + msg="expected {} occurrences of {!r}, but found {}".format( + count, msg, actual + ), + ) class TestCaseWithCLocale(unittest.TestCase): @@ -261,9 +272,10 @@ class TestCaseWithCLocale(unittest.TestCase): Use utils.temporary_locale if you want a context manager ("with" statement). """ + def setUp(self): self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') + locale.setlocale(locale.LC_ALL, "C") def tearDown(self): locale.setlocale(locale.LC_ALL, self.old_locale) diff --git a/pelican/tests/test_cache.py b/pelican/tests/test_cache.py index 564f1d31..6dc91b2c 100644 --- a/pelican/tests/test_cache.py +++ b/pelican/tests/test_cache.py @@ -8,31 +8,30 @@ from pelican.tests.support import get_context, get_settings, unittest CUR_DIR = os.path.dirname(__file__) -CONTENT_DIR = os.path.join(CUR_DIR, 'content') +CONTENT_DIR = os.path.join(CUR_DIR, "content") class TestCache(unittest.TestCase): - def setUp(self): - self.temp_cache = mkdtemp(prefix='pelican_cache.') + self.temp_cache = mkdtemp(prefix="pelican_cache.") def tearDown(self): rmtree(self.temp_cache) def _get_cache_enabled_settings(self): settings = get_settings() - settings['CACHE_CONTENT'] = True - settings['LOAD_CONTENT_CACHE'] = True - settings['CACHE_PATH'] = self.temp_cache + settings["CACHE_CONTENT"] = True + settings["LOAD_CONTENT_CACHE"] = True + settings["CACHE_PATH"] = self.temp_cache return settings def test_generator_caching(self): """Test that cached and uncached content is same in generator level""" settings = self._get_cache_enabled_settings() - settings['CONTENT_CACHING_LAYER'] = 'generator' - settings['PAGE_PATHS'] = ['TestPages'] - settings['DEFAULT_DATE'] = (1970, 1, 1) - settings['READERS'] = {'asc': None} + settings["CONTENT_CACHING_LAYER"] = "generator" + settings["PAGE_PATHS"] = ["TestPages"] + settings["DEFAULT_DATE"] = (1970, 1, 1) + settings["READERS"] = {"asc": None} context = get_context(settings) def sorted_titles(items): @@ -40,15 +39,23 @@ class TestCache(unittest.TestCase): # Articles generator = ArticlesGenerator( - context=context.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() uncached_articles = sorted_titles(generator.articles) uncached_drafts = sorted_titles(generator.drafts) generator = ArticlesGenerator( - context=context.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() cached_articles = sorted_titles(generator.articles) cached_drafts = sorted_titles(generator.drafts) @@ -58,16 +65,24 @@ class TestCache(unittest.TestCase): # Pages generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() uncached_pages = sorted_titles(generator.pages) uncached_hidden_pages = sorted_titles(generator.hidden_pages) uncached_draft_pages = sorted_titles(generator.draft_pages) generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() cached_pages = sorted_titles(generator.pages) cached_hidden_pages = sorted_titles(generator.hidden_pages) @@ -80,10 +95,10 @@ class TestCache(unittest.TestCase): def test_reader_caching(self): """Test that cached and uncached content is same in reader level""" settings = self._get_cache_enabled_settings() - settings['CONTENT_CACHING_LAYER'] = 'reader' - settings['PAGE_PATHS'] = ['TestPages'] - settings['DEFAULT_DATE'] = (1970, 1, 1) - settings['READERS'] = {'asc': None} + settings["CONTENT_CACHING_LAYER"] = "reader" + settings["PAGE_PATHS"] = ["TestPages"] + settings["DEFAULT_DATE"] = (1970, 1, 1) + settings["READERS"] = {"asc": None} context = get_context(settings) def sorted_titles(items): @@ -91,15 +106,23 @@ class TestCache(unittest.TestCase): # Articles generator = ArticlesGenerator( - context=context.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() uncached_articles = sorted_titles(generator.articles) uncached_drafts = sorted_titles(generator.drafts) generator = ArticlesGenerator( - context=context.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() cached_articles = sorted_titles(generator.articles) cached_drafts = sorted_titles(generator.drafts) @@ -109,15 +132,23 @@ class TestCache(unittest.TestCase): # Pages generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() uncached_pages = sorted_titles(generator.pages) uncached_hidden_pages = sorted_titles(generator.hidden_pages) generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() cached_pages = sorted_titles(generator.pages) cached_hidden_pages = sorted_titles(generator.hidden_pages) @@ -128,20 +159,28 @@ class TestCache(unittest.TestCase): def test_article_object_caching(self): """Test Article objects caching at the generator level""" settings = self._get_cache_enabled_settings() - settings['CONTENT_CACHING_LAYER'] = 'generator' - settings['DEFAULT_DATE'] = (1970, 1, 1) - settings['READERS'] = {'asc': None} + settings["CONTENT_CACHING_LAYER"] = "generator" + settings["DEFAULT_DATE"] = (1970, 1, 1) + settings["READERS"] = {"asc": None} context = get_context(settings) generator = ArticlesGenerator( - context=context.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() - self.assertTrue(hasattr(generator, '_cache')) + self.assertTrue(hasattr(generator, "_cache")) generator = ArticlesGenerator( - context=context.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.readers.read_file = MagicMock() generator.generate_context() """ @@ -158,18 +197,26 @@ class TestCache(unittest.TestCase): def test_article_reader_content_caching(self): """Test raw article content caching at the reader level""" settings = self._get_cache_enabled_settings() - settings['READERS'] = {'asc': None} + settings["READERS"] = {"asc": None} context = get_context(settings) generator = ArticlesGenerator( - context=context.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() - self.assertTrue(hasattr(generator.readers, '_cache')) + self.assertTrue(hasattr(generator.readers, "_cache")) generator = ArticlesGenerator( - context=context.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) readers = generator.readers.readers for reader in readers.values(): reader.read = MagicMock() @@ -182,44 +229,58 @@ class TestCache(unittest.TestCase): used in --ignore-cache or autoreload mode""" settings = self._get_cache_enabled_settings() - settings['READERS'] = {'asc': None} + settings["READERS"] = {"asc": None} context = get_context(settings) generator = ArticlesGenerator( - context=context.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.readers.read_file = MagicMock() generator.generate_context() - self.assertTrue(hasattr(generator, '_cache_open')) + self.assertTrue(hasattr(generator, "_cache_open")) orig_call_count = generator.readers.read_file.call_count - settings['LOAD_CONTENT_CACHE'] = False + settings["LOAD_CONTENT_CACHE"] = False generator = ArticlesGenerator( - context=context.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.readers.read_file = MagicMock() generator.generate_context() - self.assertEqual( - generator.readers.read_file.call_count, - orig_call_count) + self.assertEqual(generator.readers.read_file.call_count, orig_call_count) def test_page_object_caching(self): """Test Page objects caching at the generator level""" settings = self._get_cache_enabled_settings() - settings['CONTENT_CACHING_LAYER'] = 'generator' - settings['PAGE_PATHS'] = ['TestPages'] - settings['READERS'] = {'asc': None} + settings["CONTENT_CACHING_LAYER"] = "generator" + settings["PAGE_PATHS"] = ["TestPages"] + settings["READERS"] = {"asc": None} context = get_context(settings) generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() - self.assertTrue(hasattr(generator, '_cache')) + self.assertTrue(hasattr(generator, "_cache")) generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.readers.read_file = MagicMock() generator.generate_context() """ @@ -231,19 +292,27 @@ class TestCache(unittest.TestCase): def test_page_reader_content_caching(self): """Test raw page content caching at the reader level""" settings = self._get_cache_enabled_settings() - settings['PAGE_PATHS'] = ['TestPages'] - settings['READERS'] = {'asc': None} + settings["PAGE_PATHS"] = ["TestPages"] + settings["READERS"] = {"asc": None} context = get_context(settings) generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() - self.assertTrue(hasattr(generator.readers, '_cache')) + self.assertTrue(hasattr(generator.readers, "_cache")) generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) readers = generator.readers.readers for reader in readers.values(): reader.read = MagicMock() @@ -256,24 +325,30 @@ class TestCache(unittest.TestCase): used in --ignore_cache or autoreload mode""" settings = self._get_cache_enabled_settings() - settings['PAGE_PATHS'] = ['TestPages'] - settings['READERS'] = {'asc': None} + settings["PAGE_PATHS"] = ["TestPages"] + settings["READERS"] = {"asc": None} context = get_context(settings) generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.readers.read_file = MagicMock() generator.generate_context() - self.assertTrue(hasattr(generator, '_cache_open')) + self.assertTrue(hasattr(generator, "_cache_open")) orig_call_count = generator.readers.read_file.call_count - settings['LOAD_CONTENT_CACHE'] = False + settings["LOAD_CONTENT_CACHE"] = False generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.readers.read_file = MagicMock() generator.generate_context() - self.assertEqual( - generator.readers.read_file.call_count, - orig_call_count) + self.assertEqual(generator.readers.read_file.call_count, orig_call_count) diff --git a/pelican/tests/test_cli.py b/pelican/tests/test_cli.py index 13b307e7..0b9656be 100644 --- a/pelican/tests/test_cli.py +++ b/pelican/tests/test_cli.py @@ -5,68 +5,77 @@ 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}) + 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}) + 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', {}] + '""': "", + "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}) + 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'] + invalid_items = ["k= 1", "k =1", "k", "k v"] for item in invalid_items: with self.assertRaises(ValueError): - parse_arguments(f'-e {item}'.split()) + parse_arguments(f"-e {item}".split()) def test_parse_invalid_json(self): invalid_json = { - '', 'False', 'True', 'None', 'some other string', - '{"foo": bar}', '[foo]' + "", + "False", + "True", + "None", + "some other string", + '{"foo": bar}', + "[foo]", } for v in invalid_json: with self.assertRaises(ValueError): - parse_arguments(['-e ', 'k=' + v]) + 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"' - ]) + 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' + "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"' - ]) + 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' + "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_contents.py b/pelican/tests/test_contents.py index 3a223b5a..9dc7b70d 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -10,9 +10,8 @@ from jinja2.utils import generate_lorem_ipsum from pelican.contents import Article, Author, Category, Page, Static from pelican.plugins.signals import content_object_init from pelican.settings import DEFAULT_CONFIG -from pelican.tests.support import (LoggedTestCase, get_context, get_settings, - unittest) -from pelican.utils import (path_to_url, posixize_path, truncate_html_words) +from pelican.tests.support import LoggedTestCase, get_context, get_settings, unittest +from pelican.utils import path_to_url, posixize_path, truncate_html_words # generate one paragraph, enclosed with

    @@ -21,25 +20,24 @@ TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False) class TestBase(LoggedTestCase): - def setUp(self): super().setUp() self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') + locale.setlocale(locale.LC_ALL, "C") self.page_kwargs = { - 'content': TEST_CONTENT, - 'context': { - 'localsiteurl': '', - 'generated_content': {}, - 'static_content': {}, - 'static_links': set() + "content": TEST_CONTENT, + "context": { + "localsiteurl": "", + "generated_content": {}, + "static_content": {}, + "static_links": set(), }, - 'metadata': { - 'summary': TEST_SUMMARY, - 'title': 'foo bar', - 'author': Author('Blogger', DEFAULT_CONFIG), + "metadata": { + "summary": TEST_SUMMARY, + "title": "foo bar", + "author": Author("Blogger", DEFAULT_CONFIG), }, - 'source_path': '/path/to/file/foo.ext' + "source_path": "/path/to/file/foo.ext", } self._disable_limit_filter() @@ -49,10 +47,12 @@ class TestBase(LoggedTestCase): def _disable_limit_filter(self): from pelican.contents import logger + logger.disable_filter() def _enable_limit_filter(self): from pelican.contents import logger + logger.enable_filter() def _copy_page_kwargs(self): @@ -72,9 +72,12 @@ class TestPage(TestBase): def test_use_args(self): # Creating a page with arguments passed to the constructor should use # them to initialise object's attributes. - metadata = {'foo': 'bar', 'foobar': 'baz', 'title': 'foobar', } - page = Page(TEST_CONTENT, metadata=metadata, - context={'localsiteurl': ''}) + metadata = { + "foo": "bar", + "foobar": "baz", + "title": "foobar", + } + page = Page(TEST_CONTENT, metadata=metadata, context={"localsiteurl": ""}) for key, value in metadata.items(): self.assertTrue(hasattr(page, key)) self.assertEqual(value, getattr(page, key)) @@ -82,13 +85,14 @@ class TestPage(TestBase): def test_mandatory_properties(self): # If the title is not set, must throw an exception. - page = Page('content') + page = Page("content") self.assertFalse(page._has_valid_mandatory_properties()) self.assertLogCountEqual( - count=1, - msg="Skipping .*: could not find information about 'title'", - level=logging.ERROR) - page = Page('content', metadata={'title': 'foobar'}) + count=1, + msg="Skipping .*: could not find information about 'title'", + level=logging.ERROR, + ) + page = Page("content", metadata={"title": "foobar"}) self.assertTrue(page._has_valid_mandatory_properties()) def test_summary_from_metadata(self): @@ -101,31 +105,32 @@ class TestPage(TestBase): # generated summary should not exceed the given length. page_kwargs = self._copy_page_kwargs() settings = get_settings() - page_kwargs['settings'] = settings - del page_kwargs['metadata']['summary'] - settings['SUMMARY_MAX_LENGTH'] = None + page_kwargs["settings"] = settings + del page_kwargs["metadata"]["summary"] + settings["SUMMARY_MAX_LENGTH"] = None page = Page(**page_kwargs) self.assertEqual(page.summary, TEST_CONTENT) - settings['SUMMARY_MAX_LENGTH'] = 10 + settings["SUMMARY_MAX_LENGTH"] = 10 page = Page(**page_kwargs) self.assertEqual(page.summary, truncate_html_words(TEST_CONTENT, 10)) - settings['SUMMARY_MAX_LENGTH'] = 0 + settings["SUMMARY_MAX_LENGTH"] = 0 page = Page(**page_kwargs) - self.assertEqual(page.summary, '') + self.assertEqual(page.summary, "") def test_summary_end_suffix(self): # If a :SUMMARY_END_SUFFIX: is set, and there is no other summary, # generated summary should contain the specified marker at the end. page_kwargs = self._copy_page_kwargs() settings = get_settings() - page_kwargs['settings'] = settings - del page_kwargs['metadata']['summary'] - settings['SUMMARY_END_SUFFIX'] = 'test_marker' - settings['SUMMARY_MAX_LENGTH'] = 10 + page_kwargs["settings"] = settings + del page_kwargs["metadata"]["summary"] + settings["SUMMARY_END_SUFFIX"] = "test_marker" + settings["SUMMARY_MAX_LENGTH"] = 10 page = Page(**page_kwargs) - self.assertEqual(page.summary, truncate_html_words(TEST_CONTENT, 10, - 'test_marker')) - self.assertIn('test_marker', page.summary) + self.assertEqual( + page.summary, truncate_html_words(TEST_CONTENT, 10, "test_marker") + ) + self.assertIn("test_marker", page.summary) def test_summary_get_summary_warning(self): """calling ._get_summary() should issue a warning""" @@ -134,57 +139,61 @@ class TestPage(TestBase): self.assertEqual(page.summary, TEST_SUMMARY) self.assertEqual(page._get_summary(), TEST_SUMMARY) self.assertLogCountEqual( - count=1, - msg=r"_get_summary\(\) has been deprecated since 3\.6\.4\. " - "Use the summary decorator instead", - level=logging.WARNING) + count=1, + msg=r"_get_summary\(\) has been deprecated since 3\.6\.4\. " + "Use the summary decorator instead", + level=logging.WARNING, + ) def test_slug(self): page_kwargs = self._copy_page_kwargs() settings = get_settings() - page_kwargs['settings'] = settings - settings['SLUGIFY_SOURCE'] = "title" + page_kwargs["settings"] = settings + settings["SLUGIFY_SOURCE"] = "title" page = Page(**page_kwargs) - self.assertEqual(page.slug, 'foo-bar') - settings['SLUGIFY_SOURCE'] = "basename" + self.assertEqual(page.slug, "foo-bar") + settings["SLUGIFY_SOURCE"] = "basename" page = Page(**page_kwargs) - self.assertEqual(page.slug, 'foo') + self.assertEqual(page.slug, "foo") # test slug from title with unicode and case inputs = ( # (title, expected, preserve_case, use_unicode) - ('指導書', 'zhi-dao-shu', False, False), - ('指導書', 'Zhi-Dao-Shu', True, False), - ('指導書', '指導書', False, True), - ('指導書', '指導書', True, True), - ('Çığ', 'cig', False, False), - ('Çığ', 'Cig', True, False), - ('Çığ', 'çığ', False, True), - ('Çığ', 'Çığ', True, True), + ("指導書", "zhi-dao-shu", False, False), + ("指導書", "Zhi-Dao-Shu", True, False), + ("指導書", "指導書", False, True), + ("指導書", "指導書", True, True), + ("Çığ", "cig", False, False), + ("Çığ", "Cig", True, False), + ("Çığ", "çığ", False, True), + ("Çığ", "Çığ", True, True), ) settings = get_settings() page_kwargs = self._copy_page_kwargs() - page_kwargs['settings'] = settings + page_kwargs["settings"] = settings for title, expected, preserve_case, use_unicode in inputs: - settings['SLUGIFY_PRESERVE_CASE'] = preserve_case - settings['SLUGIFY_USE_UNICODE'] = use_unicode - page_kwargs['metadata']['title'] = title + settings["SLUGIFY_PRESERVE_CASE"] = preserve_case + settings["SLUGIFY_USE_UNICODE"] = use_unicode + page_kwargs["metadata"]["title"] = title page = Page(**page_kwargs) - self.assertEqual(page.slug, expected, - (title, preserve_case, use_unicode)) + self.assertEqual(page.slug, expected, (title, preserve_case, use_unicode)) def test_defaultlang(self): # If no lang is given, default to the default one. page = Page(**self.page_kwargs) - self.assertEqual(page.lang, DEFAULT_CONFIG['DEFAULT_LANG']) + self.assertEqual(page.lang, DEFAULT_CONFIG["DEFAULT_LANG"]) # it is possible to specify the lang in the metadata infos - self.page_kwargs['metadata'].update({'lang': 'fr', }) + self.page_kwargs["metadata"].update( + { + "lang": "fr", + } + ) page = Page(**self.page_kwargs) - self.assertEqual(page.lang, 'fr') + self.assertEqual(page.lang, "fr") def test_save_as(self): # If a lang is not the default lang, save_as should be set @@ -195,7 +204,11 @@ class TestPage(TestBase): self.assertEqual(page.save_as, "pages/foo-bar.html") # if a language is defined, save_as should include it accordingly - self.page_kwargs['metadata'].update({'lang': 'fr', }) + self.page_kwargs["metadata"].update( + { + "lang": "fr", + } + ) page = Page(**self.page_kwargs) self.assertEqual(page.save_as, "pages/foo-bar-fr.html") @@ -206,34 +219,32 @@ class TestPage(TestBase): # If 'source_path' is None, 'relative_source_path' should # also return None - page_kwargs['source_path'] = None + page_kwargs["source_path"] = None page = Page(**page_kwargs) self.assertIsNone(page.relative_source_path) page_kwargs = self._copy_page_kwargs() settings = get_settings() - full_path = page_kwargs['source_path'] + full_path = page_kwargs["source_path"] - settings['PATH'] = os.path.dirname(full_path) - page_kwargs['settings'] = settings + settings["PATH"] = os.path.dirname(full_path) + page_kwargs["settings"] = settings page = Page(**page_kwargs) # if 'source_path' is set, 'relative_source_path' should # return the relative path from 'PATH' to 'source_path' self.assertEqual( page.relative_source_path, - os.path.relpath( - full_path, - os.path.dirname(full_path) - )) + os.path.relpath(full_path, os.path.dirname(full_path)), + ) def test_metadata_url_format(self): # Arbitrary metadata should be passed through url_format() page = Page(**self.page_kwargs) - self.assertIn('summary', page.url_format.keys()) - page.metadata['directory'] = 'test-dir' - page.settings = get_settings(PAGE_SAVE_AS='{directory}/{slug}') - self.assertEqual(page.save_as, 'test-dir/foo-bar') + self.assertIn("summary", page.url_format.keys()) + page.metadata["directory"] = "test-dir" + page.settings = get_settings(PAGE_SAVE_AS="{directory}/{slug}") + self.assertEqual(page.save_as, "test-dir/foo-bar") def test_datetime(self): # If DATETIME is set to a tuple, it should be used to override LOCALE @@ -242,28 +253,28 @@ class TestPage(TestBase): page_kwargs = self._copy_page_kwargs() # set its date to dt - page_kwargs['metadata']['date'] = dt + page_kwargs["metadata"]["date"] = dt page = Page(**page_kwargs) # page.locale_date is a unicode string in both python2 and python3 - dt_date = dt.strftime(DEFAULT_CONFIG['DEFAULT_DATE_FORMAT']) + dt_date = dt.strftime(DEFAULT_CONFIG["DEFAULT_DATE_FORMAT"]) self.assertEqual(page.locale_date, dt_date) - page_kwargs['settings'] = get_settings() + page_kwargs["settings"] = get_settings() # I doubt this can work on all platforms ... if platform == "win32": - locale = 'jpn' + locale = "jpn" else: - locale = 'ja_JP.utf8' - page_kwargs['settings']['DATE_FORMATS'] = {'jp': (locale, - '%Y-%m-%d(%a)')} - page_kwargs['metadata']['lang'] = 'jp' + locale = "ja_JP.utf8" + page_kwargs["settings"]["DATE_FORMATS"] = {"jp": (locale, "%Y-%m-%d(%a)")} + page_kwargs["metadata"]["lang"] = "jp" import locale as locale_module + try: page = Page(**page_kwargs) - self.assertEqual(page.locale_date, '2015-09-13(\u65e5)') + self.assertEqual(page.locale_date, "2015-09-13(\u65e5)") except locale_module.Error: # The constructor of ``Page`` will try to set the locale to # ``ja_JP.utf8``. But this attempt will failed when there is no @@ -277,22 +288,21 @@ class TestPage(TestBase): def test_template(self): # Pages default to page, metadata overwrites default_page = Page(**self.page_kwargs) - self.assertEqual('page', default_page.template) + self.assertEqual("page", default_page.template) page_kwargs = self._copy_page_kwargs() - page_kwargs['metadata']['template'] = 'custom' + page_kwargs["metadata"]["template"] = "custom" custom_page = Page(**page_kwargs) - self.assertEqual('custom', custom_page.template) + self.assertEqual("custom", custom_page.template) def test_signal(self): def receiver_test_function(sender): receiver_test_function.has_been_called = True pass + receiver_test_function.has_been_called = False content_object_init.connect(receiver_test_function) - self.assertIn( - receiver_test_function, - content_object_init.receivers_for(Page)) + self.assertIn(receiver_test_function, content_object_init.receivers_for(Page)) self.assertFalse(receiver_test_function.has_been_called) Page(**self.page_kwargs) @@ -303,102 +313,106 @@ class TestPage(TestBase): # filenames, tags and categories. settings = get_settings() args = self.page_kwargs.copy() - args['settings'] = settings + args["settings"] = settings # Tag - args['content'] = ('A simple test, with a ' - 'link') + args["content"] = "A simple test, with a " 'link' page = Page(**args) - content = page.get_content('http://notmyidea.org') + content = page.get_content("http://notmyidea.org") self.assertEqual( content, - ('A simple test, with a ' - 'link')) + ( + "A simple test, with a " + 'link' + ), + ) # Category - args['content'] = ('A simple test, with a ' - 'link') + args["content"] = ( + "A simple test, with a " 'link' + ) page = Page(**args) - content = page.get_content('http://notmyidea.org') + content = page.get_content("http://notmyidea.org") self.assertEqual( content, - ('A simple test, with a ' - 'link')) + ( + "A simple test, with a " + 'link' + ), + ) def test_intrasite_link(self): - cls_name = '_DummyArticle' - article = type(cls_name, (object,), {'url': 'article.html'}) + cls_name = "_DummyArticle" + article = type(cls_name, (object,), {"url": "article.html"}) args = self.page_kwargs.copy() - args['settings'] = get_settings() - args['source_path'] = 'content' - args['context']['generated_content'] = {'article.rst': article} + args["settings"] = get_settings() + args["source_path"] = "content" + args["context"]["generated_content"] = {"article.rst": article} # Classic intrasite link via filename - args['content'] = ( - 'A simple test, with a ' - 'link' + args["content"] = ( + "A simple test, with a " 'link' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( content, - 'A simple test, with a ' - 'link' + "A simple test, with a " + 'link', ) # fragment - args['content'] = ( - 'A simple test, with a ' + args["content"] = ( + "A simple test, with a " 'link' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( content, - 'A simple test, with a ' - 'link' + "A simple test, with a " + 'link', ) # query - args['content'] = ( - 'A simple test, with a ' + args["content"] = ( + "A simple test, with a " 'link' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( content, - 'A simple test, with a ' + "A simple test, with a " 'link' + '?utm_whatever=234&highlight=word">link', ) # combination - args['content'] = ( - 'A simple test, with a ' + args["content"] = ( + "A simple test, with a " 'link' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( content, - 'A simple test, with a ' + "A simple test, with a " 'link' + '?utm_whatever=234&highlight=word#section-2">link', ) # also test for summary in metadata parsed = ( - 'A simple summary test, with a ' - 'link' + "A simple summary test, with a " 'link' ) linked = ( - 'A simple summary test, with a ' + "A simple summary test, with a " 'link' ) - args['settings']['FORMATTED_FIELDS'] = ['summary', 'custom'] - args['metadata']['summary'] = parsed - args['metadata']['custom'] = parsed - args['context']['localsiteurl'] = 'http://notmyidea.org' + args["settings"]["FORMATTED_FIELDS"] = ["summary", "custom"] + args["metadata"]["summary"] = parsed + args["metadata"]["custom"] = parsed + args["context"]["localsiteurl"] = "http://notmyidea.org" p = Page(**args) # This is called implicitly from all generators and Pelican.run() once # all files are processed. Here we process just one page so it needs @@ -408,252 +422,236 @@ class TestPage(TestBase): self.assertEqual(p.custom, linked) def test_intrasite_link_more(self): - cls_name = '_DummyAsset' + cls_name = "_DummyAsset" args = self.page_kwargs.copy() - args['settings'] = get_settings() - args['source_path'] = 'content' - args['context']['static_content'] = { - 'images/poster.jpg': - type(cls_name, (object,), {'url': 'images/poster.jpg'}), - 'assets/video.mp4': - type(cls_name, (object,), {'url': 'assets/video.mp4'}), - 'images/graph.svg': - type(cls_name, (object,), {'url': 'images/graph.svg'}), + args["settings"] = get_settings() + args["source_path"] = "content" + args["context"]["static_content"] = { + "images/poster.jpg": type( + cls_name, (object,), {"url": "images/poster.jpg"} + ), + "assets/video.mp4": type(cls_name, (object,), {"url": "assets/video.mp4"}), + "images/graph.svg": type(cls_name, (object,), {"url": "images/graph.svg"}), } - args['context']['generated_content'] = { - 'reference.rst': - type(cls_name, (object,), {'url': 'reference.html'}), + args["context"]["generated_content"] = { + "reference.rst": type(cls_name, (object,), {"url": "reference.html"}), } # video.poster - args['content'] = ( - 'There is a video with poster ' + args["content"] = ( + "There is a video with poster " '' + "" ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( content, - 'There is a video with poster ' + "There is a video with poster " '' + "", ) # object.data - args['content'] = ( - 'There is a svg object ' + args["content"] = ( + "There is a svg object " '' - '' + "" ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( content, - 'There is a svg object ' + "There is a svg object " '' - '' + "", ) # blockquote.cite - args['content'] = ( - 'There is a blockquote with cite attribute ' + args["content"] = ( + "There is a blockquote with cite attribute " '

    blah blah
    ' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( content, - 'There is a blockquote with cite attribute ' + "There is a blockquote with cite attribute " '
    ' - 'blah blah' - '
    ' + "blah blah" + "", ) def test_intrasite_link_absolute(self): """Test that absolute URLs are merged properly.""" args = self.page_kwargs.copy() - args['settings'] = get_settings( - STATIC_URL='http://static.cool.site/{path}', - ARTICLE_URL='http://blog.cool.site/{slug}.html') - args['source_path'] = 'content' - args['context']['static_content'] = { - 'images/poster.jpg': - Static('', settings=args['settings'], - source_path='images/poster.jpg'), + args["settings"] = get_settings( + STATIC_URL="http://static.cool.site/{path}", + ARTICLE_URL="http://blog.cool.site/{slug}.html", + ) + args["source_path"] = "content" + args["context"]["static_content"] = { + "images/poster.jpg": Static( + "", settings=args["settings"], source_path="images/poster.jpg" + ), } - args['context']['generated_content'] = { - 'article.rst': - Article('', settings=args['settings'], metadata={ - 'slug': 'article', 'title': 'Article'}) + args["context"]["generated_content"] = { + "article.rst": Article( + "", + settings=args["settings"], + metadata={"slug": "article", "title": "Article"}, + ) } # Article link will go to blog - args['content'] = ( - 'Article' - ) - content = Page(**args).get_content('http://cool.site') + args["content"] = 'Article' + content = Page(**args).get_content("http://cool.site") self.assertEqual( - content, - 'Article' + content, 'Article' ) # Page link will go to the main site - args['content'] = ( - 'Index' - ) - content = Page(**args).get_content('http://cool.site') + args["content"] = 'Index' + content = Page(**args).get_content("http://cool.site") + self.assertEqual(content, 'Index') + + # Image link will go to static + args["content"] = '' + content = Page(**args).get_content("http://cool.site") self.assertEqual( - content, - 'Index' + content, '' ) # Image link will go to static - args['content'] = ( - '' - ) - content = Page(**args).get_content('http://cool.site') + args["content"] = '' + content = Page(**args).get_content("http://cool.site") self.assertEqual( - content, - '' - ) - - # Image link will go to static - args['content'] = ( - '' - ) - content = Page(**args).get_content('http://cool.site') - self.assertEqual( - content, - '' + content, '' ) def test_intrasite_link_escape(self): - article = type( - '_DummyArticle', (object,), {'url': 'article-spaces.html'}) - asset = type( - '_DummyAsset', (object,), {'url': 'name@example.com'}) + article = type("_DummyArticle", (object,), {"url": "article-spaces.html"}) + asset = type("_DummyAsset", (object,), {"url": "name@example.com"}) args = self.page_kwargs.copy() - args['settings'] = get_settings() - args['source_path'] = 'content' - args['context']['generated_content'] = {'article spaces.rst': article} - args['context']['static_content'] = {'name@example.com': asset} + args["settings"] = get_settings() + args["source_path"] = "content" + args["context"]["generated_content"] = {"article spaces.rst": article} + args["context"]["static_content"] = {"name@example.com": asset} expected_output = ( - 'A simple test with a ' + "A simple test with a " 'link ' 'file' ) # not escaped - args['content'] = ( - 'A simple test with a ' + args["content"] = ( + "A simple test with a " 'link ' 'file' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual(content, expected_output) # html escaped - args['content'] = ( - 'A simple test with a ' + args["content"] = ( + "A simple test with a " 'link ' 'file' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual(content, expected_output) # url escaped - args['content'] = ( - 'A simple test with a ' + args["content"] = ( + "A simple test with a " 'link ' 'file' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual(content, expected_output) # html and url escaped - args['content'] = ( - 'A simple test with a ' + args["content"] = ( + "A simple test with a " 'link ' 'file' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual(content, expected_output) def test_intrasite_link_markdown_spaces(self): - cls_name = '_DummyArticle' - article = type(cls_name, (object,), {'url': 'article-spaces.html'}) + cls_name = "_DummyArticle" + article = type(cls_name, (object,), {"url": "article-spaces.html"}) args = self.page_kwargs.copy() - args['settings'] = get_settings() - args['source_path'] = 'content' - args['context']['generated_content'] = {'article spaces.rst': article} + args["settings"] = get_settings() + args["source_path"] = "content" + args["context"]["generated_content"] = {"article spaces.rst": article} # An intrasite link via filename with %20 as a space - args['content'] = ( - 'A simple test, with a ' - 'link' + args["content"] = ( + "A simple test, with a " 'link' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( content, - 'A simple test, with a ' - 'link' + "A simple test, with a " + 'link', ) def test_intrasite_link_source_and_generated(self): - """Test linking both to the source and the generated article - """ - cls_name = '_DummyAsset' + """Test linking both to the source and the generated article""" + cls_name = "_DummyAsset" args = self.page_kwargs.copy() - args['settings'] = get_settings() - args['source_path'] = 'content' - args['context']['generated_content'] = { - 'article.rst': type(cls_name, (object,), {'url': 'article.html'})} - args['context']['static_content'] = { - 'article.rst': type(cls_name, (object,), {'url': 'article.rst'})} + args["settings"] = get_settings() + args["source_path"] = "content" + args["context"]["generated_content"] = { + "article.rst": type(cls_name, (object,), {"url": "article.html"}) + } + args["context"]["static_content"] = { + "article.rst": type(cls_name, (object,), {"url": "article.rst"}) + } - args['content'] = ( - 'A simple test, with a link to an' + args["content"] = ( + "A simple test, with a link to an" 'article and its' 'source' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( content, - 'A simple test, with a link to an' + "A simple test, with a link to an" 'article and its' - 'source' + 'source', ) def test_intrasite_link_to_static_content_with_filename(self): - """Test linking to a static resource with deprecated {filename} - """ - cls_name = '_DummyAsset' + """Test linking to a static resource with deprecated {filename}""" + cls_name = "_DummyAsset" args = self.page_kwargs.copy() - args['settings'] = get_settings() - args['source_path'] = 'content' - args['context']['static_content'] = { - 'poster.jpg': - type(cls_name, (object,), {'url': 'images/poster.jpg'})} + args["settings"] = get_settings() + args["source_path"] = "content" + args["context"]["static_content"] = { + "poster.jpg": type(cls_name, (object,), {"url": "images/poster.jpg"}) + } - args['content'] = ( - 'A simple test, with a link to a' + args["content"] = ( + "A simple test, with a link to a" 'poster' ) - content = Page(**args).get_content('http://notmyidea.org') + content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( content, - 'A simple test, with a link to a' - 'poster' + "A simple test, with a link to a" + 'poster', ) def test_multiple_authors(self): @@ -661,9 +659,11 @@ class TestPage(TestBase): args = self.page_kwargs.copy() content = Page(**args) assert content.authors == [content.author] - args['metadata'].pop('author') - args['metadata']['authors'] = [Author('First Author', DEFAULT_CONFIG), - Author('Second Author', DEFAULT_CONFIG)] + args["metadata"].pop("author") + args["metadata"]["authors"] = [ + Author("First Author", DEFAULT_CONFIG), + Author("Second Author", DEFAULT_CONFIG), + ] content = Page(**args) assert content.authors assert content.author == content.authors[0] @@ -673,173 +673,184 @@ class TestArticle(TestBase): def test_template(self): # Articles default to article, metadata overwrites default_article = Article(**self.page_kwargs) - self.assertEqual('article', default_article.template) + self.assertEqual("article", default_article.template) article_kwargs = self._copy_page_kwargs() - article_kwargs['metadata']['template'] = 'custom' + article_kwargs["metadata"]["template"] = "custom" custom_article = Article(**article_kwargs) - self.assertEqual('custom', custom_article.template) + self.assertEqual("custom", custom_article.template) def test_slugify_category_author(self): settings = get_settings() - settings['SLUG_REGEX_SUBSTITUTIONS'] = [ - (r'C#', 'csharp'), - (r'[^\w\s-]', ''), - (r'(?u)\A\s*', ''), - (r'(?u)\s*\Z', ''), - (r'[-\s]+', '-'), + settings["SLUG_REGEX_SUBSTITUTIONS"] = [ + (r"C#", "csharp"), + (r"[^\w\s-]", ""), + (r"(?u)\A\s*", ""), + (r"(?u)\s*\Z", ""), + (r"[-\s]+", "-"), ] - settings['ARTICLE_URL'] = '{author}/{category}/{slug}/' - settings['ARTICLE_SAVE_AS'] = '{author}/{category}/{slug}/index.html' + settings["ARTICLE_URL"] = "{author}/{category}/{slug}/" + settings["ARTICLE_SAVE_AS"] = "{author}/{category}/{slug}/index.html" article_kwargs = self._copy_page_kwargs() - article_kwargs['metadata']['author'] = Author("O'Brien", settings) - article_kwargs['metadata']['category'] = Category( - 'C# & stuff', settings) - article_kwargs['metadata']['title'] = 'fnord' - article_kwargs['settings'] = settings + article_kwargs["metadata"]["author"] = Author("O'Brien", settings) + article_kwargs["metadata"]["category"] = Category("C# & stuff", settings) + article_kwargs["metadata"]["title"] = "fnord" + article_kwargs["settings"] = settings article = Article(**article_kwargs) - self.assertEqual(article.url, 'obrien/csharp-stuff/fnord/') - self.assertEqual( - article.save_as, 'obrien/csharp-stuff/fnord/index.html') + self.assertEqual(article.url, "obrien/csharp-stuff/fnord/") + self.assertEqual(article.save_as, "obrien/csharp-stuff/fnord/index.html") def test_slugify_with_author_substitutions(self): settings = get_settings() - settings['AUTHOR_REGEX_SUBSTITUTIONS'] = [ - ('Alexander Todorov', 'atodorov'), - ('Krasimir Tsonev', 'krasimir'), - (r'[^\w\s-]', ''), - (r'(?u)\A\s*', ''), - (r'(?u)\s*\Z', ''), - (r'[-\s]+', '-'), + settings["AUTHOR_REGEX_SUBSTITUTIONS"] = [ + ("Alexander Todorov", "atodorov"), + ("Krasimir Tsonev", "krasimir"), + (r"[^\w\s-]", ""), + (r"(?u)\A\s*", ""), + (r"(?u)\s*\Z", ""), + (r"[-\s]+", "-"), ] - settings['ARTICLE_URL'] = 'blog/{author}/{slug}/' - settings['ARTICLE_SAVE_AS'] = 'blog/{author}/{slug}/index.html' + settings["ARTICLE_URL"] = "blog/{author}/{slug}/" + settings["ARTICLE_SAVE_AS"] = "blog/{author}/{slug}/index.html" article_kwargs = self._copy_page_kwargs() - article_kwargs['metadata']['author'] = Author('Alexander Todorov', - settings) - article_kwargs['metadata']['title'] = 'fnord' - article_kwargs['settings'] = settings + article_kwargs["metadata"]["author"] = Author("Alexander Todorov", settings) + article_kwargs["metadata"]["title"] = "fnord" + article_kwargs["settings"] = settings article = Article(**article_kwargs) - self.assertEqual(article.url, 'blog/atodorov/fnord/') - self.assertEqual(article.save_as, 'blog/atodorov/fnord/index.html') + self.assertEqual(article.url, "blog/atodorov/fnord/") + self.assertEqual(article.save_as, "blog/atodorov/fnord/index.html") def test_slugify_category_with_dots(self): settings = get_settings() - settings['CATEGORY_REGEX_SUBSTITUTIONS'] = [ - ('Fedora QA', 'fedora.qa'), + settings["CATEGORY_REGEX_SUBSTITUTIONS"] = [ + ("Fedora QA", "fedora.qa"), ] - settings['ARTICLE_URL'] = '{category}/{slug}/' + settings["ARTICLE_URL"] = "{category}/{slug}/" article_kwargs = self._copy_page_kwargs() - article_kwargs['metadata']['category'] = Category('Fedora QA', - settings) - article_kwargs['metadata']['title'] = 'This Week in Fedora QA' - article_kwargs['settings'] = settings + article_kwargs["metadata"]["category"] = Category("Fedora QA", settings) + article_kwargs["metadata"]["title"] = "This Week in Fedora QA" + article_kwargs["settings"] = settings article = Article(**article_kwargs) - self.assertEqual(article.url, 'fedora.qa/this-week-in-fedora-qa/') + self.assertEqual(article.url, "fedora.qa/this-week-in-fedora-qa/") def test_valid_save_as_detects_breakout(self): settings = get_settings() article_kwargs = self._copy_page_kwargs() - article_kwargs['metadata']['slug'] = '../foo' - article_kwargs['settings'] = settings + article_kwargs["metadata"]["slug"] = "../foo" + article_kwargs["settings"] = settings article = Article(**article_kwargs) self.assertFalse(article._has_valid_save_as()) def test_valid_save_as_detects_breakout_to_root(self): settings = get_settings() article_kwargs = self._copy_page_kwargs() - article_kwargs['metadata']['slug'] = '/foo' - article_kwargs['settings'] = settings + article_kwargs["metadata"]["slug"] = "/foo" + article_kwargs["settings"] = settings article = Article(**article_kwargs) self.assertFalse(article._has_valid_save_as()) def test_valid_save_as_passes_valid(self): settings = get_settings() article_kwargs = self._copy_page_kwargs() - article_kwargs['metadata']['slug'] = 'foo' - article_kwargs['settings'] = settings + article_kwargs["metadata"]["slug"] = "foo" + article_kwargs["settings"] = settings article = Article(**article_kwargs) self.assertTrue(article._has_valid_save_as()) class TestStatic(LoggedTestCase): - def setUp(self): super().setUp() self.settings = get_settings( - STATIC_SAVE_AS='{path}', - STATIC_URL='{path}', - PAGE_SAVE_AS=os.path.join('outpages', '{slug}.html'), - PAGE_URL='outpages/{slug}.html') + STATIC_SAVE_AS="{path}", + STATIC_URL="{path}", + PAGE_SAVE_AS=os.path.join("outpages", "{slug}.html"), + PAGE_URL="outpages/{slug}.html", + ) self.context = get_context(self.settings) - self.static = Static(content=None, metadata={}, settings=self.settings, - source_path=posix_join('dir', 'foo.jpg'), - context=self.context) + self.static = Static( + content=None, + metadata={}, + settings=self.settings, + source_path=posix_join("dir", "foo.jpg"), + context=self.context, + ) - self.context['static_content'][self.static.source_path] = self.static + self.context["static_content"][self.static.source_path] = self.static def tearDown(self): pass def test_attach_to_same_dir(self): - """attach_to() overrides a static file's save_as and url. - """ + """attach_to() overrides a static file's save_as and url.""" page = Page( content="fake page", - metadata={'title': 'fakepage'}, + metadata={"title": "fakepage"}, settings=self.settings, - source_path=os.path.join('dir', 'fakepage.md')) + source_path=os.path.join("dir", "fakepage.md"), + ) self.static.attach_to(page) - expected_save_as = os.path.join('outpages', 'foo.jpg') + expected_save_as = os.path.join("outpages", "foo.jpg") self.assertEqual(self.static.save_as, expected_save_as) self.assertEqual(self.static.url, path_to_url(expected_save_as)) def test_attach_to_parent_dir(self): - """attach_to() preserves dirs inside the linking document dir. - """ - page = Page(content="fake page", metadata={'title': 'fakepage'}, - settings=self.settings, source_path='fakepage.md') + """attach_to() preserves dirs inside the linking document dir.""" + page = Page( + content="fake page", + metadata={"title": "fakepage"}, + settings=self.settings, + source_path="fakepage.md", + ) self.static.attach_to(page) - expected_save_as = os.path.join('outpages', 'dir', 'foo.jpg') + expected_save_as = os.path.join("outpages", "dir", "foo.jpg") self.assertEqual(self.static.save_as, expected_save_as) self.assertEqual(self.static.url, path_to_url(expected_save_as)) def test_attach_to_other_dir(self): - """attach_to() ignores dirs outside the linking document dir. - """ - page = Page(content="fake page", - metadata={'title': 'fakepage'}, settings=self.settings, - source_path=os.path.join('dir', 'otherdir', 'fakepage.md')) + """attach_to() ignores dirs outside the linking document dir.""" + page = Page( + content="fake page", + metadata={"title": "fakepage"}, + settings=self.settings, + source_path=os.path.join("dir", "otherdir", "fakepage.md"), + ) self.static.attach_to(page) - expected_save_as = os.path.join('outpages', 'foo.jpg') + expected_save_as = os.path.join("outpages", "foo.jpg") self.assertEqual(self.static.save_as, expected_save_as) self.assertEqual(self.static.url, path_to_url(expected_save_as)) def test_attach_to_ignores_subsequent_calls(self): - """attach_to() does nothing when called a second time. - """ - page = Page(content="fake page", - metadata={'title': 'fakepage'}, settings=self.settings, - source_path=os.path.join('dir', 'fakepage.md')) + """attach_to() does nothing when called a second time.""" + page = Page( + content="fake page", + metadata={"title": "fakepage"}, + settings=self.settings, + source_path=os.path.join("dir", "fakepage.md"), + ) self.static.attach_to(page) otherdir_settings = self.settings.copy() - otherdir_settings.update(dict( - PAGE_SAVE_AS=os.path.join('otherpages', '{slug}.html'), - PAGE_URL='otherpages/{slug}.html')) + otherdir_settings.update( + dict( + PAGE_SAVE_AS=os.path.join("otherpages", "{slug}.html"), + PAGE_URL="otherpages/{slug}.html", + ) + ) otherdir_page = Page( content="other page", - metadata={'title': 'otherpage'}, + metadata={"title": "otherpage"}, settings=otherdir_settings, - source_path=os.path.join('dir', 'otherpage.md')) + source_path=os.path.join("dir", "otherpage.md"), + ) self.static.attach_to(otherdir_page) - otherdir_save_as = os.path.join('otherpages', 'foo.jpg') + otherdir_save_as = os.path.join("otherpages", "foo.jpg") self.assertNotEqual(self.static.save_as, otherdir_save_as) self.assertNotEqual(self.static.url, path_to_url(otherdir_save_as)) @@ -851,9 +862,10 @@ class TestStatic(LoggedTestCase): page = Page( content="fake page", - metadata={'title': 'fakepage'}, + metadata={"title": "fakepage"}, settings=self.settings, - source_path=os.path.join('dir', 'fakepage.md')) + source_path=os.path.join("dir", "fakepage.md"), + ) self.static.attach_to(page) self.assertEqual(self.static.save_as, original_save_as) @@ -867,9 +879,10 @@ class TestStatic(LoggedTestCase): page = Page( content="fake page", - metadata={'title': 'fakepage'}, + metadata={"title": "fakepage"}, settings=self.settings, - source_path=os.path.join('dir', 'fakepage.md')) + source_path=os.path.join("dir", "fakepage.md"), + ) self.static.attach_to(page) self.assertEqual(self.static.save_as, self.static.source_path) @@ -881,38 +894,41 @@ class TestStatic(LoggedTestCase): """ customstatic = Static( content=None, - metadata=dict(save_as='customfoo.jpg', url='customfoo.jpg'), + metadata=dict(save_as="customfoo.jpg", url="customfoo.jpg"), settings=self.settings, - source_path=os.path.join('dir', 'foo.jpg'), - context=self.settings.copy()) + source_path=os.path.join("dir", "foo.jpg"), + context=self.settings.copy(), + ) page = Page( content="fake page", - metadata={'title': 'fakepage'}, settings=self.settings, - source_path=os.path.join('dir', 'fakepage.md')) + metadata={"title": "fakepage"}, + settings=self.settings, + source_path=os.path.join("dir", "fakepage.md"), + ) customstatic.attach_to(page) - self.assertEqual(customstatic.save_as, 'customfoo.jpg') - self.assertEqual(customstatic.url, 'customfoo.jpg') + self.assertEqual(customstatic.save_as, "customfoo.jpg") + self.assertEqual(customstatic.url, "customfoo.jpg") def test_attach_link_syntax(self): - """{attach} link syntax triggers output path override & url replacement. - """ + """{attach} link syntax triggers output path override & url replacement.""" html = 'link' page = Page( content=html, - metadata={'title': 'fakepage'}, + metadata={"title": "fakepage"}, settings=self.settings, - source_path=os.path.join('dir', 'otherdir', 'fakepage.md'), - context=self.context) - content = page.get_content('') + source_path=os.path.join("dir", "otherdir", "fakepage.md"), + context=self.context, + ) + content = page.get_content("") self.assertNotEqual( - content, html, - "{attach} link syntax did not trigger URL replacement.") + content, html, "{attach} link syntax did not trigger URL replacement." + ) - expected_save_as = os.path.join('outpages', 'foo.jpg') + expected_save_as = os.path.join("outpages", "foo.jpg") self.assertEqual(self.static.save_as, expected_save_as) self.assertEqual(self.static.url, path_to_url(expected_save_as)) @@ -922,11 +938,12 @@ class TestStatic(LoggedTestCase): html = 'link' page = Page( content=html, - metadata={'title': 'fakepage'}, + metadata={"title": "fakepage"}, settings=self.settings, - source_path=os.path.join('dir', 'otherdir', 'fakepage.md'), - context=self.context) - content = page.get_content('') + source_path=os.path.join("dir", "otherdir", "fakepage.md"), + context=self.context, + ) + content = page.get_content("") self.assertNotEqual(content, html) @@ -936,11 +953,12 @@ class TestStatic(LoggedTestCase): html = 'link' page = Page( content=html, - metadata={'title': 'fakepage'}, + metadata={"title": "fakepage"}, settings=self.settings, - source_path=os.path.join('dir', 'otherdir', 'fakepage.md'), - context=self.context) - content = page.get_content('') + source_path=os.path.join("dir", "otherdir", "fakepage.md"), + context=self.context, + ) + content = page.get_content("") self.assertNotEqual(content, html) @@ -950,11 +968,12 @@ class TestStatic(LoggedTestCase): html = 'link' page = Page( content=html, - metadata={'title': 'fakepage'}, + metadata={"title": "fakepage"}, settings=self.settings, - source_path=os.path.join('dir', 'otherdir', 'fakepage.md'), - context=self.context) - content = page.get_content('') + source_path=os.path.join("dir", "otherdir", "fakepage.md"), + context=self.context, + ) + content = page.get_content("") self.assertNotEqual(content, html) @@ -964,52 +983,62 @@ class TestStatic(LoggedTestCase): html = 'link' page = Page( content=html, - metadata={'title': 'fakepage'}, + metadata={"title": "fakepage"}, settings=self.settings, - source_path=os.path.join('dir', 'otherdir', 'fakepage.md'), - context=self.context) - content = page.get_content('') + source_path=os.path.join("dir", "otherdir", "fakepage.md"), + context=self.context, + ) + content = page.get_content("") self.assertNotEqual(content, html) - expected_html = ('link') + expected_html = ( + 'link' + ) self.assertEqual(content, expected_html) def test_unknown_link_syntax(self): "{unknown} link syntax should trigger warning." html = 'link' - page = Page(content=html, - metadata={'title': 'fakepage'}, settings=self.settings, - source_path=os.path.join('dir', 'otherdir', 'fakepage.md'), - context=self.context) - content = page.get_content('') + page = Page( + content=html, + metadata={"title": "fakepage"}, + settings=self.settings, + source_path=os.path.join("dir", "otherdir", "fakepage.md"), + context=self.context, + ) + content = page.get_content("") self.assertEqual(content, html) self.assertLogCountEqual( count=1, msg="Replacement Indicator 'unknown' not recognized, " - "skipping replacement", - level=logging.WARNING) + "skipping replacement", + level=logging.WARNING, + ) def test_link_to_unknown_file(self): "{filename} link to unknown file should trigger warning." html = 'link' - page = Page(content=html, - metadata={'title': 'fakepage'}, settings=self.settings, - source_path=os.path.join('dir', 'otherdir', 'fakepage.md'), - context=self.context) - content = page.get_content('') + page = Page( + content=html, + metadata={"title": "fakepage"}, + settings=self.settings, + source_path=os.path.join("dir", "otherdir", "fakepage.md"), + context=self.context, + ) + content = page.get_content("") self.assertEqual(content, html) self.assertLogCountEqual( count=1, msg="Unable to find 'foo', skipping url replacement.", - level=logging.WARNING) + level=logging.WARNING, + ) def test_index_link_syntax_with_spaces(self): """{index} link syntax triggers url replacement @@ -1018,18 +1047,20 @@ class TestStatic(LoggedTestCase): html = 'link' page = Page( content=html, - metadata={'title': 'fakepage'}, + metadata={"title": "fakepage"}, settings=self.settings, - source_path=os.path.join('dir', 'otherdir', 'fakepage.md'), - context=self.context) - content = page.get_content('') + source_path=os.path.join("dir", "otherdir", "fakepage.md"), + context=self.context, + ) + content = page.get_content("") self.assertNotEqual(content, html) - expected_html = ('link') + expected_html = ( + 'link' + ) self.assertEqual(content, expected_html) def test_not_save_as_draft(self): @@ -1037,12 +1068,15 @@ class TestStatic(LoggedTestCase): static = Static( content=None, - metadata=dict(status='draft',), + metadata=dict( + status="draft", + ), settings=self.settings, - source_path=os.path.join('dir', 'foo.jpg'), - context=self.settings.copy()) + source_path=os.path.join("dir", "foo.jpg"), + context=self.settings.copy(), + ) - expected_save_as = posixize_path(os.path.join('dir', 'foo.jpg')) - self.assertEqual(static.status, 'draft') + expected_save_as = posixize_path(os.path.join("dir", "foo.jpg")) + self.assertEqual(static.status, "draft") self.assertEqual(static.save_as, expected_save_as) self.assertEqual(static.url, path_to_url(expected_save_as)) diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 05c37269..52adb2c9 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -4,293 +4,383 @@ from shutil import copy, rmtree from tempfile import mkdtemp from unittest.mock import MagicMock -from pelican.generators import (ArticlesGenerator, Generator, PagesGenerator, - PelicanTemplateNotFound, StaticGenerator, - TemplatePagesGenerator) -from pelican.tests.support import (can_symlink, get_context, get_settings, - unittest, TestCaseWithCLocale) +from pelican.generators import ( + ArticlesGenerator, + Generator, + PagesGenerator, + PelicanTemplateNotFound, + StaticGenerator, + TemplatePagesGenerator, +) +from pelican.tests.support import ( + can_symlink, + get_context, + get_settings, + unittest, + TestCaseWithCLocale, +) from pelican.writers import Writer CUR_DIR = os.path.dirname(__file__) -CONTENT_DIR = os.path.join(CUR_DIR, 'content') +CONTENT_DIR = os.path.join(CUR_DIR, "content") class TestGenerator(TestCaseWithCLocale): def setUp(self): super().setUp() self.settings = get_settings() - self.settings['READERS'] = {'asc': None} - self.generator = Generator(self.settings.copy(), self.settings, - CUR_DIR, self.settings['THEME'], None) + self.settings["READERS"] = {"asc": None} + self.generator = Generator( + self.settings.copy(), self.settings, CUR_DIR, self.settings["THEME"], None + ) def test_include_path(self): - self.settings['IGNORE_FILES'] = {'ignored1.rst', 'ignored2.rst'} + self.settings["IGNORE_FILES"] = {"ignored1.rst", "ignored2.rst"} - filename = os.path.join(CUR_DIR, 'content', 'article.rst') + filename = os.path.join(CUR_DIR, "content", "article.rst") include_path = self.generator._include_path self.assertTrue(include_path(filename)) - self.assertTrue(include_path(filename, extensions=('rst',))) - self.assertFalse(include_path(filename, extensions=('md',))) + self.assertTrue(include_path(filename, extensions=("rst",))) + self.assertFalse(include_path(filename, extensions=("md",))) - ignored_file = os.path.join(CUR_DIR, 'content', 'ignored1.rst') + ignored_file = os.path.join(CUR_DIR, "content", "ignored1.rst") self.assertFalse(include_path(ignored_file)) def test_get_files_exclude(self): - """Test that Generator.get_files() properly excludes directories. - """ + """Test that Generator.get_files() properly excludes directories.""" # We use our own Generator so we can give it our own content path generator = Generator( context=self.settings.copy(), settings=self.settings, - path=os.path.join(CUR_DIR, 'nested_content'), - theme=self.settings['THEME'], output_path=None) + path=os.path.join(CUR_DIR, "nested_content"), + theme=self.settings["THEME"], + output_path=None, + ) - filepaths = generator.get_files(paths=['maindir']) + filepaths = generator.get_files(paths=["maindir"]) found_files = {os.path.basename(f) for f in filepaths} - expected_files = {'maindir.md', 'subdir.md'} + expected_files = {"maindir.md", "subdir.md"} self.assertFalse( - expected_files - found_files, - "get_files() failed to find one or more files") + expected_files - found_files, "get_files() failed to find one or more files" + ) # Test string as `paths` argument rather than list - filepaths = generator.get_files(paths='maindir') + filepaths = generator.get_files(paths="maindir") found_files = {os.path.basename(f) for f in filepaths} - expected_files = {'maindir.md', 'subdir.md'} + expected_files = {"maindir.md", "subdir.md"} self.assertFalse( - expected_files - found_files, - "get_files() failed to find one or more files") + expected_files - found_files, "get_files() failed to find one or more files" + ) - filepaths = generator.get_files(paths=[''], exclude=['maindir']) + filepaths = generator.get_files(paths=[""], exclude=["maindir"]) found_files = {os.path.basename(f) for f in filepaths} self.assertNotIn( - 'maindir.md', found_files, - "get_files() failed to exclude a top-level directory") + "maindir.md", + found_files, + "get_files() failed to exclude a top-level directory", + ) self.assertNotIn( - 'subdir.md', found_files, - "get_files() failed to exclude a subdir of an excluded directory") + "subdir.md", + found_files, + "get_files() failed to exclude a subdir of an excluded directory", + ) filepaths = generator.get_files( - paths=[''], - exclude=[os.path.join('maindir', 'subdir')]) + paths=[""], exclude=[os.path.join("maindir", "subdir")] + ) found_files = {os.path.basename(f) for f in filepaths} self.assertNotIn( - 'subdir.md', found_files, - "get_files() failed to exclude a subdirectory") + "subdir.md", found_files, "get_files() failed to exclude a subdirectory" + ) - filepaths = generator.get_files(paths=[''], exclude=['subdir']) + filepaths = generator.get_files(paths=[""], exclude=["subdir"]) found_files = {os.path.basename(f) for f in filepaths} self.assertIn( - 'subdir.md', found_files, - "get_files() excluded a subdirectory by name, ignoring its path") + "subdir.md", + found_files, + "get_files() excluded a subdirectory by name, ignoring its path", + ) def test_custom_jinja_environment(self): """ - Test that setting the JINJA_ENVIRONMENT - properly gets set from the settings config + Test that setting the JINJA_ENVIRONMENT + properly gets set from the settings config """ settings = get_settings() - comment_start_string = 'abc' - comment_end_string = '/abc' - settings['JINJA_ENVIRONMENT'] = { - 'comment_start_string': comment_start_string, - 'comment_end_string': comment_end_string + comment_start_string = "abc" + comment_end_string = "/abc" + settings["JINJA_ENVIRONMENT"] = { + "comment_start_string": comment_start_string, + "comment_end_string": comment_end_string, } - generator = Generator(settings.copy(), settings, - CUR_DIR, settings['THEME'], None) - self.assertEqual(comment_start_string, - generator.env.comment_start_string) - self.assertEqual(comment_end_string, - generator.env.comment_end_string) + generator = Generator( + settings.copy(), settings, CUR_DIR, settings["THEME"], None + ) + self.assertEqual(comment_start_string, generator.env.comment_start_string) + self.assertEqual(comment_end_string, generator.env.comment_end_string) def test_theme_overrides(self): """ - Test that the THEME_TEMPLATES_OVERRIDES configuration setting is - utilized correctly in the Generator. + Test that the THEME_TEMPLATES_OVERRIDES configuration setting is + utilized correctly in the Generator. """ - override_dirs = (os.path.join(CUR_DIR, 'theme_overrides', 'level1'), - os.path.join(CUR_DIR, 'theme_overrides', 'level2')) - self.settings['THEME_TEMPLATES_OVERRIDES'] = override_dirs + override_dirs = ( + os.path.join(CUR_DIR, "theme_overrides", "level1"), + os.path.join(CUR_DIR, "theme_overrides", "level2"), + ) + self.settings["THEME_TEMPLATES_OVERRIDES"] = override_dirs generator = Generator( context=self.settings.copy(), settings=self.settings, path=CUR_DIR, - theme=self.settings['THEME'], - output_path=None) + theme=self.settings["THEME"], + output_path=None, + ) - filename = generator.get_template('article').filename + filename = generator.get_template("article").filename self.assertEqual(override_dirs[0], os.path.dirname(filename)) - self.assertEqual('article.html', os.path.basename(filename)) + self.assertEqual("article.html", os.path.basename(filename)) - filename = generator.get_template('authors').filename + filename = generator.get_template("authors").filename self.assertEqual(override_dirs[1], os.path.dirname(filename)) - self.assertEqual('authors.html', os.path.basename(filename)) + self.assertEqual("authors.html", os.path.basename(filename)) - filename = generator.get_template('taglist').filename - self.assertEqual(os.path.join(self.settings['THEME'], 'templates'), - os.path.dirname(filename)) + filename = generator.get_template("taglist").filename + self.assertEqual( + os.path.join(self.settings["THEME"], "templates"), os.path.dirname(filename) + ) self.assertNotIn(os.path.dirname(filename), override_dirs) - self.assertEqual('taglist.html', os.path.basename(filename)) + self.assertEqual("taglist.html", os.path.basename(filename)) def test_simple_prefix(self): """ - Test `!simple` theme prefix. + Test `!simple` theme prefix. """ - filename = self.generator.get_template('!simple/authors').filename + filename = self.generator.get_template("!simple/authors").filename expected_path = os.path.join( - os.path.dirname(CUR_DIR), 'themes', 'simple', 'templates') + os.path.dirname(CUR_DIR), "themes", "simple", "templates" + ) self.assertEqual(expected_path, os.path.dirname(filename)) - self.assertEqual('authors.html', os.path.basename(filename)) + self.assertEqual("authors.html", os.path.basename(filename)) def test_theme_prefix(self): """ - Test `!theme` theme prefix. + Test `!theme` theme prefix. """ - filename = self.generator.get_template('!theme/authors').filename + filename = self.generator.get_template("!theme/authors").filename expected_path = os.path.join( - os.path.dirname(CUR_DIR), 'themes', 'notmyidea', 'templates') + os.path.dirname(CUR_DIR), "themes", "notmyidea", "templates" + ) self.assertEqual(expected_path, os.path.dirname(filename)) - self.assertEqual('authors.html', os.path.basename(filename)) + self.assertEqual("authors.html", os.path.basename(filename)) def test_bad_prefix(self): """ - Test unknown/bad theme prefix throws exception. + Test unknown/bad theme prefix throws exception. """ - self.assertRaises(PelicanTemplateNotFound, self.generator.get_template, - '!UNKNOWN/authors') + self.assertRaises( + PelicanTemplateNotFound, self.generator.get_template, "!UNKNOWN/authors" + ) class TestArticlesGenerator(unittest.TestCase): - @classmethod def setUpClass(cls): settings = get_settings() - settings['DEFAULT_CATEGORY'] = 'Default' - settings['DEFAULT_DATE'] = (1970, 1, 1) - settings['READERS'] = {'asc': None} - settings['CACHE_CONTENT'] = False + settings["DEFAULT_CATEGORY"] = "Default" + settings["DEFAULT_DATE"] = (1970, 1, 1) + settings["READERS"] = {"asc": None} + settings["CACHE_CONTENT"] = False context = get_context(settings) cls.generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) cls.generator.generate_context() cls.articles = cls.distill_articles(cls.generator.articles) cls.drafts = cls.distill_articles(cls.generator.drafts) cls.hidden_articles = cls.distill_articles(cls.generator.hidden_articles) def setUp(self): - self.temp_cache = mkdtemp(prefix='pelican_cache.') + self.temp_cache = mkdtemp(prefix="pelican_cache.") def tearDown(self): rmtree(self.temp_cache) @staticmethod def distill_articles(articles): - return [[article.title, article.status, article.category.name, - article.template] for article in articles] + return [ + [article.title, article.status, article.category.name, article.template] + for article in articles + ] def test_generate_feeds(self): settings = get_settings() - settings['CACHE_PATH'] = self.temp_cache + settings["CACHE_PATH"] = self.temp_cache generator = ArticlesGenerator( - context=settings, settings=settings, - path=None, theme=settings['THEME'], output_path=None) + context=settings, + settings=settings, + path=None, + theme=settings["THEME"], + output_path=None, + ) writer = MagicMock() generator.generate_feeds(writer) - writer.write_feed.assert_called_with([], settings, - 'feeds/all.atom.xml', - 'feeds/all.atom.xml') + writer.write_feed.assert_called_with( + [], settings, "feeds/all.atom.xml", "feeds/all.atom.xml" + ) generator = ArticlesGenerator( - context=settings, settings=get_settings(FEED_ALL_ATOM=None), - path=None, theme=settings['THEME'], output_path=None) + context=settings, + settings=get_settings(FEED_ALL_ATOM=None), + path=None, + theme=settings["THEME"], + output_path=None, + ) writer = MagicMock() generator.generate_feeds(writer) self.assertFalse(writer.write_feed.called) def test_generate_feeds_override_url(self): settings = get_settings() - settings['CACHE_PATH'] = self.temp_cache - settings['FEED_ALL_ATOM_URL'] = 'feeds/atom/all/' + settings["CACHE_PATH"] = self.temp_cache + settings["FEED_ALL_ATOM_URL"] = "feeds/atom/all/" generator = ArticlesGenerator( - context=settings, settings=settings, - path=None, theme=settings['THEME'], output_path=None) + context=settings, + settings=settings, + path=None, + theme=settings["THEME"], + output_path=None, + ) writer = MagicMock() generator.generate_feeds(writer) - writer.write_feed.assert_called_with([], settings, - 'feeds/all.atom.xml', - 'feeds/atom/all/') + writer.write_feed.assert_called_with( + [], settings, "feeds/all.atom.xml", "feeds/atom/all/" + ) def test_generate_context(self): articles_expected = [ - ['Article title', 'published', 'Default', 'article'], - ['Article with markdown and summary metadata multi', 'published', - 'Default', 'article'], - ['Article with markdown and nested summary metadata', 'published', - 'Default', 'article'], - ['Article with markdown and summary metadata single', 'published', - 'Default', 'article'], - ['Article with markdown containing footnotes', 'published', - 'Default', 'article'], - ['Article with template', 'published', 'Default', 'custom'], - ['Metadata tags as list!', 'published', 'Default', 'article'], - ['Rst with filename metadata', 'published', 'yeah', 'article'], - ['One -, two --, three --- dashes!', 'published', 'Default', - 'article'], - ['One -, two --, three --- dashes!', 'published', 'Default', - 'article'], - ['Test Markdown extensions', 'published', 'Default', 'article'], - ['Test markdown File', 'published', 'test', 'article'], - ['Test md File', 'published', 'test', 'article'], - ['Test mdown File', 'published', 'test', 'article'], - ['Test metadata duplicates', 'published', 'test', 'article'], - ['Test mkd File', 'published', 'test', 'article'], - ['This is a super article !', 'published', 'Yeah', 'article'], - ['This is a super article !', 'published', 'Yeah', 'article'], - ['Article with Nonconformant HTML meta tags', 'published', - 'Default', 'article'], - ['This is a super article !', 'published', 'yeah', 'article'], - ['This is a super article !', 'published', 'yeah', 'article'], - ['This is a super article !', 'published', 'yeah', 'article'], - ['This is a super article !', 'published', 'yeah', 'article'], - ['This is a super article !', 'published', 'yeah', 'article'], - ['This is a super article !', 'published', 'yeah', 'article'], - ['This is a super article !', 'published', 'yeah', 'article'], - ['This is a super article !', 'published', 'yeah', 'article'], - ['This is a super article !', 'published', 'Default', 'article'], - ['Article with an inline SVG', 'published', 'Default', 'article'], - ['Article with markdown and empty tags', 'published', 'Default', - 'article'], - ['This is an article with category !', 'published', 'yeah', - 'article'], - ['This is an article with multiple authors!', 'published', - 'Default', 'article'], - ['This is an article with multiple authors!', 'published', - 'Default', 'article'], - ['This is an article with multiple authors in list format!', - 'published', 'Default', 'article'], - ['This is an article with multiple authors in lastname, ' - 'firstname format!', 'published', 'Default', 'article'], - ['This is an article without category !', 'published', 'Default', - 'article'], - ['This is an article without category !', 'published', - 'TestCategory', 'article'], - ['An Article With Code Block To Test Typogrify Ignore', - 'published', 'Default', 'article'], - ['マックOS X 10.8でパイソンとVirtualenvをインストールと設定', - 'published', '指導書', 'article'], + ["Article title", "published", "Default", "article"], + [ + "Article with markdown and summary metadata multi", + "published", + "Default", + "article", + ], + [ + "Article with markdown and nested summary metadata", + "published", + "Default", + "article", + ], + [ + "Article with markdown and summary metadata single", + "published", + "Default", + "article", + ], + [ + "Article with markdown containing footnotes", + "published", + "Default", + "article", + ], + ["Article with template", "published", "Default", "custom"], + ["Metadata tags as list!", "published", "Default", "article"], + ["Rst with filename metadata", "published", "yeah", "article"], + ["One -, two --, three --- dashes!", "published", "Default", "article"], + ["One -, two --, three --- dashes!", "published", "Default", "article"], + ["Test Markdown extensions", "published", "Default", "article"], + ["Test markdown File", "published", "test", "article"], + ["Test md File", "published", "test", "article"], + ["Test mdown File", "published", "test", "article"], + ["Test metadata duplicates", "published", "test", "article"], + ["Test mkd File", "published", "test", "article"], + ["This is a super article !", "published", "Yeah", "article"], + ["This is a super article !", "published", "Yeah", "article"], + [ + "Article with Nonconformant HTML meta tags", + "published", + "Default", + "article", + ], + ["This is a super article !", "published", "yeah", "article"], + ["This is a super article !", "published", "yeah", "article"], + ["This is a super article !", "published", "yeah", "article"], + ["This is a super article !", "published", "yeah", "article"], + ["This is a super article !", "published", "yeah", "article"], + ["This is a super article !", "published", "yeah", "article"], + ["This is a super article !", "published", "yeah", "article"], + ["This is a super article !", "published", "yeah", "article"], + ["This is a super article !", "published", "Default", "article"], + ["Article with an inline SVG", "published", "Default", "article"], + ["Article with markdown and empty tags", "published", "Default", "article"], + ["This is an article with category !", "published", "yeah", "article"], + [ + "This is an article with multiple authors!", + "published", + "Default", + "article", + ], + [ + "This is an article with multiple authors!", + "published", + "Default", + "article", + ], + [ + "This is an article with multiple authors in list format!", + "published", + "Default", + "article", + ], + [ + "This is an article with multiple authors in lastname, " + "firstname format!", + "published", + "Default", + "article", + ], + [ + "This is an article without category !", + "published", + "Default", + "article", + ], + [ + "This is an article without category !", + "published", + "TestCategory", + "article", + ], + [ + "An Article With Code Block To Test Typogrify Ignore", + "published", + "Default", + "article", + ], + [ + "マックOS X 10.8でパイソンとVirtualenvをインストールと設定", + "published", + "指導書", + "article", + ], ] self.assertEqual(sorted(articles_expected), sorted(self.articles)) def test_articles_draft(self): draft_articles_expected = [ - ['Draft article', 'draft', 'Default', 'article'], + ["Draft article", "draft", "Default", "article"], ] self.assertEqual(sorted(draft_articles_expected), sorted(self.drafts)) def test_articles_hidden(self): hidden_articles_expected = [ - ['Hidden article', 'hidden', 'Default', 'article'], + ["Hidden article", "hidden", "Default", "article"], ] self.assertEqual(sorted(hidden_articles_expected), sorted(self.hidden_articles)) @@ -301,27 +391,30 @@ class TestArticlesGenerator(unittest.TestCase): # terms of process order will define the name for that category categories = [cat.name for cat, _ in self.generator.categories] categories_alternatives = ( - sorted(['Default', 'TestCategory', 'Yeah', 'test', '指導書']), - sorted(['Default', 'TestCategory', 'yeah', 'test', '指導書']), + sorted(["Default", "TestCategory", "Yeah", "test", "指導書"]), + sorted(["Default", "TestCategory", "yeah", "test", "指導書"]), ) self.assertIn(sorted(categories), categories_alternatives) # test for slug categories = [cat.slug for cat, _ in self.generator.categories] - categories_expected = ['default', 'testcategory', 'yeah', 'test', - 'zhi-dao-shu'] + categories_expected = ["default", "testcategory", "yeah", "test", "zhi-dao-shu"] self.assertEqual(sorted(categories), sorted(categories_expected)) def test_do_not_use_folder_as_category(self): settings = get_settings() - settings['DEFAULT_CATEGORY'] = 'Default' - settings['DEFAULT_DATE'] = (1970, 1, 1) - settings['USE_FOLDER_AS_CATEGORY'] = False - settings['CACHE_PATH'] = self.temp_cache - settings['READERS'] = {'asc': None} + settings["DEFAULT_CATEGORY"] = "Default" + settings["DEFAULT_DATE"] = (1970, 1, 1) + settings["USE_FOLDER_AS_CATEGORY"] = False + settings["CACHE_PATH"] = self.temp_cache + settings["READERS"] = {"asc": None} context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() # test for name # categories are grouped by slug; if two categories have the same slug @@ -329,61 +422,79 @@ class TestArticlesGenerator(unittest.TestCase): # terms of process order will define the name for that category categories = [cat.name for cat, _ in generator.categories] categories_alternatives = ( - sorted(['Default', 'Yeah', 'test', '指導書']), - sorted(['Default', 'yeah', 'test', '指導書']), + sorted(["Default", "Yeah", "test", "指導書"]), + sorted(["Default", "yeah", "test", "指導書"]), ) self.assertIn(sorted(categories), categories_alternatives) # test for slug categories = [cat.slug for cat, _ in generator.categories] - categories_expected = ['default', 'yeah', 'test', 'zhi-dao-shu'] + categories_expected = ["default", "yeah", "test", "zhi-dao-shu"] self.assertEqual(sorted(categories), sorted(categories_expected)) def test_direct_templates_save_as_url_default(self): - settings = get_settings() - settings['CACHE_PATH'] = self.temp_cache + settings["CACHE_PATH"] = self.temp_cache context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=None, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=None, + theme=settings["THEME"], + output_path=None, + ) write = MagicMock() generator.generate_direct_templates(write) - write.assert_called_with("archives.html", - generator.get_template("archives"), context, - articles=generator.articles, - dates=generator.dates, blog=True, - template_name='archives', - page_name='archives', url="archives.html") + write.assert_called_with( + "archives.html", + generator.get_template("archives"), + context, + articles=generator.articles, + dates=generator.dates, + blog=True, + template_name="archives", + page_name="archives", + url="archives.html", + ) def test_direct_templates_save_as_url_modified(self): - settings = get_settings() - settings['DIRECT_TEMPLATES'] = ['archives'] - settings['ARCHIVES_SAVE_AS'] = 'archives/index.html' - settings['ARCHIVES_URL'] = 'archives/' - settings['CACHE_PATH'] = self.temp_cache + settings["DIRECT_TEMPLATES"] = ["archives"] + settings["ARCHIVES_SAVE_AS"] = "archives/index.html" + settings["ARCHIVES_URL"] = "archives/" + settings["CACHE_PATH"] = self.temp_cache generator = ArticlesGenerator( - context=settings, settings=settings, - path=None, theme=settings['THEME'], output_path=None) + context=settings, + settings=settings, + path=None, + theme=settings["THEME"], + output_path=None, + ) write = MagicMock() generator.generate_direct_templates(write) - write.assert_called_with("archives/index.html", - generator.get_template("archives"), settings, - articles=generator.articles, - dates=generator.dates, blog=True, - template_name='archives', - page_name='archives/index', - url="archives/") + write.assert_called_with( + "archives/index.html", + generator.get_template("archives"), + settings, + articles=generator.articles, + dates=generator.dates, + blog=True, + template_name="archives", + page_name="archives/index", + url="archives/", + ) def test_direct_templates_save_as_false(self): - settings = get_settings() - settings['DIRECT_TEMPLATES'] = ['archives'] - settings['ARCHIVES_SAVE_AS'] = False - settings['CACHE_PATH'] = self.temp_cache + settings["DIRECT_TEMPLATES"] = ["archives"] + settings["ARCHIVES_SAVE_AS"] = False + settings["CACHE_PATH"] = self.temp_cache generator = ArticlesGenerator( - context=settings, settings=settings, - path=None, theme=settings['THEME'], output_path=None) + context=settings, + settings=settings, + path=None, + theme=settings["THEME"], + output_path=None, + ) write = MagicMock() generator.generate_direct_templates(write) self.assertEqual(write.call_count, 0) @@ -392,10 +503,13 @@ class TestArticlesGenerator(unittest.TestCase): """ Custom template articles get the field but standard/unset are None """ - custom_template = ['Article with template', 'published', 'Default', - 'custom'] - standard_template = ['This is a super article !', 'published', 'Yeah', - 'article'] + custom_template = ["Article with template", "published", "Default", "custom"] + standard_template = [ + "This is a super article !", + "published", + "Yeah", + "article", + ] self.assertIn(custom_template, self.articles) self.assertIn(standard_template, self.articles) @@ -403,126 +517,135 @@ class TestArticlesGenerator(unittest.TestCase): """Test correctness of the period_archives context values.""" settings = get_settings() - settings['CACHE_PATH'] = self.temp_cache + settings["CACHE_PATH"] = self.temp_cache # No period archives enabled: context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() - period_archives = generator.context['period_archives'] + period_archives = generator.context["period_archives"] self.assertEqual(len(period_archives.items()), 0) # Year archives enabled: - settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html' - settings['YEAR_ARCHIVE_URL'] = 'posts/{date:%Y}/' + settings["YEAR_ARCHIVE_SAVE_AS"] = "posts/{date:%Y}/index.html" + settings["YEAR_ARCHIVE_URL"] = "posts/{date:%Y}/" context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() - period_archives = generator.context['period_archives'] + period_archives = generator.context["period_archives"] abbreviated_archives = { - granularity: {period['period'] for period in periods} + granularity: {period["period"] for period in periods} for granularity, periods in period_archives.items() } - expected = {'year': {(1970,), (2010,), (2012,), (2014,)}} + expected = {"year": {(1970,), (2010,), (2012,), (2014,)}} self.assertEqual(expected, abbreviated_archives) # Month archives enabled: - settings['MONTH_ARCHIVE_SAVE_AS'] = \ - 'posts/{date:%Y}/{date:%b}/index.html' - settings['MONTH_ARCHIVE_URL'] = \ - 'posts/{date:%Y}/{date:%b}/' + settings["MONTH_ARCHIVE_SAVE_AS"] = "posts/{date:%Y}/{date:%b}/index.html" + settings["MONTH_ARCHIVE_URL"] = "posts/{date:%Y}/{date:%b}/" context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() - period_archives = generator.context['period_archives'] + period_archives = generator.context["period_archives"] abbreviated_archives = { - granularity: {period['period'] for period in periods} + granularity: {period["period"] for period in periods} for granularity, periods in period_archives.items() } expected = { - 'year': {(1970,), (2010,), (2012,), (2014,)}, - 'month': { - (1970, 'January'), - (2010, 'December'), - (2012, 'December'), - (2012, 'November'), - (2012, 'October'), - (2014, 'February'), + "year": {(1970,), (2010,), (2012,), (2014,)}, + "month": { + (1970, "January"), + (2010, "December"), + (2012, "December"), + (2012, "November"), + (2012, "October"), + (2014, "February"), }, } self.assertEqual(expected, abbreviated_archives) # Day archives enabled: - settings['DAY_ARCHIVE_SAVE_AS'] = \ - 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html' - settings['DAY_ARCHIVE_URL'] = \ - 'posts/{date:%Y}/{date:%b}/{date:%d}/' + settings[ + "DAY_ARCHIVE_SAVE_AS" + ] = "posts/{date:%Y}/{date:%b}/{date:%d}/index.html" + settings["DAY_ARCHIVE_URL"] = "posts/{date:%Y}/{date:%b}/{date:%d}/" context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() - period_archives = generator.context['period_archives'] + period_archives = generator.context["period_archives"] abbreviated_archives = { - granularity: {period['period'] for period in periods} + granularity: {period["period"] for period in periods} for granularity, periods in period_archives.items() } expected = { - 'year': {(1970,), (2010,), (2012,), (2014,)}, - 'month': { - (1970, 'January'), - (2010, 'December'), - (2012, 'December'), - (2012, 'November'), - (2012, 'October'), - (2014, 'February'), + "year": {(1970,), (2010,), (2012,), (2014,)}, + "month": { + (1970, "January"), + (2010, "December"), + (2012, "December"), + (2012, "November"), + (2012, "October"), + (2014, "February"), }, - 'day': { - (1970, 'January', 1), - (2010, 'December', 2), - (2012, 'December', 20), - (2012, 'November', 29), - (2012, 'October', 30), - (2012, 'October', 31), - (2014, 'February', 9), + "day": { + (1970, "January", 1), + (2010, "December", 2), + (2012, "December", 20), + (2012, "November", 29), + (2012, "October", 30), + (2012, "October", 31), + (2014, "February", 9), }, } self.assertEqual(expected, abbreviated_archives) # Further item values tests filtered_archives = [ - p for p in period_archives['day'] - if p['period'] == (2014, 'February', 9) + p for p in period_archives["day"] if p["period"] == (2014, "February", 9) ] self.assertEqual(len(filtered_archives), 1) sample_archive = filtered_archives[0] - self.assertEqual(sample_archive['period_num'], (2014, 2, 9)) - self.assertEqual( - sample_archive['save_as'], 'posts/2014/Feb/09/index.html') - self.assertEqual( - sample_archive['url'], 'posts/2014/Feb/09/') + self.assertEqual(sample_archive["period_num"], (2014, 2, 9)) + self.assertEqual(sample_archive["save_as"], "posts/2014/Feb/09/index.html") + self.assertEqual(sample_archive["url"], "posts/2014/Feb/09/") articles = [ - d for d in generator.articles if - d.date.year == 2014 and - d.date.month == 2 and - d.date.day == 9 + d + for d in generator.articles + if d.date.year == 2014 and d.date.month == 2 and d.date.day == 9 ] - self.assertEqual(len(sample_archive['articles']), len(articles)) + self.assertEqual(len(sample_archive["articles"]), len(articles)) dates = [ - d for d in generator.dates if - d.date.year == 2014 and - d.date.month == 2 and - d.date.day == 9 + d + for d in generator.dates + if d.date.year == 2014 and d.date.month == 2 and d.date.day == 9 ] - self.assertEqual(len(sample_archive['dates']), len(dates)) - self.assertEqual(sample_archive['dates'][0].title, dates[0].title) - self.assertEqual(sample_archive['dates'][0].date, dates[0].date) + self.assertEqual(len(sample_archive["dates"]), len(dates)) + self.assertEqual(sample_archive["dates"][0].title, dates[0].title) + self.assertEqual(sample_archive["dates"][0].date, dates[0].date) def test_period_in_timeperiod_archive(self): """ @@ -531,13 +654,17 @@ class TestArticlesGenerator(unittest.TestCase): """ settings = get_settings() - settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html' - settings['YEAR_ARCHIVE_URL'] = 'posts/{date:%Y}/' - settings['CACHE_PATH'] = self.temp_cache + settings["YEAR_ARCHIVE_SAVE_AS"] = "posts/{date:%Y}/index.html" + settings["YEAR_ARCHIVE_URL"] = "posts/{date:%Y}/" + settings["CACHE_PATH"] = self.temp_cache context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() write = MagicMock() generator.generate_period_archives(write) @@ -547,196 +674,257 @@ class TestArticlesGenerator(unittest.TestCase): # among other things it must have at least been called with this context["period"] = (1970,) context["period_num"] = (1970,) - write.assert_called_with("posts/1970/index.html", - generator.get_template("period_archives"), - context, blog=True, articles=articles, - dates=dates, template_name='period_archives', - url="posts/1970/", - all_articles=generator.articles) + write.assert_called_with( + "posts/1970/index.html", + generator.get_template("period_archives"), + context, + blog=True, + articles=articles, + dates=dates, + template_name="period_archives", + url="posts/1970/", + all_articles=generator.articles, + ) - settings['MONTH_ARCHIVE_SAVE_AS'] = \ - 'posts/{date:%Y}/{date:%b}/index.html' - settings['MONTH_ARCHIVE_URL'] = \ - 'posts/{date:%Y}/{date:%b}/' + settings["MONTH_ARCHIVE_SAVE_AS"] = "posts/{date:%Y}/{date:%b}/index.html" + settings["MONTH_ARCHIVE_URL"] = "posts/{date:%Y}/{date:%b}/" context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) - generator.generate_context() - write = MagicMock() - generator.generate_period_archives(write) - dates = [d for d in generator.dates - if d.date.year == 1970 and d.date.month == 1] - articles = [d for d in generator.articles - if d.date.year == 1970 and d.date.month == 1] - self.assertEqual(len(dates), 1) - context["period"] = (1970, "January") - context["period_num"] = (1970, 1) - # among other things it must have at least been called with this - write.assert_called_with("posts/1970/Jan/index.html", - generator.get_template("period_archives"), - context, blog=True, articles=articles, - dates=dates, template_name='period_archives', - url="posts/1970/Jan/", - all_articles=generator.articles) - - settings['DAY_ARCHIVE_SAVE_AS'] = \ - 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html' - settings['DAY_ARCHIVE_URL'] = \ - 'posts/{date:%Y}/{date:%b}/{date:%d}/' - context = get_context(settings) - generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() write = MagicMock() generator.generate_period_archives(write) dates = [ - d for d in generator.dates if - d.date.year == 1970 and - d.date.month == 1 and - d.date.day == 1 + d for d in generator.dates if d.date.year == 1970 and d.date.month == 1 ] articles = [ - d for d in generator.articles if - d.date.year == 1970 and - d.date.month == 1 and - d.date.day == 1 + d for d in generator.articles if d.date.year == 1970 and d.date.month == 1 + ] + self.assertEqual(len(dates), 1) + context["period"] = (1970, "January") + context["period_num"] = (1970, 1) + # among other things it must have at least been called with this + write.assert_called_with( + "posts/1970/Jan/index.html", + generator.get_template("period_archives"), + context, + blog=True, + articles=articles, + dates=dates, + template_name="period_archives", + url="posts/1970/Jan/", + all_articles=generator.articles, + ) + + settings[ + "DAY_ARCHIVE_SAVE_AS" + ] = "posts/{date:%Y}/{date:%b}/{date:%d}/index.html" + settings["DAY_ARCHIVE_URL"] = "posts/{date:%Y}/{date:%b}/{date:%d}/" + context = get_context(settings) + generator = ArticlesGenerator( + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) + generator.generate_context() + write = MagicMock() + generator.generate_period_archives(write) + dates = [ + d + for d in generator.dates + if d.date.year == 1970 and d.date.month == 1 and d.date.day == 1 + ] + articles = [ + d + for d in generator.articles + if d.date.year == 1970 and d.date.month == 1 and d.date.day == 1 ] self.assertEqual(len(dates), 1) context["period"] = (1970, "January", 1) context["period_num"] = (1970, 1, 1) # among other things it must have at least been called with this - write.assert_called_with("posts/1970/Jan/01/index.html", - generator.get_template("period_archives"), - context, blog=True, articles=articles, - dates=dates, template_name='period_archives', - url="posts/1970/Jan/01/", - all_articles=generator.articles) + write.assert_called_with( + "posts/1970/Jan/01/index.html", + generator.get_template("period_archives"), + context, + blog=True, + articles=articles, + dates=dates, + template_name="period_archives", + url="posts/1970/Jan/01/", + all_articles=generator.articles, + ) def test_nonexistent_template(self): """Attempt to load a non-existent template""" settings = get_settings() context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=None, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=None, + theme=settings["THEME"], + output_path=None, + ) self.assertRaises(Exception, generator.get_template, "not_a_template") def test_generate_authors(self): """Check authors generation.""" authors = [author.name for author, _ in self.generator.authors] authors_expected = sorted( - ['Alexis Métaireau', 'Author, First', 'Author, Second', - 'First Author', 'Second Author']) + [ + "Alexis Métaireau", + "Author, First", + "Author, Second", + "First Author", + "Second Author", + ] + ) self.assertEqual(sorted(authors), authors_expected) # test for slug authors = [author.slug for author, _ in self.generator.authors] - authors_expected = ['alexis-metaireau', 'author-first', - 'author-second', 'first-author', 'second-author'] + authors_expected = [ + "alexis-metaireau", + "author-first", + "author-second", + "first-author", + "second-author", + ] self.assertEqual(sorted(authors), sorted(authors_expected)) def test_standard_metadata_in_default_metadata(self): settings = get_settings() - settings['CACHE_CONTENT'] = False - settings['DEFAULT_CATEGORY'] = 'Default' - settings['DEFAULT_DATE'] = (1970, 1, 1) - settings['DEFAULT_METADATA'] = (('author', 'Blogger'), - # category will be ignored in favor of - # DEFAULT_CATEGORY - ('category', 'Random'), - ('tags', 'general, untagged')) + settings["CACHE_CONTENT"] = False + settings["DEFAULT_CATEGORY"] = "Default" + settings["DEFAULT_DATE"] = (1970, 1, 1) + settings["DEFAULT_METADATA"] = ( + ("author", "Blogger"), + # category will be ignored in favor of + # DEFAULT_CATEGORY + ("category", "Random"), + ("tags", "general, untagged"), + ) context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() authors = sorted([author.name for author, _ in generator.authors]) - authors_expected = sorted(['Alexis Métaireau', 'Blogger', - 'Author, First', 'Author, Second', - 'First Author', 'Second Author']) + authors_expected = sorted( + [ + "Alexis Métaireau", + "Blogger", + "Author, First", + "Author, Second", + "First Author", + "Second Author", + ] + ) self.assertEqual(authors, authors_expected) - categories = sorted([category.name - for category, _ in generator.categories]) + categories = sorted([category.name for category, _ in generator.categories]) categories_expected = [ - sorted(['Default', 'TestCategory', 'yeah', 'test', '指導書']), - sorted(['Default', 'TestCategory', 'Yeah', 'test', '指導書'])] + sorted(["Default", "TestCategory", "yeah", "test", "指導書"]), + sorted(["Default", "TestCategory", "Yeah", "test", "指導書"]), + ] self.assertIn(categories, categories_expected) tags = sorted([tag.name for tag in generator.tags]) - tags_expected = sorted(['bar', 'foo', 'foobar', 'general', 'untagged', - 'パイソン', 'マック']) + tags_expected = sorted( + ["bar", "foo", "foobar", "general", "untagged", "パイソン", "マック"] + ) self.assertEqual(tags, tags_expected) def test_article_order_by(self): settings = get_settings() - settings['DEFAULT_CATEGORY'] = 'Default' - settings['DEFAULT_DATE'] = (1970, 1, 1) - settings['ARTICLE_ORDER_BY'] = 'title' + settings["DEFAULT_CATEGORY"] = "Default" + settings["DEFAULT_DATE"] = (1970, 1, 1) + settings["ARTICLE_ORDER_BY"] = "title" context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() expected = [ - 'An Article With Code Block To Test Typogrify Ignore', - 'Article title', - 'Article with Nonconformant HTML meta tags', - 'Article with an inline SVG', - 'Article with markdown and empty tags', - 'Article with markdown and nested summary metadata', - 'Article with markdown and summary metadata multi', - 'Article with markdown and summary metadata single', - 'Article with markdown containing footnotes', - 'Article with template', - 'Metadata tags as list!', - 'One -, two --, three --- dashes!', - 'One -, two --, three --- dashes!', - 'Rst with filename metadata', - 'Test Markdown extensions', - 'Test markdown File', - 'Test md File', - 'Test mdown File', - 'Test metadata duplicates', - 'Test mkd File', - 'This is a super article !', - 'This is a super article !', - 'This is a super article !', - 'This is a super article !', - 'This is a super article !', - 'This is a super article !', - 'This is a super article !', - 'This is a super article !', - 'This is a super article !', - 'This is a super article !', - 'This is a super article !', - 'This is an article with category !', - ('This is an article with multiple authors in lastname, ' - 'firstname format!'), - 'This is an article with multiple authors in list format!', - 'This is an article with multiple authors!', - 'This is an article with multiple authors!', - 'This is an article without category !', - 'This is an article without category !', - 'マックOS X 10.8でパイソンとVirtualenvをインストールと設定'] + "An Article With Code Block To Test Typogrify Ignore", + "Article title", + "Article with Nonconformant HTML meta tags", + "Article with an inline SVG", + "Article with markdown and empty tags", + "Article with markdown and nested summary metadata", + "Article with markdown and summary metadata multi", + "Article with markdown and summary metadata single", + "Article with markdown containing footnotes", + "Article with template", + "Metadata tags as list!", + "One -, two --, three --- dashes!", + "One -, two --, three --- dashes!", + "Rst with filename metadata", + "Test Markdown extensions", + "Test markdown File", + "Test md File", + "Test mdown File", + "Test metadata duplicates", + "Test mkd File", + "This is a super article !", + "This is a super article !", + "This is a super article !", + "This is a super article !", + "This is a super article !", + "This is a super article !", + "This is a super article !", + "This is a super article !", + "This is a super article !", + "This is a super article !", + "This is a super article !", + "This is an article with category !", + ( + "This is an article with multiple authors in lastname, " + "firstname format!" + ), + "This is an article with multiple authors in list format!", + "This is an article with multiple authors!", + "This is an article with multiple authors!", + "This is an article without category !", + "This is an article without category !", + "マックOS X 10.8でパイソンとVirtualenvをインストールと設定", + ] articles = [article.title for article in generator.articles] self.assertEqual(articles, expected) # reversed title settings = get_settings() - settings['DEFAULT_CATEGORY'] = 'Default' - settings['DEFAULT_DATE'] = (1970, 1, 1) - settings['ARTICLE_ORDER_BY'] = 'reversed-title' + settings["DEFAULT_CATEGORY"] = "Default" + settings["DEFAULT_DATE"] = (1970, 1, 1) + settings["ARTICLE_ORDER_BY"] = "reversed-title" context = get_context(settings) generator = ArticlesGenerator( - context=context, settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CONTENT_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() articles = [article.title for article in generator.articles] @@ -750,7 +938,7 @@ class TestPageGenerator(unittest.TestCase): # to match expected def setUp(self): - self.temp_cache = mkdtemp(prefix='pelican_cache.') + self.temp_cache = mkdtemp(prefix="pelican_cache.") def tearDown(self): rmtree(self.temp_cache) @@ -760,112 +948,125 @@ class TestPageGenerator(unittest.TestCase): def test_generate_context(self): settings = get_settings() - settings['CACHE_PATH'] = self.temp_cache - settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR - settings['DEFAULT_DATE'] = (1970, 1, 1) + settings["CACHE_PATH"] = self.temp_cache + settings["PAGE_PATHS"] = ["TestPages"] # relative to CUR_DIR + settings["DEFAULT_DATE"] = (1970, 1, 1) context = get_context(settings) generator = PagesGenerator( - context=context, settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() pages = self.distill_pages(generator.pages) hidden_pages = self.distill_pages(generator.hidden_pages) draft_pages = self.distill_pages(generator.draft_pages) pages_expected = [ - ['This is a test page', 'published', 'page'], - ['This is a markdown test page', 'published', 'page'], - ['This is a test page with a preset template', 'published', - 'custom'], - ['Page with a bunch of links', 'published', 'page'], - ['Page with static links', 'published', 'page'], - ['A Page (Test) for sorting', 'published', 'page'], + ["This is a test page", "published", "page"], + ["This is a markdown test page", "published", "page"], + ["This is a test page with a preset template", "published", "custom"], + ["Page with a bunch of links", "published", "page"], + ["Page with static links", "published", "page"], + ["A Page (Test) for sorting", "published", "page"], ] hidden_pages_expected = [ - ['This is a test hidden page', 'hidden', 'page'], - ['This is a markdown test hidden page', 'hidden', 'page'], - ['This is a test hidden page with a custom template', 'hidden', - 'custom'], + ["This is a test hidden page", "hidden", "page"], + ["This is a markdown test hidden page", "hidden", "page"], + ["This is a test hidden page with a custom template", "hidden", "custom"], ] draft_pages_expected = [ - ['This is a test draft page', 'draft', 'page'], - ['This is a markdown test draft page', 'draft', 'page'], - ['This is a test draft page with a custom template', 'draft', - 'custom'], + ["This is a test draft page", "draft", "page"], + ["This is a markdown test draft page", "draft", "page"], + ["This is a test draft page with a custom template", "draft", "custom"], ] self.assertEqual(sorted(pages_expected), sorted(pages)) self.assertEqual( sorted(pages_expected), - sorted(self.distill_pages(generator.context['pages']))) + sorted(self.distill_pages(generator.context["pages"])), + ) self.assertEqual(sorted(hidden_pages_expected), sorted(hidden_pages)) self.assertEqual(sorted(draft_pages_expected), sorted(draft_pages)) self.assertEqual( sorted(hidden_pages_expected), - sorted(self.distill_pages(generator.context['hidden_pages']))) + sorted(self.distill_pages(generator.context["hidden_pages"])), + ) self.assertEqual( sorted(draft_pages_expected), - sorted(self.distill_pages(generator.context['draft_pages']))) + sorted(self.distill_pages(generator.context["draft_pages"])), + ) def test_generate_sorted(self): settings = get_settings() - settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR - settings['CACHE_PATH'] = self.temp_cache - settings['DEFAULT_DATE'] = (1970, 1, 1) + settings["PAGE_PATHS"] = ["TestPages"] # relative to CUR_DIR + settings["CACHE_PATH"] = self.temp_cache + settings["DEFAULT_DATE"] = (1970, 1, 1) context = get_context(settings) # default sort (filename) pages_expected_sorted_by_filename = [ - ['This is a test page', 'published', 'page'], - ['This is a markdown test page', 'published', 'page'], - ['A Page (Test) for sorting', 'published', 'page'], - ['Page with a bunch of links', 'published', 'page'], - ['Page with static links', 'published', 'page'], - ['This is a test page with a preset template', 'published', - 'custom'], + ["This is a test page", "published", "page"], + ["This is a markdown test page", "published", "page"], + ["A Page (Test) for sorting", "published", "page"], + ["Page with a bunch of links", "published", "page"], + ["Page with static links", "published", "page"], + ["This is a test page with a preset template", "published", "custom"], ] generator = PagesGenerator( - context=context, settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() pages = self.distill_pages(generator.pages) self.assertEqual(pages_expected_sorted_by_filename, pages) # sort by title pages_expected_sorted_by_title = [ - ['A Page (Test) for sorting', 'published', 'page'], - ['Page with a bunch of links', 'published', 'page'], - ['Page with static links', 'published', 'page'], - ['This is a markdown test page', 'published', 'page'], - ['This is a test page', 'published', 'page'], - ['This is a test page with a preset template', 'published', - 'custom'], + ["A Page (Test) for sorting", "published", "page"], + ["Page with a bunch of links", "published", "page"], + ["Page with static links", "published", "page"], + ["This is a markdown test page", "published", "page"], + ["This is a test page", "published", "page"], + ["This is a test page with a preset template", "published", "custom"], ] - settings['PAGE_ORDER_BY'] = 'title' + settings["PAGE_ORDER_BY"] = "title" context = get_context(settings) generator = PagesGenerator( - context=context.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context.copy(), + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() pages = self.distill_pages(generator.pages) self.assertEqual(pages_expected_sorted_by_title, pages) # sort by title reversed pages_expected_sorted_by_title = [ - ['This is a test page with a preset template', 'published', - 'custom'], - ['This is a test page', 'published', 'page'], - ['This is a markdown test page', 'published', 'page'], - ['Page with static links', 'published', 'page'], - ['Page with a bunch of links', 'published', 'page'], - ['A Page (Test) for sorting', 'published', 'page'], + ["This is a test page with a preset template", "published", "custom"], + ["This is a test page", "published", "page"], + ["This is a markdown test page", "published", "page"], + ["Page with static links", "published", "page"], + ["Page with a bunch of links", "published", "page"], + ["A Page (Test) for sorting", "published", "page"], ] - settings['PAGE_ORDER_BY'] = 'reversed-title' + settings["PAGE_ORDER_BY"] = "reversed-title" context = get_context(settings) generator = PagesGenerator( - context=context, settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() pages = self.distill_pages(generator.pages) self.assertEqual(pages_expected_sorted_by_title, pages) @@ -876,18 +1077,22 @@ class TestPageGenerator(unittest.TestCase): are generated correctly on pages """ settings = get_settings() - settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR - settings['CACHE_PATH'] = self.temp_cache - settings['DEFAULT_DATE'] = (1970, 1, 1) + settings["PAGE_PATHS"] = ["TestPages"] # relative to CUR_DIR + settings["CACHE_PATH"] = self.temp_cache + settings["DEFAULT_DATE"] = (1970, 1, 1) context = get_context(settings) generator = PagesGenerator( - context=context, settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() pages_by_title = {p.title: p for p in generator.pages} - test_content = pages_by_title['Page with a bunch of links'].content + test_content = pages_by_title["Page with a bunch of links"].content self.assertIn('', test_content) self.assertIn('', test_content) @@ -897,80 +1102,80 @@ class TestPageGenerator(unittest.TestCase): are included in context['static_links'] """ settings = get_settings() - settings['PAGE_PATHS'] = ['TestPages/page_with_static_links.md'] - settings['CACHE_PATH'] = self.temp_cache - settings['DEFAULT_DATE'] = (1970, 1, 1) + settings["PAGE_PATHS"] = ["TestPages/page_with_static_links.md"] + settings["CACHE_PATH"] = self.temp_cache + settings["DEFAULT_DATE"] = (1970, 1, 1) context = get_context(settings) generator = PagesGenerator( - context=context, settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + context=context, + settings=settings, + path=CUR_DIR, + theme=settings["THEME"], + output_path=None, + ) generator.generate_context() - self.assertIn('pelican/tests/TestPages/image0.jpg', - context['static_links']) - self.assertIn('pelican/tests/TestPages/image1.jpg', - context['static_links']) + self.assertIn("pelican/tests/TestPages/image0.jpg", context["static_links"]) + self.assertIn("pelican/tests/TestPages/image1.jpg", context["static_links"]) class TestTemplatePagesGenerator(TestCaseWithCLocale): - TEMPLATE_CONTENT = "foo: {{ foo }}" def setUp(self): super().setUp() - self.temp_content = mkdtemp(prefix='pelicantests.') - self.temp_output = mkdtemp(prefix='pelicantests.') + self.temp_content = mkdtemp(prefix="pelicantests.") + self.temp_output = mkdtemp(prefix="pelicantests.") def tearDown(self): rmtree(self.temp_content) rmtree(self.temp_output) def test_generate_output(self): - settings = get_settings() - settings['STATIC_PATHS'] = ['static'] - settings['TEMPLATE_PAGES'] = { - 'template/source.html': 'generated/file.html' - } + settings["STATIC_PATHS"] = ["static"] + settings["TEMPLATE_PAGES"] = {"template/source.html": "generated/file.html"} generator = TemplatePagesGenerator( - context={'foo': 'bar'}, settings=settings, - path=self.temp_content, theme='', output_path=self.temp_output) + context={"foo": "bar"}, + settings=settings, + path=self.temp_content, + theme="", + output_path=self.temp_output, + ) # create a dummy template file - template_dir = os.path.join(self.temp_content, 'template') - template_path = os.path.join(template_dir, 'source.html') + template_dir = os.path.join(self.temp_content, "template") + template_path = os.path.join(template_dir, "source.html") os.makedirs(template_dir) - with open(template_path, 'w') as template_file: + with open(template_path, "w") as template_file: template_file.write(self.TEMPLATE_CONTENT) writer = Writer(self.temp_output, settings=settings) generator.generate_output(writer) - output_path = os.path.join(self.temp_output, 'generated', 'file.html') + output_path = os.path.join(self.temp_output, "generated", "file.html") # output file has been generated self.assertTrue(os.path.exists(output_path)) # output content is correct with open(output_path) as output_file: - self.assertEqual(output_file.read(), 'foo: bar') + self.assertEqual(output_file.read(), "foo: bar") class TestStaticGenerator(unittest.TestCase): - def setUp(self): - self.content_path = os.path.join(CUR_DIR, 'mixed_content') - self.temp_content = mkdtemp(prefix='testcontent.') - self.temp_output = mkdtemp(prefix='testoutput.') + self.content_path = os.path.join(CUR_DIR, "mixed_content") + self.temp_content = mkdtemp(prefix="testcontent.") + self.temp_output = mkdtemp(prefix="testoutput.") self.settings = get_settings() - self.settings['PATH'] = self.temp_content - self.settings['STATIC_PATHS'] = ["static"] - self.settings['OUTPUT_PATH'] = self.temp_output + self.settings["PATH"] = self.temp_content + self.settings["STATIC_PATHS"] = ["static"] + self.settings["OUTPUT_PATH"] = self.temp_output os.mkdir(os.path.join(self.temp_content, "static")) - self.startfile = os.path.join(self.temp_content, - "static", "staticfile") + self.startfile = os.path.join(self.temp_content, "static", "staticfile") self.endfile = os.path.join(self.temp_output, "static", "staticfile") self.generator = StaticGenerator( context=get_context(), @@ -978,7 +1183,7 @@ class TestStaticGenerator(unittest.TestCase): path=self.temp_content, theme="", output_path=self.temp_output, - ) + ) def tearDown(self): rmtree(self.temp_content) @@ -989,155 +1194,198 @@ class TestStaticGenerator(unittest.TestCase): def test_theme_static_paths_dirs(self): """Test that StaticGenerator properly copies also files mentioned in - TEMPLATE_STATIC_PATHS, not just directories.""" + TEMPLATE_STATIC_PATHS, not just directories.""" settings = get_settings(PATH=self.content_path) context = get_context(settings, staticfiles=[]) StaticGenerator( - context=context, settings=settings, - path=settings['PATH'], output_path=self.temp_output, - theme=settings['THEME']).generate_output(None) + context=context, + settings=settings, + path=settings["PATH"], + output_path=self.temp_output, + theme=settings["THEME"], + ).generate_output(None) # The content of dirs listed in THEME_STATIC_PATHS (defaulting to # "static") is put into the output - self.assertTrue(os.path.isdir(os.path.join(self.temp_output, - "theme/css/"))) - self.assertTrue(os.path.isdir(os.path.join(self.temp_output, - "theme/fonts/"))) + self.assertTrue(os.path.isdir(os.path.join(self.temp_output, "theme/css/"))) + self.assertTrue(os.path.isdir(os.path.join(self.temp_output, "theme/fonts/"))) def test_theme_static_paths_files(self): """Test that StaticGenerator properly copies also files mentioned in - TEMPLATE_STATIC_PATHS, not just directories.""" + TEMPLATE_STATIC_PATHS, not just directories.""" settings = get_settings( PATH=self.content_path, - THEME_STATIC_PATHS=['static/css/fonts.css', 'static/fonts/'],) + THEME_STATIC_PATHS=["static/css/fonts.css", "static/fonts/"], + ) context = get_context(settings, staticfiles=[]) StaticGenerator( - context=context, settings=settings, - path=settings['PATH'], output_path=self.temp_output, - theme=settings['THEME']).generate_output(None) + context=context, + settings=settings, + path=settings["PATH"], + output_path=self.temp_output, + theme=settings["THEME"], + ).generate_output(None) # Only the content of dirs and files listed in THEME_STATIC_PATHS are # put into the output, not everything from static/ - self.assertFalse(os.path.isdir(os.path.join(self.temp_output, - "theme/css/"))) - self.assertFalse(os.path.isdir(os.path.join(self.temp_output, - "theme/fonts/"))) + self.assertFalse(os.path.isdir(os.path.join(self.temp_output, "theme/css/"))) + self.assertFalse(os.path.isdir(os.path.join(self.temp_output, "theme/fonts/"))) - self.assertTrue(os.path.isfile(os.path.join( - self.temp_output, "theme/Yanone_Kaffeesatz_400.eot"))) - self.assertTrue(os.path.isfile(os.path.join( - self.temp_output, "theme/Yanone_Kaffeesatz_400.svg"))) - self.assertTrue(os.path.isfile(os.path.join( - self.temp_output, "theme/Yanone_Kaffeesatz_400.ttf"))) - self.assertTrue(os.path.isfile(os.path.join( - self.temp_output, "theme/Yanone_Kaffeesatz_400.woff"))) - self.assertTrue(os.path.isfile(os.path.join( - self.temp_output, "theme/Yanone_Kaffeesatz_400.woff2"))) - self.assertTrue(os.path.isfile(os.path.join(self.temp_output, - "theme/font.css"))) - self.assertTrue(os.path.isfile(os.path.join(self.temp_output, - "theme/fonts.css"))) + self.assertTrue( + os.path.isfile( + os.path.join(self.temp_output, "theme/Yanone_Kaffeesatz_400.eot") + ) + ) + self.assertTrue( + os.path.isfile( + os.path.join(self.temp_output, "theme/Yanone_Kaffeesatz_400.svg") + ) + ) + self.assertTrue( + os.path.isfile( + os.path.join(self.temp_output, "theme/Yanone_Kaffeesatz_400.ttf") + ) + ) + self.assertTrue( + os.path.isfile( + os.path.join(self.temp_output, "theme/Yanone_Kaffeesatz_400.woff") + ) + ) + self.assertTrue( + os.path.isfile( + os.path.join(self.temp_output, "theme/Yanone_Kaffeesatz_400.woff2") + ) + ) + self.assertTrue( + os.path.isfile(os.path.join(self.temp_output, "theme/font.css")) + ) + self.assertTrue( + os.path.isfile(os.path.join(self.temp_output, "theme/fonts.css")) + ) def test_static_excludes(self): - """Test that StaticGenerator respects STATIC_EXCLUDES. - """ + """Test that StaticGenerator respects STATIC_EXCLUDES.""" settings = get_settings( - STATIC_EXCLUDES=['subdir'], + STATIC_EXCLUDES=["subdir"], PATH=self.content_path, - STATIC_PATHS=[''],) + STATIC_PATHS=[""], + ) context = get_context(settings) StaticGenerator( - context=context, settings=settings, - path=settings['PATH'], output_path=self.temp_output, - theme=settings['THEME']).generate_context() + context=context, + settings=settings, + path=settings["PATH"], + output_path=self.temp_output, + theme=settings["THEME"], + ).generate_context() - staticnames = [os.path.basename(c.source_path) - for c in context['staticfiles']] + staticnames = [os.path.basename(c.source_path) for c in context["staticfiles"]] self.assertNotIn( - 'subdir_fake_image.jpg', staticnames, - "StaticGenerator processed a file in a STATIC_EXCLUDES directory") + "subdir_fake_image.jpg", + staticnames, + "StaticGenerator processed a file in a STATIC_EXCLUDES directory", + ) self.assertIn( - 'fake_image.jpg', staticnames, - "StaticGenerator skipped a file that it should have included") + "fake_image.jpg", + staticnames, + "StaticGenerator skipped a file that it should have included", + ) def test_static_exclude_sources(self): - """Test that StaticGenerator respects STATIC_EXCLUDE_SOURCES. - """ + """Test that StaticGenerator respects STATIC_EXCLUDE_SOURCES.""" settings = get_settings( STATIC_EXCLUDE_SOURCES=True, PATH=self.content_path, - PAGE_PATHS=[''], - STATIC_PATHS=[''], - CACHE_CONTENT=False,) + PAGE_PATHS=[""], + STATIC_PATHS=[""], + CACHE_CONTENT=False, + ) context = get_context(settings) for generator_class in (PagesGenerator, StaticGenerator): generator_class( - context=context, settings=settings, - path=settings['PATH'], output_path=self.temp_output, - theme=settings['THEME']).generate_context() + context=context, + settings=settings, + path=settings["PATH"], + output_path=self.temp_output, + theme=settings["THEME"], + ).generate_context() - staticnames = [os.path.basename(c.source_path) - for c in context['staticfiles']] + staticnames = [os.path.basename(c.source_path) for c in context["staticfiles"]] self.assertFalse( any(name.endswith(".md") for name in staticnames), - "STATIC_EXCLUDE_SOURCES=True failed to exclude a markdown file") + "STATIC_EXCLUDE_SOURCES=True failed to exclude a markdown file", + ) settings.update(STATIC_EXCLUDE_SOURCES=False) context = get_context(settings) for generator_class in (PagesGenerator, StaticGenerator): generator_class( - context=context, settings=settings, - path=settings['PATH'], output_path=self.temp_output, - theme=settings['THEME']).generate_context() + context=context, + settings=settings, + path=settings["PATH"], + output_path=self.temp_output, + theme=settings["THEME"], + ).generate_context() - staticnames = [os.path.basename(c.source_path) - for c in context['staticfiles']] + staticnames = [os.path.basename(c.source_path) for c in context["staticfiles"]] self.assertTrue( any(name.endswith(".md") for name in staticnames), - "STATIC_EXCLUDE_SOURCES=False failed to include a markdown file") + "STATIC_EXCLUDE_SOURCES=False failed to include a markdown file", + ) def test_static_links(self): - """Test that StaticGenerator uses files in static_links - """ + """Test that StaticGenerator uses files in static_links""" settings = get_settings( - STATIC_EXCLUDES=['subdir'], + STATIC_EXCLUDES=["subdir"], PATH=self.content_path, - STATIC_PATHS=[],) + STATIC_PATHS=[], + ) context = get_context(settings) - context['static_links'] |= {'short_page.md', 'subdir_fake_image.jpg'} + context["static_links"] |= {"short_page.md", "subdir_fake_image.jpg"} StaticGenerator( - context=context, settings=settings, - path=settings['PATH'], output_path=self.temp_output, - theme=settings['THEME']).generate_context() + context=context, + settings=settings, + path=settings["PATH"], + output_path=self.temp_output, + theme=settings["THEME"], + ).generate_context() staticfiles_names = [ - os.path.basename(c.source_path) for c in context['staticfiles']] + os.path.basename(c.source_path) for c in context["staticfiles"] + ] - static_content_names = [ - os.path.basename(c) for c in context['static_content']] + static_content_names = [os.path.basename(c) for c in context["static_content"]] self.assertIn( - 'short_page.md', staticfiles_names, - "StaticGenerator skipped a file that it should have included") + "short_page.md", + staticfiles_names, + "StaticGenerator skipped a file that it should have included", + ) self.assertIn( - 'short_page.md', static_content_names, - "StaticGenerator skipped a file that it should have included") + "short_page.md", + static_content_names, + "StaticGenerator skipped a file that it should have included", + ) self.assertIn( - 'subdir_fake_image.jpg', staticfiles_names, - "StaticGenerator skipped a file that it should have included") + "subdir_fake_image.jpg", + staticfiles_names, + "StaticGenerator skipped a file that it should have included", + ) self.assertIn( - 'subdir_fake_image.jpg', static_content_names, - "StaticGenerator skipped a file that it should have included") + "subdir_fake_image.jpg", + static_content_names, + "StaticGenerator skipped a file that it should have included", + ) def test_copy_one_file(self): with open(self.startfile, "w") as f: @@ -1160,7 +1408,7 @@ class TestStaticGenerator(unittest.TestCase): staticfile = MagicMock() staticfile.source_path = self.startfile staticfile.save_as = self.endfile - self.settings['STATIC_CHECK_IF_MODIFIED'] = True + self.settings["STATIC_CHECK_IF_MODIFIED"] = True with open(staticfile.source_path, "w") as f: f.write("a") os.mkdir(os.path.join(self.temp_output, "static")) @@ -1181,7 +1429,7 @@ class TestStaticGenerator(unittest.TestCase): self.assertTrue(isnewer) def test_skip_file_when_source_is_not_newer(self): - self.settings['STATIC_CHECK_IF_MODIFIED'] = True + self.settings["STATIC_CHECK_IF_MODIFIED"] = True with open(self.startfile, "w") as f: f.write("staticcontent") os.mkdir(os.path.join(self.temp_output, "static")) @@ -1201,7 +1449,7 @@ class TestStaticGenerator(unittest.TestCase): self.assertFalse(os.path.samefile(self.startfile, self.endfile)) def test_output_file_is_linked_to_source(self): - self.settings['STATIC_CREATE_LINKS'] = True + self.settings["STATIC_CREATE_LINKS"] = True with open(self.startfile, "w") as f: f.write("staticcontent") self.generator.generate_context() @@ -1209,7 +1457,7 @@ class TestStaticGenerator(unittest.TestCase): self.assertTrue(os.path.samefile(self.startfile, self.endfile)) def test_output_file_exists_and_is_newer(self): - self.settings['STATIC_CREATE_LINKS'] = True + self.settings["STATIC_CREATE_LINKS"] = True with open(self.startfile, "w") as f: f.write("staticcontent") os.mkdir(os.path.join(self.temp_output, "static")) @@ -1219,9 +1467,9 @@ class TestStaticGenerator(unittest.TestCase): self.generator.generate_output(None) self.assertTrue(os.path.samefile(self.startfile, self.endfile)) - @unittest.skipUnless(can_symlink(), 'No symlink privilege') + @unittest.skipUnless(can_symlink(), "No symlink privilege") def test_can_symlink_when_hardlink_not_possible(self): - self.settings['STATIC_CREATE_LINKS'] = True + self.settings["STATIC_CREATE_LINKS"] = True with open(self.startfile, "w") as f: f.write("staticcontent") os.mkdir(os.path.join(self.temp_output, "static")) @@ -1230,9 +1478,9 @@ class TestStaticGenerator(unittest.TestCase): self.generator.generate_output(None) self.assertTrue(os.path.islink(self.endfile)) - @unittest.skipUnless(can_symlink(), 'No symlink privilege') + @unittest.skipUnless(can_symlink(), "No symlink privilege") def test_existing_symlink_is_considered_up_to_date(self): - self.settings['STATIC_CREATE_LINKS'] = True + self.settings["STATIC_CREATE_LINKS"] = True with open(self.startfile, "w") as f: f.write("staticcontent") os.mkdir(os.path.join(self.temp_output, "static")) @@ -1243,9 +1491,9 @@ class TestStaticGenerator(unittest.TestCase): requires_update = self.generator._file_update_required(staticfile) self.assertFalse(requires_update) - @unittest.skipUnless(can_symlink(), 'No symlink privilege') + @unittest.skipUnless(can_symlink(), "No symlink privilege") def test_invalid_symlink_is_overwritten(self): - self.settings['STATIC_CREATE_LINKS'] = True + self.settings["STATIC_CREATE_LINKS"] = True with open(self.startfile, "w") as f: f.write("staticcontent") os.mkdir(os.path.join(self.temp_output, "static")) @@ -1263,14 +1511,14 @@ class TestStaticGenerator(unittest.TestCase): # os.path.realpath is broken on Windows before python3.8 for symlinks. # This is a (ugly) workaround. # see: https://bugs.python.org/issue9949 - if os.name == 'nt' and sys.version_info < (3, 8): + if os.name == "nt" and sys.version_info < (3, 8): + def get_real_path(path): return os.readlink(path) if os.path.islink(path) else path else: get_real_path = os.path.realpath - self.assertEqual(get_real_path(self.endfile), - get_real_path(self.startfile)) + self.assertEqual(get_real_path(self.endfile), get_real_path(self.startfile)) def test_delete_existing_file_before_mkdir(self): with open(self.startfile, "w") as f: @@ -1279,16 +1527,14 @@ class TestStaticGenerator(unittest.TestCase): f.write("This file should be a directory") self.generator.generate_context() self.generator.generate_output(None) - self.assertTrue( - os.path.isdir(os.path.join(self.temp_output, "static"))) + self.assertTrue(os.path.isdir(os.path.join(self.temp_output, "static"))) self.assertTrue(os.path.isfile(self.endfile)) class TestJinja2Environment(TestCaseWithCLocale): - def setUp(self): - self.temp_content = mkdtemp(prefix='pelicantests.') - self.temp_output = mkdtemp(prefix='pelicantests.') + self.temp_content = mkdtemp(prefix="pelicantests.") + self.temp_output = mkdtemp(prefix="pelicantests.") def tearDown(self): rmtree(self.temp_content) @@ -1296,27 +1542,29 @@ class TestJinja2Environment(TestCaseWithCLocale): def _test_jinja2_helper(self, additional_settings, content, expected): settings = get_settings() - settings['STATIC_PATHS'] = ['static'] - settings['TEMPLATE_PAGES'] = { - 'template/source.html': 'generated/file.html' - } + settings["STATIC_PATHS"] = ["static"] + settings["TEMPLATE_PAGES"] = {"template/source.html": "generated/file.html"} settings.update(additional_settings) generator = TemplatePagesGenerator( - context={'foo': 'foo', 'bar': 'bar'}, settings=settings, - path=self.temp_content, theme='', output_path=self.temp_output) + context={"foo": "foo", "bar": "bar"}, + settings=settings, + path=self.temp_content, + theme="", + output_path=self.temp_output, + ) # create a dummy template file - template_dir = os.path.join(self.temp_content, 'template') - template_path = os.path.join(template_dir, 'source.html') + template_dir = os.path.join(self.temp_content, "template") + template_path = os.path.join(template_dir, "source.html") os.makedirs(template_dir) - with open(template_path, 'w') as template_file: + with open(template_path, "w") as template_file: template_file.write(content) writer = Writer(self.temp_output, settings=settings) generator.generate_output(writer) - output_path = os.path.join(self.temp_output, 'generated', 'file.html') + output_path = os.path.join(self.temp_output, "generated", "file.html") # output file has been generated self.assertTrue(os.path.exists(output_path)) @@ -1327,32 +1575,32 @@ class TestJinja2Environment(TestCaseWithCLocale): def test_jinja2_filter(self): """JINJA_FILTERS adds custom filters to Jinja2 environment""" - content = 'foo: {{ foo|custom_filter }}, bar: {{ bar|custom_filter }}' - settings = {'JINJA_FILTERS': {'custom_filter': lambda x: x.upper()}} - expected = 'foo: FOO, bar: BAR' + content = "foo: {{ foo|custom_filter }}, bar: {{ bar|custom_filter }}" + settings = {"JINJA_FILTERS": {"custom_filter": lambda x: x.upper()}} + expected = "foo: FOO, bar: BAR" self._test_jinja2_helper(settings, content, expected) def test_jinja2_test(self): """JINJA_TESTS adds custom tests to Jinja2 environment""" - content = 'foo {{ foo is custom_test }}, bar {{ bar is custom_test }}' - settings = {'JINJA_TESTS': {'custom_test': lambda x: x == 'bar'}} - expected = 'foo False, bar True' + content = "foo {{ foo is custom_test }}, bar {{ bar is custom_test }}" + settings = {"JINJA_TESTS": {"custom_test": lambda x: x == "bar"}} + expected = "foo False, bar True" self._test_jinja2_helper(settings, content, expected) def test_jinja2_global(self): """JINJA_GLOBALS adds custom globals to Jinja2 environment""" - content = '{{ custom_global }}' - settings = {'JINJA_GLOBALS': {'custom_global': 'foobar'}} - expected = 'foobar' + content = "{{ custom_global }}" + settings = {"JINJA_GLOBALS": {"custom_global": "foobar"}} + expected = "foobar" self._test_jinja2_helper(settings, content, expected) def test_jinja2_extension(self): """JINJA_ENVIRONMENT adds extensions to Jinja2 environment""" - content = '{% set stuff = [] %}{% do stuff.append(1) %}{{ stuff }}' - settings = {'JINJA_ENVIRONMENT': {'extensions': ['jinja2.ext.do']}} - expected = '[1]' + content = "{% set stuff = [] %}{% do stuff.append(1) %}{{ stuff }}" + settings = {"JINJA_ENVIRONMENT": {"extensions": ["jinja2.ext.do"]}} + expected = "[1]" self._test_jinja2_helper(settings, content, expected) diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index 870d3001..05ef5bbd 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -4,26 +4,35 @@ from posixpath import join as posix_join from unittest.mock import patch from pelican.settings import DEFAULT_CONFIG -from pelican.tests.support import (mute, skipIfNoExecutable, temporary_folder, - unittest, TestCaseWithCLocale) -from pelican.tools.pelican_import import (blogger2fields, build_header, - build_markdown_header, - decode_wp_content, - download_attachments, fields2pelican, - get_attachments, tumblr2fields, - wp2fields, - ) +from pelican.tests.support import ( + mute, + skipIfNoExecutable, + temporary_folder, + unittest, + TestCaseWithCLocale, +) +from pelican.tools.pelican_import import ( + blogger2fields, + build_header, + build_markdown_header, + decode_wp_content, + download_attachments, + fields2pelican, + get_attachments, + tumblr2fields, + wp2fields, +) from pelican.utils import path_to_file_url, slugify CUR_DIR = os.path.abspath(os.path.dirname(__file__)) -BLOGGER_XML_SAMPLE = os.path.join(CUR_DIR, 'content', 'bloggerexport.xml') -WORDPRESS_XML_SAMPLE = os.path.join(CUR_DIR, 'content', 'wordpressexport.xml') -WORDPRESS_ENCODED_CONTENT_SAMPLE = os.path.join(CUR_DIR, - 'content', - 'wordpress_content_encoded') -WORDPRESS_DECODED_CONTENT_SAMPLE = os.path.join(CUR_DIR, - 'content', - 'wordpress_content_decoded') +BLOGGER_XML_SAMPLE = os.path.join(CUR_DIR, "content", "bloggerexport.xml") +WORDPRESS_XML_SAMPLE = os.path.join(CUR_DIR, "content", "wordpressexport.xml") +WORDPRESS_ENCODED_CONTENT_SAMPLE = os.path.join( + CUR_DIR, "content", "wordpress_content_encoded" +) +WORDPRESS_DECODED_CONTENT_SAMPLE = os.path.join( + CUR_DIR, "content", "wordpress_content_decoded" +) try: from bs4 import BeautifulSoup @@ -36,10 +45,9 @@ except ImportError: LXML = False -@skipIfNoExecutable(['pandoc', '--version']) -@unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module') +@skipIfNoExecutable(["pandoc", "--version"]) +@unittest.skipUnless(BeautifulSoup, "Needs BeautifulSoup module") class TestBloggerXmlImporter(TestCaseWithCLocale): - def setUp(self): super().setUp() self.posts = blogger2fields(BLOGGER_XML_SAMPLE) @@ -50,16 +58,17 @@ class TestBloggerXmlImporter(TestCaseWithCLocale): """ test_posts = list(self.posts) kinds = {x[8] for x in test_posts} - self.assertEqual({'page', 'article', 'comment'}, kinds) - page_titles = {x[0] for x in test_posts if x[8] == 'page'} - self.assertEqual({'Test page', 'Test page 2'}, page_titles) - article_titles = {x[0] for x in test_posts if x[8] == 'article'} - self.assertEqual({'Black as Egypt\'s Night', 'The Steel Windpipe'}, - article_titles) - comment_titles = {x[0] for x in test_posts if x[8] == 'comment'} - self.assertEqual({'Mishka, always a pleasure to read your ' - 'adventures!...'}, - comment_titles) + self.assertEqual({"page", "article", "comment"}, kinds) + page_titles = {x[0] for x in test_posts if x[8] == "page"} + self.assertEqual({"Test page", "Test page 2"}, page_titles) + article_titles = {x[0] for x in test_posts if x[8] == "article"} + self.assertEqual( + {"Black as Egypt's Night", "The Steel Windpipe"}, article_titles + ) + comment_titles = {x[0] for x in test_posts if x[8] == "comment"} + self.assertEqual( + {"Mishka, always a pleasure to read your " "adventures!..."}, comment_titles + ) def test_recognise_status_with_correct_filename(self): """Check that importerer outputs only statuses 'published' and 'draft', @@ -67,24 +76,25 @@ class TestBloggerXmlImporter(TestCaseWithCLocale): """ test_posts = list(self.posts) statuses = {x[7] for x in test_posts} - self.assertEqual({'published', 'draft'}, statuses) + self.assertEqual({"published", "draft"}, statuses) - draft_filenames = {x[2] for x in test_posts if x[7] == 'draft'} + draft_filenames = {x[2] for x in test_posts if x[7] == "draft"} # draft filenames are id-based - self.assertEqual({'page-4386962582497458967', - 'post-1276418104709695660'}, draft_filenames) + self.assertEqual( + {"page-4386962582497458967", "post-1276418104709695660"}, draft_filenames + ) - published_filenames = {x[2] for x in test_posts if x[7] == 'published'} + published_filenames = {x[2] for x in test_posts if x[7] == "published"} # published filenames are url-based, except comments - self.assertEqual({'the-steel-windpipe', - 'test-page', - 'post-5590533389087749201'}, published_filenames) + self.assertEqual( + {"the-steel-windpipe", "test-page", "post-5590533389087749201"}, + published_filenames, + ) -@skipIfNoExecutable(['pandoc', '--version']) -@unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module') +@skipIfNoExecutable(["pandoc", "--version"]) +@unittest.skipUnless(BeautifulSoup, "Needs BeautifulSoup module") class TestWordpressXmlImporter(TestCaseWithCLocale): - def setUp(self): super().setUp() self.posts = wp2fields(WORDPRESS_XML_SAMPLE) @@ -92,30 +102,49 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): def test_ignore_empty_posts(self): self.assertTrue(self.posts) - for (title, content, fname, date, author, - categ, tags, status, kind, format) in self.posts: + for ( + title, + content, + fname, + date, + author, + categ, + tags, + status, + kind, + format, + ) in self.posts: self.assertTrue(title.strip()) def test_recognise_page_kind(self): - """ Check that we recognise pages in wordpress, as opposed to posts """ + """Check that we recognise pages in wordpress, as opposed to posts""" self.assertTrue(self.posts) # Collect (title, filename, kind) of non-empty posts recognised as page pages_data = [] - for (title, content, fname, date, author, - categ, tags, status, kind, format) in self.posts: - if kind == 'page': + for ( + title, + content, + fname, + date, + author, + categ, + tags, + status, + kind, + format, + ) in self.posts: + if kind == "page": pages_data.append((title, fname)) self.assertEqual(2, len(pages_data)) - self.assertEqual(('Page', 'contact'), pages_data[0]) - self.assertEqual(('Empty Page', 'empty'), pages_data[1]) + self.assertEqual(("Page", "contact"), pages_data[0]) + self.assertEqual(("Empty Page", "empty"), pages_data[1]) def test_dirpage_directive_for_page_kind(self): silent_f2p = mute(True)(fields2pelican) test_post = filter(lambda p: p[0].startswith("Empty Page"), self.posts) with temporary_folder() as temp: - fname = list(silent_f2p(test_post, 'markdown', - temp, dirpage=True))[0] - self.assertTrue(fname.endswith('pages%sempty.md' % os.path.sep)) + fname = list(silent_f2p(test_post, "markdown", temp, dirpage=True))[0] + self.assertTrue(fname.endswith("pages%sempty.md" % os.path.sep)) def test_dircat(self): silent_f2p = mute(True)(fields2pelican) @@ -125,14 +154,13 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): if len(post[5]) > 0: # Has a category test_posts.append(post) with temporary_folder() as temp: - fnames = list(silent_f2p(test_posts, 'markdown', - temp, dircat=True)) - subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS'] + fnames = list(silent_f2p(test_posts, "markdown", temp, dircat=True)) + subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"] index = 0 for post in test_posts: name = post[2] category = slugify(post[5][0], regex_subs=subs, preserve_case=True) - name += '.md' + name += ".md" filename = os.path.join(category, name) out_name = fnames[index] self.assertTrue(out_name.endswith(filename)) @@ -141,9 +169,19 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): def test_unless_custom_post_all_items_should_be_pages_or_posts(self): self.assertTrue(self.posts) pages_data = [] - for (title, content, fname, date, author, categ, - tags, status, kind, format) in self.posts: - if kind == 'page' or kind == 'article': + for ( + title, + content, + fname, + date, + author, + categ, + tags, + status, + kind, + format, + ) in self.posts: + if kind == "page" or kind == "article": pass else: pages_data.append((title, fname)) @@ -152,40 +190,45 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): def test_recognise_custom_post_type(self): self.assertTrue(self.custposts) cust_data = [] - for (title, content, fname, date, author, categ, - tags, status, kind, format) in self.custposts: - if kind == 'article' or kind == 'page': + for ( + title, + content, + fname, + date, + author, + categ, + tags, + status, + kind, + format, + ) in self.custposts: + if kind == "article" or kind == "page": pass else: cust_data.append((title, kind)) self.assertEqual(3, len(cust_data)) + self.assertEqual(("A custom post in category 4", "custom1"), cust_data[0]) + self.assertEqual(("A custom post in category 5", "custom1"), cust_data[1]) self.assertEqual( - ('A custom post in category 4', 'custom1'), - cust_data[0]) - self.assertEqual( - ('A custom post in category 5', 'custom1'), - cust_data[1]) - self.assertEqual( - ('A 2nd custom post type also in category 5', 'custom2'), - cust_data[2]) + ("A 2nd custom post type also in category 5", "custom2"), cust_data[2] + ) def test_custom_posts_put_in_own_dir(self): silent_f2p = mute(True)(fields2pelican) test_posts = [] for post in self.custposts: # check post kind - if post[8] == 'article' or post[8] == 'page': + if post[8] == "article" or post[8] == "page": pass else: test_posts.append(post) with temporary_folder() as temp: - fnames = list(silent_f2p(test_posts, 'markdown', - temp, wp_custpost=True)) + fnames = list(silent_f2p(test_posts, "markdown", temp, wp_custpost=True)) index = 0 for post in test_posts: name = post[2] kind = post[8] - name += '.md' + name += ".md" filename = os.path.join(kind, name) out_name = fnames[index] self.assertTrue(out_name.endswith(filename)) @@ -196,20 +239,21 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): test_posts = [] for post in self.custposts: # check post kind - if post[8] == 'article' or post[8] == 'page': + if post[8] == "article" or post[8] == "page": pass else: test_posts.append(post) with temporary_folder() as temp: - fnames = list(silent_f2p(test_posts, 'markdown', temp, - wp_custpost=True, dircat=True)) - subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS'] + fnames = list( + silent_f2p(test_posts, "markdown", temp, wp_custpost=True, dircat=True) + ) + subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"] index = 0 for post in test_posts: name = post[2] kind = post[8] category = slugify(post[5][0], regex_subs=subs, preserve_case=True) - name += '.md' + name += ".md" filename = os.path.join(kind, category, name) out_name = fnames[index] self.assertTrue(out_name.endswith(filename)) @@ -221,16 +265,19 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): test_posts = [] for post in self.custposts: # check post kind - if post[8] == 'page': + if post[8] == "page": test_posts.append(post) with temporary_folder() as temp: - fnames = list(silent_f2p(test_posts, 'markdown', temp, - wp_custpost=True, dirpage=False)) + fnames = list( + silent_f2p( + test_posts, "markdown", temp, wp_custpost=True, dirpage=False + ) + ) index = 0 for post in test_posts: name = post[2] - name += '.md' - filename = os.path.join('pages', name) + name += ".md" + filename = os.path.join("pages", name) out_name = fnames[index] self.assertFalse(out_name.endswith(filename)) @@ -238,117 +285,114 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): test_posts = list(self.posts) def r(f): - with open(f, encoding='utf-8') as infile: + with open(f, encoding="utf-8") as infile: return infile.read() + silent_f2p = mute(True)(fields2pelican) with temporary_folder() as temp: - - rst_files = (r(f) for f - in silent_f2p(test_posts, 'markdown', temp)) - self.assertTrue(any(' entities in " - "the title. You can't miss them.") - self.assertNotIn('&', title) + self.assertTrue( + title, + "A normal post with some entities in " + "the title. You can't miss them.", + ) + self.assertNotIn("&", title) def test_decode_wp_content_returns_empty(self): - """ Check that given an empty string we return an empty string.""" + """Check that given an empty string we return an empty string.""" self.assertEqual(decode_wp_content(""), "") def test_decode_wp_content(self): - """ Check that we can decode a wordpress content string.""" + """Check that we can decode a wordpress content string.""" with open(WORDPRESS_ENCODED_CONTENT_SAMPLE) as encoded_file: encoded_content = encoded_file.read() with open(WORDPRESS_DECODED_CONTENT_SAMPLE) as decoded_file: decoded_content = decoded_file.read() self.assertEqual( - decode_wp_content(encoded_content, br=False), - decoded_content) + decode_wp_content(encoded_content, br=False), decoded_content + ) def test_preserve_verbatim_formatting(self): def r(f): - with open(f, encoding='utf-8') as infile: + with open(f, encoding="utf-8") as infile: return infile.read() - silent_f2p = mute(True)(fields2pelican) - test_post = filter( - lambda p: p[0].startswith("Code in List"), - self.posts) - with temporary_folder() as temp: - md = [r(f) for f in silent_f2p(test_post, 'markdown', temp)][0] - self.assertTrue(re.search(r'\s+a = \[1, 2, 3\]', md)) - self.assertTrue(re.search(r'\s+b = \[4, 5, 6\]', md)) - for_line = re.search(r'\s+for i in zip\(a, b\):', md).group(0) - print_line = re.search(r'\s+print i', md).group(0) - self.assertTrue( - for_line.rindex('for') < print_line.rindex('print')) + silent_f2p = mute(True)(fields2pelican) + test_post = filter(lambda p: p[0].startswith("Code in List"), self.posts) + with temporary_folder() as temp: + md = [r(f) for f in silent_f2p(test_post, "markdown", temp)][0] + self.assertTrue(re.search(r"\s+a = \[1, 2, 3\]", md)) + self.assertTrue(re.search(r"\s+b = \[4, 5, 6\]", md)) + + for_line = re.search(r"\s+for i in zip\(a, b\):", md).group(0) + print_line = re.search(r"\s+print i", md).group(0) + self.assertTrue(for_line.rindex("for") < print_line.rindex("print")) def test_code_in_list(self): def r(f): - with open(f, encoding='utf-8') as infile: + with open(f, encoding="utf-8") as infile: return infile.read() + silent_f2p = mute(True)(fields2pelican) - test_post = filter( - lambda p: p[0].startswith("Code in List"), - self.posts) + test_post = filter(lambda p: p[0].startswith("Code in List"), self.posts) with temporary_folder() as temp: - md = [r(f) for f in silent_f2p(test_post, 'markdown', temp)][0] - sample_line = re.search(r'- This is a code sample', md).group(0) - code_line = re.search(r'\s+a = \[1, 2, 3\]', md).group(0) - self.assertTrue(sample_line.rindex('This') < code_line.rindex('a')) + md = [r(f) for f in silent_f2p(test_post, "markdown", temp)][0] + sample_line = re.search(r"- This is a code sample", md).group(0) + code_line = re.search(r"\s+a = \[1, 2, 3\]", md).group(0) + self.assertTrue(sample_line.rindex("This") < code_line.rindex("a")) def test_dont_use_smart_quotes(self): def r(f): - with open(f, encoding='utf-8') as infile: + with open(f, encoding="utf-8") as infile: return infile.read() + silent_f2p = mute(True)(fields2pelican) - test_post = filter( - lambda p: p[0].startswith("Post with raw data"), - self.posts) + test_post = filter(lambda p: p[0].startswith("Post with raw data"), self.posts) with temporary_folder() as temp: - md = [r(f) for f in silent_f2p(test_post, 'markdown', temp)][0] + md = [r(f) for f in silent_f2p(test_post, "markdown", temp)][0] escaped_quotes = re.search(r'\\[\'"“”‘’]', md) self.assertFalse(escaped_quotes) def test_convert_caption_to_figure(self): def r(f): - with open(f, encoding='utf-8') as infile: + with open(f, encoding="utf-8") as infile: return infile.read() - silent_f2p = mute(True)(fields2pelican) - test_post = filter( - lambda p: p[0].startswith("Caption on image"), - self.posts) - with temporary_folder() as temp: - md = [r(f) for f in silent_f2p(test_post, 'markdown', temp)][0] - caption = re.search(r'\[caption', md) + silent_f2p = mute(True)(fields2pelican) + test_post = filter(lambda p: p[0].startswith("Caption on image"), self.posts) + with temporary_folder() as temp: + md = [r(f) for f in silent_f2p(test_post, "markdown", temp)][0] + + caption = re.search(r"\[caption", md) self.assertFalse(caption) for occurence in [ - '/theme/img/xpelican.png.pagespeed.ic.Rjep0025-y.png', - '/theme/img/xpelican-3.png.pagespeed.ic.m-NAIdRCOM.png', - '/theme/img/xpelican.png.pagespeed.ic.Rjep0025-y.png', - 'This is a pelican', - 'This also a pelican', - 'Yet another pelican', + "/theme/img/xpelican.png.pagespeed.ic.Rjep0025-y.png", + "/theme/img/xpelican-3.png.pagespeed.ic.m-NAIdRCOM.png", + "/theme/img/xpelican.png.pagespeed.ic.Rjep0025-y.png", + "This is a pelican", + "This also a pelican", + "Yet another pelican", ]: # pandoc 2.x converts into ![text](src) # pandoc 3.x converts into
    src
    text
    @@ -357,70 +401,97 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): class TestBuildHeader(unittest.TestCase): def test_build_header(self): - header = build_header('test', None, None, None, None, None) - self.assertEqual(header, 'test\n####\n\n') + header = build_header("test", None, None, None, None, None) + self.assertEqual(header, "test\n####\n\n") def test_build_header_with_fields(self): header_data = [ - 'Test Post', - '2014-11-04', - 'Alexis Métaireau', - ['Programming'], - ['Pelican', 'Python'], - 'test-post', + "Test Post", + "2014-11-04", + "Alexis Métaireau", + ["Programming"], + ["Pelican", "Python"], + "test-post", ] - expected_docutils = '\n'.join([ - 'Test Post', - '#########', - ':date: 2014-11-04', - ':author: Alexis Métaireau', - ':category: Programming', - ':tags: Pelican, Python', - ':slug: test-post', - '\n', - ]) + expected_docutils = "\n".join( + [ + "Test Post", + "#########", + ":date: 2014-11-04", + ":author: Alexis Métaireau", + ":category: Programming", + ":tags: Pelican, Python", + ":slug: test-post", + "\n", + ] + ) - expected_md = '\n'.join([ - 'Title: Test Post', - 'Date: 2014-11-04', - 'Author: Alexis Métaireau', - 'Category: Programming', - 'Tags: Pelican, Python', - 'Slug: test-post', - '\n', - ]) + expected_md = "\n".join( + [ + "Title: Test Post", + "Date: 2014-11-04", + "Author: Alexis Métaireau", + "Category: Programming", + "Tags: Pelican, Python", + "Slug: test-post", + "\n", + ] + ) self.assertEqual(build_header(*header_data), expected_docutils) self.assertEqual(build_markdown_header(*header_data), expected_md) def test_build_header_with_east_asian_characters(self): - header = build_header('これは広い幅の文字だけで構成されたタイトルです', - None, None, None, None, None) + header = build_header( + "これは広い幅の文字だけで構成されたタイトルです", + None, + None, + None, + None, + None, + ) - self.assertEqual(header, - ('これは広い幅の文字だけで構成されたタイトルです\n' - '##############################################' - '\n\n')) - - def test_galleries_added_to_header(self): - header = build_header('test', None, None, None, None, None, - attachments=['output/test1', 'output/test2']) - self.assertEqual(header, ('test\n####\n' - ':attachments: output/test1, ' - 'output/test2\n\n')) - - def test_galleries_added_to_markdown_header(self): - header = build_markdown_header('test', None, None, None, None, None, - attachments=['output/test1', - 'output/test2']) self.assertEqual( header, - 'Title: test\nAttachments: output/test1, output/test2\n\n') + ( + "これは広い幅の文字だけで構成されたタイトルです\n" + "##############################################" + "\n\n" + ), + ) + + def test_galleries_added_to_header(self): + header = build_header( + "test", + None, + None, + None, + None, + None, + attachments=["output/test1", "output/test2"], + ) + self.assertEqual( + header, ("test\n####\n" ":attachments: output/test1, " "output/test2\n\n") + ) + + def test_galleries_added_to_markdown_header(self): + header = build_markdown_header( + "test", + None, + None, + None, + None, + None, + attachments=["output/test1", "output/test2"], + ) + self.assertEqual( + header, "Title: test\nAttachments: output/test1, output/test2\n\n" + ) -@unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module') -@unittest.skipUnless(LXML, 'Needs lxml module') +@unittest.skipUnless(BeautifulSoup, "Needs BeautifulSoup module") +@unittest.skipUnless(LXML, "Needs lxml module") class TestWordpressXMLAttachements(TestCaseWithCLocale): def setUp(self): super().setUp() @@ -435,38 +506,45 @@ class TestWordpressXMLAttachements(TestCaseWithCLocale): for post in self.attachments.keys(): if post is None: expected = { - ('https://upload.wikimedia.org/wikipedia/commons/' - 'thumb/2/2c/Pelican_lakes_entrance02.jpg/' - '240px-Pelican_lakes_entrance02.jpg') + ( + "https://upload.wikimedia.org/wikipedia/commons/" + "thumb/2/2c/Pelican_lakes_entrance02.jpg/" + "240px-Pelican_lakes_entrance02.jpg" + ) } self.assertEqual(self.attachments[post], expected) - elif post == 'with-excerpt': - expected_invalid = ('http://thisurlisinvalid.notarealdomain/' - 'not_an_image.jpg') - expected_pelikan = ('http://en.wikipedia.org/wiki/' - 'File:Pelikan_Walvis_Bay.jpg') - self.assertEqual(self.attachments[post], - {expected_invalid, expected_pelikan}) - elif post == 'with-tags': - expected_invalid = ('http://thisurlisinvalid.notarealdomain') + elif post == "with-excerpt": + expected_invalid = ( + "http://thisurlisinvalid.notarealdomain/" "not_an_image.jpg" + ) + expected_pelikan = ( + "http://en.wikipedia.org/wiki/" "File:Pelikan_Walvis_Bay.jpg" + ) + self.assertEqual( + self.attachments[post], {expected_invalid, expected_pelikan} + ) + elif post == "with-tags": + expected_invalid = "http://thisurlisinvalid.notarealdomain" self.assertEqual(self.attachments[post], {expected_invalid}) else: - self.fail('all attachments should match to a ' - 'filename or None, {}' - .format(post)) + self.fail( + "all attachments should match to a " "filename or None, {}".format( + post + ) + ) def test_download_attachments(self): - real_file = os.path.join(CUR_DIR, 'content/article.rst') + real_file = os.path.join(CUR_DIR, "content/article.rst") good_url = path_to_file_url(real_file) - bad_url = 'http://localhost:1/not_a_file.txt' + bad_url = "http://localhost:1/not_a_file.txt" silent_da = mute()(download_attachments) with temporary_folder() as temp: locations = list(silent_da(temp, [good_url, bad_url])) self.assertEqual(1, len(locations)) directory = locations[0] self.assertTrue( - directory.endswith(posix_join('content', 'article.rst')), - directory) + directory.endswith(posix_join("content", "article.rst")), directory + ) class TestTumblrImporter(TestCaseWithCLocale): @@ -484,32 +562,42 @@ class TestTumblrImporter(TestCaseWithCLocale): "timestamp": 1573162000, "format": "html", "slug": "a-slug", - "tags": [ - "economics" - ], + "tags": ["economics"], "state": "published", - "photos": [ { "caption": "", "original_size": { "url": "https://..fccdc2360ba7182a.jpg", "width": 634, - "height": 789 + "height": 789, }, - }] + } + ], } ] + get.side_effect = get_posts posts = list(tumblr2fields("api_key", "blogname")) self.assertEqual( - [('Photo', - '\n', - '2019-11-07-a-slug', '2019-11-07 21:26:40+0000', 'testy', ['photo'], - ['economics'], 'published', 'article', 'html')], + [ + ( + "Photo", + '\n', + "2019-11-07-a-slug", + "2019-11-07 21:26:40+0000", + "testy", + ["photo"], + ["economics"], + "published", + "article", + "html", + ) + ], posts, - posts) + posts, + ) @patch("pelican.tools.pelican_import._get_tumblr_posts") def test_video_embed(self, get): @@ -531,40 +619,39 @@ class TestTumblrImporter(TestCaseWithCLocale): "source_title": "youtube.com", "caption": "

    Caption

    ", "player": [ - { - "width": 250, - "embed_code": - "" - }, - { - "width": 400, - "embed_code": - "" - }, - { - "width": 500, - "embed_code": - "" - } + {"width": 250, "embed_code": ""}, + {"width": 400, "embed_code": ""}, + {"width": 500, "embed_code": ""}, ], "video_type": "youtube", } - ] + ] + get.side_effect = get_posts posts = list(tumblr2fields("api_key", "blogname")) self.assertEqual( - [('youtube.com', - '

    via

    \n

    Caption

    ' - '\n' - '\n' - '\n', - '2017-07-07-the-slug', - '2017-07-07 20:31:41+0000', 'testy', ['video'], [], 'published', - 'article', 'html')], + [ + ( + "youtube.com", + '

    via

    \n

    Caption

    ' + "\n" + "\n" + "\n", + "2017-07-07-the-slug", + "2017-07-07 20:31:41+0000", + "testy", + ["video"], + [], + "published", + "article", + "html", + ) + ], posts, - posts) + posts, + ) @patch("pelican.tools.pelican_import._get_tumblr_posts") def test_broken_video_embed(self, get): @@ -581,42 +668,43 @@ class TestTumblrImporter(TestCaseWithCLocale): "timestamp": 1471192655, "state": "published", "format": "html", - "tags": [ - "interviews" - ], - "source_url": - "https://href.li/?https://www.youtube.com/watch?v=b", + "tags": ["interviews"], + "source_url": "https://href.li/?https://www.youtube.com/watch?v=b", "source_title": "youtube.com", - "caption": - "

    Caption

    ", + "caption": "

    Caption

    ", "player": [ { "width": 250, # If video is gone, embed_code is False - "embed_code": False + "embed_code": False, }, - { - "width": 400, - "embed_code": False - }, - { - "width": 500, - "embed_code": False - } + {"width": 400, "embed_code": False}, + {"width": 500, "embed_code": False}, ], "video_type": "youtube", } ] + get.side_effect = get_posts posts = list(tumblr2fields("api_key", "blogname")) self.assertEqual( - [('youtube.com', - '

    via

    \n

    Caption

    ' - '

    (This video isn\'t available anymore.)

    \n', - '2016-08-14-the-slug', - '2016-08-14 16:37:35+0000', 'testy', ['video'], ['interviews'], - 'published', 'article', 'html')], + [ + ( + "youtube.com", + '

    via

    \n

    Caption

    ' + "

    (This video isn't available anymore.)

    \n", + "2016-08-14-the-slug", + "2016-08-14 16:37:35+0000", + "testy", + ["video"], + ["interviews"], + "published", + "article", + "html", + ) + ], posts, - posts) + posts, + ) diff --git a/pelican/tests/test_log.py b/pelican/tests/test_log.py index 1f2fb83a..8791fc7c 100644 --- a/pelican/tests/test_log.py +++ b/pelican/tests/test_log.py @@ -35,48 +35,41 @@ class TestLog(unittest.TestCase): def test_log_filter(self): def do_logging(): for i in range(5): - self.logger.warning('Log %s', i) - self.logger.warning('Another log %s', i) + self.logger.warning("Log %s", i) + self.logger.warning("Another log %s", i) + # no filter with self.reset_logger(): do_logging() + self.assertEqual(self.handler.count_logs("Log \\d", logging.WARNING), 5) self.assertEqual( - self.handler.count_logs('Log \\d', logging.WARNING), - 5) - self.assertEqual( - self.handler.count_logs('Another log \\d', logging.WARNING), - 5) + self.handler.count_logs("Another log \\d", logging.WARNING), 5 + ) # filter by template with self.reset_logger(): - log.LimitFilter._ignore.add((logging.WARNING, 'Log %s')) + log.LimitFilter._ignore.add((logging.WARNING, "Log %s")) do_logging() + self.assertEqual(self.handler.count_logs("Log \\d", logging.WARNING), 0) self.assertEqual( - self.handler.count_logs('Log \\d', logging.WARNING), - 0) - self.assertEqual( - self.handler.count_logs('Another log \\d', logging.WARNING), - 5) + self.handler.count_logs("Another log \\d", logging.WARNING), 5 + ) # filter by exact message with self.reset_logger(): - log.LimitFilter._ignore.add((logging.WARNING, 'Log 3')) + log.LimitFilter._ignore.add((logging.WARNING, "Log 3")) do_logging() + self.assertEqual(self.handler.count_logs("Log \\d", logging.WARNING), 4) self.assertEqual( - self.handler.count_logs('Log \\d', logging.WARNING), - 4) - self.assertEqual( - self.handler.count_logs('Another log \\d', logging.WARNING), - 5) + self.handler.count_logs("Another log \\d", logging.WARNING), 5 + ) # filter by both with self.reset_logger(): - log.LimitFilter._ignore.add((logging.WARNING, 'Log 3')) - log.LimitFilter._ignore.add((logging.WARNING, 'Another log %s')) + log.LimitFilter._ignore.add((logging.WARNING, "Log 3")) + log.LimitFilter._ignore.add((logging.WARNING, "Another log %s")) do_logging() + self.assertEqual(self.handler.count_logs("Log \\d", logging.WARNING), 4) self.assertEqual( - self.handler.count_logs('Log \\d', logging.WARNING), - 4) - self.assertEqual( - self.handler.count_logs('Another log \\d', logging.WARNING), - 0) + self.handler.count_logs("Another log \\d", logging.WARNING), 0 + ) diff --git a/pelican/tests/test_paginator.py b/pelican/tests/test_paginator.py index f8233eb4..2160421f 100644 --- a/pelican/tests/test_paginator.py +++ b/pelican/tests/test_paginator.py @@ -17,17 +17,17 @@ class TestPage(unittest.TestCase): def setUp(self): super().setUp() self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') + locale.setlocale(locale.LC_ALL, "C") self.page_kwargs = { - 'content': TEST_CONTENT, - 'context': { - 'localsiteurl': '', + "content": TEST_CONTENT, + "context": { + "localsiteurl": "", }, - 'metadata': { - 'summary': TEST_SUMMARY, - 'title': 'foo bar', + "metadata": { + "summary": TEST_SUMMARY, + "title": "foo bar", }, - 'source_path': '/path/to/file/foo.ext' + "source_path": "/path/to/file/foo.ext", } def tearDown(self): @@ -37,68 +37,79 @@ class TestPage(unittest.TestCase): settings = get_settings() # fix up pagination rules from pelican.paginator import PaginationRule + pagination_rules = [ - PaginationRule(*r) for r in settings.get( - 'PAGINATION_PATTERNS', - DEFAULT_CONFIG['PAGINATION_PATTERNS'], + PaginationRule(*r) + for r in settings.get( + "PAGINATION_PATTERNS", + DEFAULT_CONFIG["PAGINATION_PATTERNS"], ) ] - settings['PAGINATION_PATTERNS'] = sorted( + settings["PAGINATION_PATTERNS"] = sorted( pagination_rules, key=lambda r: r[0], ) - self.page_kwargs['metadata']['author'] = Author('Blogger', settings) - object_list = [Article(**self.page_kwargs), - Article(**self.page_kwargs)] - paginator = Paginator('foobar.foo', 'foobar/foo', object_list, - settings) + self.page_kwargs["metadata"]["author"] = Author("Blogger", settings) + object_list = [Article(**self.page_kwargs), Article(**self.page_kwargs)] + paginator = Paginator("foobar.foo", "foobar/foo", object_list, settings) page = paginator.page(1) - self.assertEqual(page.save_as, 'foobar.foo') + self.assertEqual(page.save_as, "foobar.foo") def test_custom_pagination_pattern(self): from pelican.paginator import PaginationRule - settings = get_settings() - settings['PAGINATION_PATTERNS'] = [PaginationRule(*r) for r in [ - (1, '/{url}', '{base_name}/index.html'), - (2, '/{url}{number}/', '{base_name}/{number}/index.html') - ]] - self.page_kwargs['metadata']['author'] = Author('Blogger', settings) - object_list = [Article(**self.page_kwargs), - Article(**self.page_kwargs)] - paginator = Paginator('blog/index.html', '//blog.my.site/', - object_list, settings, 1) + settings = get_settings() + settings["PAGINATION_PATTERNS"] = [ + PaginationRule(*r) + for r in [ + (1, "/{url}", "{base_name}/index.html"), + (2, "/{url}{number}/", "{base_name}/{number}/index.html"), + ] + ] + + self.page_kwargs["metadata"]["author"] = Author("Blogger", settings) + object_list = [Article(**self.page_kwargs), Article(**self.page_kwargs)] + paginator = Paginator( + "blog/index.html", "//blog.my.site/", object_list, settings, 1 + ) # The URL *has to* stay absolute (with // in the front), so verify that page1 = paginator.page(1) - self.assertEqual(page1.save_as, 'blog/index.html') - self.assertEqual(page1.url, '//blog.my.site/') + self.assertEqual(page1.save_as, "blog/index.html") + self.assertEqual(page1.url, "//blog.my.site/") page2 = paginator.page(2) - self.assertEqual(page2.save_as, 'blog/2/index.html') - self.assertEqual(page2.url, '//blog.my.site/2/') + self.assertEqual(page2.save_as, "blog/2/index.html") + self.assertEqual(page2.url, "//blog.my.site/2/") def test_custom_pagination_pattern_last_page(self): from pelican.paginator import PaginationRule - settings = get_settings() - settings['PAGINATION_PATTERNS'] = [PaginationRule(*r) for r in [ - (1, '/{url}1/', '{base_name}/1/index.html'), - (2, '/{url}{number}/', '{base_name}/{number}/index.html'), - (-1, '/{url}', '{base_name}/index.html'), - ]] - self.page_kwargs['metadata']['author'] = Author('Blogger', settings) - object_list = [Article(**self.page_kwargs), - Article(**self.page_kwargs), - Article(**self.page_kwargs)] - paginator = Paginator('blog/index.html', '//blog.my.site/', - object_list, settings, 1) + settings = get_settings() + settings["PAGINATION_PATTERNS"] = [ + PaginationRule(*r) + for r in [ + (1, "/{url}1/", "{base_name}/1/index.html"), + (2, "/{url}{number}/", "{base_name}/{number}/index.html"), + (-1, "/{url}", "{base_name}/index.html"), + ] + ] + + self.page_kwargs["metadata"]["author"] = Author("Blogger", settings) + object_list = [ + Article(**self.page_kwargs), + Article(**self.page_kwargs), + Article(**self.page_kwargs), + ] + paginator = Paginator( + "blog/index.html", "//blog.my.site/", object_list, settings, 1 + ) # The URL *has to* stay absolute (with // in the front), so verify that page1 = paginator.page(1) - self.assertEqual(page1.save_as, 'blog/1/index.html') - self.assertEqual(page1.url, '//blog.my.site/1/') + self.assertEqual(page1.save_as, "blog/1/index.html") + self.assertEqual(page1.url, "//blog.my.site/1/") page2 = paginator.page(2) - self.assertEqual(page2.save_as, 'blog/2/index.html') - self.assertEqual(page2.url, '//blog.my.site/2/') + self.assertEqual(page2.save_as, "blog/2/index.html") + self.assertEqual(page2.url, "//blog.my.site/2/") page3 = paginator.page(3) - self.assertEqual(page3.save_as, 'blog/index.html') - self.assertEqual(page3.url, '//blog.my.site/') + self.assertEqual(page3.save_as, "blog/index.html") + self.assertEqual(page3.url, "//blog.my.site/") diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 885c2138..3c0c0572 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -20,9 +20,10 @@ from pelican.tests.support import ( ) CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) -SAMPLES_PATH = os.path.abspath(os.path.join( - CURRENT_DIR, os.pardir, os.pardir, 'samples')) -OUTPUT_PATH = os.path.abspath(os.path.join(CURRENT_DIR, 'output')) +SAMPLES_PATH = os.path.abspath( + os.path.join(CURRENT_DIR, os.pardir, os.pardir, "samples") +) +OUTPUT_PATH = os.path.abspath(os.path.join(CURRENT_DIR, "output")) INPUT_PATH = os.path.join(SAMPLES_PATH, "content") SAMPLE_CONFIG = os.path.join(SAMPLES_PATH, "pelican.conf.py") @@ -31,9 +32,9 @@ SAMPLE_FR_CONFIG = os.path.join(SAMPLES_PATH, "pelican.conf_FR.py") def recursiveDiff(dcmp): diff = { - 'diff_files': [os.path.join(dcmp.right, f) for f in dcmp.diff_files], - 'left_only': [os.path.join(dcmp.right, f) for f in dcmp.left_only], - 'right_only': [os.path.join(dcmp.right, f) for f in dcmp.right_only], + "diff_files": [os.path.join(dcmp.right, f) for f in dcmp.diff_files], + "left_only": [os.path.join(dcmp.right, f) for f in dcmp.left_only], + "right_only": [os.path.join(dcmp.right, f) for f in dcmp.right_only], } for sub_dcmp in dcmp.subdirs.values(): for k, v in recursiveDiff(sub_dcmp).items(): @@ -47,11 +48,11 @@ class TestPelican(LoggedTestCase): def setUp(self): super().setUp() - self.temp_path = mkdtemp(prefix='pelicantests.') - self.temp_cache = mkdtemp(prefix='pelican_cache.') + self.temp_path = mkdtemp(prefix="pelicantests.") + self.temp_cache = mkdtemp(prefix="pelican_cache.") self.maxDiff = None self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') + locale.setlocale(locale.LC_ALL, "C") def tearDown(self): read_settings() # cleanup PYGMENTS_RST_OPTIONS @@ -70,8 +71,8 @@ class TestPelican(LoggedTestCase): if proc.returncode != 0: msg = self._formatMessage( msg, - "%s and %s differ:\nstdout:\n%s\nstderr\n%s" % - (left_path, right_path, out, err) + "%s and %s differ:\nstdout:\n%s\nstderr\n%s" + % (left_path, right_path, out, err), ) raise self.failureException(msg) @@ -85,136 +86,154 @@ class TestPelican(LoggedTestCase): self.assertTrue( generator_classes[-1] is StaticGenerator, - "StaticGenerator must be the last generator, but it isn't!") + "StaticGenerator must be the last generator, but it isn't!", + ) self.assertIsInstance( - generator_classes, Sequence, - "_get_generator_classes() must return a Sequence to preserve order") + generator_classes, + Sequence, + "_get_generator_classes() must return a Sequence to preserve order", + ) - @skipIfNoExecutable(['git', '--version']) + @skipIfNoExecutable(["git", "--version"]) def test_basic_generation_works(self): # when running pelican without settings, it should pick up the default # ones and generate correct output without raising any exception - settings = read_settings(path=None, override={ - 'PATH': INPUT_PATH, - 'OUTPUT_PATH': self.temp_path, - 'CACHE_PATH': self.temp_cache, - 'LOCALE': locale.normalize('en_US'), - }) + settings = read_settings( + path=None, + override={ + "PATH": INPUT_PATH, + "OUTPUT_PATH": self.temp_path, + "CACHE_PATH": self.temp_cache, + "LOCALE": locale.normalize("en_US"), + }, + ) pelican = Pelican(settings=settings) mute(True)(pelican.run)() - self.assertDirsEqual( - self.temp_path, os.path.join(OUTPUT_PATH, 'basic') - ) + self.assertDirsEqual(self.temp_path, os.path.join(OUTPUT_PATH, "basic")) self.assertLogCountEqual( count=1, msg="Unable to find.*skipping url replacement", - level=logging.WARNING) + level=logging.WARNING, + ) - @skipIfNoExecutable(['git', '--version']) + @skipIfNoExecutable(["git", "--version"]) def test_custom_generation_works(self): # the same thing with a specified set of settings should work - settings = read_settings(path=SAMPLE_CONFIG, override={ - 'PATH': INPUT_PATH, - 'OUTPUT_PATH': self.temp_path, - 'CACHE_PATH': self.temp_cache, - 'LOCALE': locale.normalize('en_US.UTF-8'), - }) + settings = read_settings( + path=SAMPLE_CONFIG, + override={ + "PATH": INPUT_PATH, + "OUTPUT_PATH": self.temp_path, + "CACHE_PATH": self.temp_cache, + "LOCALE": locale.normalize("en_US.UTF-8"), + }, + ) pelican = Pelican(settings=settings) mute(True)(pelican.run)() - self.assertDirsEqual( - self.temp_path, os.path.join(OUTPUT_PATH, 'custom') - ) + self.assertDirsEqual(self.temp_path, os.path.join(OUTPUT_PATH, "custom")) - @skipIfNoExecutable(['git', '--version']) - @unittest.skipUnless(locale_available('fr_FR.UTF-8') or - locale_available('French'), 'French locale needed') + @skipIfNoExecutable(["git", "--version"]) + @unittest.skipUnless( + locale_available("fr_FR.UTF-8") or locale_available("French"), + "French locale needed", + ) def test_custom_locale_generation_works(self): - '''Test that generation with fr_FR.UTF-8 locale works''' - if sys.platform == 'win32': - our_locale = 'French' + """Test that generation with fr_FR.UTF-8 locale works""" + if sys.platform == "win32": + our_locale = "French" else: - our_locale = 'fr_FR.UTF-8' + our_locale = "fr_FR.UTF-8" - settings = read_settings(path=SAMPLE_FR_CONFIG, override={ - 'PATH': INPUT_PATH, - 'OUTPUT_PATH': self.temp_path, - 'CACHE_PATH': self.temp_cache, - 'LOCALE': our_locale, - }) + settings = read_settings( + path=SAMPLE_FR_CONFIG, + override={ + "PATH": INPUT_PATH, + "OUTPUT_PATH": self.temp_path, + "CACHE_PATH": self.temp_cache, + "LOCALE": our_locale, + }, + ) pelican = Pelican(settings=settings) mute(True)(pelican.run)() - self.assertDirsEqual( - self.temp_path, os.path.join(OUTPUT_PATH, 'custom_locale') - ) + self.assertDirsEqual(self.temp_path, os.path.join(OUTPUT_PATH, "custom_locale")) def test_theme_static_paths_copy(self): # the same thing with a specified set of settings should work - settings = read_settings(path=SAMPLE_CONFIG, override={ - 'PATH': INPUT_PATH, - 'OUTPUT_PATH': self.temp_path, - 'CACHE_PATH': self.temp_cache, - 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, 'very'), - os.path.join(SAMPLES_PATH, 'kinda'), - os.path.join(SAMPLES_PATH, - 'theme_standard')] - }) + settings = read_settings( + path=SAMPLE_CONFIG, + override={ + "PATH": INPUT_PATH, + "OUTPUT_PATH": self.temp_path, + "CACHE_PATH": self.temp_cache, + "THEME_STATIC_PATHS": [ + os.path.join(SAMPLES_PATH, "very"), + os.path.join(SAMPLES_PATH, "kinda"), + os.path.join(SAMPLES_PATH, "theme_standard"), + ], + }, + ) pelican = Pelican(settings=settings) mute(True)(pelican.run)() - theme_output = os.path.join(self.temp_path, 'theme') - extra_path = os.path.join(theme_output, 'exciting', 'new', 'files') + theme_output = os.path.join(self.temp_path, "theme") + extra_path = os.path.join(theme_output, "exciting", "new", "files") - for file in ['a_stylesheet', 'a_template']: + for file in ["a_stylesheet", "a_template"]: self.assertTrue(os.path.exists(os.path.join(theme_output, file))) - for file in ['wow!', 'boom!', 'bap!', 'zap!']: + for file in ["wow!", "boom!", "bap!", "zap!"]: self.assertTrue(os.path.exists(os.path.join(extra_path, file))) def test_theme_static_paths_copy_single_file(self): # the same thing with a specified set of settings should work - settings = read_settings(path=SAMPLE_CONFIG, override={ - 'PATH': INPUT_PATH, - 'OUTPUT_PATH': self.temp_path, - 'CACHE_PATH': self.temp_cache, - 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, - 'theme_standard')] - }) + settings = read_settings( + path=SAMPLE_CONFIG, + override={ + "PATH": INPUT_PATH, + "OUTPUT_PATH": self.temp_path, + "CACHE_PATH": self.temp_cache, + "THEME_STATIC_PATHS": [os.path.join(SAMPLES_PATH, "theme_standard")], + }, + ) pelican = Pelican(settings=settings) mute(True)(pelican.run)() - theme_output = os.path.join(self.temp_path, 'theme') + theme_output = os.path.join(self.temp_path, "theme") - for file in ['a_stylesheet', 'a_template']: + for file in ["a_stylesheet", "a_template"]: self.assertTrue(os.path.exists(os.path.join(theme_output, file))) def test_write_only_selected(self): """Test that only the selected files are written""" - settings = read_settings(path=None, override={ - 'PATH': INPUT_PATH, - 'OUTPUT_PATH': self.temp_path, - 'CACHE_PATH': self.temp_cache, - 'WRITE_SELECTED': [ - os.path.join(self.temp_path, 'oh-yeah.html'), - os.path.join(self.temp_path, 'categories.html'), - ], - 'LOCALE': locale.normalize('en_US'), - }) + settings = read_settings( + path=None, + override={ + "PATH": INPUT_PATH, + "OUTPUT_PATH": self.temp_path, + "CACHE_PATH": self.temp_cache, + "WRITE_SELECTED": [ + os.path.join(self.temp_path, "oh-yeah.html"), + os.path.join(self.temp_path, "categories.html"), + ], + "LOCALE": locale.normalize("en_US"), + }, + ) pelican = Pelican(settings=settings) logger = logging.getLogger() orig_level = logger.getEffectiveLevel() logger.setLevel(logging.INFO) mute(True)(pelican.run)() logger.setLevel(orig_level) - self.assertLogCountEqual( - count=2, - msg="Writing .*", - level=logging.INFO) + self.assertLogCountEqual(count=2, msg="Writing .*", level=logging.INFO) def test_cyclic_intersite_links_no_warnings(self): - settings = read_settings(path=None, override={ - 'PATH': os.path.join(CURRENT_DIR, 'cyclic_intersite_links'), - 'OUTPUT_PATH': self.temp_path, - 'CACHE_PATH': self.temp_cache, - }) + settings = read_settings( + path=None, + override={ + "PATH": os.path.join(CURRENT_DIR, "cyclic_intersite_links"), + "OUTPUT_PATH": self.temp_path, + "CACHE_PATH": self.temp_cache, + }, + ) pelican = Pelican(settings=settings) mute(True)(pelican.run)() # There are four different intersite links: @@ -230,41 +249,48 @@ class TestPelican(LoggedTestCase): self.assertLogCountEqual( count=1, msg="Unable to find '.*\\.rst', skipping url replacement.", - level=logging.WARNING) + level=logging.WARNING, + ) def test_md_extensions_deprecation(self): """Test that a warning is issued if MD_EXTENSIONS is used""" - settings = read_settings(path=None, override={ - 'PATH': INPUT_PATH, - 'OUTPUT_PATH': self.temp_path, - 'CACHE_PATH': self.temp_cache, - 'MD_EXTENSIONS': {}, - }) + settings = read_settings( + path=None, + override={ + "PATH": INPUT_PATH, + "OUTPUT_PATH": self.temp_path, + "CACHE_PATH": self.temp_cache, + "MD_EXTENSIONS": {}, + }, + ) pelican = Pelican(settings=settings) mute(True)(pelican.run)() self.assertLogCountEqual( count=1, msg="MD_EXTENSIONS is deprecated use MARKDOWN instead.", - level=logging.WARNING) + level=logging.WARNING, + ) def test_parse_errors(self): # Verify that just an error is printed and the application doesn't # abort, exit or something. - settings = read_settings(path=None, override={ - 'PATH': os.path.abspath(os.path.join(CURRENT_DIR, 'parse_error')), - 'OUTPUT_PATH': self.temp_path, - 'CACHE_PATH': self.temp_cache, - }) + settings = read_settings( + path=None, + override={ + "PATH": os.path.abspath(os.path.join(CURRENT_DIR, "parse_error")), + "OUTPUT_PATH": self.temp_path, + "CACHE_PATH": self.temp_cache, + }, + ) pelican = Pelican(settings=settings) mute(True)(pelican.run)() self.assertLogCountEqual( - count=1, - msg="Could not process .*parse_error.rst", - level=logging.ERROR) + count=1, msg="Could not process .*parse_error.rst", level=logging.ERROR + ) def test_module_load(self): """Test loading via python -m pelican --help displays the help""" - output = subprocess.check_output([ - sys.executable, '-m', 'pelican', '--help' - ]).decode('ascii', 'replace') - assert 'usage:' in output + output = subprocess.check_output( + [sys.executable, "-m", "pelican", "--help"] + ).decode("ascii", "replace") + assert "usage:" in output diff --git a/pelican/tests/test_plugins.py b/pelican/tests/test_plugins.py index 348c3e94..4f02022c 100644 --- a/pelican/tests/test_plugins.py +++ b/pelican/tests/test_plugins.py @@ -2,27 +2,26 @@ import os from contextlib import contextmanager import pelican.tests.dummy_plugins.normal_plugin.normal_plugin as normal_plugin -from pelican.plugins._utils import (get_namespace_plugins, get_plugin_name, - load_plugins) +from pelican.plugins._utils import get_namespace_plugins, get_plugin_name, load_plugins from pelican.tests.support import unittest @contextmanager def tmp_namespace_path(path): - '''Context manager for temporarily appending namespace plugin packages + """Context manager for temporarily appending namespace plugin packages path: path containing the `pelican` folder This modifies the `pelican.__path__` and lets the `pelican.plugins` namespace package resolve it from that. - ''' + """ # This avoids calls to internal `pelican.plugins.__path__._recalculate()` # as it should not be necessary import pelican old_path = pelican.__path__[:] try: - pelican.__path__.append(os.path.join(path, 'pelican')) + pelican.__path__.append(os.path.join(path, "pelican")) yield finally: pelican.__path__ = old_path @@ -30,38 +29,38 @@ def tmp_namespace_path(path): class PluginTest(unittest.TestCase): _PLUGIN_FOLDER = os.path.join( - os.path.abspath(os.path.dirname(__file__)), - 'dummy_plugins') - _NS_PLUGIN_FOLDER = os.path.join(_PLUGIN_FOLDER, 'namespace_plugin') - _NORMAL_PLUGIN_FOLDER = os.path.join(_PLUGIN_FOLDER, 'normal_plugin') + os.path.abspath(os.path.dirname(__file__)), "dummy_plugins" + ) + _NS_PLUGIN_FOLDER = os.path.join(_PLUGIN_FOLDER, "namespace_plugin") + _NORMAL_PLUGIN_FOLDER = os.path.join(_PLUGIN_FOLDER, "normal_plugin") def test_namespace_path_modification(self): import pelican import pelican.plugins + old_path = pelican.__path__[:] # not existing path - path = os.path.join(self._PLUGIN_FOLDER, 'foo') + path = os.path.join(self._PLUGIN_FOLDER, "foo") with tmp_namespace_path(path): - self.assertIn( - os.path.join(path, 'pelican'), - pelican.__path__) + self.assertIn(os.path.join(path, "pelican"), pelican.__path__) # foo/pelican does not exist, so it won't propagate self.assertNotIn( - os.path.join(path, 'pelican', 'plugins'), - pelican.plugins.__path__) + os.path.join(path, "pelican", "plugins"), pelican.plugins.__path__ + ) # verify that we restored path back self.assertEqual(pelican.__path__, old_path) # existing path with tmp_namespace_path(self._NS_PLUGIN_FOLDER): self.assertIn( - os.path.join(self._NS_PLUGIN_FOLDER, 'pelican'), - pelican.__path__) + os.path.join(self._NS_PLUGIN_FOLDER, "pelican"), pelican.__path__ + ) # /namespace_plugin/pelican exists, so it should be in self.assertIn( - os.path.join(self._NS_PLUGIN_FOLDER, 'pelican', 'plugins'), - pelican.plugins.__path__) + os.path.join(self._NS_PLUGIN_FOLDER, "pelican", "plugins"), + pelican.plugins.__path__, + ) self.assertEqual(pelican.__path__, old_path) def test_get_namespace_plugins(self): @@ -71,11 +70,11 @@ class PluginTest(unittest.TestCase): # with plugin with tmp_namespace_path(self._NS_PLUGIN_FOLDER): ns_plugins = get_namespace_plugins() - self.assertEqual(len(ns_plugins), len(existing_ns_plugins)+1) - self.assertIn('pelican.plugins.ns_plugin', ns_plugins) + self.assertEqual(len(ns_plugins), len(existing_ns_plugins) + 1) + self.assertIn("pelican.plugins.ns_plugin", ns_plugins) self.assertEqual( - ns_plugins['pelican.plugins.ns_plugin'].NAME, - 'namespace plugin') + ns_plugins["pelican.plugins.ns_plugin"].NAME, "namespace plugin" + ) # should be back to existing namespace plugins outside `with` ns_plugins = get_namespace_plugins() @@ -91,15 +90,14 @@ class PluginTest(unittest.TestCase): with tmp_namespace_path(self._NS_PLUGIN_FOLDER): # with no `PLUGINS` setting, load namespace plugins plugins = load_plugins({}) - self.assertEqual(len(plugins), len(existing_ns_plugins)+1, plugins) + self.assertEqual(len(plugins), len(existing_ns_plugins) + 1, plugins) self.assertEqual( - {'pelican.plugins.ns_plugin'} | get_plugin_names(existing_ns_plugins), - get_plugin_names(plugins)) + {"pelican.plugins.ns_plugin"} | get_plugin_names(existing_ns_plugins), + get_plugin_names(plugins), + ) # disable namespace plugins with `PLUGINS = []` - SETTINGS = { - 'PLUGINS': [] - } + SETTINGS = {"PLUGINS": []} plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 0, plugins) @@ -107,34 +105,35 @@ class PluginTest(unittest.TestCase): # normal plugin SETTINGS = { - 'PLUGINS': ['normal_plugin'], - 'PLUGIN_PATHS': [self._NORMAL_PLUGIN_FOLDER] + "PLUGINS": ["normal_plugin"], + "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER], } plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 1, plugins) - self.assertEqual( - {'normal_plugin'}, - get_plugin_names(plugins)) + self.assertEqual({"normal_plugin"}, get_plugin_names(plugins)) # normal submodule/subpackage plugins SETTINGS = { - 'PLUGINS': [ - 'normal_submodule_plugin.subplugin', - 'normal_submodule_plugin.subpackage.subpackage', + "PLUGINS": [ + "normal_submodule_plugin.subplugin", + "normal_submodule_plugin.subpackage.subpackage", ], - 'PLUGIN_PATHS': [self._NORMAL_PLUGIN_FOLDER] + "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER], } plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 2, plugins) self.assertEqual( - {'normal_submodule_plugin.subplugin', - 'normal_submodule_plugin.subpackage.subpackage'}, - get_plugin_names(plugins)) + { + "normal_submodule_plugin.subplugin", + "normal_submodule_plugin.subpackage.subpackage", + }, + get_plugin_names(plugins), + ) # ensure normal plugins are loaded only once SETTINGS = { - 'PLUGINS': ['normal_plugin'], - 'PLUGIN_PATHS': [self._NORMAL_PLUGIN_FOLDER], + "PLUGINS": ["normal_plugin"], + "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER], } plugins = load_plugins(SETTINGS) for plugin in load_plugins(SETTINGS): @@ -143,40 +142,33 @@ class PluginTest(unittest.TestCase): self.assertIn(plugin, plugins) # namespace plugin short - SETTINGS = { - 'PLUGINS': ['ns_plugin'] - } + SETTINGS = {"PLUGINS": ["ns_plugin"]} plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 1, plugins) - self.assertEqual( - {'pelican.plugins.ns_plugin'}, - get_plugin_names(plugins)) + self.assertEqual({"pelican.plugins.ns_plugin"}, get_plugin_names(plugins)) # namespace plugin long - SETTINGS = { - 'PLUGINS': ['pelican.plugins.ns_plugin'] - } + SETTINGS = {"PLUGINS": ["pelican.plugins.ns_plugin"]} plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 1, plugins) - self.assertEqual( - {'pelican.plugins.ns_plugin'}, - get_plugin_names(plugins)) + self.assertEqual({"pelican.plugins.ns_plugin"}, get_plugin_names(plugins)) # normal and namespace plugin SETTINGS = { - 'PLUGINS': ['normal_plugin', 'ns_plugin'], - 'PLUGIN_PATHS': [self._NORMAL_PLUGIN_FOLDER] + "PLUGINS": ["normal_plugin", "ns_plugin"], + "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER], } plugins = load_plugins(SETTINGS) self.assertEqual(len(plugins), 2, plugins) self.assertEqual( - {'normal_plugin', 'pelican.plugins.ns_plugin'}, - get_plugin_names(plugins)) + {"normal_plugin", "pelican.plugins.ns_plugin"}, + get_plugin_names(plugins), + ) def test_get_plugin_name(self): self.assertEqual( get_plugin_name(normal_plugin), - 'pelican.tests.dummy_plugins.normal_plugin.normal_plugin', + "pelican.tests.dummy_plugins.normal_plugin.normal_plugin", ) class NoopPlugin: @@ -185,7 +177,9 @@ class PluginTest(unittest.TestCase): self.assertEqual( get_plugin_name(NoopPlugin), - 'PluginTest.test_get_plugin_name..NoopPlugin') + "PluginTest.test_get_plugin_name..NoopPlugin", + ) self.assertEqual( get_plugin_name(NoopPlugin()), - 'PluginTest.test_get_plugin_name..NoopPlugin') + "PluginTest.test_get_plugin_name..NoopPlugin", + ) diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index 753a353d..cf0f39f1 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -7,7 +7,7 @@ from pelican.utils import SafeDatetime CUR_DIR = os.path.dirname(__file__) -CONTENT_PATH = os.path.join(CUR_DIR, 'content') +CONTENT_PATH = os.path.join(CUR_DIR, "content") def _path(*args): @@ -15,7 +15,6 @@ def _path(*args): class ReaderTest(unittest.TestCase): - def read_file(self, path, **kwargs): # Isolate from future API changes to readers.read_file @@ -29,26 +28,24 @@ class ReaderTest(unittest.TestCase): self.assertEqual( value, real_value, - 'Expected %s to have value %s, but was %s' % - (key, value, real_value)) + "Expected %s to have value %s, but was %s" + % (key, value, real_value), + ) else: self.fail( - 'Expected %s to have value %s, but was not in Dict' % - (key, value)) + "Expected %s to have value %s, but was not in Dict" % (key, value) + ) class TestAssertDictHasSubset(ReaderTest): def setUp(self): - self.dictionary = { - 'key-a': 'val-a', - 'key-b': 'val-b' - } + self.dictionary = {"key-a": "val-a", "key-b": "val-b"} def tearDown(self): self.dictionary = None def test_subset(self): - self.assertDictHasSubset(self.dictionary, {'key-a': 'val-a'}) + self.assertDictHasSubset(self.dictionary, {"key-a": "val-a"}) def test_equal(self): self.assertDictHasSubset(self.dictionary, self.dictionary) @@ -56,269 +53,260 @@ class TestAssertDictHasSubset(ReaderTest): def test_fail_not_set(self): self.assertRaisesRegex( AssertionError, - r'Expected.*key-c.*to have value.*val-c.*but was not in Dict', + r"Expected.*key-c.*to have value.*val-c.*but was not in Dict", self.assertDictHasSubset, self.dictionary, - {'key-c': 'val-c'}) + {"key-c": "val-c"}, + ) def test_fail_wrong_val(self): self.assertRaisesRegex( AssertionError, - r'Expected .*key-a.* to have value .*val-b.* but was .*val-a.*', + r"Expected .*key-a.* to have value .*val-b.* but was .*val-a.*", self.assertDictHasSubset, self.dictionary, - {'key-a': 'val-b'}) + {"key-a": "val-b"}, + ) class DefaultReaderTest(ReaderTest): - def test_readfile_unknown_extension(self): with self.assertRaises(TypeError): - self.read_file(path='article_with_metadata.unknownextension') + self.read_file(path="article_with_metadata.unknownextension") def test_readfile_path_metadata_implicit_dates(self): - test_file = 'article_with_metadata_implicit_dates.html' - page = self.read_file(path=test_file, DEFAULT_DATE='fs') + test_file = "article_with_metadata_implicit_dates.html" + page = self.read_file(path=test_file, DEFAULT_DATE="fs") expected = { - 'date': SafeDatetime.fromtimestamp( - os.stat(_path(test_file)).st_mtime), - 'modified': SafeDatetime.fromtimestamp( - os.stat(_path(test_file)).st_mtime) + "date": SafeDatetime.fromtimestamp(os.stat(_path(test_file)).st_mtime), + "modified": SafeDatetime.fromtimestamp(os.stat(_path(test_file)).st_mtime), } self.assertDictHasSubset(page.metadata, expected) def test_readfile_path_metadata_explicit_dates(self): - test_file = 'article_with_metadata_explicit_dates.html' - page = self.read_file(path=test_file, DEFAULT_DATE='fs') + test_file = "article_with_metadata_explicit_dates.html" + page = self.read_file(path=test_file, DEFAULT_DATE="fs") expected = { - 'date': SafeDatetime(2010, 12, 2, 10, 14), - 'modified': SafeDatetime(2010, 12, 31, 23, 59) + "date": SafeDatetime(2010, 12, 2, 10, 14), + "modified": SafeDatetime(2010, 12, 31, 23, 59), } self.assertDictHasSubset(page.metadata, expected) def test_readfile_path_metadata_implicit_date_explicit_modified(self): - test_file = 'article_with_metadata_implicit_date_explicit_modified.html' - page = self.read_file(path=test_file, DEFAULT_DATE='fs') + test_file = "article_with_metadata_implicit_date_explicit_modified.html" + page = self.read_file(path=test_file, DEFAULT_DATE="fs") expected = { - 'date': SafeDatetime.fromtimestamp( - os.stat(_path(test_file)).st_mtime), - 'modified': SafeDatetime(2010, 12, 2, 10, 14), + "date": SafeDatetime.fromtimestamp(os.stat(_path(test_file)).st_mtime), + "modified": SafeDatetime(2010, 12, 2, 10, 14), } self.assertDictHasSubset(page.metadata, expected) def test_readfile_path_metadata_explicit_date_implicit_modified(self): - test_file = 'article_with_metadata_explicit_date_implicit_modified.html' - page = self.read_file(path=test_file, DEFAULT_DATE='fs') + test_file = "article_with_metadata_explicit_date_implicit_modified.html" + page = self.read_file(path=test_file, DEFAULT_DATE="fs") expected = { - 'date': SafeDatetime(2010, 12, 2, 10, 14), - 'modified': SafeDatetime.fromtimestamp( - os.stat(_path(test_file)).st_mtime) + "date": SafeDatetime(2010, 12, 2, 10, 14), + "modified": SafeDatetime.fromtimestamp(os.stat(_path(test_file)).st_mtime), } self.assertDictHasSubset(page.metadata, expected) def test_find_empty_alt(self): - with patch('pelican.readers.logger') as log_mock: - content = ['', - ''] + with patch("pelican.readers.logger") as log_mock: + content = [ + '', + '', + ] for tag in content: - readers.find_empty_alt(tag, '/test/path') + readers.find_empty_alt(tag, "/test/path") log_mock.warning.assert_called_with( - 'Empty alt attribute for image %s in %s', - 'test-image.png', - '/test/path', - extra={'limit_msg': - 'Other images have empty alt attributes'} + "Empty alt attribute for image %s in %s", + "test-image.png", + "/test/path", + extra={"limit_msg": "Other images have empty alt attributes"}, ) class RstReaderTest(ReaderTest): - def test_article_with_metadata(self): - page = self.read_file(path='article_with_metadata.rst') + page = self.read_file(path="article_with_metadata.rst") expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'This is a super article !', - 'summary': '

    Multi-line metadata should be' - ' supported\nas well as inline' - ' markup and stuff to "typogrify' - '"...

    \n', - 'date': SafeDatetime(2010, 12, 2, 10, 14), - 'modified': SafeDatetime(2010, 12, 2, 10, 20), - 'tags': ['foo', 'bar', 'foobar'], - 'custom_field': 'http://notmyidea.org', + "category": "yeah", + "author": "Alexis Métaireau", + "title": "This is a super article !", + "summary": '

    Multi-line metadata should be' + " supported\nas well as inline" + " markup and stuff to "typogrify" + ""...

    \n", + "date": SafeDatetime(2010, 12, 2, 10, 14), + "modified": SafeDatetime(2010, 12, 2, 10, 20), + "tags": ["foo", "bar", "foobar"], + "custom_field": "http://notmyidea.org", } self.assertDictHasSubset(page.metadata, expected) def test_article_with_capitalized_metadata(self): - page = self.read_file(path='article_with_capitalized_metadata.rst') + page = self.read_file(path="article_with_capitalized_metadata.rst") expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'This is a super article !', - 'summary': '

    Multi-line metadata should be' - ' supported\nas well as inline' - ' markup and stuff to "typogrify' - '"...

    \n', - 'date': SafeDatetime(2010, 12, 2, 10, 14), - 'modified': SafeDatetime(2010, 12, 2, 10, 20), - 'tags': ['foo', 'bar', 'foobar'], - 'custom_field': 'http://notmyidea.org', + "category": "yeah", + "author": "Alexis Métaireau", + "title": "This is a super article !", + "summary": '

    Multi-line metadata should be' + " supported\nas well as inline" + " markup and stuff to "typogrify" + ""...

    \n", + "date": SafeDatetime(2010, 12, 2, 10, 14), + "modified": SafeDatetime(2010, 12, 2, 10, 20), + "tags": ["foo", "bar", "foobar"], + "custom_field": "http://notmyidea.org", } self.assertDictHasSubset(page.metadata, expected) def test_article_with_filename_metadata(self): page = self.read_file( - path='2012-11-29_rst_w_filename_meta#foo-bar.rst', - FILENAME_METADATA=None) + path="2012-11-29_rst_w_filename_meta#foo-bar.rst", FILENAME_METADATA=None + ) expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'Rst with filename metadata', - 'reader': 'rst', + "category": "yeah", + "author": "Alexis Métaireau", + "title": "Rst with filename metadata", + "reader": "rst", } self.assertDictHasSubset(page.metadata, expected) page = self.read_file( - path='2012-11-29_rst_w_filename_meta#foo-bar.rst', - FILENAME_METADATA=r'(?P\d{4}-\d{2}-\d{2}).*') + path="2012-11-29_rst_w_filename_meta#foo-bar.rst", + FILENAME_METADATA=r"(?P\d{4}-\d{2}-\d{2}).*", + ) expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'Rst with filename metadata', - 'date': SafeDatetime(2012, 11, 29), - 'reader': 'rst', + "category": "yeah", + "author": "Alexis Métaireau", + "title": "Rst with filename metadata", + "date": SafeDatetime(2012, 11, 29), + "reader": "rst", } self.assertDictHasSubset(page.metadata, expected) page = self.read_file( - path='2012-11-29_rst_w_filename_meta#foo-bar.rst', + path="2012-11-29_rst_w_filename_meta#foo-bar.rst", FILENAME_METADATA=( - r'(?P\d{4}-\d{2}-\d{2})' - r'_(?P.*)' - r'#(?P.*)-(?P.*)')) + r"(?P\d{4}-\d{2}-\d{2})" + r"_(?P.*)" + r"#(?P.*)-(?P.*)" + ), + ) expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'Rst with filename metadata', - 'date': SafeDatetime(2012, 11, 29), - 'slug': 'rst_w_filename_meta', - 'mymeta': 'foo', - 'reader': 'rst', + "category": "yeah", + "author": "Alexis Métaireau", + "title": "Rst with filename metadata", + "date": SafeDatetime(2012, 11, 29), + "slug": "rst_w_filename_meta", + "mymeta": "foo", + "reader": "rst", } self.assertDictHasSubset(page.metadata, expected) def test_article_with_optional_filename_metadata(self): page = self.read_file( - path='2012-11-29_rst_w_filename_meta#foo-bar.rst', - FILENAME_METADATA=r'(?P\d{4}-\d{2}-\d{2})?') + path="2012-11-29_rst_w_filename_meta#foo-bar.rst", + FILENAME_METADATA=r"(?P\d{4}-\d{2}-\d{2})?", + ) expected = { - 'date': SafeDatetime(2012, 11, 29), - 'reader': 'rst', + "date": SafeDatetime(2012, 11, 29), + "reader": "rst", } self.assertDictHasSubset(page.metadata, expected) page = self.read_file( - path='article.rst', - FILENAME_METADATA=r'(?P\d{4}-\d{2}-\d{2})?') + path="article.rst", FILENAME_METADATA=r"(?P\d{4}-\d{2}-\d{2})?" + ) expected = { - 'reader': 'rst', + "reader": "rst", } self.assertDictHasSubset(page.metadata, expected) - self.assertNotIn('date', page.metadata, 'Date should not be set.') + self.assertNotIn("date", page.metadata, "Date should not be set.") def test_article_metadata_key_lowercase(self): # Keys of metadata should be lowercase. reader = readers.RstReader(settings=get_settings()) - content, metadata = reader.read( - _path('article_with_uppercase_metadata.rst')) + content, metadata = reader.read(_path("article_with_uppercase_metadata.rst")) - self.assertIn('category', metadata, 'Key should be lowercase.') - self.assertEqual('Yeah', metadata.get('category'), - 'Value keeps case.') + self.assertIn("category", metadata, "Key should be lowercase.") + self.assertEqual("Yeah", metadata.get("category"), "Value keeps case.") def test_article_extra_path_metadata(self): - input_with_metadata = '2012-11-29_rst_w_filename_meta#foo-bar.rst' + input_with_metadata = "2012-11-29_rst_w_filename_meta#foo-bar.rst" page_metadata = self.read_file( path=input_with_metadata, FILENAME_METADATA=( - r'(?P\d{4}-\d{2}-\d{2})' - r'_(?P.*)' - r'#(?P.*)-(?P.*)' + r"(?P\d{4}-\d{2}-\d{2})" + r"_(?P.*)" + r"#(?P.*)-(?P.*)" ), EXTRA_PATH_METADATA={ - input_with_metadata: { - 'key-1a': 'value-1a', - 'key-1b': 'value-1b' - } - } + input_with_metadata: {"key-1a": "value-1a", "key-1b": "value-1b"} + }, ) expected_metadata = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'Rst with filename metadata', - 'date': SafeDatetime(2012, 11, 29), - 'slug': 'rst_w_filename_meta', - 'mymeta': 'foo', - 'reader': 'rst', - 'key-1a': 'value-1a', - 'key-1b': 'value-1b' + "category": "yeah", + "author": "Alexis Métaireau", + "title": "Rst with filename metadata", + "date": SafeDatetime(2012, 11, 29), + "slug": "rst_w_filename_meta", + "mymeta": "foo", + "reader": "rst", + "key-1a": "value-1a", + "key-1b": "value-1b", } self.assertDictHasSubset(page_metadata.metadata, expected_metadata) - input_file_path_without_metadata = 'article.rst' + input_file_path_without_metadata = "article.rst" page_without_metadata = self.read_file( path=input_file_path_without_metadata, EXTRA_PATH_METADATA={ - input_file_path_without_metadata: { - 'author': 'Charlès Overwrite' - } - } + input_file_path_without_metadata: {"author": "Charlès Overwrite"} + }, ) expected_without_metadata = { - 'category': 'misc', - 'author': 'Charlès Overwrite', - 'title': 'Article title', - 'reader': 'rst', + "category": "misc", + "author": "Charlès Overwrite", + "title": "Article title", + "reader": "rst", } self.assertDictHasSubset( - page_without_metadata.metadata, - expected_without_metadata) + page_without_metadata.metadata, expected_without_metadata + ) def test_article_extra_path_metadata_dont_overwrite(self): # EXTRA_PATH_METADATA['author'] should get ignored # since we don't overwrite already set values - input_file_path = '2012-11-29_rst_w_filename_meta#foo-bar.rst' + input_file_path = "2012-11-29_rst_w_filename_meta#foo-bar.rst" page = self.read_file( path=input_file_path, FILENAME_METADATA=( - r'(?P\d{4}-\d{2}-\d{2})' - r'_(?P.*)' - r'#(?P.*)-(?P.*)' + r"(?P\d{4}-\d{2}-\d{2})" + r"_(?P.*)" + r"#(?P.*)-(?P.*)" ), EXTRA_PATH_METADATA={ - input_file_path: { - 'author': 'Charlès Overwrite', - 'key-1b': 'value-1b' - } - } + input_file_path: {"author": "Charlès Overwrite", "key-1b": "value-1b"} + }, ) expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'Rst with filename metadata', - 'date': SafeDatetime(2012, 11, 29), - 'slug': 'rst_w_filename_meta', - 'mymeta': 'foo', - 'reader': 'rst', - 'key-1b': 'value-1b' + "category": "yeah", + "author": "Alexis Métaireau", + "title": "Rst with filename metadata", + "date": SafeDatetime(2012, 11, 29), + "slug": "rst_w_filename_meta", + "mymeta": "foo", + "reader": "rst", + "key-1b": "value-1b", } self.assertDictHasSubset(page.metadata, expected) @@ -328,15 +316,19 @@ class RstReaderTest(ReaderTest): path = "TestCategory/article_without_category.rst" epm = { - parent: {'epmr_inherit': parent, - 'epmr_override': parent, }, - notparent: {'epmr_bogus': notparent}, - path: {'epmr_override': path, }, - } + parent: { + "epmr_inherit": parent, + "epmr_override": parent, + }, + notparent: {"epmr_bogus": notparent}, + path: { + "epmr_override": path, + }, + } expected_metadata = { - 'epmr_inherit': parent, - 'epmr_override': path, - } + "epmr_inherit": parent, + "epmr_override": path, + } page = self.read_file(path=path, EXTRA_PATH_METADATA=epm) self.assertDictHasSubset(page.metadata, expected_metadata) @@ -357,152 +349,157 @@ class RstReaderTest(ReaderTest): def test_typogrify(self): # if nothing is specified in the settings, the content should be # unmodified - page = self.read_file(path='article.rst') - expected = ('

    THIS is some content. With some stuff to ' - '"typogrify"...

    \n

    Now with added ' - 'support for ' - 'TLA.

    \n') + page = self.read_file(path="article.rst") + expected = ( + "

    THIS is some content. With some stuff to " + ""typogrify"...

    \n

    Now with added " + 'support for ' + "TLA.

    \n" + ) self.assertEqual(page.content, expected) try: # otherwise, typogrify should be applied - page = self.read_file(path='article.rst', TYPOGRIFY=True) + page = self.read_file(path="article.rst", TYPOGRIFY=True) expected = ( '

    THIS is some content. ' - 'With some stuff to “typogrify”…

    \n' + "With some stuff to “typogrify”…

    \n" '

    Now with added support for TLA.

    \n') + 'acronym">TLA.

    \n' + ) self.assertEqual(page.content, expected) except ImportError: - return unittest.skip('need the typogrify distribution') + return unittest.skip("need the typogrify distribution") def test_typogrify_summary(self): # if nothing is specified in the settings, the summary should be # unmodified - page = self.read_file(path='article_with_metadata.rst') - expected = ('

    Multi-line metadata should be' - ' supported\nas well as inline' - ' markup and stuff to "typogrify' - '"...

    \n') + page = self.read_file(path="article_with_metadata.rst") + expected = ( + '

    Multi-line metadata should be' + " supported\nas well as inline" + " markup and stuff to "typogrify" + ""...

    \n" + ) - self.assertEqual(page.metadata['summary'], expected) + self.assertEqual(page.metadata["summary"], expected) try: # otherwise, typogrify should be applied - page = self.read_file(path='article_with_metadata.rst', - TYPOGRIFY=True) - expected = ('

    Multi-line metadata should be' - ' supported\nas well as inline' - ' markup and stuff to “typogrify' - '”…

    \n') + page = self.read_file(path="article_with_metadata.rst", TYPOGRIFY=True) + expected = ( + '

    Multi-line metadata should be' + " supported\nas well as inline" + " markup and stuff to “typogrify" + "”…

    \n" + ) - self.assertEqual(page.metadata['summary'], expected) + self.assertEqual(page.metadata["summary"], expected) except ImportError: - return unittest.skip('need the typogrify distribution') + return unittest.skip("need the typogrify distribution") def test_typogrify_ignore_tags(self): try: # typogrify should be able to ignore user specified tags, # but tries to be clever with widont extension - page = self.read_file(path='article.rst', TYPOGRIFY=True, - TYPOGRIFY_IGNORE_TAGS=['p']) - expected = ('

    THIS is some content. With some stuff to ' - '"typogrify"...

    \n

    Now with added ' - 'support for ' - 'TLA.

    \n') + page = self.read_file( + path="article.rst", TYPOGRIFY=True, TYPOGRIFY_IGNORE_TAGS=["p"] + ) + expected = ( + "

    THIS is some content. With some stuff to " + ""typogrify"...

    \n

    Now with added " + 'support for ' + "TLA.

    \n" + ) self.assertEqual(page.content, expected) # typogrify should ignore code blocks by default because # code blocks are composed inside the pre tag - page = self.read_file(path='article_with_code_block.rst', - TYPOGRIFY=True) + page = self.read_file(path="article_with_code_block.rst", TYPOGRIFY=True) - expected = ('

    An article with some code

    \n' - '
    '
    -                        'x'
    -                        ' &'
    -                        ' y\n
    \n' - '

    A block quote:

    \n
    \nx ' - '& y
    \n' - '

    Normal:\nx' - ' &' - ' y' - '

    \n') + expected = ( + "

    An article with some code

    \n" + '
    '
    +                'x'
    +                ' &'
    +                ' y\n
    \n' + "

    A block quote:

    \n
    \nx " + '& y
    \n' + "

    Normal:\nx" + ' &' + " y" + "

    \n" + ) self.assertEqual(page.content, expected) # instruct typogrify to also ignore blockquotes - page = self.read_file(path='article_with_code_block.rst', - TYPOGRIFY=True, - TYPOGRIFY_IGNORE_TAGS=['blockquote']) + page = self.read_file( + path="article_with_code_block.rst", + TYPOGRIFY=True, + TYPOGRIFY_IGNORE_TAGS=["blockquote"], + ) - expected = ('

    An article with some code

    \n' - '
    '
    -                        'x'
    -                        ' &'
    -                        ' y\n
    \n' - '

    A block quote:

    \n
    \nx ' - '& y
    \n' - '

    Normal:\nx' - ' &' - ' y' - '

    \n') + expected = ( + "

    An article with some code

    \n" + '
    '
    +                'x'
    +                ' &'
    +                ' y\n
    \n' + "

    A block quote:

    \n
    \nx " + "& y
    \n" + "

    Normal:\nx" + ' &' + " y" + "

    \n" + ) self.assertEqual(page.content, expected) except ImportError: - return unittest.skip('need the typogrify distribution') + return unittest.skip("need the typogrify distribution") except TypeError: - return unittest.skip('need typogrify version 2.0.4 or later') + return unittest.skip("need typogrify version 2.0.4 or later") def test_article_with_multiple_authors(self): - page = self.read_file(path='article_with_multiple_authors.rst') - expected = { - 'authors': ['First Author', 'Second Author'] - } + page = self.read_file(path="article_with_multiple_authors.rst") + expected = {"authors": ["First Author", "Second Author"]} self.assertDictHasSubset(page.metadata, expected) def test_article_with_multiple_authors_semicolon(self): - page = self.read_file( - path='article_with_multiple_authors_semicolon.rst') - expected = { - 'authors': ['Author, First', 'Author, Second'] - } + page = self.read_file(path="article_with_multiple_authors_semicolon.rst") + expected = {"authors": ["Author, First", "Author, Second"]} self.assertDictHasSubset(page.metadata, expected) def test_article_with_multiple_authors_list(self): - page = self.read_file(path='article_with_multiple_authors_list.rst') - expected = { - 'authors': ['Author, First', 'Author, Second'] - } + page = self.read_file(path="article_with_multiple_authors_list.rst") + expected = {"authors": ["Author, First", "Author, Second"]} self.assertDictHasSubset(page.metadata, expected) def test_default_date_formats(self): - tuple_date = self.read_file(path='article.rst', - DEFAULT_DATE=(2012, 5, 1)) - string_date = self.read_file(path='article.rst', - DEFAULT_DATE='2012-05-01') + tuple_date = self.read_file(path="article.rst", DEFAULT_DATE=(2012, 5, 1)) + string_date = self.read_file(path="article.rst", DEFAULT_DATE="2012-05-01") - self.assertEqual(tuple_date.metadata['date'], - string_date.metadata['date']) + self.assertEqual(tuple_date.metadata["date"], string_date.metadata["date"]) def test_parse_error(self): # Verify that it raises an Exception, not nothing and not SystemExit or # some such with self.assertRaisesRegex(Exception, "underline too short"): - self.read_file(path='../parse_error/parse_error.rst') + self.read_file(path="../parse_error/parse_error.rst") def test_typogrify_dashes_config(self): # Test default config page = self.read_file( - path='article_with_typogrify_dashes.rst', + path="article_with_typogrify_dashes.rst", TYPOGRIFY=True, - TYPOGRIFY_DASHES='default') + TYPOGRIFY_DASHES="default", + ) expected = "

    One: -; Two: —; Three: —-

    \n" expected_title = "One -, two —, three —- dashes!" @@ -511,9 +508,10 @@ class RstReaderTest(ReaderTest): # Test 'oldschool' variant page = self.read_file( - path='article_with_typogrify_dashes.rst', + path="article_with_typogrify_dashes.rst", TYPOGRIFY=True, - TYPOGRIFY_DASHES='oldschool') + TYPOGRIFY_DASHES="oldschool", + ) expected = "

    One: -; Two: –; Three: —

    \n" expected_title = "One -, two –, three — dashes!" @@ -522,9 +520,10 @@ class RstReaderTest(ReaderTest): # Test 'oldschool_inverted' variant page = self.read_file( - path='article_with_typogrify_dashes.rst', + path="article_with_typogrify_dashes.rst", TYPOGRIFY=True, - TYPOGRIFY_DASHES='oldschool_inverted') + TYPOGRIFY_DASHES="oldschool_inverted", + ) expected = "

    One: -; Two: —; Three: –

    \n" expected_title = "One -, two —, three – dashes!" @@ -534,75 +533,73 @@ class RstReaderTest(ReaderTest): @unittest.skipUnless(readers.Markdown, "markdown isn't installed") class MdReaderTest(ReaderTest): - def test_article_with_metadata(self): reader = readers.MarkdownReader(settings=get_settings()) - content, metadata = reader.read( - _path('article_with_md_extension.md')) + content, metadata = reader.read(_path("article_with_md_extension.md")) expected = { - 'category': 'test', - 'title': 'Test md File', - 'summary': '

    I have a lot to test

    ', - 'date': SafeDatetime(2010, 12, 2, 10, 14), - 'modified': SafeDatetime(2010, 12, 2, 10, 20), - 'tags': ['foo', 'bar', 'foobar'], + "category": "test", + "title": "Test md File", + "summary": "

    I have a lot to test

    ", + "date": SafeDatetime(2010, 12, 2, 10, 14), + "modified": SafeDatetime(2010, 12, 2, 10, 20), + "tags": ["foo", "bar", "foobar"], } self.assertDictHasSubset(metadata, expected) content, metadata = reader.read( - _path('article_with_markdown_and_nonascii_summary.md')) + _path("article_with_markdown_and_nonascii_summary.md") + ) expected = { - 'title': 'マックOS X 10.8でパイソンとVirtualenvをインストールと設定', - 'summary': '

    パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。

    ', - 'category': '指導書', - 'date': SafeDatetime(2012, 12, 20), - 'modified': SafeDatetime(2012, 12, 22), - 'tags': ['パイソン', 'マック'], - 'slug': 'python-virtualenv-on-mac-osx-mountain-lion-10.8', + "title": "マックOS X 10.8でパイソンとVirtualenvをインストールと設定", + "summary": "

    パイソンとVirtualenvをまっくでインストールする方法について明確に説明します。

    ", + "category": "指導書", + "date": SafeDatetime(2012, 12, 20), + "modified": SafeDatetime(2012, 12, 22), + "tags": ["パイソン", "マック"], + "slug": "python-virtualenv-on-mac-osx-mountain-lion-10.8", } self.assertDictHasSubset(metadata, expected) def test_article_with_footnote(self): settings = get_settings() - ec = settings['MARKDOWN']['extension_configs'] - ec['markdown.extensions.footnotes'] = {'SEPARATOR': '-'} + ec = settings["MARKDOWN"]["extension_configs"] + ec["markdown.extensions.footnotes"] = {"SEPARATOR": "-"} reader = readers.MarkdownReader(settings) - content, metadata = reader.read( - _path('article_with_markdown_and_footnote.md')) + content, metadata = reader.read(_path("article_with_markdown_and_footnote.md")) expected_content = ( - '

    This is some content' + "

    This is some content" '1' - ' with some footnotes' + ">1" + " with some footnotes" '2

    \n' - '
    \n' '
    \n
      \n
    1. \n' - '

      Numbered footnote ' + "

      Numbered footnote " '

      \n' '
    2. \n
    3. \n' - '

      Named footnote ' + "

      Named footnote " '

      \n' - '
    4. \n
    \n
    ') + "\n\n
    " + ) expected_metadata = { - 'title': 'Article with markdown containing footnotes', - 'summary': ( - '

    Summary with inline markup ' - 'should be supported.

    '), - 'date': SafeDatetime(2012, 10, 31), - 'modified': SafeDatetime(2012, 11, 1), - 'multiline': [ - 'Line Metadata should be handle properly.', - 'See syntax of Meta-Data extension of ' - 'Python Markdown package:', - 'If a line is indented by 4 or more spaces,', - 'that line is assumed to be an additional line of the value', - 'for the previous keyword.', - 'A keyword may have as many lines as desired.', - ] + "title": "Article with markdown containing footnotes", + "summary": ( + "

    Summary with inline markup " + "should be supported.

    " + ), + "date": SafeDatetime(2012, 10, 31), + "modified": SafeDatetime(2012, 11, 1), + "multiline": [ + "Line Metadata should be handle properly.", + "See syntax of Meta-Data extension of " "Python Markdown package:", + "If a line is indented by 4 or more spaces,", + "that line is assumed to be an additional line of the value", + "for the previous keyword.", + "A keyword may have as many lines as desired.", + ], } self.assertEqual(content, expected_content) self.assertDictHasSubset(metadata, expected_metadata) @@ -611,163 +608,173 @@ class MdReaderTest(ReaderTest): reader = readers.MarkdownReader(settings=get_settings()) # test to ensure the md file extension is being processed by the # correct reader - content, metadata = reader.read( - _path('article_with_md_extension.md')) + content, metadata = reader.read(_path("article_with_md_extension.md")) expected = ( "

    Test Markdown File Header

    \n" "

    Used for pelican test

    \n" - "

    The quick brown fox jumped over the lazy dog's back.

    ") + "

    The quick brown fox jumped over the lazy dog's back.

    " + ) self.assertEqual(content, expected) # test to ensure the mkd file extension is being processed by the # correct reader - content, metadata = reader.read( - _path('article_with_mkd_extension.mkd')) - expected = ("

    Test Markdown File Header

    \n

    Used for pelican" - " test

    \n

    This is another markdown test file. Uses" - " the mkd extension.

    ") + content, metadata = reader.read(_path("article_with_mkd_extension.mkd")) + expected = ( + "

    Test Markdown File Header

    \n

    Used for pelican" + " test

    \n

    This is another markdown test file. Uses" + " the mkd extension.

    " + ) self.assertEqual(content, expected) # test to ensure the markdown file extension is being processed by the # correct reader content, metadata = reader.read( - _path('article_with_markdown_extension.markdown')) - expected = ("

    Test Markdown File Header

    \n

    Used for pelican" - " test

    \n

    This is another markdown test file. Uses" - " the markdown extension.

    ") + _path("article_with_markdown_extension.markdown") + ) + expected = ( + "

    Test Markdown File Header

    \n

    Used for pelican" + " test

    \n

    This is another markdown test file. Uses" + " the markdown extension.

    " + ) self.assertEqual(content, expected) # test to ensure the mdown file extension is being processed by the # correct reader - content, metadata = reader.read( - _path('article_with_mdown_extension.mdown')) - expected = ("

    Test Markdown File Header

    \n

    Used for pelican" - " test

    \n

    This is another markdown test file. Uses" - " the mdown extension.

    ") + content, metadata = reader.read(_path("article_with_mdown_extension.mdown")) + expected = ( + "

    Test Markdown File Header

    \n

    Used for pelican" + " test

    \n

    This is another markdown test file. Uses" + " the mdown extension.

    " + ) self.assertEqual(content, expected) def test_article_with_markdown_markup_extension(self): # test to ensure the markdown markup extension is being processed as # expected page = self.read_file( - path='article_with_markdown_markup_extensions.md', + path="article_with_markdown_markup_extensions.md", MARKDOWN={ - 'extension_configs': { - 'markdown.extensions.toc': {}, - 'markdown.extensions.codehilite': {}, - 'markdown.extensions.extra': {} + "extension_configs": { + "markdown.extensions.toc": {}, + "markdown.extensions.codehilite": {}, + "markdown.extensions.extra": {}, } - } + }, + ) + expected = ( + '
    \n' + "\n" + "
    \n" + '

    Level1

    \n' + '

    Level2

    ' ) - expected = ('
    \n' - '\n' - '
    \n' - '

    Level1

    \n' - '

    Level2

    ') self.assertEqual(page.content, expected) def test_article_with_filename_metadata(self): page = self.read_file( - path='2012-11-30_md_w_filename_meta#foo-bar.md', - FILENAME_METADATA=None) + path="2012-11-30_md_w_filename_meta#foo-bar.md", FILENAME_METADATA=None + ) expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', + "category": "yeah", + "author": "Alexis Métaireau", } self.assertDictHasSubset(page.metadata, expected) page = self.read_file( - path='2012-11-30_md_w_filename_meta#foo-bar.md', - FILENAME_METADATA=r'(?P\d{4}-\d{2}-\d{2}).*') + path="2012-11-30_md_w_filename_meta#foo-bar.md", + FILENAME_METADATA=r"(?P\d{4}-\d{2}-\d{2}).*", + ) expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'date': SafeDatetime(2012, 11, 30), + "category": "yeah", + "author": "Alexis Métaireau", + "date": SafeDatetime(2012, 11, 30), } self.assertDictHasSubset(page.metadata, expected) page = self.read_file( - path='2012-11-30_md_w_filename_meta#foo-bar.md', + path="2012-11-30_md_w_filename_meta#foo-bar.md", FILENAME_METADATA=( - r'(?P\d{4}-\d{2}-\d{2})' - r'_(?P.*)' - r'#(?P.*)-(?P.*)')) + r"(?P\d{4}-\d{2}-\d{2})" + r"_(?P.*)" + r"#(?P.*)-(?P.*)" + ), + ) expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'date': SafeDatetime(2012, 11, 30), - 'slug': 'md_w_filename_meta', - 'mymeta': 'foo', + "category": "yeah", + "author": "Alexis Métaireau", + "date": SafeDatetime(2012, 11, 30), + "slug": "md_w_filename_meta", + "mymeta": "foo", } self.assertDictHasSubset(page.metadata, expected) def test_article_with_optional_filename_metadata(self): page = self.read_file( - path='2012-11-30_md_w_filename_meta#foo-bar.md', - FILENAME_METADATA=r'(?P\d{4}-\d{2}-\d{2})?') + path="2012-11-30_md_w_filename_meta#foo-bar.md", + FILENAME_METADATA=r"(?P\d{4}-\d{2}-\d{2})?", + ) expected = { - 'date': SafeDatetime(2012, 11, 30), - 'reader': 'markdown', + "date": SafeDatetime(2012, 11, 30), + "reader": "markdown", } self.assertDictHasSubset(page.metadata, expected) page = self.read_file( - path='empty.md', - FILENAME_METADATA=r'(?P\d{4}-\d{2}-\d{2})?') + path="empty.md", FILENAME_METADATA=r"(?P\d{4}-\d{2}-\d{2})?" + ) expected = { - 'reader': 'markdown', + "reader": "markdown", } self.assertDictHasSubset(page.metadata, expected) - self.assertNotIn('date', page.metadata, 'Date should not be set.') + self.assertNotIn("date", page.metadata, "Date should not be set.") def test_duplicate_tags_or_authors_are_removed(self): reader = readers.MarkdownReader(settings=get_settings()) - content, metadata = reader.read( - _path('article_with_duplicate_tags_authors.md')) + content, metadata = reader.read(_path("article_with_duplicate_tags_authors.md")) expected = { - 'tags': ['foo', 'bar', 'foobar'], - 'authors': ['Author, First', 'Author, Second'], + "tags": ["foo", "bar", "foobar"], + "authors": ["Author, First", "Author, Second"], } self.assertDictHasSubset(metadata, expected) def test_metadata_not_parsed_for_metadata(self): settings = get_settings() - settings['FORMATTED_FIELDS'] = ['summary'] + settings["FORMATTED_FIELDS"] = ["summary"] reader = readers.MarkdownReader(settings=settings) content, metadata = reader.read( - _path('article_with_markdown_and_nested_metadata.md')) + _path("article_with_markdown_and_nested_metadata.md") + ) expected = { - 'title': 'Article with markdown and nested summary metadata', - 'summary': '

    Test: This metadata value looks like metadata

    ', + "title": "Article with markdown and nested summary metadata", + "summary": "

    Test: This metadata value looks like metadata

    ", } self.assertDictHasSubset(metadata, expected) def test_empty_file(self): reader = readers.MarkdownReader(settings=get_settings()) - content, metadata = reader.read( - _path('empty.md')) + content, metadata = reader.read(_path("empty.md")) self.assertEqual(metadata, {}) - self.assertEqual(content, '') + self.assertEqual(content, "") def test_empty_file_with_bom(self): reader = readers.MarkdownReader(settings=get_settings()) - content, metadata = reader.read( - _path('empty_with_bom.md')) + content, metadata = reader.read(_path("empty_with_bom.md")) self.assertEqual(metadata, {}) - self.assertEqual(content, '') + self.assertEqual(content, "") def test_typogrify_dashes_config(self): # Test default config page = self.read_file( - path='article_with_typogrify_dashes.md', + path="article_with_typogrify_dashes.md", TYPOGRIFY=True, - TYPOGRIFY_DASHES='default') + TYPOGRIFY_DASHES="default", + ) expected = "

    One: -; Two: —; Three: —-

    " expected_title = "One -, two —, three —- dashes!" @@ -776,9 +783,10 @@ class MdReaderTest(ReaderTest): # Test 'oldschool' variant page = self.read_file( - path='article_with_typogrify_dashes.md', + path="article_with_typogrify_dashes.md", TYPOGRIFY=True, - TYPOGRIFY_DASHES='oldschool') + TYPOGRIFY_DASHES="oldschool", + ) expected = "

    One: -; Two: –; Three: —

    " expected_title = "One -, two –, three — dashes!" @@ -787,9 +795,10 @@ class MdReaderTest(ReaderTest): # Test 'oldschool_inverted' variant page = self.read_file( - path='article_with_typogrify_dashes.md', + path="article_with_typogrify_dashes.md", TYPOGRIFY=True, - TYPOGRIFY_DASHES='oldschool_inverted') + TYPOGRIFY_DASHES="oldschool_inverted", + ) expected = "

    One: -; Two: —; Three: –

    " expected_title = "One -, two —, three – dashes!" @@ -797,124 +806,130 @@ class MdReaderTest(ReaderTest): self.assertEqual(page.title, expected_title) def test_metadata_has_no_discarded_data(self): - md_filename = 'article_with_markdown_and_empty_tags.md' + md_filename = "article_with_markdown_and_empty_tags.md" - r = readers.Readers(cache_name='cache', settings=get_settings( - CACHE_CONTENT=True)) + r = readers.Readers( + cache_name="cache", settings=get_settings(CACHE_CONTENT=True) + ) page = r.read_file(base_path=CONTENT_PATH, path=md_filename) - __, cached_metadata = r.get_cached_data( - _path(md_filename), (None, None)) + __, cached_metadata = r.get_cached_data(_path(md_filename), (None, None)) - expected = { - 'title': 'Article with markdown and empty tags' - } + expected = {"title": "Article with markdown and empty tags"} self.assertEqual(cached_metadata, expected) - self.assertNotIn('tags', page.metadata) + self.assertNotIn("tags", page.metadata) self.assertDictHasSubset(page.metadata, expected) class HTMLReaderTest(ReaderTest): def test_article_with_comments(self): - page = self.read_file(path='article_with_comments.html') + page = self.read_file(path="article_with_comments.html") - self.assertEqual(''' + self.assertEqual( + """ Body content - ''', page.content) + """, + page.content, + ) def test_article_with_keywords(self): - page = self.read_file(path='article_with_keywords.html') + page = self.read_file(path="article_with_keywords.html") expected = { - 'tags': ['foo', 'bar', 'foobar'], + "tags": ["foo", "bar", "foobar"], } self.assertDictHasSubset(page.metadata, expected) def test_article_with_metadata(self): - page = self.read_file(path='article_with_metadata.html') + page = self.read_file(path="article_with_metadata.html") expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'This is a super article !', - 'summary': 'Summary and stuff', - 'date': SafeDatetime(2010, 12, 2, 10, 14), - 'tags': ['foo', 'bar', 'foobar'], - 'custom_field': 'http://notmyidea.org', + "category": "yeah", + "author": "Alexis Métaireau", + "title": "This is a super article !", + "summary": "Summary and stuff", + "date": SafeDatetime(2010, 12, 2, 10, 14), + "tags": ["foo", "bar", "foobar"], + "custom_field": "http://notmyidea.org", } self.assertDictHasSubset(page.metadata, expected) def test_article_with_multiple_similar_metadata_tags(self): - page = self.read_file(path='article_with_multiple_metadata_tags.html') + page = self.read_file(path="article_with_multiple_metadata_tags.html") expected = { - 'custom_field': ['https://getpelican.com', 'https://www.eff.org'], + "custom_field": ["https://getpelican.com", "https://www.eff.org"], } self.assertDictHasSubset(page.metadata, expected) def test_article_with_multiple_authors(self): - page = self.read_file(path='article_with_multiple_authors.html') - expected = { - 'authors': ['First Author', 'Second Author'] - } + page = self.read_file(path="article_with_multiple_authors.html") + expected = {"authors": ["First Author", "Second Author"]} self.assertDictHasSubset(page.metadata, expected) def test_article_with_metadata_and_contents_attrib(self): - page = self.read_file(path='article_with_metadata_and_contents.html') + page = self.read_file(path="article_with_metadata_and_contents.html") expected = { - 'category': 'yeah', - 'author': 'Alexis Métaireau', - 'title': 'This is a super article !', - 'summary': 'Summary and stuff', - 'date': SafeDatetime(2010, 12, 2, 10, 14), - 'tags': ['foo', 'bar', 'foobar'], - 'custom_field': 'http://notmyidea.org', + "category": "yeah", + "author": "Alexis Métaireau", + "title": "This is a super article !", + "summary": "Summary and stuff", + "date": SafeDatetime(2010, 12, 2, 10, 14), + "tags": ["foo", "bar", "foobar"], + "custom_field": "http://notmyidea.org", } self.assertDictHasSubset(page.metadata, expected) def test_article_with_null_attributes(self): - page = self.read_file(path='article_with_null_attributes.html') + page = self.read_file(path="article_with_null_attributes.html") - self.assertEqual(''' + self.assertEqual( + """ Ensure that empty attributes are copied properly. - ''', page.content) + """, + page.content, + ) def test_article_with_attributes_containing_double_quotes(self): - page = self.read_file(path='article_with_attributes_containing_' + - 'double_quotes.html') - self.assertEqual(''' + page = self.read_file( + path="article_with_attributes_containing_" + "double_quotes.html" + ) + self.assertEqual( + """ Ensure that if an attribute value contains a double quote, it is surrounded with single quotes, otherwise with double quotes. Span content Span content Span content - ''', page.content) + """, + page.content, + ) def test_article_metadata_key_lowercase(self): # Keys of metadata should be lowercase. - page = self.read_file(path='article_with_uppercase_metadata.html') + page = self.read_file(path="article_with_uppercase_metadata.html") # Key should be lowercase - self.assertIn('category', page.metadata, 'Key should be lowercase.') + self.assertIn("category", page.metadata, "Key should be lowercase.") # Value should keep cases - self.assertEqual('Yeah', page.metadata.get('category')) + self.assertEqual("Yeah", page.metadata.get("category")) def test_article_with_nonconformant_meta_tags(self): - page = self.read_file(path='article_with_nonconformant_meta_tags.html') + page = self.read_file(path="article_with_nonconformant_meta_tags.html") expected = { - 'summary': 'Summary and stuff', - 'title': 'Article with Nonconformant HTML meta tags', + "summary": "Summary and stuff", + "title": "Article with Nonconformant HTML meta tags", } self.assertDictHasSubset(page.metadata, expected) def test_article_with_inline_svg(self): - page = self.read_file(path='article_with_inline_svg.html') + page = self.read_file(path="article_with_inline_svg.html") expected = { - 'title': 'Article with an inline SVG', + "title": "Article with an inline SVG", } self.assertDictHasSubset(page.metadata, expected) diff --git a/pelican/tests/test_rstdirectives.py b/pelican/tests/test_rstdirectives.py index 6b733971..46ed6f49 100644 --- a/pelican/tests/test_rstdirectives.py +++ b/pelican/tests/test_rstdirectives.py @@ -6,11 +6,11 @@ from pelican.tests.support import unittest class Test_abbr_role(unittest.TestCase): def call_it(self, text): from pelican.rstdirectives import abbr_role + rawtext = text lineno = 42 - inliner = Mock(name='inliner') - nodes, system_messages = abbr_role( - 'abbr', rawtext, text, lineno, inliner) + inliner = Mock(name="inliner") + nodes, system_messages = abbr_role("abbr", rawtext, text, lineno, inliner) self.assertEqual(system_messages, []) self.assertEqual(len(nodes), 1) return nodes[0] @@ -18,14 +18,14 @@ class Test_abbr_role(unittest.TestCase): def test(self): node = self.call_it("Abbr (Abbreviation)") self.assertEqual(node.astext(), "Abbr") - self.assertEqual(node['explanation'], "Abbreviation") + self.assertEqual(node["explanation"], "Abbreviation") def test_newlines_in_explanation(self): node = self.call_it("CUL (See you\nlater)") self.assertEqual(node.astext(), "CUL") - self.assertEqual(node['explanation'], "See you\nlater") + self.assertEqual(node["explanation"], "See you\nlater") def test_newlines_in_abbr(self): node = self.call_it("US of\nA \n (USA)") self.assertEqual(node.astext(), "US of\nA") - self.assertEqual(node['explanation'], "USA") + self.assertEqual(node["explanation"], "USA") diff --git a/pelican/tests/test_server.py b/pelican/tests/test_server.py index 9af030f8..fd616ef7 100644 --- a/pelican/tests/test_server.py +++ b/pelican/tests/test_server.py @@ -17,10 +17,9 @@ class MockServer: class TestServer(unittest.TestCase): - def setUp(self): self.server = MockServer() - self.temp_output = mkdtemp(prefix='pelicantests.') + self.temp_output = mkdtemp(prefix="pelicantests.") self.old_cwd = os.getcwd() os.chdir(self.temp_output) @@ -29,32 +28,33 @@ class TestServer(unittest.TestCase): rmtree(self.temp_output) def test_get_path_that_exists(self): - handler = ComplexHTTPRequestHandler(MockRequest(), ('0.0.0.0', 8888), - self.server) + handler = ComplexHTTPRequestHandler( + MockRequest(), ("0.0.0.0", 8888), self.server + ) handler.base_path = self.temp_output - open(os.path.join(self.temp_output, 'foo.html'), 'a').close() - os.mkdir(os.path.join(self.temp_output, 'foo')) - open(os.path.join(self.temp_output, 'foo', 'index.html'), 'a').close() + open(os.path.join(self.temp_output, "foo.html"), "a").close() + os.mkdir(os.path.join(self.temp_output, "foo")) + open(os.path.join(self.temp_output, "foo", "index.html"), "a").close() - os.mkdir(os.path.join(self.temp_output, 'bar')) - open(os.path.join(self.temp_output, 'bar', 'index.html'), 'a').close() + os.mkdir(os.path.join(self.temp_output, "bar")) + open(os.path.join(self.temp_output, "bar", "index.html"), "a").close() - os.mkdir(os.path.join(self.temp_output, 'baz')) + os.mkdir(os.path.join(self.temp_output, "baz")) - for suffix in ['', '/']: + for suffix in ["", "/"]: # foo.html has precedence over foo/index.html - path = handler.get_path_that_exists('foo' + suffix) - self.assertEqual(path, 'foo.html') + path = handler.get_path_that_exists("foo" + suffix) + self.assertEqual(path, "foo.html") # folder with index.html should return folder/index.html - path = handler.get_path_that_exists('bar' + suffix) - self.assertEqual(path, 'bar/index.html') + path = handler.get_path_that_exists("bar" + suffix) + self.assertEqual(path, "bar/index.html") # folder without index.html should return same as input - path = handler.get_path_that_exists('baz' + suffix) - self.assertEqual(path, 'baz' + suffix) + path = handler.get_path_that_exists("baz" + suffix) + self.assertEqual(path, "baz" + suffix) # not existing path should return None - path = handler.get_path_that_exists('quux' + suffix) + path = handler.get_path_that_exists("quux" + suffix) self.assertIsNone(path) diff --git a/pelican/tests/test_settings.py b/pelican/tests/test_settings.py index 0f630ad5..0e77674d 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -4,10 +4,14 @@ import os from os.path import abspath, dirname, join -from pelican.settings import (DEFAULT_CONFIG, DEFAULT_THEME, - _printf_s_to_format_field, - configure_settings, - handle_deprecated_settings, read_settings) +from pelican.settings import ( + DEFAULT_CONFIG, + DEFAULT_THEME, + _printf_s_to_format_field, + configure_settings, + handle_deprecated_settings, + read_settings, +) from pelican.tests.support import unittest @@ -16,40 +20,39 @@ class TestSettingsConfiguration(unittest.TestCase): append new values to the settings (if any), and apply basic settings optimizations. """ + def setUp(self): self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') + locale.setlocale(locale.LC_ALL, "C") self.PATH = abspath(dirname(__file__)) - default_conf = join(self.PATH, 'default_conf.py') + default_conf = join(self.PATH, "default_conf.py") self.settings = read_settings(default_conf) def tearDown(self): locale.setlocale(locale.LC_ALL, self.old_locale) def test_overwrite_existing_settings(self): - self.assertEqual(self.settings.get('SITENAME'), "Alexis' log") - self.assertEqual( - self.settings.get('SITEURL'), - 'http://blog.notmyidea.org') + self.assertEqual(self.settings.get("SITENAME"), "Alexis' log") + self.assertEqual(self.settings.get("SITEURL"), "http://blog.notmyidea.org") def test_keep_default_settings(self): # Keep default settings if not defined. self.assertEqual( - self.settings.get('DEFAULT_CATEGORY'), - DEFAULT_CONFIG['DEFAULT_CATEGORY']) + self.settings.get("DEFAULT_CATEGORY"), DEFAULT_CONFIG["DEFAULT_CATEGORY"] + ) def test_dont_copy_small_keys(self): # Do not copy keys not in caps. - self.assertNotIn('foobar', self.settings) + self.assertNotIn("foobar", self.settings) def test_read_empty_settings(self): # Ensure an empty settings file results in default settings. settings = read_settings(None) expected = copy.deepcopy(DEFAULT_CONFIG) # Added by configure settings - expected['FEED_DOMAIN'] = '' - expected['ARTICLE_EXCLUDES'] = ['pages'] - expected['PAGE_EXCLUDES'] = [''] + expected["FEED_DOMAIN"] = "" + expected["ARTICLE_EXCLUDES"] = ["pages"] + expected["PAGE_EXCLUDES"] = [""] self.maxDiff = None self.assertDictEqual(settings, expected) @@ -57,250 +60,265 @@ class TestSettingsConfiguration(unittest.TestCase): # Make sure that the results from one settings call doesn't # effect past or future instances. self.PATH = abspath(dirname(__file__)) - default_conf = join(self.PATH, 'default_conf.py') + default_conf = join(self.PATH, "default_conf.py") settings = read_settings(default_conf) - settings['SITEURL'] = 'new-value' + settings["SITEURL"] = "new-value" new_settings = read_settings(default_conf) - self.assertNotEqual(new_settings['SITEURL'], settings['SITEURL']) + self.assertNotEqual(new_settings["SITEURL"], settings["SITEURL"]) def test_defaults_not_overwritten(self): # This assumes 'SITENAME': 'A Pelican Blog' settings = read_settings(None) - settings['SITENAME'] = 'Not a Pelican Blog' - self.assertNotEqual(settings['SITENAME'], DEFAULT_CONFIG['SITENAME']) + settings["SITENAME"] = "Not a Pelican Blog" + self.assertNotEqual(settings["SITENAME"], DEFAULT_CONFIG["SITENAME"]) def test_static_path_settings_safety(self): # Disallow static paths from being strings settings = { - 'STATIC_PATHS': 'foo/bar', - 'THEME_STATIC_PATHS': 'bar/baz', + "STATIC_PATHS": "foo/bar", + "THEME_STATIC_PATHS": "bar/baz", # These 4 settings are required to run configure_settings - 'PATH': '.', - 'THEME': DEFAULT_THEME, - 'SITEURL': 'http://blog.notmyidea.org/', - 'LOCALE': '', + "PATH": ".", + "THEME": DEFAULT_THEME, + "SITEURL": "http://blog.notmyidea.org/", + "LOCALE": "", } configure_settings(settings) + self.assertEqual(settings["STATIC_PATHS"], DEFAULT_CONFIG["STATIC_PATHS"]) self.assertEqual( - settings['STATIC_PATHS'], - DEFAULT_CONFIG['STATIC_PATHS']) - self.assertEqual( - settings['THEME_STATIC_PATHS'], - DEFAULT_CONFIG['THEME_STATIC_PATHS']) + settings["THEME_STATIC_PATHS"], DEFAULT_CONFIG["THEME_STATIC_PATHS"] + ) def test_configure_settings(self): # Manipulations to settings should be applied correctly. settings = { - 'SITEURL': 'http://blog.notmyidea.org/', - 'LOCALE': '', - 'PATH': os.curdir, - 'THEME': DEFAULT_THEME, + "SITEURL": "http://blog.notmyidea.org/", + "LOCALE": "", + "PATH": os.curdir, + "THEME": DEFAULT_THEME, } configure_settings(settings) # SITEURL should not have a trailing slash - self.assertEqual(settings['SITEURL'], 'http://blog.notmyidea.org') + self.assertEqual(settings["SITEURL"], "http://blog.notmyidea.org") # FEED_DOMAIN, if undefined, should default to SITEURL - self.assertEqual(settings['FEED_DOMAIN'], 'http://blog.notmyidea.org') + self.assertEqual(settings["FEED_DOMAIN"], "http://blog.notmyidea.org") - settings['FEED_DOMAIN'] = 'http://feeds.example.com' + settings["FEED_DOMAIN"] = "http://feeds.example.com" configure_settings(settings) - self.assertEqual(settings['FEED_DOMAIN'], 'http://feeds.example.com') + self.assertEqual(settings["FEED_DOMAIN"], "http://feeds.example.com") def test_theme_settings_exceptions(self): settings = self.settings # Check that theme lookup in "pelican/themes" functions as expected - settings['THEME'] = os.path.split(settings['THEME'])[1] + settings["THEME"] = os.path.split(settings["THEME"])[1] configure_settings(settings) - self.assertEqual(settings['THEME'], DEFAULT_THEME) + self.assertEqual(settings["THEME"], DEFAULT_THEME) # Check that non-existent theme raises exception - settings['THEME'] = 'foo' + settings["THEME"] = "foo" self.assertRaises(Exception, configure_settings, settings) def test_deprecated_dir_setting(self): settings = self.settings - settings['ARTICLE_DIR'] = 'foo' - settings['PAGE_DIR'] = 'bar' + settings["ARTICLE_DIR"] = "foo" + settings["PAGE_DIR"] = "bar" settings = handle_deprecated_settings(settings) - self.assertEqual(settings['ARTICLE_PATHS'], ['foo']) - self.assertEqual(settings['PAGE_PATHS'], ['bar']) + self.assertEqual(settings["ARTICLE_PATHS"], ["foo"]) + self.assertEqual(settings["PAGE_PATHS"], ["bar"]) with self.assertRaises(KeyError): - settings['ARTICLE_DIR'] - settings['PAGE_DIR'] + settings["ARTICLE_DIR"] + settings["PAGE_DIR"] def test_default_encoding(self): # Test that the user locale is set if not specified in settings - locale.setlocale(locale.LC_ALL, 'C') + locale.setlocale(locale.LC_ALL, "C") # empty string = user system locale - self.assertEqual(self.settings['LOCALE'], ['']) + self.assertEqual(self.settings["LOCALE"], [""]) configure_settings(self.settings) lc_time = locale.getlocale(locale.LC_TIME) # should be set to user locale # explicitly set locale to user pref and test - locale.setlocale(locale.LC_TIME, '') + locale.setlocale(locale.LC_TIME, "") self.assertEqual(lc_time, locale.getlocale(locale.LC_TIME)) def test_invalid_settings_throw_exception(self): # Test that the path name is valid # test that 'PATH' is set - settings = { - } + settings = {} self.assertRaises(Exception, configure_settings, settings) # Test that 'PATH' is valid - settings['PATH'] = '' + settings["PATH"] = "" self.assertRaises(Exception, configure_settings, settings) # Test nonexistent THEME - settings['PATH'] = os.curdir - settings['THEME'] = 'foo' + settings["PATH"] = os.curdir + settings["THEME"] = "foo" self.assertRaises(Exception, configure_settings, settings) def test__printf_s_to_format_field(self): - for s in ('%s', '{%s}', '{%s'): - option = 'foo/{}/bar.baz'.format(s) - result = _printf_s_to_format_field(option, 'slug') - expected = option % 'qux' - found = result.format(slug='qux') + for s in ("%s", "{%s}", "{%s"): + option = "foo/{}/bar.baz".format(s) + result = _printf_s_to_format_field(option, "slug") + expected = option % "qux" + found = result.format(slug="qux") self.assertEqual(expected, found) def test_deprecated_extra_templates_paths(self): settings = self.settings - settings['EXTRA_TEMPLATES_PATHS'] = ['/foo/bar', '/ha'] + settings["EXTRA_TEMPLATES_PATHS"] = ["/foo/bar", "/ha"] settings = handle_deprecated_settings(settings) - self.assertEqual(settings['THEME_TEMPLATES_OVERRIDES'], - ['/foo/bar', '/ha']) - self.assertNotIn('EXTRA_TEMPLATES_PATHS', settings) + self.assertEqual(settings["THEME_TEMPLATES_OVERRIDES"], ["/foo/bar", "/ha"]) + self.assertNotIn("EXTRA_TEMPLATES_PATHS", settings) def test_deprecated_paginated_direct_templates(self): settings = self.settings - settings['PAGINATED_DIRECT_TEMPLATES'] = ['index', 'archives'] - settings['PAGINATED_TEMPLATES'] = {'index': 10, 'category': None} + settings["PAGINATED_DIRECT_TEMPLATES"] = ["index", "archives"] + settings["PAGINATED_TEMPLATES"] = {"index": 10, "category": None} settings = handle_deprecated_settings(settings) - self.assertEqual(settings['PAGINATED_TEMPLATES'], - {'index': 10, 'category': None, 'archives': None}) - self.assertNotIn('PAGINATED_DIRECT_TEMPLATES', settings) + self.assertEqual( + settings["PAGINATED_TEMPLATES"], + {"index": 10, "category": None, "archives": None}, + ) + self.assertNotIn("PAGINATED_DIRECT_TEMPLATES", settings) def test_deprecated_paginated_direct_templates_from_file(self): # This is equivalent to reading a settings file that has # PAGINATED_DIRECT_TEMPLATES defined but no PAGINATED_TEMPLATES. - settings = read_settings(None, override={ - 'PAGINATED_DIRECT_TEMPLATES': ['index', 'archives'] - }) - self.assertEqual(settings['PAGINATED_TEMPLATES'], { - 'archives': None, - 'author': None, - 'index': None, - 'category': None, - 'tag': None}) - self.assertNotIn('PAGINATED_DIRECT_TEMPLATES', settings) + settings = read_settings( + None, override={"PAGINATED_DIRECT_TEMPLATES": ["index", "archives"]} + ) + self.assertEqual( + settings["PAGINATED_TEMPLATES"], + { + "archives": None, + "author": None, + "index": None, + "category": None, + "tag": None, + }, + ) + self.assertNotIn("PAGINATED_DIRECT_TEMPLATES", settings) def test_theme_and_extra_templates_exception(self): settings = self.settings - settings['EXTRA_TEMPLATES_PATHS'] = ['/ha'] - settings['THEME_TEMPLATES_OVERRIDES'] = ['/foo/bar'] + settings["EXTRA_TEMPLATES_PATHS"] = ["/ha"] + settings["THEME_TEMPLATES_OVERRIDES"] = ["/foo/bar"] self.assertRaises(Exception, handle_deprecated_settings, settings) def test_slug_and_slug_regex_substitutions_exception(self): settings = {} - settings['SLUG_REGEX_SUBSTITUTIONS'] = [('C++', 'cpp')] - settings['TAG_SUBSTITUTIONS'] = [('C#', 'csharp')] + settings["SLUG_REGEX_SUBSTITUTIONS"] = [("C++", "cpp")] + settings["TAG_SUBSTITUTIONS"] = [("C#", "csharp")] self.assertRaises(Exception, handle_deprecated_settings, settings) def test_deprecated_slug_substitutions(self): - default_slug_regex_subs = self.settings['SLUG_REGEX_SUBSTITUTIONS'] + default_slug_regex_subs = self.settings["SLUG_REGEX_SUBSTITUTIONS"] # If no deprecated setting is set, don't set new ones settings = {} settings = handle_deprecated_settings(settings) - self.assertNotIn('SLUG_REGEX_SUBSTITUTIONS', settings) - self.assertNotIn('TAG_REGEX_SUBSTITUTIONS', settings) - self.assertNotIn('CATEGORY_REGEX_SUBSTITUTIONS', settings) - self.assertNotIn('AUTHOR_REGEX_SUBSTITUTIONS', settings) + self.assertNotIn("SLUG_REGEX_SUBSTITUTIONS", settings) + self.assertNotIn("TAG_REGEX_SUBSTITUTIONS", settings) + self.assertNotIn("CATEGORY_REGEX_SUBSTITUTIONS", settings) + self.assertNotIn("AUTHOR_REGEX_SUBSTITUTIONS", settings) # If SLUG_SUBSTITUTIONS is set, set {SLUG, AUTHOR}_REGEX_SUBSTITUTIONS # correctly, don't set {CATEGORY, TAG}_REGEX_SUBSTITUTIONS settings = {} - settings['SLUG_SUBSTITUTIONS'] = [('C++', 'cpp')] + settings["SLUG_SUBSTITUTIONS"] = [("C++", "cpp")] settings = handle_deprecated_settings(settings) - self.assertEqual(settings.get('SLUG_REGEX_SUBSTITUTIONS'), - [(r'C\+\+', 'cpp')] + default_slug_regex_subs) - self.assertNotIn('TAG_REGEX_SUBSTITUTIONS', settings) - self.assertNotIn('CATEGORY_REGEX_SUBSTITUTIONS', settings) - self.assertEqual(settings.get('AUTHOR_REGEX_SUBSTITUTIONS'), - default_slug_regex_subs) + self.assertEqual( + settings.get("SLUG_REGEX_SUBSTITUTIONS"), + [(r"C\+\+", "cpp")] + default_slug_regex_subs, + ) + self.assertNotIn("TAG_REGEX_SUBSTITUTIONS", settings) + self.assertNotIn("CATEGORY_REGEX_SUBSTITUTIONS", settings) + self.assertEqual( + settings.get("AUTHOR_REGEX_SUBSTITUTIONS"), default_slug_regex_subs + ) # If {CATEGORY, TAG, AUTHOR}_SUBSTITUTIONS are set, set # {CATEGORY, TAG, AUTHOR}_REGEX_SUBSTITUTIONS correctly, don't set # SLUG_REGEX_SUBSTITUTIONS settings = {} - settings['TAG_SUBSTITUTIONS'] = [('C#', 'csharp')] - settings['CATEGORY_SUBSTITUTIONS'] = [('C#', 'csharp')] - settings['AUTHOR_SUBSTITUTIONS'] = [('Alexander Todorov', 'atodorov')] + settings["TAG_SUBSTITUTIONS"] = [("C#", "csharp")] + settings["CATEGORY_SUBSTITUTIONS"] = [("C#", "csharp")] + settings["AUTHOR_SUBSTITUTIONS"] = [("Alexander Todorov", "atodorov")] settings = handle_deprecated_settings(settings) - self.assertNotIn('SLUG_REGEX_SUBSTITUTIONS', settings) - self.assertEqual(settings['TAG_REGEX_SUBSTITUTIONS'], - [(r'C\#', 'csharp')] + default_slug_regex_subs) - self.assertEqual(settings['CATEGORY_REGEX_SUBSTITUTIONS'], - [(r'C\#', 'csharp')] + default_slug_regex_subs) - self.assertEqual(settings['AUTHOR_REGEX_SUBSTITUTIONS'], - [(r'Alexander\ Todorov', 'atodorov')] + - default_slug_regex_subs) + self.assertNotIn("SLUG_REGEX_SUBSTITUTIONS", settings) + self.assertEqual( + settings["TAG_REGEX_SUBSTITUTIONS"], + [(r"C\#", "csharp")] + default_slug_regex_subs, + ) + self.assertEqual( + settings["CATEGORY_REGEX_SUBSTITUTIONS"], + [(r"C\#", "csharp")] + default_slug_regex_subs, + ) + self.assertEqual( + settings["AUTHOR_REGEX_SUBSTITUTIONS"], + [(r"Alexander\ Todorov", "atodorov")] + default_slug_regex_subs, + ) # If {SLUG, CATEGORY, TAG, AUTHOR}_SUBSTITUTIONS are set, set # {SLUG, CATEGORY, TAG, AUTHOR}_REGEX_SUBSTITUTIONS correctly settings = {} - settings['SLUG_SUBSTITUTIONS'] = [('C++', 'cpp')] - settings['TAG_SUBSTITUTIONS'] = [('C#', 'csharp')] - settings['CATEGORY_SUBSTITUTIONS'] = [('C#', 'csharp')] - settings['AUTHOR_SUBSTITUTIONS'] = [('Alexander Todorov', 'atodorov')] + settings["SLUG_SUBSTITUTIONS"] = [("C++", "cpp")] + settings["TAG_SUBSTITUTIONS"] = [("C#", "csharp")] + settings["CATEGORY_SUBSTITUTIONS"] = [("C#", "csharp")] + settings["AUTHOR_SUBSTITUTIONS"] = [("Alexander Todorov", "atodorov")] settings = handle_deprecated_settings(settings) - self.assertEqual(settings['TAG_REGEX_SUBSTITUTIONS'], - [(r'C\+\+', 'cpp')] + [(r'C\#', 'csharp')] + - default_slug_regex_subs) - self.assertEqual(settings['CATEGORY_REGEX_SUBSTITUTIONS'], - [(r'C\+\+', 'cpp')] + [(r'C\#', 'csharp')] + - default_slug_regex_subs) - self.assertEqual(settings['AUTHOR_REGEX_SUBSTITUTIONS'], - [(r'Alexander\ Todorov', 'atodorov')] + - default_slug_regex_subs) + self.assertEqual( + settings["TAG_REGEX_SUBSTITUTIONS"], + [(r"C\+\+", "cpp")] + [(r"C\#", "csharp")] + default_slug_regex_subs, + ) + self.assertEqual( + settings["CATEGORY_REGEX_SUBSTITUTIONS"], + [(r"C\+\+", "cpp")] + [(r"C\#", "csharp")] + default_slug_regex_subs, + ) + self.assertEqual( + settings["AUTHOR_REGEX_SUBSTITUTIONS"], + [(r"Alexander\ Todorov", "atodorov")] + default_slug_regex_subs, + ) # Handle old 'skip' flags correctly settings = {} - settings['SLUG_SUBSTITUTIONS'] = [('C++', 'cpp', True)] - settings['AUTHOR_SUBSTITUTIONS'] = [('Alexander Todorov', 'atodorov', - False)] + settings["SLUG_SUBSTITUTIONS"] = [("C++", "cpp", True)] + settings["AUTHOR_SUBSTITUTIONS"] = [("Alexander Todorov", "atodorov", False)] settings = handle_deprecated_settings(settings) - self.assertEqual(settings.get('SLUG_REGEX_SUBSTITUTIONS'), - [(r'C\+\+', 'cpp')] + - [(r'(?u)\A\s*', ''), (r'(?u)\s*\Z', '')]) - self.assertEqual(settings['AUTHOR_REGEX_SUBSTITUTIONS'], - [(r'Alexander\ Todorov', 'atodorov')] + - default_slug_regex_subs) + self.assertEqual( + settings.get("SLUG_REGEX_SUBSTITUTIONS"), + [(r"C\+\+", "cpp")] + [(r"(?u)\A\s*", ""), (r"(?u)\s*\Z", "")], + ) + self.assertEqual( + settings["AUTHOR_REGEX_SUBSTITUTIONS"], + [(r"Alexander\ Todorov", "atodorov")] + default_slug_regex_subs, + ) def test_deprecated_slug_substitutions_from_file(self): # This is equivalent to reading a settings file that has # SLUG_SUBSTITUTIONS defined but no SLUG_REGEX_SUBSTITUTIONS. - settings = read_settings(None, override={ - 'SLUG_SUBSTITUTIONS': [('C++', 'cpp')] - }) - self.assertEqual(settings['SLUG_REGEX_SUBSTITUTIONS'], - [(r'C\+\+', 'cpp')] + - self.settings['SLUG_REGEX_SUBSTITUTIONS']) - self.assertNotIn('SLUG_SUBSTITUTIONS', settings) + settings = read_settings( + None, override={"SLUG_SUBSTITUTIONS": [("C++", "cpp")]} + ) + self.assertEqual( + settings["SLUG_REGEX_SUBSTITUTIONS"], + [(r"C\+\+", "cpp")] + self.settings["SLUG_REGEX_SUBSTITUTIONS"], + ) + self.assertNotIn("SLUG_SUBSTITUTIONS", settings) diff --git a/pelican/tests/test_testsuite.py b/pelican/tests/test_testsuite.py index fa930139..a9a0c200 100644 --- a/pelican/tests/test_testsuite.py +++ b/pelican/tests/test_testsuite.py @@ -4,7 +4,6 @@ from pelican.tests.support import unittest class TestSuiteTest(unittest.TestCase): - def test_error_on_warning(self): with self.assertRaises(UserWarning): - warnings.warn('test warning') + warnings.warn("test warning") diff --git a/pelican/tests/test_urlwrappers.py b/pelican/tests/test_urlwrappers.py index 66ae1524..13632e3a 100644 --- a/pelican/tests/test_urlwrappers.py +++ b/pelican/tests/test_urlwrappers.py @@ -5,22 +5,22 @@ from pelican.urlwrappers import Author, Category, Tag, URLWrapper class TestURLWrapper(unittest.TestCase): def test_ordering(self): # URLWrappers are sorted by name - wrapper_a = URLWrapper(name='first', settings={}) - wrapper_b = URLWrapper(name='last', settings={}) + wrapper_a = URLWrapper(name="first", settings={}) + wrapper_b = URLWrapper(name="last", settings={}) self.assertFalse(wrapper_a > wrapper_b) self.assertFalse(wrapper_a >= wrapper_b) self.assertFalse(wrapper_a == wrapper_b) self.assertTrue(wrapper_a != wrapper_b) self.assertTrue(wrapper_a <= wrapper_b) self.assertTrue(wrapper_a < wrapper_b) - wrapper_b.name = 'first' + wrapper_b.name = "first" self.assertFalse(wrapper_a > wrapper_b) self.assertTrue(wrapper_a >= wrapper_b) self.assertTrue(wrapper_a == wrapper_b) self.assertFalse(wrapper_a != wrapper_b) self.assertTrue(wrapper_a <= wrapper_b) self.assertFalse(wrapper_a < wrapper_b) - wrapper_a.name = 'last' + wrapper_a.name = "last" self.assertTrue(wrapper_a > wrapper_b) self.assertTrue(wrapper_a >= wrapper_b) self.assertFalse(wrapper_a == wrapper_b) @@ -29,57 +29,68 @@ class TestURLWrapper(unittest.TestCase): self.assertFalse(wrapper_a < wrapper_b) def test_equality(self): - tag = Tag('test', settings={}) - cat = Category('test', settings={}) - author = Author('test', settings={}) + tag = Tag("test", settings={}) + cat = Category("test", settings={}) + author = Author("test", settings={}) # same name, but different class self.assertNotEqual(tag, cat) self.assertNotEqual(tag, author) # should be equal vs text representing the same name - self.assertEqual(tag, 'test') + self.assertEqual(tag, "test") # should not be equal vs binary - self.assertNotEqual(tag, b'test') + self.assertNotEqual(tag, b"test") # Tags describing the same should be equal - tag_equal = Tag('Test', settings={}) + tag_equal = Tag("Test", settings={}) self.assertEqual(tag, tag_equal) # Author describing the same should be equal - author_equal = Author('Test', settings={}) + author_equal = Author("Test", settings={}) self.assertEqual(author, author_equal) - cat_ascii = Category('指導書', settings={}) - self.assertEqual(cat_ascii, 'zhi dao shu') + cat_ascii = Category("指導書", settings={}) + self.assertEqual(cat_ascii, "zhi dao shu") def test_slugify_with_substitutions_and_dots(self): - tag = Tag('Tag Dot', settings={'TAG_REGEX_SUBSTITUTIONS': [ - ('Tag Dot', 'tag.dot'), - ]}) - cat = Category('Category Dot', - settings={'CATEGORY_REGEX_SUBSTITUTIONS': [ - ('Category Dot', 'cat.dot'), - ]}) + tag = Tag( + "Tag Dot", + settings={ + "TAG_REGEX_SUBSTITUTIONS": [ + ("Tag Dot", "tag.dot"), + ] + }, + ) + cat = Category( + "Category Dot", + settings={ + "CATEGORY_REGEX_SUBSTITUTIONS": [ + ("Category Dot", "cat.dot"), + ] + }, + ) - self.assertEqual(tag.slug, 'tag.dot') - self.assertEqual(cat.slug, 'cat.dot') + self.assertEqual(tag.slug, "tag.dot") + self.assertEqual(cat.slug, "cat.dot") def test_author_slug_substitutions(self): - settings = {'AUTHOR_REGEX_SUBSTITUTIONS': [ - ('Alexander Todorov', 'atodorov'), - ('Krasimir Tsonev', 'krasimir'), - (r'[^\w\s-]', ''), - (r'(?u)\A\s*', ''), - (r'(?u)\s*\Z', ''), - (r'[-\s]+', '-'), - ]} + settings = { + "AUTHOR_REGEX_SUBSTITUTIONS": [ + ("Alexander Todorov", "atodorov"), + ("Krasimir Tsonev", "krasimir"), + (r"[^\w\s-]", ""), + (r"(?u)\A\s*", ""), + (r"(?u)\s*\Z", ""), + (r"[-\s]+", "-"), + ] + } - author1 = Author('Mr. Senko', settings=settings) - author2 = Author('Alexander Todorov', settings=settings) - author3 = Author('Krasimir Tsonev', settings=settings) + author1 = Author("Mr. Senko", settings=settings) + author2 = Author("Alexander Todorov", settings=settings) + author3 = Author("Krasimir Tsonev", settings=settings) - self.assertEqual(author1.slug, 'mr-senko') - self.assertEqual(author2.slug, 'atodorov') - self.assertEqual(author3.slug, 'krasimir') + self.assertEqual(author1.slug, "mr-senko") + self.assertEqual(author2.slug, "atodorov") + self.assertEqual(author3.slug, "krasimir") diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 40aff005..22dd8e38 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -14,25 +14,29 @@ except ModuleNotFoundError: from pelican import utils from pelican.generators import TemplatePagesGenerator from pelican.settings import read_settings -from pelican.tests.support import (LoggedTestCase, get_article, - locale_available, unittest) +from pelican.tests.support import ( + LoggedTestCase, + get_article, + locale_available, + unittest, +) from pelican.writers import Writer class TestUtils(LoggedTestCase): - _new_attribute = 'new_value' + _new_attribute = "new_value" def setUp(self): super().setUp() - self.temp_output = mkdtemp(prefix='pelicantests.') + self.temp_output = mkdtemp(prefix="pelicantests.") def tearDown(self): super().tearDown() shutil.rmtree(self.temp_output) @utils.deprecated_attribute( - old='_old_attribute', new='_new_attribute', - since=(3, 1, 0), remove=(4, 1, 3)) + old="_old_attribute", new="_new_attribute", since=(3, 1, 0), remove=(4, 1, 3) + ) def _old_attribute(): return None @@ -41,69 +45,109 @@ class TestUtils(LoggedTestCase): self.assertEqual(value, self._new_attribute) self.assertLogCountEqual( count=1, - msg=('_old_attribute has been deprecated since 3.1.0 and will be ' - 'removed by version 4.1.3. Use _new_attribute instead'), - level=logging.WARNING) + msg=( + "_old_attribute has been deprecated since 3.1.0 and will be " + "removed by version 4.1.3. Use _new_attribute instead" + ), + level=logging.WARNING, + ) def test_get_date(self): # valid ones date = utils.SafeDatetime(year=2012, month=11, day=22) - date_hour = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11) + date_hour = utils.SafeDatetime(year=2012, month=11, day=22, hour=22, minute=11) date_hour_z = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, - tzinfo=timezone.utc) + year=2012, month=11, day=22, hour=22, minute=11, tzinfo=timezone.utc + ) date_hour_est = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, - tzinfo=ZoneInfo("EST")) + year=2012, month=11, day=22, hour=22, minute=11, tzinfo=ZoneInfo("EST") + ) date_hour_sec = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, second=10) + year=2012, month=11, day=22, hour=22, minute=11, second=10 + ) date_hour_sec_z = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, second=10, - tzinfo=timezone.utc) + year=2012, + month=11, + day=22, + hour=22, + minute=11, + second=10, + tzinfo=timezone.utc, + ) date_hour_sec_est = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, second=10, - tzinfo=ZoneInfo("EST")) + year=2012, + month=11, + day=22, + hour=22, + minute=11, + second=10, + tzinfo=ZoneInfo("EST"), + ) date_hour_sec_frac_z = utils.SafeDatetime( - year=2012, month=11, day=22, hour=22, minute=11, second=10, - microsecond=123000, tzinfo=timezone.utc) + year=2012, + month=11, + day=22, + hour=22, + minute=11, + second=10, + microsecond=123000, + tzinfo=timezone.utc, + ) dates = { - '2012-11-22': date, - '2012/11/22': date, - '2012-11-22 22:11': date_hour, - '2012/11/22 22:11': date_hour, - '22-11-2012': date, - '22/11/2012': date, - '22.11.2012': date, - '22.11.2012 22:11': date_hour, - '2012-11-22T22:11Z': date_hour_z, - '2012-11-22T22:11-0500': date_hour_est, - '2012-11-22 22:11:10': date_hour_sec, - '2012-11-22T22:11:10Z': date_hour_sec_z, - '2012-11-22T22:11:10-0500': date_hour_sec_est, - '2012-11-22T22:11:10.123Z': date_hour_sec_frac_z, + "2012-11-22": date, + "2012/11/22": date, + "2012-11-22 22:11": date_hour, + "2012/11/22 22:11": date_hour, + "22-11-2012": date, + "22/11/2012": date, + "22.11.2012": date, + "22.11.2012 22:11": date_hour, + "2012-11-22T22:11Z": date_hour_z, + "2012-11-22T22:11-0500": date_hour_est, + "2012-11-22 22:11:10": date_hour_sec, + "2012-11-22T22:11:10Z": date_hour_sec_z, + "2012-11-22T22:11:10-0500": date_hour_sec_est, + "2012-11-22T22:11:10.123Z": date_hour_sec_frac_z, } # examples from http://www.w3.org/TR/NOTE-datetime iso_8601_date = utils.SafeDatetime(year=1997, month=7, day=16) iso_8601_date_hour_tz = utils.SafeDatetime( - year=1997, month=7, day=16, hour=19, minute=20, - tzinfo=ZoneInfo("Europe/London")) + year=1997, + month=7, + day=16, + hour=19, + minute=20, + tzinfo=ZoneInfo("Europe/London"), + ) iso_8601_date_hour_sec_tz = utils.SafeDatetime( - year=1997, month=7, day=16, hour=19, minute=20, second=30, - tzinfo=ZoneInfo("Europe/London")) + year=1997, + month=7, + day=16, + hour=19, + minute=20, + second=30, + tzinfo=ZoneInfo("Europe/London"), + ) iso_8601_date_hour_sec_ms_tz = utils.SafeDatetime( - year=1997, month=7, day=16, hour=19, minute=20, second=30, - microsecond=450000, tzinfo=ZoneInfo("Europe/London")) + year=1997, + month=7, + day=16, + hour=19, + minute=20, + second=30, + microsecond=450000, + tzinfo=ZoneInfo("Europe/London"), + ) iso_8601 = { - '1997-07-16': iso_8601_date, - '1997-07-16T19:20+01:00': iso_8601_date_hour_tz, - '1997-07-16T19:20:30+01:00': iso_8601_date_hour_sec_tz, - '1997-07-16T19:20:30.45+01:00': iso_8601_date_hour_sec_ms_tz, + "1997-07-16": iso_8601_date, + "1997-07-16T19:20+01:00": iso_8601_date_hour_tz, + "1997-07-16T19:20:30+01:00": iso_8601_date_hour_sec_tz, + "1997-07-16T19:20:30.45+01:00": iso_8601_date_hour_sec_ms_tz, } # invalid ones - invalid_dates = ['2010-110-12', 'yay'] + invalid_dates = ["2010-110-12", "yay"] for value, expected in dates.items(): self.assertEqual(utils.get_date(value), expected, value) @@ -115,219 +159,247 @@ class TestUtils(LoggedTestCase): self.assertRaises(ValueError, utils.get_date, item) def test_slugify(self): - - samples = (('this is a test', 'this-is-a-test'), - ('this is a test', 'this-is-a-test'), - ('this → is ← a ↑ test', 'this-is-a-test'), - ('this--is---a test', 'this-is-a-test'), - ('unicode測試許功蓋,你看到了嗎?', - 'unicodece-shi-xu-gong-gai-ni-kan-dao-liao-ma'), - ('大飯原発4号機、18日夜起動へ', - 'da-fan-yuan-fa-4hao-ji-18ri-ye-qi-dong-he'),) + samples = ( + ("this is a test", "this-is-a-test"), + ("this is a test", "this-is-a-test"), + ("this → is ← a ↑ test", "this-is-a-test"), + ("this--is---a test", "this-is-a-test"), + ( + "unicode測試許功蓋,你看到了嗎?", + "unicodece-shi-xu-gong-gai-ni-kan-dao-liao-ma", + ), + ( + "大飯原発4号機、18日夜起動へ", + "da-fan-yuan-fa-4hao-ji-18ri-ye-qi-dong-he", + ), + ) settings = read_settings() - subs = settings['SLUG_REGEX_SUBSTITUTIONS'] + subs = settings["SLUG_REGEX_SUBSTITUTIONS"] for value, expected in samples: self.assertEqual(utils.slugify(value, regex_subs=subs), expected) - self.assertEqual(utils.slugify('Cat', regex_subs=subs), 'cat') + self.assertEqual(utils.slugify("Cat", regex_subs=subs), "cat") self.assertEqual( - utils.slugify('Cat', regex_subs=subs, preserve_case=False), 'cat') + utils.slugify("Cat", regex_subs=subs, preserve_case=False), "cat" + ) self.assertEqual( - utils.slugify('Cat', regex_subs=subs, preserve_case=True), 'Cat') + utils.slugify("Cat", regex_subs=subs, preserve_case=True), "Cat" + ) def test_slugify_use_unicode(self): - samples = ( - ('this is a test', 'this-is-a-test'), - ('this is a test', 'this-is-a-test'), - ('this → is ← a ↑ test', 'this-is-a-test'), - ('this--is---a test', 'this-is-a-test'), - ('unicode測試許功蓋,你看到了嗎?', 'unicode測試許功蓋你看到了嗎'), - ('Çığ', 'çığ') + ("this is a test", "this-is-a-test"), + ("this is a test", "this-is-a-test"), + ("this → is ← a ↑ test", "this-is-a-test"), + ("this--is---a test", "this-is-a-test"), + ("unicode測試許功蓋,你看到了嗎?", "unicode測試許功蓋你看到了嗎"), + ("Çığ", "çığ"), ) settings = read_settings() - subs = settings['SLUG_REGEX_SUBSTITUTIONS'] + subs = settings["SLUG_REGEX_SUBSTITUTIONS"] for value, expected in samples: self.assertEqual( - utils.slugify(value, regex_subs=subs, use_unicode=True), - expected) + utils.slugify(value, regex_subs=subs, use_unicode=True), expected + ) # check with preserve case for value, expected in samples: self.assertEqual( - utils.slugify('Çığ', regex_subs=subs, - preserve_case=True, use_unicode=True), - 'Çığ') + utils.slugify( + "Çığ", regex_subs=subs, preserve_case=True, use_unicode=True + ), + "Çığ", + ) # check normalization samples = ( - ('大飯原発4号機、18日夜起動へ', '大飯原発4号機18日夜起動へ'), + ("大飯原発4号機、18日夜起動へ", "大飯原発4号機18日夜起動へ"), ( - '\N{LATIN SMALL LETTER C}\N{COMBINING CEDILLA}', - '\N{LATIN SMALL LETTER C WITH CEDILLA}' - ) + "\N{LATIN SMALL LETTER C}\N{COMBINING CEDILLA}", + "\N{LATIN SMALL LETTER C WITH CEDILLA}", + ), ) for value, expected in samples: self.assertEqual( - utils.slugify(value, regex_subs=subs, use_unicode=True), - expected) + utils.slugify(value, regex_subs=subs, use_unicode=True), expected + ) def test_slugify_substitute(self): - - samples = (('C++ is based on C', 'cpp-is-based-on-c'), - ('C+++ test C+ test', 'cpp-test-c-test'), - ('c++, c#, C#, C++', 'cpp-c-sharp-c-sharp-cpp'), - ('c++-streams', 'cpp-streams'),) + samples = ( + ("C++ is based on C", "cpp-is-based-on-c"), + ("C+++ test C+ test", "cpp-test-c-test"), + ("c++, c#, C#, C++", "cpp-c-sharp-c-sharp-cpp"), + ("c++-streams", "cpp-streams"), + ) settings = read_settings() subs = [ - (r'C\+\+', 'CPP'), - (r'C#', 'C-SHARP'), - ] + settings['SLUG_REGEX_SUBSTITUTIONS'] + (r"C\+\+", "CPP"), + (r"C#", "C-SHARP"), + ] + settings["SLUG_REGEX_SUBSTITUTIONS"] for value, expected in samples: self.assertEqual(utils.slugify(value, regex_subs=subs), expected) def test_slugify_substitute_and_keeping_non_alphanum(self): - - samples = (('Fedora QA', 'fedora.qa'), - ('C++ is used by Fedora QA', 'cpp is used by fedora.qa'), - ('C++ is based on C', 'cpp is based on c'), - ('C+++ test C+ test', 'cpp+ test c+ test'),) + samples = ( + ("Fedora QA", "fedora.qa"), + ("C++ is used by Fedora QA", "cpp is used by fedora.qa"), + ("C++ is based on C", "cpp is based on c"), + ("C+++ test C+ test", "cpp+ test c+ test"), + ) subs = [ - (r'Fedora QA', 'fedora.qa'), - (r'c\+\+', 'cpp'), + (r"Fedora QA", "fedora.qa"), + (r"c\+\+", "cpp"), ] for value, expected in samples: self.assertEqual(utils.slugify(value, regex_subs=subs), expected) def test_get_relative_path(self): - - samples = ((os.path.join('test', 'test.html'), os.pardir), - (os.path.join('test', 'test', 'test.html'), - os.path.join(os.pardir, os.pardir)), - ('test.html', os.curdir), - (os.path.join('/test', 'test.html'), os.pardir), - (os.path.join('/test', 'test', 'test.html'), - os.path.join(os.pardir, os.pardir)), - ('/test.html', os.curdir),) + samples = ( + (os.path.join("test", "test.html"), os.pardir), + ( + os.path.join("test", "test", "test.html"), + os.path.join(os.pardir, os.pardir), + ), + ("test.html", os.curdir), + (os.path.join("/test", "test.html"), os.pardir), + ( + os.path.join("/test", "test", "test.html"), + os.path.join(os.pardir, os.pardir), + ), + ("/test.html", os.curdir), + ) for value, expected in samples: self.assertEqual(utils.get_relative_path(value), expected) def test_truncate_html_words(self): # Plain text. + self.assertEqual(utils.truncate_html_words("short string", 20), "short string") self.assertEqual( - utils.truncate_html_words('short string', 20), - 'short string') - self.assertEqual( - utils.truncate_html_words('word ' * 100, 20), - 'word ' * 20 + '…') + utils.truncate_html_words("word " * 100, 20), "word " * 20 + "…" + ) # Plain text with Unicode content. self.assertEqual( utils.truncate_html_words( - '我愿意这样,朋友——我独自远行,不但没有你,\ - 并且再没有别的影在黑暗里。', 12 + "我愿意这样,朋友——我独自远行,不但没有你,\ + 并且再没有别的影在黑暗里。", + 12, ), - '我愿意这样,朋友——我独自远行' + ' …') + "我愿意这样,朋友——我独自远行" + " …", + ) self.assertEqual( utils.truncate_html_words( - 'Ты мелькнула, ты предстала, Снова сердце задрожало,', 3 + "Ты мелькнула, ты предстала, Снова сердце задрожало,", 3 ), - 'Ты мелькнула, ты' + ' …') + "Ты мелькнула, ты" + " …", + ) self.assertEqual( - utils.truncate_html_words( - 'Trong đầm gì đẹp bằng sen', 4 - ), - 'Trong đầm gì đẹp' + ' …') + utils.truncate_html_words("Trong đầm gì đẹp bằng sen", 4), + "Trong đầm gì đẹp" + " …", + ) # Words enclosed or intervaled by HTML tags. self.assertEqual( - utils.truncate_html_words('

    ' + 'word ' * 100 + '

    ', 20), - '

    ' + 'word ' * 20 + '…

    ') + utils.truncate_html_words("

    " + "word " * 100 + "

    ", 20), + "

    " + "word " * 20 + "…

    ", + ) self.assertEqual( utils.truncate_html_words( - '' + 'word ' * 100 + '', 20), - '' + 'word ' * 20 + '…') + '' + "word " * 100 + "", 20 + ), + '' + "word " * 20 + "…", + ) self.assertEqual( - utils.truncate_html_words('
    ' + 'word ' * 100, 20), - '
    ' + 'word ' * 20 + '…') + utils.truncate_html_words("
    " + "word " * 100, 20), + "
    " + "word " * 20 + "…", + ) self.assertEqual( - utils.truncate_html_words('' + 'word ' * 100, 20), - '' + 'word ' * 20 + '…') + utils.truncate_html_words("" + "word " * 100, 20), + "" + "word " * 20 + "…", + ) # Words enclosed or intervaled by HTML tags with a custom end # marker containing HTML tags. self.assertEqual( - utils.truncate_html_words('

    ' + 'word ' * 100 + '

    ', 20, - 'marker'), - '

    ' + 'word ' * 20 + 'marker

    ') + utils.truncate_html_words( + "

    " + "word " * 100 + "

    ", 20, "marker" + ), + "

    " + "word " * 20 + "marker

    ", + ) self.assertEqual( utils.truncate_html_words( - '' + 'word ' * 100 + '', 20, - 'marker'), - '' + 'word ' * 20 + 'marker') + '' + "word " * 100 + "", + 20, + "marker", + ), + '' + "word " * 20 + "marker", + ) self.assertEqual( - utils.truncate_html_words('
    ' + 'word ' * 100, 20, - 'marker'), - '
    ' + 'word ' * 20 + 'marker') + utils.truncate_html_words( + "
    " + "word " * 100, 20, "marker" + ), + "
    " + "word " * 20 + "marker", + ) self.assertEqual( - utils.truncate_html_words('' + 'word ' * 100, 20, - 'marker'), - '' + 'word ' * 20 + 'marker') + utils.truncate_html_words( + "" + "word " * 100, 20, "marker" + ), + "" + "word " * 20 + "marker", + ) # Words with hypens and apostrophes. + self.assertEqual(utils.truncate_html_words("a-b " * 100, 20), "a-b " * 20 + "…") self.assertEqual( - utils.truncate_html_words("a-b " * 100, 20), - "a-b " * 20 + '…') - self.assertEqual( - utils.truncate_html_words("it's " * 100, 20), - "it's " * 20 + '…') + utils.truncate_html_words("it's " * 100, 20), "it's " * 20 + "…" + ) # Words with HTML entity references. self.assertEqual( - utils.truncate_html_words("é " * 100, 20), - "é " * 20 + '…') + utils.truncate_html_words("é " * 100, 20), "é " * 20 + "…" + ) self.assertEqual( utils.truncate_html_words("café " * 100, 20), - "café " * 20 + '…') + "café " * 20 + "…", + ) self.assertEqual( utils.truncate_html_words("èlite " * 100, 20), - "èlite " * 20 + '…') + "èlite " * 20 + "…", + ) self.assertEqual( utils.truncate_html_words("cafetiére " * 100, 20), - "cafetiére " * 20 + '…') + "cafetiére " * 20 + "…", + ) self.assertEqual( - utils.truncate_html_words("∫dx " * 100, 20), - "∫dx " * 20 + '…') + utils.truncate_html_words("∫dx " * 100, 20), "∫dx " * 20 + "…" + ) # Words with HTML character references inside and outside # the ASCII range. self.assertEqual( - utils.truncate_html_words("é " * 100, 20), - "é " * 20 + '…') + utils.truncate_html_words("é " * 100, 20), "é " * 20 + "…" + ) self.assertEqual( - utils.truncate_html_words("∫dx " * 100, 20), - "∫dx " * 20 + '…') + utils.truncate_html_words("∫dx " * 100, 20), "∫dx " * 20 + "…" + ) # Words with invalid or broken HTML references. + self.assertEqual(utils.truncate_html_words("&invalid;", 20), "&invalid;") self.assertEqual( - utils.truncate_html_words('&invalid;', 20), '&invalid;') + utils.truncate_html_words("�", 20), "�" + ) self.assertEqual( - utils.truncate_html_words('�', 20), '�') - self.assertEqual( - utils.truncate_html_words('�', 20), '�') - self.assertEqual( - utils.truncate_html_words('&mdash text', 20), '&mdash text') - self.assertEqual( - utils.truncate_html_words('Ӓ text', 20), 'Ӓ text') - self.assertEqual( - utils.truncate_html_words('઼ text', 20), '઼ text') + utils.truncate_html_words("�", 20), "�" + ) + self.assertEqual(utils.truncate_html_words("&mdash text", 20), "&mdash text") + self.assertEqual(utils.truncate_html_words("Ӓ text", 20), "Ӓ text") + self.assertEqual(utils.truncate_html_words("઼ text", 20), "઼ text") def test_process_translations(self): fr_articles = [] @@ -335,65 +407,135 @@ class TestUtils(LoggedTestCase): # create a bunch of articles # 0: no translation metadata - fr_articles.append(get_article(lang='fr', slug='yay0', title='Titre', - content='en français')) - en_articles.append(get_article(lang='en', slug='yay0', title='Title', - content='in english')) + fr_articles.append( + get_article(lang="fr", slug="yay0", title="Titre", content="en français") + ) + en_articles.append( + get_article(lang="en", slug="yay0", title="Title", content="in english") + ) # 1: translation metadata on default lang - fr_articles.append(get_article(lang='fr', slug='yay1', title='Titre', - content='en français')) - en_articles.append(get_article(lang='en', slug='yay1', title='Title', - content='in english', - translation='true')) + fr_articles.append( + get_article(lang="fr", slug="yay1", title="Titre", content="en français") + ) + en_articles.append( + get_article( + lang="en", + slug="yay1", + title="Title", + content="in english", + translation="true", + ) + ) # 2: translation metadata not on default lang - fr_articles.append(get_article(lang='fr', slug='yay2', title='Titre', - content='en français', - translation='true')) - en_articles.append(get_article(lang='en', slug='yay2', title='Title', - content='in english')) + fr_articles.append( + get_article( + lang="fr", + slug="yay2", + title="Titre", + content="en français", + translation="true", + ) + ) + en_articles.append( + get_article(lang="en", slug="yay2", title="Title", content="in english") + ) # 3: back to default language detection if all items have the # translation metadata - fr_articles.append(get_article(lang='fr', slug='yay3', title='Titre', - content='en français', - translation='yep')) - en_articles.append(get_article(lang='en', slug='yay3', title='Title', - content='in english', - translation='yes')) + fr_articles.append( + get_article( + lang="fr", + slug="yay3", + title="Titre", + content="en français", + translation="yep", + ) + ) + en_articles.append( + get_article( + lang="en", + slug="yay3", + title="Title", + content="in english", + translation="yes", + ) + ) # 4-5: translation pairs with the same slug but different category - fr_articles.append(get_article(lang='fr', slug='yay4', title='Titre', - content='en français', category='foo')) - en_articles.append(get_article(lang='en', slug='yay4', title='Title', - content='in english', category='foo')) - fr_articles.append(get_article(lang='fr', slug='yay4', title='Titre', - content='en français', category='bar')) - en_articles.append(get_article(lang='en', slug='yay4', title='Title', - content='in english', category='bar')) + fr_articles.append( + get_article( + lang="fr", + slug="yay4", + title="Titre", + content="en français", + category="foo", + ) + ) + en_articles.append( + get_article( + lang="en", + slug="yay4", + title="Title", + content="in english", + category="foo", + ) + ) + fr_articles.append( + get_article( + lang="fr", + slug="yay4", + title="Titre", + content="en français", + category="bar", + ) + ) + en_articles.append( + get_article( + lang="en", + slug="yay4", + title="Title", + content="in english", + category="bar", + ) + ) # try adding articles in both orders - for lang0_articles, lang1_articles in ((fr_articles, en_articles), - (en_articles, fr_articles)): + for lang0_articles, lang1_articles in ( + (fr_articles, en_articles), + (en_articles, fr_articles), + ): articles = lang0_articles + lang1_articles # test process_translations with falsy translation_id - index, trans = utils.process_translations( - articles, translation_id=None) + index, trans = utils.process_translations(articles, translation_id=None) for i in range(6): for lang_articles in [en_articles, fr_articles]: self.assertIn(lang_articles[i], index) self.assertNotIn(lang_articles[i], trans) # test process_translations with simple and complex translation_id - for translation_id in ['slug', {'slug', 'category'}]: + for translation_id in ["slug", {"slug", "category"}]: index, trans = utils.process_translations( - articles, translation_id=translation_id) + articles, translation_id=translation_id + ) - for a in [en_articles[0], fr_articles[1], en_articles[2], - en_articles[3], en_articles[4], en_articles[5]]: + for a in [ + en_articles[0], + fr_articles[1], + en_articles[2], + en_articles[3], + en_articles[4], + en_articles[5], + ]: self.assertIn(a, index) self.assertNotIn(a, trans) - for a in [fr_articles[0], en_articles[1], fr_articles[2], - fr_articles[3], fr_articles[4], fr_articles[5]]: + for a in [ + fr_articles[0], + en_articles[1], + fr_articles[2], + fr_articles[3], + fr_articles[4], + fr_articles[5], + ]: self.assertIn(a, trans) self.assertNotIn(a, index) @@ -403,18 +545,17 @@ class TestUtils(LoggedTestCase): for a_arts in [en_articles, fr_articles]: for b_arts in [en_articles, fr_articles]: - if translation_id == 'slug': + if translation_id == "slug": self.assertIn(a_arts[4], b_arts[5].translations) self.assertIn(a_arts[5], b_arts[4].translations) - elif translation_id == {'slug', 'category'}: + elif translation_id == {"slug", "category"}: self.assertNotIn(a_arts[4], b_arts[5].translations) self.assertNotIn(a_arts[5], b_arts[4].translations) def test_clean_output_dir(self): retention = () - test_directory = os.path.join(self.temp_output, - 'clean_output') - content = os.path.join(os.path.dirname(__file__), 'content') + test_directory = os.path.join(self.temp_output, "clean_output") + content = os.path.join(os.path.dirname(__file__), "content") shutil.copytree(content, test_directory) utils.clean_output_dir(test_directory, retention) self.assertTrue(os.path.isdir(test_directory)) @@ -423,17 +564,15 @@ class TestUtils(LoggedTestCase): def test_clean_output_dir_not_there(self): retention = () - test_directory = os.path.join(self.temp_output, - 'does_not_exist') + test_directory = os.path.join(self.temp_output, "does_not_exist") utils.clean_output_dir(test_directory, retention) self.assertFalse(os.path.exists(test_directory)) def test_clean_output_dir_is_file(self): retention = () - test_directory = os.path.join(self.temp_output, - 'this_is_a_file') - f = open(test_directory, 'w') - f.write('') + test_directory = os.path.join(self.temp_output, "this_is_a_file") + f = open(test_directory, "w") + f.write("") f.close() utils.clean_output_dir(test_directory, retention) self.assertFalse(os.path.exists(test_directory)) @@ -442,223 +581,230 @@ class TestUtils(LoggedTestCase): d = utils.SafeDatetime(2012, 8, 29) # simple formatting - self.assertEqual(utils.strftime(d, '%d/%m/%y'), '29/08/12') - self.assertEqual(utils.strftime(d, '%d/%m/%Y'), '29/08/2012') + self.assertEqual(utils.strftime(d, "%d/%m/%y"), "29/08/12") + self.assertEqual(utils.strftime(d, "%d/%m/%Y"), "29/08/2012") # RFC 3339 self.assertEqual( - utils.strftime(d, '%Y-%m-%dT%H:%M:%SZ'), - '2012-08-29T00:00:00Z') + utils.strftime(d, "%Y-%m-%dT%H:%M:%SZ"), "2012-08-29T00:00:00Z" + ) # % escaped - self.assertEqual(utils.strftime(d, '%d%%%m%%%y'), '29%08%12') - self.assertEqual(utils.strftime(d, '%d %% %m %% %y'), '29 % 08 % 12') + self.assertEqual(utils.strftime(d, "%d%%%m%%%y"), "29%08%12") + self.assertEqual(utils.strftime(d, "%d %% %m %% %y"), "29 % 08 % 12") # not valid % formatter - self.assertEqual(utils.strftime(d, '10% reduction in %Y'), - '10% reduction in 2012') - self.assertEqual(utils.strftime(d, '%10 reduction in %Y'), - '%10 reduction in 2012') + self.assertEqual( + utils.strftime(d, "10% reduction in %Y"), "10% reduction in 2012" + ) + self.assertEqual( + utils.strftime(d, "%10 reduction in %Y"), "%10 reduction in 2012" + ) # with text - self.assertEqual(utils.strftime(d, 'Published in %d-%m-%Y'), - 'Published in 29-08-2012') + self.assertEqual( + utils.strftime(d, "Published in %d-%m-%Y"), "Published in 29-08-2012" + ) # with non-ascii text self.assertEqual( - utils.strftime(d, '%d/%m/%Y Øl trinken beim Besäufnis'), - '29/08/2012 Øl trinken beim Besäufnis') + utils.strftime(d, "%d/%m/%Y Øl trinken beim Besäufnis"), + "29/08/2012 Øl trinken beim Besäufnis", + ) # alternative formatting options - self.assertEqual(utils.strftime(d, '%-d/%-m/%y'), '29/8/12') - self.assertEqual(utils.strftime(d, '%-H:%-M:%-S'), '0:0:0') + self.assertEqual(utils.strftime(d, "%-d/%-m/%y"), "29/8/12") + self.assertEqual(utils.strftime(d, "%-H:%-M:%-S"), "0:0:0") d = utils.SafeDatetime(2012, 8, 9) - self.assertEqual(utils.strftime(d, '%-d/%-m/%y'), '9/8/12') + self.assertEqual(utils.strftime(d, "%-d/%-m/%y"), "9/8/12") d = utils.SafeDatetime(2021, 1, 8) - self.assertEqual(utils.strftime(d, '%G - %-V - %u'), '2021 - 1 - 5') + self.assertEqual(utils.strftime(d, "%G - %-V - %u"), "2021 - 1 - 5") # test the output of utils.strftime in a different locale # Turkish locale - @unittest.skipUnless(locale_available('tr_TR.UTF-8') or - locale_available('Turkish'), - 'Turkish locale needed') + @unittest.skipUnless( + locale_available("tr_TR.UTF-8") or locale_available("Turkish"), + "Turkish locale needed", + ) def test_strftime_locale_dependent_turkish(self): - temp_locale = 'Turkish' if platform == 'win32' else 'tr_TR.UTF-8' + temp_locale = "Turkish" if platform == "win32" else "tr_TR.UTF-8" with utils.temporary_locale(temp_locale): d = utils.SafeDatetime(2012, 8, 29) # simple - self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 Ağustos 2012') - self.assertEqual(utils.strftime(d, '%A, %d %B %Y'), - 'Çarşamba, 29 Ağustos 2012') + self.assertEqual(utils.strftime(d, "%d %B %Y"), "29 Ağustos 2012") + self.assertEqual( + utils.strftime(d, "%A, %d %B %Y"), "Çarşamba, 29 Ağustos 2012" + ) # with text self.assertEqual( - utils.strftime(d, 'Yayınlanma tarihi: %A, %d %B %Y'), - 'Yayınlanma tarihi: Çarşamba, 29 Ağustos 2012') + utils.strftime(d, "Yayınlanma tarihi: %A, %d %B %Y"), + "Yayınlanma tarihi: Çarşamba, 29 Ağustos 2012", + ) # non-ascii format candidate (someone might pass it… for some reason) self.assertEqual( - utils.strftime(d, '%Y yılında %üretim artışı'), - '2012 yılında %üretim artışı') + utils.strftime(d, "%Y yılında %üretim artışı"), + "2012 yılında %üretim artışı", + ) # test the output of utils.strftime in a different locale # French locale - @unittest.skipUnless(locale_available('fr_FR.UTF-8') or - locale_available('French'), - 'French locale needed') + @unittest.skipUnless( + locale_available("fr_FR.UTF-8") or locale_available("French"), + "French locale needed", + ) def test_strftime_locale_dependent_french(self): - temp_locale = 'French' if platform == 'win32' else 'fr_FR.UTF-8' + temp_locale = "French" if platform == "win32" else "fr_FR.UTF-8" with utils.temporary_locale(temp_locale): d = utils.SafeDatetime(2012, 8, 29) # simple - self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 août 2012') + self.assertEqual(utils.strftime(d, "%d %B %Y"), "29 août 2012") # depending on OS, the first letter is m or M - self.assertTrue(utils.strftime(d, '%A') in ('mercredi', 'Mercredi')) + self.assertTrue(utils.strftime(d, "%A") in ("mercredi", "Mercredi")) # with text self.assertEqual( - utils.strftime(d, 'Écrit le %d %B %Y'), - 'Écrit le 29 août 2012') + utils.strftime(d, "Écrit le %d %B %Y"), "Écrit le 29 août 2012" + ) # non-ascii format candidate (someone might pass it… for some reason) - self.assertEqual( - utils.strftime(d, '%écrits en %Y'), - '%écrits en 2012') + self.assertEqual(utils.strftime(d, "%écrits en %Y"), "%écrits en 2012") def test_maybe_pluralize(self): - self.assertEqual( - utils.maybe_pluralize(0, 'Article', 'Articles'), - '0 Articles') - self.assertEqual( - utils.maybe_pluralize(1, 'Article', 'Articles'), - '1 Article') - self.assertEqual( - utils.maybe_pluralize(2, 'Article', 'Articles'), - '2 Articles') + self.assertEqual(utils.maybe_pluralize(0, "Article", "Articles"), "0 Articles") + self.assertEqual(utils.maybe_pluralize(1, "Article", "Articles"), "1 Article") + self.assertEqual(utils.maybe_pluralize(2, "Article", "Articles"), "2 Articles") def test_temporary_locale(self): # test with default LC category orig_locale = locale.setlocale(locale.LC_ALL) - with utils.temporary_locale('C'): - self.assertEqual(locale.setlocale(locale.LC_ALL), 'C') + with utils.temporary_locale("C"): + self.assertEqual(locale.setlocale(locale.LC_ALL), "C") self.assertEqual(locale.setlocale(locale.LC_ALL), orig_locale) # test with custom LC category orig_locale = locale.setlocale(locale.LC_TIME) - with utils.temporary_locale('C', locale.LC_TIME): - self.assertEqual(locale.setlocale(locale.LC_TIME), 'C') + with utils.temporary_locale("C", locale.LC_TIME): + self.assertEqual(locale.setlocale(locale.LC_TIME), "C") self.assertEqual(locale.setlocale(locale.LC_TIME), orig_locale) class TestCopy(unittest.TestCase): - '''Tests the copy utility''' + """Tests the copy utility""" def setUp(self): - self.root_dir = mkdtemp(prefix='pelicantests.') + self.root_dir = mkdtemp(prefix="pelicantests.") self.old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, 'C') + locale.setlocale(locale.LC_ALL, "C") def tearDown(self): shutil.rmtree(self.root_dir) locale.setlocale(locale.LC_ALL, self.old_locale) def _create_file(self, *path): - with open(os.path.join(self.root_dir, *path), 'w') as f: - f.write('42\n') + with open(os.path.join(self.root_dir, *path), "w") as f: + f.write("42\n") def _create_dir(self, *path): os.makedirs(os.path.join(self.root_dir, *path)) def _exist_file(self, *path): path = os.path.join(self.root_dir, *path) - self.assertTrue(os.path.isfile(path), 'File does not exist: %s' % path) + self.assertTrue(os.path.isfile(path), "File does not exist: %s" % path) def _exist_dir(self, *path): path = os.path.join(self.root_dir, *path) - self.assertTrue(os.path.exists(path), - 'Directory does not exist: %s' % path) + self.assertTrue(os.path.exists(path), "Directory does not exist: %s" % path) def test_copy_file_same_path(self): - self._create_file('a.txt') - utils.copy(os.path.join(self.root_dir, 'a.txt'), - os.path.join(self.root_dir, 'b.txt')) - self._exist_file('b.txt') + self._create_file("a.txt") + utils.copy( + os.path.join(self.root_dir, "a.txt"), os.path.join(self.root_dir, "b.txt") + ) + self._exist_file("b.txt") def test_copy_file_different_path(self): - self._create_dir('a') - self._create_dir('b') - self._create_file('a', 'a.txt') - utils.copy(os.path.join(self.root_dir, 'a', 'a.txt'), - os.path.join(self.root_dir, 'b', 'b.txt')) - self._exist_dir('b') - self._exist_file('b', 'b.txt') + self._create_dir("a") + self._create_dir("b") + self._create_file("a", "a.txt") + utils.copy( + os.path.join(self.root_dir, "a", "a.txt"), + os.path.join(self.root_dir, "b", "b.txt"), + ) + self._exist_dir("b") + self._exist_file("b", "b.txt") def test_copy_file_create_dirs(self): - self._create_file('a.txt') + self._create_file("a.txt") utils.copy( - os.path.join(self.root_dir, 'a.txt'), - os.path.join(self.root_dir, 'b0', 'b1', 'b2', 'b3', 'b.txt')) - self._exist_dir('b0') - self._exist_dir('b0', 'b1') - self._exist_dir('b0', 'b1', 'b2') - self._exist_dir('b0', 'b1', 'b2', 'b3') - self._exist_file('b0', 'b1', 'b2', 'b3', 'b.txt') + os.path.join(self.root_dir, "a.txt"), + os.path.join(self.root_dir, "b0", "b1", "b2", "b3", "b.txt"), + ) + self._exist_dir("b0") + self._exist_dir("b0", "b1") + self._exist_dir("b0", "b1", "b2") + self._exist_dir("b0", "b1", "b2", "b3") + self._exist_file("b0", "b1", "b2", "b3", "b.txt") def test_copy_dir_same_path(self): - self._create_dir('a') - self._create_file('a', 'a.txt') - utils.copy(os.path.join(self.root_dir, 'a'), - os.path.join(self.root_dir, 'b')) - self._exist_dir('b') - self._exist_file('b', 'a.txt') + self._create_dir("a") + self._create_file("a", "a.txt") + utils.copy(os.path.join(self.root_dir, "a"), os.path.join(self.root_dir, "b")) + self._exist_dir("b") + self._exist_file("b", "a.txt") def test_copy_dir_different_path(self): - self._create_dir('a0') - self._create_dir('a0', 'a1') - self._create_file('a0', 'a1', 'a.txt') - self._create_dir('b0') - utils.copy(os.path.join(self.root_dir, 'a0', 'a1'), - os.path.join(self.root_dir, 'b0', 'b1')) - self._exist_dir('b0', 'b1') - self._exist_file('b0', 'b1', 'a.txt') + self._create_dir("a0") + self._create_dir("a0", "a1") + self._create_file("a0", "a1", "a.txt") + self._create_dir("b0") + utils.copy( + os.path.join(self.root_dir, "a0", "a1"), + os.path.join(self.root_dir, "b0", "b1"), + ) + self._exist_dir("b0", "b1") + self._exist_file("b0", "b1", "a.txt") def test_copy_dir_create_dirs(self): - self._create_dir('a') - self._create_file('a', 'a.txt') - utils.copy(os.path.join(self.root_dir, 'a'), - os.path.join(self.root_dir, 'b0', 'b1', 'b2', 'b3', 'b')) - self._exist_dir('b0') - self._exist_dir('b0', 'b1') - self._exist_dir('b0', 'b1', 'b2') - self._exist_dir('b0', 'b1', 'b2', 'b3') - self._exist_dir('b0', 'b1', 'b2', 'b3', 'b') - self._exist_file('b0', 'b1', 'b2', 'b3', 'b', 'a.txt') + self._create_dir("a") + self._create_file("a", "a.txt") + utils.copy( + os.path.join(self.root_dir, "a"), + os.path.join(self.root_dir, "b0", "b1", "b2", "b3", "b"), + ) + self._exist_dir("b0") + self._exist_dir("b0", "b1") + self._exist_dir("b0", "b1", "b2") + self._exist_dir("b0", "b1", "b2", "b3") + self._exist_dir("b0", "b1", "b2", "b3", "b") + self._exist_file("b0", "b1", "b2", "b3", "b", "a.txt") class TestDateFormatter(unittest.TestCase): - '''Tests that the output of DateFormatter jinja filter is same as - utils.strftime''' + """Tests that the output of DateFormatter jinja filter is same as + utils.strftime""" def setUp(self): # prepare a temp content and output folder - self.temp_content = mkdtemp(prefix='pelicantests.') - self.temp_output = mkdtemp(prefix='pelicantests.') + self.temp_content = mkdtemp(prefix="pelicantests.") + self.temp_output = mkdtemp(prefix="pelicantests.") # prepare a template file - template_dir = os.path.join(self.temp_content, 'template') - template_path = os.path.join(template_dir, 'source.html') + template_dir = os.path.join(self.temp_content, "template") + template_path = os.path.join(template_dir, "source.html") os.makedirs(template_dir) - with open(template_path, 'w') as template_file: + with open(template_path, "w") as template_file: template_file.write('date = {{ date|strftime("%A, %d %B %Y") }}') self.date = utils.SafeDatetime(2012, 8, 29) @@ -666,136 +812,128 @@ class TestDateFormatter(unittest.TestCase): shutil.rmtree(self.temp_content) shutil.rmtree(self.temp_output) # reset locale to default - locale.setlocale(locale.LC_ALL, '') + locale.setlocale(locale.LC_ALL, "") - @unittest.skipUnless(locale_available('fr_FR.UTF-8') or - locale_available('French'), - 'French locale needed') + @unittest.skipUnless( + locale_available("fr_FR.UTF-8") or locale_available("French"), + "French locale needed", + ) def test_french_strftime(self): # This test tries to reproduce an issue that # occurred with python3.3 under macos10 only - temp_locale = 'French' if platform == 'win32' else 'fr_FR.UTF-8' + temp_locale = "French" if platform == "win32" else "fr_FR.UTF-8" with utils.temporary_locale(temp_locale): date = utils.SafeDatetime(2014, 8, 14) # we compare the lower() dates since macos10 returns # "Jeudi" for %A whereas linux reports "jeudi" self.assertEqual( - 'jeudi, 14 août 2014', - utils.strftime(date, date_format="%A, %d %B %Y").lower()) + "jeudi, 14 août 2014", + utils.strftime(date, date_format="%A, %d %B %Y").lower(), + ) df = utils.DateFormatter() self.assertEqual( - 'jeudi, 14 août 2014', - df(date, date_format="%A, %d %B %Y").lower()) + "jeudi, 14 août 2014", df(date, date_format="%A, %d %B %Y").lower() + ) # Let us now set the global locale to C: - with utils.temporary_locale('C'): + with utils.temporary_locale("C"): # DateFormatter should still work as expected # since it is the whole point of DateFormatter # (This is where pre-2014/4/15 code fails on macos10) df_date = df(date, date_format="%A, %d %B %Y").lower() - self.assertEqual('jeudi, 14 août 2014', df_date) + self.assertEqual("jeudi, 14 août 2014", df_date) - @unittest.skipUnless(locale_available('fr_FR.UTF-8') or - locale_available('French'), - 'French locale needed') + @unittest.skipUnless( + locale_available("fr_FR.UTF-8") or locale_available("French"), + "French locale needed", + ) def test_french_locale(self): - if platform == 'win32': - locale_string = 'French' + if platform == "win32": + locale_string = "French" else: - locale_string = 'fr_FR.UTF-8' + locale_string = "fr_FR.UTF-8" settings = read_settings( override={ - 'LOCALE': locale_string, - 'TEMPLATE_PAGES': { - 'template/source.html': 'generated/file.html' - } - }) + "LOCALE": locale_string, + "TEMPLATE_PAGES": {"template/source.html": "generated/file.html"}, + } + ) generator = TemplatePagesGenerator( - {'date': self.date}, settings, - self.temp_content, '', self.temp_output) - generator.env.filters.update({'strftime': utils.DateFormatter()}) + {"date": self.date}, settings, self.temp_content, "", self.temp_output + ) + generator.env.filters.update({"strftime": utils.DateFormatter()}) writer = Writer(self.temp_output, settings=settings) generator.generate_output(writer) - output_path = os.path.join( - self.temp_output, 'generated', 'file.html') + output_path = os.path.join(self.temp_output, "generated", "file.html") # output file has been generated self.assertTrue(os.path.exists(output_path)) # output content is correct with utils.pelican_open(output_path) as output_file: - self.assertEqual(output_file, - utils.strftime(self.date, 'date = %A, %d %B %Y')) + self.assertEqual( + output_file, utils.strftime(self.date, "date = %A, %d %B %Y") + ) - @unittest.skipUnless(locale_available('tr_TR.UTF-8') or - locale_available('Turkish'), - 'Turkish locale needed') + @unittest.skipUnless( + locale_available("tr_TR.UTF-8") or locale_available("Turkish"), + "Turkish locale needed", + ) def test_turkish_locale(self): - if platform == 'win32': - locale_string = 'Turkish' + if platform == "win32": + locale_string = "Turkish" else: - locale_string = 'tr_TR.UTF-8' + locale_string = "tr_TR.UTF-8" settings = read_settings( override={ - 'LOCALE': locale_string, - 'TEMPLATE_PAGES': { - 'template/source.html': 'generated/file.html' - } - }) + "LOCALE": locale_string, + "TEMPLATE_PAGES": {"template/source.html": "generated/file.html"}, + } + ) generator = TemplatePagesGenerator( - {'date': self.date}, settings, - self.temp_content, '', self.temp_output) - generator.env.filters.update({'strftime': utils.DateFormatter()}) + {"date": self.date}, settings, self.temp_content, "", self.temp_output + ) + generator.env.filters.update({"strftime": utils.DateFormatter()}) writer = Writer(self.temp_output, settings=settings) generator.generate_output(writer) - output_path = os.path.join( - self.temp_output, 'generated', 'file.html') + output_path = os.path.join(self.temp_output, "generated", "file.html") # output file has been generated self.assertTrue(os.path.exists(output_path)) # output content is correct with utils.pelican_open(output_path) as output_file: - self.assertEqual(output_file, - utils.strftime(self.date, 'date = %A, %d %B %Y')) + self.assertEqual( + output_file, utils.strftime(self.date, "date = %A, %d %B %Y") + ) class TestSanitisedJoin(unittest.TestCase): def test_detect_parent_breakout(self): with self.assertRaisesRegex( - RuntimeError, - "Attempted to break out of output directory to " - "(.*?:)?/foo/test"): # (.*?:)? accounts for Windows root - utils.sanitised_join( - "/foo/bar", - "../test" - ) + RuntimeError, + "Attempted to break out of output directory to " "(.*?:)?/foo/test", + ): # (.*?:)? accounts for Windows root + utils.sanitised_join("/foo/bar", "../test") def test_detect_root_breakout(self): with self.assertRaisesRegex( - RuntimeError, - "Attempted to break out of output directory to " - "(.*?:)?/test"): # (.*?:)? accounts for Windows root - utils.sanitised_join( - "/foo/bar", - "/test" - ) + RuntimeError, + "Attempted to break out of output directory to " "(.*?:)?/test", + ): # (.*?:)? accounts for Windows root + utils.sanitised_join("/foo/bar", "/test") def test_pass_deep_subpaths(self): self.assertEqual( - utils.sanitised_join( - "/foo/bar", - "test" - ), - utils.posixize_path( - os.path.abspath(os.path.join("/foo/bar", "test"))) + utils.sanitised_join("/foo/bar", "test"), + utils.posixize_path(os.path.abspath(os.path.join("/foo/bar", "test"))), ) @@ -812,7 +950,7 @@ class TestMemoized(unittest.TestCase): container = Container() with unittest.mock.patch.object( - container, "_get", side_effect=lambda x: x + container, "_get", side_effect=lambda x: x ) as get_mock: self.assertEqual("foo", container.get("foo")) get_mock.assert_called_once_with("foo") diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 95e196ba..27102f38 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -47,74 +47,69 @@ def decode_wp_content(content, br=True): pre_index += 1 content = content + last_pre - content = re.sub(r'
    \s*
    ', "\n\n", content) - allblocks = ('(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|' - 'td|th|div|dl|dd|dt|ul|ol|li|pre|select|option|form|' - 'map|area|blockquote|address|math|style|p|h[1-6]|hr|' - 'fieldset|noscript|samp|legend|section|article|aside|' - 'hgroup|header|footer|nav|figure|figcaption|details|' - 'menu|summary)') - content = re.sub(r'(<' + allblocks + r'[^>]*>)', "\n\\1", content) - content = re.sub(r'()', "\\1\n\n", content) + content = re.sub(r"
    \s*
    ", "\n\n", content) + allblocks = ( + "(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|" + "td|th|div|dl|dd|dt|ul|ol|li|pre|select|option|form|" + "map|area|blockquote|address|math|style|p|h[1-6]|hr|" + "fieldset|noscript|samp|legend|section|article|aside|" + "hgroup|header|footer|nav|figure|figcaption|details|" + "menu|summary)" + ) + content = re.sub(r"(<" + allblocks + r"[^>]*>)", "\n\\1", content) + content = re.sub(r"()", "\\1\n\n", content) # content = content.replace("\r\n", "\n") if " inside object/embed - content = re.sub(r'\s*]*)>\s*', "", content) - content = re.sub(r'\s*\s*', '', content) + content = re.sub(r"\s*]*)>\s*", "", content) + content = re.sub(r"\s*\s*", "", content) # content = re.sub(r'/\n\n+/', '\n\n', content) - pgraphs = filter(lambda s: s != "", re.split(r'\n\s*\n', content)) + pgraphs = filter(lambda s: s != "", re.split(r"\n\s*\n", content)) content = "" for p in pgraphs: content = content + "

    " + p.strip() + "

    \n" # under certain strange conditions it could create # a P of entirely whitespace - content = re.sub(r'

    \s*

    ', '', content) - content = re.sub( - r'

    ([^<]+)', - "

    \\1

    ", - content) + content = re.sub(r"

    \s*

    ", "", content) + content = re.sub(r"

    ([^<]+)", "

    \\1

    ", content) # don't wrap tags - content = re.sub( - r'

    \s*(]*>)\s*

    ', - "\\1", - content) + content = re.sub(r"

    \s*(]*>)\s*

    ", "\\1", content) # problem with nested lists - content = re.sub(r'

    (', "\\1", content) - content = re.sub(r'

    ]*)>', "

    ", content) - content = content.replace('

    ', '

    ') - content = re.sub(r'

    \s*(]*>)', "\\1", content) - content = re.sub(r'(]*>)\s*

    ', "\\1", content) + content = re.sub(r"

    (", "\\1", content) + content = re.sub(r"

    ]*)>", "

    ", content) + content = content.replace("

    ", "

    ") + content = re.sub(r"

    \s*(]*>)", "\\1", content) + content = re.sub(r"(]*>)\s*

    ", "\\1", content) if br: + def _preserve_newline(match): return match.group(0).replace("\n", "") - content = re.sub( - r'/<(script|style).*?<\/\\1>/s', - _preserve_newline, - content) + + content = re.sub(r"/<(script|style).*?<\/\\1>/s", _preserve_newline, content) # optionally make line breaks - content = re.sub(r'(?)\s*\n', "
    \n", content) + content = re.sub(r"(?)\s*\n", "
    \n", content) content = content.replace("", "\n") + content = re.sub(r"(]*>)\s*
    ", "\\1", content) content = re.sub( - r'(]*>)\s*
    ', "\\1", - content) - content = re.sub( - r'
    (\s*]*>)', - '\\1', - content) - content = re.sub(r'\n

    ', "

    ", content) + r"
    (\s*]*>)", "\\1", content + ) + content = re.sub(r"\n

    ", "

    ", content) if pre_tags: + def _multi_replace(dic, string): - pattern = r'|'.join(map(re.escape, dic.keys())) + pattern = r"|".join(map(re.escape, dic.keys())) return re.sub(pattern, lambda m: dic[m.group()], string) + content = _multi_replace(pre_tags, content) # convert [caption] tags into
    content = re.sub( - r'\[caption(?:.*?)(?:caption=\"(.*?)\")?\]' - r'((?:\)?(?:\)(?:\<\/a\>)?)\s?(.*?)\[\/caption\]', - r'
    \n\2\n
    \1\3
    \n
    ', - content) + r"\[caption(?:.*?)(?:caption=\"(.*?)\")?\]" + r"((?:\)?(?:\)(?:\<\/a\>)?)\s?(.*?)\[\/caption\]", + r"
    \n\2\n
    \1\3
    \n
    ", + content, + ) return content @@ -124,10 +119,12 @@ def xml_to_soup(xml): try: from bs4 import BeautifulSoup except ImportError: - error = ('Missing dependency "BeautifulSoup4" and "lxml" required to ' - 'import XML files.') + error = ( + 'Missing dependency "BeautifulSoup4" and "lxml" required to ' + "import XML files." + ) sys.exit(error) - with open(xml, encoding='utf-8') as infile: + with open(xml, encoding="utf-8") as infile: xmlfile = infile.read() soup = BeautifulSoup(xmlfile, "xml") return soup @@ -144,111 +141,125 @@ def wp2fields(xml, wp_custpost=False): """Opens a wordpress XML file, and yield Pelican fields""" soup = xml_to_soup(xml) - items = soup.rss.channel.findAll('item') + items = soup.rss.channel.findAll("item") for item in items: - - if item.find('status').string in ["publish", "draft"]: - + if item.find("status").string in ["publish", "draft"]: try: # Use HTMLParser due to issues with BeautifulSoup 3 title = unescape(item.title.contents[0]) except IndexError: - title = 'No title [%s]' % item.find('post_name').string + title = "No title [%s]" % item.find("post_name").string logger.warning('Post "%s" is lacking a proper title', title) - post_name = item.find('post_name').string - post_id = item.find('post_id').string + post_name = item.find("post_name").string + post_id = item.find("post_id").string filename = get_filename(post_name, post_id) - content = item.find('encoded').string - raw_date = item.find('post_date').string - if raw_date == '0000-00-00 00:00:00': + content = item.find("encoded").string + raw_date = item.find("post_date").string + if raw_date == "0000-00-00 00:00:00": date = None else: - date_object = SafeDatetime.strptime( - raw_date, '%Y-%m-%d %H:%M:%S') - date = date_object.strftime('%Y-%m-%d %H:%M') - author = item.find('creator').string + date_object = SafeDatetime.strptime(raw_date, "%Y-%m-%d %H:%M:%S") + date = date_object.strftime("%Y-%m-%d %H:%M") + author = item.find("creator").string - categories = [cat.string for cat - in item.findAll('category', {'domain': 'category'})] + categories = [ + cat.string for cat in item.findAll("category", {"domain": "category"}) + ] - tags = [tag.string for tag - in item.findAll('category', {'domain': 'post_tag'})] + tags = [ + tag.string for tag in item.findAll("category", {"domain": "post_tag"}) + ] # To publish a post the status should be 'published' - status = 'published' if item.find('status').string == "publish" \ - else item.find('status').string + status = ( + "published" + if item.find("status").string == "publish" + else item.find("status").string + ) - kind = 'article' - post_type = item.find('post_type').string - if post_type == 'page': - kind = 'page' + kind = "article" + post_type = item.find("post_type").string + if post_type == "page": + kind = "page" elif wp_custpost: - if post_type == 'post': + if post_type == "post": pass # Old behaviour was to name everything not a page as an # article.Theoretically all attachments have status == inherit # so no attachments should be here. But this statement is to # maintain existing behaviour in case that doesn't hold true. - elif post_type == 'attachment': + elif post_type == "attachment": pass else: kind = post_type - yield (title, content, filename, date, author, categories, - tags, status, kind, 'wp-html') + yield ( + title, + content, + filename, + date, + author, + categories, + tags, + status, + kind, + "wp-html", + ) def blogger2fields(xml): """Opens a blogger XML file, and yield Pelican fields""" soup = xml_to_soup(xml) - entries = soup.feed.findAll('entry') + entries = soup.feed.findAll("entry") for entry in entries: raw_kind = entry.find( - 'category', {'scheme': 'http://schemas.google.com/g/2005#kind'} - ).get('term') - if raw_kind == 'http://schemas.google.com/blogger/2008/kind#post': - kind = 'article' - elif raw_kind == 'http://schemas.google.com/blogger/2008/kind#comment': - kind = 'comment' - elif raw_kind == 'http://schemas.google.com/blogger/2008/kind#page': - kind = 'page' + "category", {"scheme": "http://schemas.google.com/g/2005#kind"} + ).get("term") + if raw_kind == "http://schemas.google.com/blogger/2008/kind#post": + kind = "article" + elif raw_kind == "http://schemas.google.com/blogger/2008/kind#comment": + kind = "comment" + elif raw_kind == "http://schemas.google.com/blogger/2008/kind#page": + kind = "page" else: continue try: - assert kind != 'comment' - filename = entry.find('link', {'rel': 'alternate'})['href'] + assert kind != "comment" + filename = entry.find("link", {"rel": "alternate"})["href"] filename = os.path.splitext(os.path.basename(filename))[0] except (AssertionError, TypeError, KeyError): - filename = entry.find('id').string.split('.')[-1] + filename = entry.find("id").string.split(".")[-1] - title = entry.find('title').string or '' + title = entry.find("title").string or "" - content = entry.find('content').string - raw_date = entry.find('published').string - if hasattr(SafeDatetime, 'fromisoformat'): + content = entry.find("content").string + raw_date = entry.find("published").string + if hasattr(SafeDatetime, "fromisoformat"): date_object = SafeDatetime.fromisoformat(raw_date) else: - date_object = SafeDatetime.strptime( - raw_date[:23], '%Y-%m-%dT%H:%M:%S.%f') - date = date_object.strftime('%Y-%m-%d %H:%M') - author = entry.find('author').find('name').string + date_object = SafeDatetime.strptime(raw_date[:23], "%Y-%m-%dT%H:%M:%S.%f") + date = date_object.strftime("%Y-%m-%d %H:%M") + author = entry.find("author").find("name").string # blogger posts only have tags, no category - tags = [tag.get('term') for tag in entry.findAll( - 'category', {'scheme': 'http://www.blogger.com/atom/ns#'})] + tags = [ + tag.get("term") + for tag in entry.findAll( + "category", {"scheme": "http://www.blogger.com/atom/ns#"} + ) + ] # Drafts have yes - status = 'published' + status = "published" try: - if entry.find('control').find('draft').string == 'yes': - status = 'draft' + if entry.find("control").find("draft").string == "yes": + status = "draft" except AttributeError: pass - yield (title, content, filename, date, author, None, tags, status, - kind, 'html') + yield (title, content, filename, date, author, None, tags, status, kind, "html") def dc2fields(file): @@ -256,9 +267,11 @@ def dc2fields(file): try: from bs4 import BeautifulSoup except ImportError: - error = ('Missing dependency ' - '"BeautifulSoup4" and "lxml" required ' - 'to import Dotclear files.') + error = ( + "Missing dependency " + '"BeautifulSoup4" and "lxml" required ' + "to import Dotclear files." + ) sys.exit(error) in_cat = False @@ -266,15 +279,14 @@ def dc2fields(file): category_list = {} posts = [] - with open(file, encoding='utf-8') as f: - + with open(file, encoding="utf-8") as f: for line in f: # remove final \n line = line[:-1] - if line.startswith('[category'): + if line.startswith("[category"): in_cat = True - elif line.startswith('[post'): + elif line.startswith("[post"): in_post = True elif in_cat: fields = line.split('","') @@ -294,7 +306,7 @@ def dc2fields(file): print("%i posts read." % len(posts)) - subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS'] + subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"] for post in posts: fields = post.split('","') @@ -329,44 +341,39 @@ def dc2fields(file): # redirect_url = fields[28][:-1] # remove seconds - post_creadt = ':'.join(post_creadt.split(':')[0:2]) + post_creadt = ":".join(post_creadt.split(":")[0:2]) - author = '' + author = "" categories = [] tags = [] if cat_id: - categories = [category_list[id].strip() for id - in cat_id.split(',')] + categories = [category_list[id].strip() for id in cat_id.split(",")] # Get tags related to a post - tag = (post_meta.replace('{', '') - .replace('}', '') - .replace('a:1:s:3:\\"tag\\";a:', '') - .replace('a:0:', '')) + tag = ( + post_meta.replace("{", "") + .replace("}", "") + .replace('a:1:s:3:\\"tag\\";a:', "") + .replace("a:0:", "") + ) if len(tag) > 1: if int(len(tag[:1])) == 1: newtag = tag.split('"')[1] tags.append( - BeautifulSoup( - newtag, - 'xml' - ) + BeautifulSoup(newtag, "xml") # bs4 always outputs UTF-8 - .decode('utf-8') + .decode("utf-8") ) else: i = 1 j = 1 - while (i <= int(tag[:1])): - newtag = tag.split('"')[j].replace('\\', '') + while i <= int(tag[:1]): + newtag = tag.split('"')[j].replace("\\", "") tags.append( - BeautifulSoup( - newtag, - 'xml' - ) + BeautifulSoup(newtag, "xml") # bs4 always outputs UTF-8 - .decode('utf-8') + .decode("utf-8") ) i = i + 1 if j < int(tag[:1]) * 2: @@ -381,116 +388,149 @@ def dc2fields(file): content = post_excerpt + post_content else: content = post_excerpt_xhtml + post_content_xhtml - content = content.replace('\\n', '') + content = content.replace("\\n", "") post_format = "html" - kind = 'article' # TODO: Recognise pages - status = 'published' # TODO: Find a way for draft posts + kind = "article" # TODO: Recognise pages + status = "published" # TODO: Find a way for draft posts - yield (post_title, content, slugify(post_title, regex_subs=subs), - post_creadt, author, categories, tags, status, kind, - post_format) + yield ( + post_title, + content, + slugify(post_title, regex_subs=subs), + post_creadt, + author, + categories, + tags, + status, + kind, + post_format, + ) def _get_tumblr_posts(api_key, blogname, offset=0): import json import urllib.request as urllib_request - url = ("https://api.tumblr.com/v2/blog/%s.tumblr.com/" - "posts?api_key=%s&offset=%d&filter=raw") % ( - blogname, api_key, offset) + + url = ( + "https://api.tumblr.com/v2/blog/%s.tumblr.com/" + "posts?api_key=%s&offset=%d&filter=raw" + ) % (blogname, api_key, offset) request = urllib_request.Request(url) handle = urllib_request.urlopen(request) - posts = json.loads(handle.read().decode('utf-8')) - return posts.get('response').get('posts') + posts = json.loads(handle.read().decode("utf-8")) + return posts.get("response").get("posts") def tumblr2fields(api_key, blogname): - """ Imports Tumblr posts (API v2)""" + """Imports Tumblr posts (API v2)""" offset = 0 posts = _get_tumblr_posts(api_key, blogname, offset) - subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS'] + subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"] while len(posts) > 0: for post in posts: - title = \ - post.get('title') or \ - post.get('source_title') or \ - post.get('type').capitalize() - slug = post.get('slug') or slugify(title, regex_subs=subs) - tags = post.get('tags') - timestamp = post.get('timestamp') + title = ( + post.get("title") + or post.get("source_title") + or post.get("type").capitalize() + ) + slug = post.get("slug") or slugify(title, regex_subs=subs) + tags = post.get("tags") + timestamp = post.get("timestamp") date = SafeDatetime.fromtimestamp( int(timestamp), tz=datetime.timezone.utc ).strftime("%Y-%m-%d %H:%M:%S%z") - slug = SafeDatetime.fromtimestamp( - int(timestamp), tz=datetime.timezone.utc - ).strftime("%Y-%m-%d-") + slug - format = post.get('format') - content = post.get('body') - type = post.get('type') - if type == 'photo': - if format == 'markdown': - fmtstr = '![%s](%s)' + slug = ( + SafeDatetime.fromtimestamp( + int(timestamp), tz=datetime.timezone.utc + ).strftime("%Y-%m-%d-") + + slug + ) + format = post.get("format") + content = post.get("body") + type = post.get("type") + if type == "photo": + if format == "markdown": + fmtstr = "![%s](%s)" else: fmtstr = '%s' - content = '\n'.join( - fmtstr % (photo.get('caption'), - photo.get('original_size').get('url')) - for photo in post.get('photos')) - elif type == 'quote': - if format == 'markdown': - fmtstr = '\n\n— %s' + content = "\n".join( + fmtstr + % (photo.get("caption"), photo.get("original_size").get("url")) + for photo in post.get("photos") + ) + elif type == "quote": + if format == "markdown": + fmtstr = "\n\n— %s" else: - fmtstr = '

    — %s

    ' - content = post.get('text') + fmtstr % post.get('source') - elif type == 'link': - if format == 'markdown': - fmtstr = '[via](%s)\n\n' + fmtstr = "

    — %s

    " + content = post.get("text") + fmtstr % post.get("source") + elif type == "link": + if format == "markdown": + fmtstr = "[via](%s)\n\n" else: fmtstr = '

    via

    \n' - content = fmtstr % post.get('url') + post.get('description') - elif type == 'audio': - if format == 'markdown': - fmtstr = '[via](%s)\n\n' + content = fmtstr % post.get("url") + post.get("description") + elif type == "audio": + if format == "markdown": + fmtstr = "[via](%s)\n\n" else: fmtstr = '

    via

    \n' - content = fmtstr % post.get('source_url') + \ - post.get('caption') + \ - post.get('player') - elif type == 'video': - if format == 'markdown': - fmtstr = '[via](%s)\n\n' + content = ( + fmtstr % post.get("source_url") + + post.get("caption") + + post.get("player") + ) + elif type == "video": + if format == "markdown": + fmtstr = "[via](%s)\n\n" else: fmtstr = '

    via

    \n' - source = fmtstr % post.get('source_url') - caption = post.get('caption') + source = fmtstr % post.get("source_url") + caption = post.get("caption") players = [ # If embed_code is False, couldn't get the video - player.get('embed_code') or None - for player in post.get('player')] + player.get("embed_code") or None + for player in post.get("player") + ] # If there are no embeddable players, say so, once - if len(players) > 0 and all( - player is None for player in players): + if len(players) > 0 and all(player is None for player in players): players = "

    (This video isn't available anymore.)

    \n" else: - players = '\n'.join(players) + players = "\n".join(players) content = source + caption + players - elif type == 'answer': - title = post.get('question') - content = ('

    ' - '%s' - ': %s' - '

    \n' - ' %s' % (post.get('asking_name'), - post.get('asking_url'), - post.get('question'), - post.get('answer'))) + elif type == "answer": + title = post.get("question") + content = ( + "

    " + '%s' + ": %s" + "

    \n" + " %s" + % ( + post.get("asking_name"), + post.get("asking_url"), + post.get("question"), + post.get("answer"), + ) + ) - content = content.rstrip() + '\n' - kind = 'article' - status = 'published' # TODO: Find a way for draft posts + content = content.rstrip() + "\n" + kind = "article" + status = "published" # TODO: Find a way for draft posts - yield (title, content, slug, date, post.get('blog_name'), [type], - tags, status, kind, format) + yield ( + title, + content, + slug, + date, + post.get("blog_name"), + [type], + tags, + status, + kind, + format, + ) offset += len(posts) posts = _get_tumblr_posts(api_key, blogname, offset) @@ -499,145 +539,167 @@ def tumblr2fields(api_key, blogname): def feed2fields(file): """Read a feed and yield pelican fields""" import feedparser + d = feedparser.parse(file) - subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS'] + subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"] for entry in d.entries: - date = (time.strftime('%Y-%m-%d %H:%M', entry.updated_parsed) - if hasattr(entry, 'updated_parsed') else None) - author = entry.author if hasattr(entry, 'author') else None - tags = ([e['term'] for e in entry.tags] - if hasattr(entry, 'tags') else None) + date = ( + time.strftime("%Y-%m-%d %H:%M", entry.updated_parsed) + if hasattr(entry, "updated_parsed") + else None + ) + author = entry.author if hasattr(entry, "author") else None + tags = [e["term"] for e in entry.tags] if hasattr(entry, "tags") else None slug = slugify(entry.title, regex_subs=subs) - kind = 'article' - yield (entry.title, entry.description, slug, date, - author, [], tags, None, kind, 'html') + kind = "article" + yield ( + entry.title, + entry.description, + slug, + date, + author, + [], + tags, + None, + kind, + "html", + ) -def build_header(title, date, author, categories, tags, slug, - status=None, attachments=None): +def build_header( + title, date, author, categories, tags, slug, status=None, attachments=None +): """Build a header from a list of fields""" from docutils.utils import column_width - header = '{}\n{}\n'.format(title, '#' * column_width(title)) + header = "{}\n{}\n".format(title, "#" * column_width(title)) if date: - header += ':date: %s\n' % date + header += ":date: %s\n" % date if author: - header += ':author: %s\n' % author + header += ":author: %s\n" % author if categories: - header += ':category: %s\n' % ', '.join(categories) + header += ":category: %s\n" % ", ".join(categories) if tags: - header += ':tags: %s\n' % ', '.join(tags) + header += ":tags: %s\n" % ", ".join(tags) if slug: - header += ':slug: %s\n' % slug + header += ":slug: %s\n" % slug if status: - header += ':status: %s\n' % status + header += ":status: %s\n" % status if attachments: - header += ':attachments: %s\n' % ', '.join(attachments) - header += '\n' + header += ":attachments: %s\n" % ", ".join(attachments) + header += "\n" return header -def build_asciidoc_header(title, date, author, categories, tags, slug, - status=None, attachments=None): +def build_asciidoc_header( + title, date, author, categories, tags, slug, status=None, attachments=None +): """Build a header from a list of fields""" - header = '= %s\n' % title + header = "= %s\n" % title if author: - header += '%s\n' % author + header += "%s\n" % author if date: - header += '%s\n' % date + header += "%s\n" % date if categories: - header += ':category: %s\n' % ', '.join(categories) + header += ":category: %s\n" % ", ".join(categories) if tags: - header += ':tags: %s\n' % ', '.join(tags) + header += ":tags: %s\n" % ", ".join(tags) if slug: - header += ':slug: %s\n' % slug + header += ":slug: %s\n" % slug if status: - header += ':status: %s\n' % status + header += ":status: %s\n" % status if attachments: - header += ':attachments: %s\n' % ', '.join(attachments) - header += '\n' + header += ":attachments: %s\n" % ", ".join(attachments) + header += "\n" return header -def build_markdown_header(title, date, author, categories, tags, - slug, status=None, attachments=None): +def build_markdown_header( + title, date, author, categories, tags, slug, status=None, attachments=None +): """Build a header from a list of fields""" - header = 'Title: %s\n' % title + header = "Title: %s\n" % title if date: - header += 'Date: %s\n' % date + header += "Date: %s\n" % date if author: - header += 'Author: %s\n' % author + header += "Author: %s\n" % author if categories: - header += 'Category: %s\n' % ', '.join(categories) + header += "Category: %s\n" % ", ".join(categories) if tags: - header += 'Tags: %s\n' % ', '.join(tags) + header += "Tags: %s\n" % ", ".join(tags) if slug: - header += 'Slug: %s\n' % slug + header += "Slug: %s\n" % slug if status: - header += 'Status: %s\n' % status + header += "Status: %s\n" % status if attachments: - header += 'Attachments: %s\n' % ', '.join(attachments) - header += '\n' + header += "Attachments: %s\n" % ", ".join(attachments) + header += "\n" return header -def get_ext(out_markup, in_markup='html'): - if out_markup == 'asciidoc': - ext = '.adoc' - elif in_markup == 'markdown' or out_markup == 'markdown': - ext = '.md' +def get_ext(out_markup, in_markup="html"): + if out_markup == "asciidoc": + ext = ".adoc" + elif in_markup == "markdown" or out_markup == "markdown": + ext = ".md" else: - ext = '.rst' + ext = ".rst" return ext -def get_out_filename(output_path, filename, ext, kind, - dirpage, dircat, categories, wp_custpost, slug_subs): +def get_out_filename( + output_path, + filename, + ext, + kind, + dirpage, + dircat, + categories, + wp_custpost, + slug_subs, +): filename = os.path.basename(filename) # Enforce filename restrictions for various filesystems at once; see # https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words # we do not need to filter words because an extension will be appended - filename = re.sub(r'[<>:"/\\|?*^% ]', '-', filename) # invalid chars - filename = filename.lstrip('.') # should not start with a dot + filename = re.sub(r'[<>:"/\\|?*^% ]', "-", filename) # invalid chars + filename = filename.lstrip(".") # should not start with a dot if not filename: - filename = '_' + filename = "_" filename = filename[:249] # allow for 5 extra characters out_filename = os.path.join(output_path, filename + ext) # option to put page posts in pages/ subdirectory - if dirpage and kind == 'page': - pages_dir = os.path.join(output_path, 'pages') + if dirpage and kind == "page": + pages_dir = os.path.join(output_path, "pages") if not os.path.isdir(pages_dir): os.mkdir(pages_dir) out_filename = os.path.join(pages_dir, filename + ext) - elif not dirpage and kind == 'page': + elif not dirpage and kind == "page": pass # option to put wp custom post types in directories with post type # names. Custom post types can also have categories so option to # create subdirectories with category names - elif kind != 'article': + elif kind != "article": if wp_custpost: typename = slugify(kind, regex_subs=slug_subs) else: - typename = '' - kind = 'article' + typename = "" + kind = "article" if dircat and (len(categories) > 0): - catname = slugify( - categories[0], regex_subs=slug_subs, preserve_case=True) + catname = slugify(categories[0], regex_subs=slug_subs, preserve_case=True) else: - catname = '' - out_filename = os.path.join(output_path, typename, - catname, filename + ext) + catname = "" + out_filename = os.path.join(output_path, typename, catname, filename + ext) if not os.path.isdir(os.path.join(output_path, typename, catname)): os.makedirs(os.path.join(output_path, typename, catname)) # option to put files in directories with categories names elif dircat and (len(categories) > 0): - catname = slugify( - categories[0], regex_subs=slug_subs, preserve_case=True) + catname = slugify(categories[0], regex_subs=slug_subs, preserve_case=True) out_filename = os.path.join(output_path, catname, filename + ext) if not os.path.isdir(os.path.join(output_path, catname)): os.mkdir(os.path.join(output_path, catname)) @@ -650,18 +712,19 @@ def get_attachments(xml): of the attachment_urls """ soup = xml_to_soup(xml) - items = soup.rss.channel.findAll('item') + items = soup.rss.channel.findAll("item") names = {} attachments = [] for item in items: - kind = item.find('post_type').string - post_name = item.find('post_name').string - post_id = item.find('post_id').string + kind = item.find("post_type").string + post_name = item.find("post_name").string + post_id = item.find("post_id").string - if kind == 'attachment': - attachments.append((item.find('post_parent').string, - item.find('attachment_url').string)) + if kind == "attachment": + attachments.append( + (item.find("post_parent").string, item.find("attachment_url").string) + ) else: filename = get_filename(post_name, post_id) names[post_id] = filename @@ -686,23 +749,23 @@ def download_attachments(output_path, urls): path = urlparse(url).path # teardown path and rebuild to negate any errors with # os.path.join and leading /'s - path = path.split('/') + path = path.split("/") filename = path.pop(-1) - localpath = '' + localpath = "" for item in path: - if sys.platform != 'win32' or ':' not in item: + if sys.platform != "win32" or ":" not in item: localpath = os.path.join(localpath, item) full_path = os.path.join(output_path, localpath) # Generate percent-encoded URL scheme, netloc, path, query, fragment = urlsplit(url) - if scheme != 'file': + if scheme != "file": path = quote(path) url = urlunsplit((scheme, netloc, path, query, fragment)) if not os.path.exists(full_path): os.makedirs(full_path) - print('downloading {}'.format(filename)) + print("downloading {}".format(filename)) try: urlretrieve(url, os.path.join(full_path, filename)) locations[url] = os.path.join(localpath, filename) @@ -713,43 +776,61 @@ def download_attachments(output_path, urls): def is_pandoc_needed(in_markup): - return in_markup in ('html', 'wp-html') + return in_markup in ("html", "wp-html") def get_pandoc_version(): - cmd = ['pandoc', '--version'] + cmd = ["pandoc", "--version"] try: output = subprocess.check_output(cmd, universal_newlines=True) except (subprocess.CalledProcessError, OSError) as e: logger.warning("Pandoc version unknown: %s", e) return () - return tuple(int(i) for i in output.split()[1].split('.')) + return tuple(int(i) for i in output.split()[1].split(".")) def update_links_to_attached_files(content, attachments): for old_url, new_path in attachments.items(): # url may occur both with http:// and https:// - http_url = old_url.replace('https://', 'http://') - https_url = old_url.replace('http://', 'https://') + http_url = old_url.replace("https://", "http://") + https_url = old_url.replace("http://", "https://") for url in [http_url, https_url]: - content = content.replace(url, '{static}' + new_path) + content = content.replace(url, "{static}" + new_path) return content def fields2pelican( - fields, out_markup, output_path, - dircat=False, strip_raw=False, disable_slugs=False, - dirpage=False, filename_template=None, filter_author=None, - wp_custpost=False, wp_attach=False, attachments=None): - + fields, + out_markup, + output_path, + dircat=False, + strip_raw=False, + disable_slugs=False, + dirpage=False, + filename_template=None, + filter_author=None, + wp_custpost=False, + wp_attach=False, + attachments=None, +): pandoc_version = get_pandoc_version() posts_require_pandoc = [] - slug_subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS'] + slug_subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"] - for (title, content, filename, date, author, categories, tags, status, - kind, in_markup) in fields: + for ( + title, + content, + filename, + date, + author, + categories, + tags, + status, + kind, + in_markup, + ) in fields: if filter_author and filter_author != author: continue if is_pandoc_needed(in_markup) and not pandoc_version: @@ -767,85 +848,120 @@ def fields2pelican( links = None ext = get_ext(out_markup, in_markup) - if ext == '.adoc': - header = build_asciidoc_header(title, date, author, categories, - tags, slug, status, attachments) - elif ext == '.md': + if ext == ".adoc": + header = build_asciidoc_header( + title, date, author, categories, tags, slug, status, attachments + ) + elif ext == ".md": header = build_markdown_header( - title, date, author, categories, tags, slug, - status, links.values() if links else None) + title, + date, + author, + categories, + tags, + slug, + status, + links.values() if links else None, + ) else: - out_markup = 'rst' - header = build_header(title, date, author, categories, - tags, slug, status, links.values() - if links else None) + out_markup = "rst" + header = build_header( + title, + date, + author, + categories, + tags, + slug, + status, + links.values() if links else None, + ) out_filename = get_out_filename( - output_path, filename, ext, kind, dirpage, dircat, - categories, wp_custpost, slug_subs) + output_path, + filename, + ext, + kind, + dirpage, + dircat, + categories, + wp_custpost, + slug_subs, + ) print(out_filename) - if in_markup in ('html', 'wp-html'): + if in_markup in ("html", "wp-html"): with tempfile.TemporaryDirectory() as tmpdir: - html_filename = os.path.join(tmpdir, 'pandoc-input.html') + html_filename = os.path.join(tmpdir, "pandoc-input.html") # Replace newlines with paragraphs wrapped with

    so # HTML is valid before conversion - if in_markup == 'wp-html': + if in_markup == "wp-html": new_content = decode_wp_content(content) else: paragraphs = content.splitlines() - paragraphs = ['

    {}

    '.format(p) for p in paragraphs] - new_content = ''.join(paragraphs) - with open(html_filename, 'w', encoding='utf-8') as fp: + paragraphs = ["

    {}

    ".format(p) for p in paragraphs] + new_content = "".join(paragraphs) + with open(html_filename, "w", encoding="utf-8") as fp: fp.write(new_content) if pandoc_version < (2,): - parse_raw = '--parse-raw' if not strip_raw else '' - wrap_none = '--wrap=none' \ - if pandoc_version >= (1, 16) else '--no-wrap' - cmd = ('pandoc --normalize {0} --from=html' - ' --to={1} {2} -o "{3}" "{4}"') - cmd = cmd.format(parse_raw, - out_markup if out_markup != 'markdown' else "gfm", - wrap_none, - out_filename, html_filename) + parse_raw = "--parse-raw" if not strip_raw else "" + wrap_none = ( + "--wrap=none" if pandoc_version >= (1, 16) else "--no-wrap" + ) + cmd = ( + "pandoc --normalize {0} --from=html" + ' --to={1} {2} -o "{3}" "{4}"' + ) + cmd = cmd.format( + parse_raw, + out_markup if out_markup != "markdown" else "gfm", + wrap_none, + out_filename, + html_filename, + ) else: - from_arg = '-f html+raw_html' if not strip_raw else '-f html' - cmd = ('pandoc {0} --to={1}-smart --wrap=none -o "{2}" "{3}"') - cmd = cmd.format(from_arg, - out_markup if out_markup != 'markdown' else "gfm", - out_filename, html_filename) + from_arg = "-f html+raw_html" if not strip_raw else "-f html" + cmd = 'pandoc {0} --to={1}-smart --wrap=none -o "{2}" "{3}"' + cmd = cmd.format( + from_arg, + out_markup if out_markup != "markdown" else "gfm", + out_filename, + html_filename, + ) try: rc = subprocess.call(cmd, shell=True) if rc < 0: - error = 'Child was terminated by signal %d' % -rc + error = "Child was terminated by signal %d" % -rc exit(error) elif rc > 0: - error = 'Please, check your Pandoc installation.' + error = "Please, check your Pandoc installation." exit(error) except OSError as e: - error = 'Pandoc execution failed: %s' % e + error = "Pandoc execution failed: %s" % e exit(error) - with open(out_filename, encoding='utf-8') as fs: + with open(out_filename, encoding="utf-8") as fs: content = fs.read() - if out_markup == 'markdown': + if out_markup == "markdown": # In markdown, to insert a
    , end a line with two # or more spaces & then a end-of-line - content = content.replace('\\\n ', ' \n') - content = content.replace('\\\n', ' \n') + content = content.replace("\\\n ", " \n") + content = content.replace("\\\n", " \n") if wp_attach and links: content = update_links_to_attached_files(content, links) - with open(out_filename, 'w', encoding='utf-8') as fs: + with open(out_filename, "w", encoding="utf-8") as fs: fs.write(header + content) if posts_require_pandoc: - logger.error("Pandoc must be installed to import the following posts:" - "\n {}".format("\n ".join(posts_require_pandoc))) + logger.error( + "Pandoc must be installed to import the following posts:" "\n {}".format( + "\n ".join(posts_require_pandoc) + ) + ) if wp_attach and attachments and None in attachments: print("downloading attachments that don't have a parent post") @@ -856,111 +972,136 @@ def fields2pelican( def main(): parser = argparse.ArgumentParser( description="Transform feed, Blogger, Dotclear, Tumblr, or " - "WordPress files into reST (rst) or Markdown (md) files. " - "Be sure to have pandoc installed.", - formatter_class=argparse.ArgumentDefaultsHelpFormatter) + "WordPress files into reST (rst) or Markdown (md) files. " + "Be sure to have pandoc installed.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument(dest="input", help="The input file to read") parser.add_argument( - dest='input', help='The input file to read') + "--blogger", action="store_true", dest="blogger", help="Blogger XML export" + ) parser.add_argument( - '--blogger', action='store_true', dest='blogger', - help='Blogger XML export') + "--dotclear", action="store_true", dest="dotclear", help="Dotclear export" + ) parser.add_argument( - '--dotclear', action='store_true', dest='dotclear', - help='Dotclear export') + "--tumblr", action="store_true", dest="tumblr", help="Tumblr export" + ) parser.add_argument( - '--tumblr', action='store_true', dest='tumblr', - help='Tumblr export') + "--wpfile", action="store_true", dest="wpfile", help="Wordpress XML export" + ) parser.add_argument( - '--wpfile', action='store_true', dest='wpfile', - help='Wordpress XML export') + "--feed", action="store_true", dest="feed", help="Feed to parse" + ) parser.add_argument( - '--feed', action='store_true', dest='feed', - help='Feed to parse') + "-o", "--output", dest="output", default="content", help="Output path" + ) parser.add_argument( - '-o', '--output', dest='output', default='content', - help='Output path') + "-m", + "--markup", + dest="markup", + default="rst", + help="Output markup format (supports rst & markdown)", + ) parser.add_argument( - '-m', '--markup', dest='markup', default='rst', - help='Output markup format (supports rst & markdown)') + "--dir-cat", + action="store_true", + dest="dircat", + help="Put files in directories with categories name", + ) parser.add_argument( - '--dir-cat', action='store_true', dest='dircat', - help='Put files in directories with categories name') + "--dir-page", + action="store_true", + dest="dirpage", + help=( + 'Put files recognised as pages in "pages/" sub-directory' + " (blogger and wordpress import only)" + ), + ) parser.add_argument( - '--dir-page', action='store_true', dest='dirpage', - help=('Put files recognised as pages in "pages/" sub-directory' - ' (blogger and wordpress import only)')) + "--filter-author", + dest="author", + help="Import only post from the specified author", + ) parser.add_argument( - '--filter-author', dest='author', - help='Import only post from the specified author') - parser.add_argument( - '--strip-raw', action='store_true', dest='strip_raw', + "--strip-raw", + action="store_true", + dest="strip_raw", help="Strip raw HTML code that can't be converted to " - "markup such as flash embeds or iframes (wordpress import only)") + "markup such as flash embeds or iframes (wordpress import only)", + ) parser.add_argument( - '--wp-custpost', action='store_true', - dest='wp_custpost', - help='Put wordpress custom post types in directories. If used with ' - '--dir-cat option directories will be created as ' - '/post_type/category/ (wordpress import only)') + "--wp-custpost", + action="store_true", + dest="wp_custpost", + help="Put wordpress custom post types in directories. If used with " + "--dir-cat option directories will be created as " + "/post_type/category/ (wordpress import only)", + ) parser.add_argument( - '--wp-attach', action='store_true', dest='wp_attach', - help='(wordpress import only) Download files uploaded to wordpress as ' - 'attachments. Files will be added to posts as a list in the post ' - 'header. All files will be downloaded, even if ' - "they aren't associated with a post. Files will be downloaded " - 'with their original path inside the output directory. ' - 'e.g. output/wp-uploads/date/postname/file.jpg ' - '-- Requires an internet connection --') + "--wp-attach", + action="store_true", + dest="wp_attach", + help="(wordpress import only) Download files uploaded to wordpress as " + "attachments. Files will be added to posts as a list in the post " + "header. All files will be downloaded, even if " + "they aren't associated with a post. Files will be downloaded " + "with their original path inside the output directory. " + "e.g. output/wp-uploads/date/postname/file.jpg " + "-- Requires an internet connection --", + ) parser.add_argument( - '--disable-slugs', action='store_true', - dest='disable_slugs', - help='Disable storing slugs from imported posts within output. ' - 'With this disabled, your Pelican URLs may not be consistent ' - 'with your original posts.') + "--disable-slugs", + action="store_true", + dest="disable_slugs", + help="Disable storing slugs from imported posts within output. " + "With this disabled, your Pelican URLs may not be consistent " + "with your original posts.", + ) parser.add_argument( - '-b', '--blogname', dest='blogname', - help="Blog name (Tumblr import only)") + "-b", "--blogname", dest="blogname", help="Blog name (Tumblr import only)" + ) args = parser.parse_args() input_type = None if args.blogger: - input_type = 'blogger' + input_type = "blogger" elif args.dotclear: - input_type = 'dotclear' + input_type = "dotclear" elif args.tumblr: - input_type = 'tumblr' + input_type = "tumblr" elif args.wpfile: - input_type = 'wordpress' + input_type = "wordpress" elif args.feed: - input_type = 'feed' + input_type = "feed" else: - error = ('You must provide either --blogger, --dotclear, ' - '--tumblr, --wpfile or --feed options') + error = ( + "You must provide either --blogger, --dotclear, " + "--tumblr, --wpfile or --feed options" + ) exit(error) if not os.path.exists(args.output): try: os.mkdir(args.output) except OSError: - error = 'Unable to create the output folder: ' + args.output + error = "Unable to create the output folder: " + args.output exit(error) - if args.wp_attach and input_type != 'wordpress': - error = ('You must be importing a wordpress xml ' - 'to use the --wp-attach option') + if args.wp_attach and input_type != "wordpress": + error = "You must be importing a wordpress xml " "to use the --wp-attach option" exit(error) - if input_type == 'blogger': + if input_type == "blogger": fields = blogger2fields(args.input) - elif input_type == 'dotclear': + elif input_type == "dotclear": fields = dc2fields(args.input) - elif input_type == 'tumblr': + elif input_type == "tumblr": fields = tumblr2fields(args.input, args.blogname) - elif input_type == 'wordpress': + elif input_type == "wordpress": fields = wp2fields(args.input, args.wp_custpost or False) - elif input_type == 'feed': + elif input_type == "feed": fields = feed2fields(args.input) if args.wp_attach: @@ -970,12 +1111,16 @@ def main(): # init logging init() - fields2pelican(fields, args.markup, args.output, - dircat=args.dircat or False, - dirpage=args.dirpage or False, - strip_raw=args.strip_raw or False, - disable_slugs=args.disable_slugs or False, - filter_author=args.author, - wp_custpost=args.wp_custpost or False, - wp_attach=args.wp_attach or False, - attachments=attachments or None) + fields2pelican( + fields, + args.markup, + args.output, + dircat=args.dircat or False, + dirpage=args.dirpage or False, + strip_raw=args.strip_raw or False, + disable_slugs=args.disable_slugs or False, + filter_author=args.author, + wp_custpost=args.wp_custpost or False, + wp_attach=args.wp_attach or False, + attachments=attachments or None, + ) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 4b6d93cc..fba0c9c3 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -19,6 +19,7 @@ except ImportError: try: import tzlocal + if hasattr(tzlocal.get_localzone(), "zone"): _DEFAULT_TIMEZONE = tzlocal.get_localzone().zone else: @@ -28,55 +29,51 @@ except ModuleNotFoundError: from pelican import __version__ -locale.setlocale(locale.LC_ALL, '') +locale.setlocale(locale.LC_ALL, "") try: _DEFAULT_LANGUAGE = locale.getlocale()[0] except ValueError: # Don't fail on macosx: "unknown locale: UTF-8" _DEFAULT_LANGUAGE = None if _DEFAULT_LANGUAGE is None: - _DEFAULT_LANGUAGE = 'en' + _DEFAULT_LANGUAGE = "en" else: - _DEFAULT_LANGUAGE = _DEFAULT_LANGUAGE.split('_')[0] + _DEFAULT_LANGUAGE = _DEFAULT_LANGUAGE.split("_")[0] -_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), - "templates") +_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates") _jinja_env = Environment( loader=FileSystemLoader(_TEMPLATES_DIR), trim_blocks=True, ) -_GITHUB_PAGES_BRANCHES = { - 'personal': 'main', - 'project': 'gh-pages' -} +_GITHUB_PAGES_BRANCHES = {"personal": "main", "project": "gh-pages"} CONF = { - 'pelican': 'pelican', - 'pelicanopts': '', - 'basedir': os.curdir, - 'ftp_host': 'localhost', - 'ftp_user': 'anonymous', - 'ftp_target_dir': '/', - 'ssh_host': 'localhost', - 'ssh_port': 22, - 'ssh_user': 'root', - 'ssh_target_dir': '/var/www', - 's3_bucket': 'my_s3_bucket', - 'cloudfiles_username': 'my_rackspace_username', - 'cloudfiles_api_key': 'my_rackspace_api_key', - 'cloudfiles_container': 'my_cloudfiles_container', - 'dropbox_dir': '~/Dropbox/Public/', - 'github_pages_branch': _GITHUB_PAGES_BRANCHES['project'], - 'default_pagination': 10, - 'siteurl': '', - 'lang': _DEFAULT_LANGUAGE, - 'timezone': _DEFAULT_TIMEZONE + "pelican": "pelican", + "pelicanopts": "", + "basedir": os.curdir, + "ftp_host": "localhost", + "ftp_user": "anonymous", + "ftp_target_dir": "/", + "ssh_host": "localhost", + "ssh_port": 22, + "ssh_user": "root", + "ssh_target_dir": "/var/www", + "s3_bucket": "my_s3_bucket", + "cloudfiles_username": "my_rackspace_username", + "cloudfiles_api_key": "my_rackspace_api_key", + "cloudfiles_container": "my_cloudfiles_container", + "dropbox_dir": "~/Dropbox/Public/", + "github_pages_branch": _GITHUB_PAGES_BRANCHES["project"], + "default_pagination": 10, + "siteurl": "", + "lang": _DEFAULT_LANGUAGE, + "timezone": _DEFAULT_TIMEZONE, } # url for list of valid timezones -_TZ_URL = 'https://en.wikipedia.org/wiki/List_of_tz_database_time_zones' +_TZ_URL = "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones" # Create a 'marked' default path, to determine if someone has supplied @@ -90,12 +87,12 @@ _DEFAULT_PATH = _DEFAULT_PATH_TYPE(os.curdir) def ask(question, answer=str, default=None, length=None): if answer == str: - r = '' + r = "" while True: if default: - r = input('> {} [{}] '.format(question, default)) + r = input("> {} [{}] ".format(question, default)) else: - r = input('> {} '.format(question)) + r = input("> {} ".format(question)) r = r.strip() @@ -104,10 +101,10 @@ def ask(question, answer=str, default=None, length=None): r = default break else: - print('You must enter something') + print("You must enter something") else: if length and len(r) != length: - print('Entry must be {} characters long'.format(length)) + print("Entry must be {} characters long".format(length)) else: break @@ -117,18 +114,18 @@ def ask(question, answer=str, default=None, length=None): r = None while True: if default is True: - r = input('> {} (Y/n) '.format(question)) + r = input("> {} (Y/n) ".format(question)) elif default is False: - r = input('> {} (y/N) '.format(question)) + r = input("> {} (y/N) ".format(question)) else: - r = input('> {} (y/n) '.format(question)) + r = input("> {} (y/n) ".format(question)) r = r.strip().lower() - if r in ('y', 'yes'): + if r in ("y", "yes"): r = True break - elif r in ('n', 'no'): + elif r in ("n", "no"): r = False break elif not r: @@ -141,9 +138,9 @@ def ask(question, answer=str, default=None, length=None): r = None while True: if default: - r = input('> {} [{}] '.format(question, default)) + r = input("> {} [{}] ".format(question, default)) else: - r = input('> {} '.format(question)) + r = input("> {} ".format(question)) r = r.strip() @@ -155,11 +152,10 @@ def ask(question, answer=str, default=None, length=None): r = int(r) break except ValueError: - print('You must enter an integer') + print("You must enter an integer") return r else: - raise NotImplementedError( - 'Argument `answer` must be str, bool, or integer') + raise NotImplementedError("Argument `answer` must be str, bool, or integer") def ask_timezone(question, default, tzurl): @@ -178,162 +174,227 @@ def ask_timezone(question, default, tzurl): def render_jinja_template(tmpl_name: str, tmpl_vars: Mapping, target_path: str): try: - with open(os.path.join(CONF['basedir'], target_path), - 'w', encoding='utf-8') as fd: + with open( + os.path.join(CONF["basedir"], target_path), "w", encoding="utf-8" + ) as fd: _template = _jinja_env.get_template(tmpl_name) fd.write(_template.render(**tmpl_vars)) except OSError as e: - print('Error: {}'.format(e)) + print("Error: {}".format(e)) def main(): parser = argparse.ArgumentParser( description="A kickstarter for Pelican", - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('-p', '--path', default=_DEFAULT_PATH, - help="The path to generate the blog into") - parser.add_argument('-t', '--title', metavar="title", - help='Set the title of the website') - parser.add_argument('-a', '--author', metavar="author", - help='Set the author name of the website') - parser.add_argument('-l', '--lang', metavar="lang", - help='Set the default web site language') + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "-p", "--path", default=_DEFAULT_PATH, help="The path to generate the blog into" + ) + parser.add_argument( + "-t", "--title", metavar="title", help="Set the title of the website" + ) + parser.add_argument( + "-a", "--author", metavar="author", help="Set the author name of the website" + ) + parser.add_argument( + "-l", "--lang", metavar="lang", help="Set the default web site language" + ) args = parser.parse_args() - print('''Welcome to pelican-quickstart v{v}. + print( + """Welcome to pelican-quickstart v{v}. This script will help you create a new Pelican-based website. Please answer the following questions so this script can generate the files needed by Pelican. - '''.format(v=__version__)) + """.format(v=__version__) + ) - project = os.path.join( - os.environ.get('VIRTUAL_ENV', os.curdir), '.project') - no_path_was_specified = hasattr(args.path, 'is_default_path') + project = os.path.join(os.environ.get("VIRTUAL_ENV", os.curdir), ".project") + no_path_was_specified = hasattr(args.path, "is_default_path") if os.path.isfile(project) and no_path_was_specified: - CONF['basedir'] = open(project).read().rstrip("\n") - print('Using project associated with current virtual environment. ' - 'Will save to:\n%s\n' % CONF['basedir']) + CONF["basedir"] = open(project).read().rstrip("\n") + print( + "Using project associated with current virtual environment. " + "Will save to:\n%s\n" % CONF["basedir"] + ) else: - CONF['basedir'] = os.path.abspath(os.path.expanduser( - ask('Where do you want to create your new web site?', - answer=str, default=args.path))) + CONF["basedir"] = os.path.abspath( + os.path.expanduser( + ask( + "Where do you want to create your new web site?", + answer=str, + default=args.path, + ) + ) + ) - CONF['sitename'] = ask('What will be the title of this web site?', - answer=str, default=args.title) - CONF['author'] = ask('Who will be the author of this web site?', - answer=str, default=args.author) - CONF['lang'] = ask('What will be the default language of this web site?', - str, args.lang or CONF['lang'], 2) + CONF["sitename"] = ask( + "What will be the title of this web site?", answer=str, default=args.title + ) + CONF["author"] = ask( + "Who will be the author of this web site?", answer=str, default=args.author + ) + CONF["lang"] = ask( + "What will be the default language of this web site?", + str, + args.lang or CONF["lang"], + 2, + ) - if ask('Do you want to specify a URL prefix? e.g., https://example.com ', - answer=bool, default=True): - CONF['siteurl'] = ask('What is your URL prefix? (see ' - 'above example; no trailing slash)', - str, CONF['siteurl']) + if ask( + "Do you want to specify a URL prefix? e.g., https://example.com ", + answer=bool, + default=True, + ): + CONF["siteurl"] = ask( + "What is your URL prefix? (see " "above example; no trailing slash)", + str, + CONF["siteurl"], + ) - CONF['with_pagination'] = ask('Do you want to enable article pagination?', - bool, bool(CONF['default_pagination'])) + CONF["with_pagination"] = ask( + "Do you want to enable article pagination?", + bool, + bool(CONF["default_pagination"]), + ) - if CONF['with_pagination']: - CONF['default_pagination'] = ask('How many articles per page ' - 'do you want?', - int, CONF['default_pagination']) + if CONF["with_pagination"]: + CONF["default_pagination"] = ask( + "How many articles per page " "do you want?", + int, + CONF["default_pagination"], + ) else: - CONF['default_pagination'] = False + CONF["default_pagination"] = False - CONF['timezone'] = ask_timezone('What is your time zone?', - CONF['timezone'], _TZ_URL) + CONF["timezone"] = ask_timezone( + "What is your time zone?", CONF["timezone"], _TZ_URL + ) - automation = ask('Do you want to generate a tasks.py/Makefile ' - 'to automate generation and publishing?', bool, True) + automation = ask( + "Do you want to generate a tasks.py/Makefile " + "to automate generation and publishing?", + bool, + True, + ) if automation: - if ask('Do you want to upload your website using FTP?', - answer=bool, default=False): - CONF['ftp'] = True, - CONF['ftp_host'] = ask('What is the hostname of your FTP server?', - str, CONF['ftp_host']) - CONF['ftp_user'] = ask('What is your username on that server?', - str, CONF['ftp_user']) - CONF['ftp_target_dir'] = ask('Where do you want to put your ' - 'web site on that server?', - str, CONF['ftp_target_dir']) - if ask('Do you want to upload your website using SSH?', - answer=bool, default=False): - CONF['ssh'] = True, - CONF['ssh_host'] = ask('What is the hostname of your SSH server?', - str, CONF['ssh_host']) - CONF['ssh_port'] = ask('What is the port of your SSH server?', - int, CONF['ssh_port']) - CONF['ssh_user'] = ask('What is your username on that server?', - str, CONF['ssh_user']) - CONF['ssh_target_dir'] = ask('Where do you want to put your ' - 'web site on that server?', - str, CONF['ssh_target_dir']) + if ask( + "Do you want to upload your website using FTP?", answer=bool, default=False + ): + CONF["ftp"] = (True,) + CONF["ftp_host"] = ask( + "What is the hostname of your FTP server?", str, CONF["ftp_host"] + ) + CONF["ftp_user"] = ask( + "What is your username on that server?", str, CONF["ftp_user"] + ) + CONF["ftp_target_dir"] = ask( + "Where do you want to put your " "web site on that server?", + str, + CONF["ftp_target_dir"], + ) + if ask( + "Do you want to upload your website using SSH?", answer=bool, default=False + ): + CONF["ssh"] = (True,) + CONF["ssh_host"] = ask( + "What is the hostname of your SSH server?", str, CONF["ssh_host"] + ) + CONF["ssh_port"] = ask( + "What is the port of your SSH server?", int, CONF["ssh_port"] + ) + CONF["ssh_user"] = ask( + "What is your username on that server?", str, CONF["ssh_user"] + ) + CONF["ssh_target_dir"] = ask( + "Where do you want to put your " "web site on that server?", + str, + CONF["ssh_target_dir"], + ) - if ask('Do you want to upload your website using Dropbox?', - answer=bool, default=False): - CONF['dropbox'] = True, - CONF['dropbox_dir'] = ask('Where is your Dropbox directory?', - str, CONF['dropbox_dir']) + if ask( + "Do you want to upload your website using Dropbox?", + answer=bool, + default=False, + ): + CONF["dropbox"] = (True,) + CONF["dropbox_dir"] = ask( + "Where is your Dropbox directory?", str, CONF["dropbox_dir"] + ) - if ask('Do you want to upload your website using S3?', - answer=bool, default=False): - CONF['s3'] = True, - CONF['s3_bucket'] = ask('What is the name of your S3 bucket?', - str, CONF['s3_bucket']) + if ask( + "Do you want to upload your website using S3?", answer=bool, default=False + ): + CONF["s3"] = (True,) + CONF["s3_bucket"] = ask( + "What is the name of your S3 bucket?", str, CONF["s3_bucket"] + ) - if ask('Do you want to upload your website using ' - 'Rackspace Cloud Files?', answer=bool, default=False): - CONF['cloudfiles'] = True, - CONF['cloudfiles_username'] = ask('What is your Rackspace ' - 'Cloud username?', str, - CONF['cloudfiles_username']) - CONF['cloudfiles_api_key'] = ask('What is your Rackspace ' - 'Cloud API key?', str, - CONF['cloudfiles_api_key']) - CONF['cloudfiles_container'] = ask('What is the name of your ' - 'Cloud Files container?', - str, - CONF['cloudfiles_container']) + if ask( + "Do you want to upload your website using " "Rackspace Cloud Files?", + answer=bool, + default=False, + ): + CONF["cloudfiles"] = (True,) + CONF["cloudfiles_username"] = ask( + "What is your Rackspace " "Cloud username?", + str, + CONF["cloudfiles_username"], + ) + CONF["cloudfiles_api_key"] = ask( + "What is your Rackspace " "Cloud API key?", + str, + CONF["cloudfiles_api_key"], + ) + CONF["cloudfiles_container"] = ask( + "What is the name of your " "Cloud Files container?", + str, + CONF["cloudfiles_container"], + ) - if ask('Do you want to upload your website using GitHub Pages?', - answer=bool, default=False): - CONF['github'] = True, - if ask('Is this your personal page (username.github.io)?', - answer=bool, default=False): - CONF['github_pages_branch'] = \ - _GITHUB_PAGES_BRANCHES['personal'] + if ask( + "Do you want to upload your website using GitHub Pages?", + answer=bool, + default=False, + ): + CONF["github"] = (True,) + if ask( + "Is this your personal page (username.github.io)?", + answer=bool, + default=False, + ): + CONF["github_pages_branch"] = _GITHUB_PAGES_BRANCHES["personal"] else: - CONF['github_pages_branch'] = \ - _GITHUB_PAGES_BRANCHES['project'] + CONF["github_pages_branch"] = _GITHUB_PAGES_BRANCHES["project"] try: - os.makedirs(os.path.join(CONF['basedir'], 'content')) + os.makedirs(os.path.join(CONF["basedir"], "content")) except OSError as e: - print('Error: {}'.format(e)) + print("Error: {}".format(e)) try: - os.makedirs(os.path.join(CONF['basedir'], 'output')) + os.makedirs(os.path.join(CONF["basedir"], "output")) except OSError as e: - print('Error: {}'.format(e)) + print("Error: {}".format(e)) conf_python = dict() for key, value in CONF.items(): conf_python[key] = repr(value) - render_jinja_template('pelicanconf.py.jinja2', conf_python, 'pelicanconf.py') + render_jinja_template("pelicanconf.py.jinja2", conf_python, "pelicanconf.py") - render_jinja_template('publishconf.py.jinja2', CONF, 'publishconf.py') + render_jinja_template("publishconf.py.jinja2", CONF, "publishconf.py") if automation: - render_jinja_template('tasks.py.jinja2', CONF, 'tasks.py') - render_jinja_template('Makefile.jinja2', CONF, 'Makefile') + render_jinja_template("tasks.py.jinja2", CONF, "tasks.py") + render_jinja_template("Makefile.jinja2", CONF, "Makefile") - print('Done. Your new project is available at %s' % CONF['basedir']) + print("Done. Your new project is available at %s" % CONF["basedir"]) if __name__ == "__main__": diff --git a/pelican/tools/pelican_themes.py b/pelican/tools/pelican_themes.py index 1ad3a333..4069f99b 100755 --- a/pelican/tools/pelican_themes.py +++ b/pelican/tools/pelican_themes.py @@ -8,7 +8,7 @@ import sys def err(msg, die=None): """Print an error message and exits if an exit code is given""" - sys.stderr.write(msg + '\n') + sys.stderr.write(msg + "\n") if die: sys.exit(die if isinstance(die, int) else 1) @@ -16,62 +16,96 @@ def err(msg, die=None): try: import pelican except ImportError: - err('Cannot import pelican.\nYou must ' - 'install Pelican in order to run this script.', - -1) + err( + "Cannot import pelican.\nYou must " + "install Pelican in order to run this script.", + -1, + ) global _THEMES_PATH _THEMES_PATH = os.path.join( - os.path.dirname( - os.path.abspath(pelican.__file__) - ), - 'themes' + os.path.dirname(os.path.abspath(pelican.__file__)), "themes" ) -__version__ = '0.2' -_BUILTIN_THEMES = ['simple', 'notmyidea'] +__version__ = "0.2" +_BUILTIN_THEMES = ["simple", "notmyidea"] def main(): """Main function""" - parser = argparse.ArgumentParser( - description="""Install themes for Pelican""") + parser = argparse.ArgumentParser(description="""Install themes for Pelican""") excl = parser.add_mutually_exclusive_group() excl.add_argument( - '-l', '--list', dest='action', action="store_const", const='list', - help="Show the themes already installed and exit") + "-l", + "--list", + dest="action", + action="store_const", + const="list", + help="Show the themes already installed and exit", + ) excl.add_argument( - '-p', '--path', dest='action', action="store_const", const='path', - help="Show the themes path and exit") + "-p", + "--path", + dest="action", + action="store_const", + const="path", + help="Show the themes path and exit", + ) excl.add_argument( - '-V', '--version', action='version', - version='pelican-themes v{}'.format(__version__), - help='Print the version of this script') + "-V", + "--version", + action="version", + version="pelican-themes v{}".format(__version__), + help="Print the version of this script", + ) parser.add_argument( - '-i', '--install', dest='to_install', nargs='+', metavar="theme path", - help='The themes to install') + "-i", + "--install", + dest="to_install", + nargs="+", + metavar="theme path", + help="The themes to install", + ) parser.add_argument( - '-r', '--remove', dest='to_remove', nargs='+', metavar="theme name", - help='The themes to remove') + "-r", + "--remove", + dest="to_remove", + nargs="+", + metavar="theme name", + help="The themes to remove", + ) parser.add_argument( - '-U', '--upgrade', dest='to_upgrade', nargs='+', - metavar="theme path", help='The themes to upgrade') + "-U", + "--upgrade", + dest="to_upgrade", + nargs="+", + metavar="theme path", + help="The themes to upgrade", + ) parser.add_argument( - '-s', '--symlink', dest='to_symlink', nargs='+', metavar="theme path", + "-s", + "--symlink", + dest="to_symlink", + nargs="+", + metavar="theme path", help="Same as `--install', but create a symbolic link instead of " - "copying the theme. Useful for theme development") + "copying the theme. Useful for theme development", + ) parser.add_argument( - '-c', '--clean', dest='clean', action="store_true", - help="Remove the broken symbolic links of the theme path") + "-c", + "--clean", + dest="clean", + action="store_true", + help="Remove the broken symbolic links of the theme path", + ) parser.add_argument( - '-v', '--verbose', dest='verbose', - action="store_true", - help="Verbose output") + "-v", "--verbose", dest="verbose", action="store_true", help="Verbose output" + ) args = parser.parse_args() @@ -79,46 +113,46 @@ def main(): to_sym = args.to_symlink or args.clean if args.action: - if args.action == 'list': + if args.action == "list": list_themes(args.verbose) - elif args.action == 'path': + elif args.action == "path": print(_THEMES_PATH) elif to_install or args.to_remove or to_sym: if args.to_remove: if args.verbose: - print('Removing themes...') + print("Removing themes...") for i in args.to_remove: remove(i, v=args.verbose) if args.to_install: if args.verbose: - print('Installing themes...') + print("Installing themes...") for i in args.to_install: install(i, v=args.verbose) if args.to_upgrade: if args.verbose: - print('Upgrading themes...') + print("Upgrading themes...") for i in args.to_upgrade: install(i, v=args.verbose, u=True) if args.to_symlink: if args.verbose: - print('Linking themes...') + print("Linking themes...") for i in args.to_symlink: symlink(i, v=args.verbose) if args.clean: if args.verbose: - print('Cleaning the themes directory...') + print("Cleaning the themes directory...") clean(v=args.verbose) else: - print('No argument given... exiting.') + print("No argument given... exiting.") def themes(): @@ -142,7 +176,7 @@ def list_themes(v=False): if v: print(theme_path + (" (symbolic link to `" + link_target + "')")) else: - print(theme_path + '@') + print(theme_path + "@") else: print(theme_path) @@ -150,51 +184,52 @@ def list_themes(v=False): def remove(theme_name, v=False): """Removes a theme""" - theme_name = theme_name.replace('/', '') + theme_name = theme_name.replace("/", "") target = os.path.join(_THEMES_PATH, theme_name) if theme_name in _BUILTIN_THEMES: - err(theme_name + ' is a builtin theme.\n' - 'You cannot remove a builtin theme with this script, ' - 'remove it by hand if you want.') + err( + theme_name + " is a builtin theme.\n" + "You cannot remove a builtin theme with this script, " + "remove it by hand if you want." + ) elif os.path.islink(target): if v: - print('Removing link `' + target + "'") + print("Removing link `" + target + "'") os.remove(target) elif os.path.isdir(target): if v: - print('Removing directory `' + target + "'") + print("Removing directory `" + target + "'") shutil.rmtree(target) elif os.path.exists(target): - err(target + ' : not a valid theme') + err(target + " : not a valid theme") else: - err(target + ' : no such file or directory') + err(target + " : no such file or directory") def install(path, v=False, u=False): """Installs a theme""" if not os.path.exists(path): - err(path + ' : no such file or directory') + err(path + " : no such file or directory") elif not os.path.isdir(path): - err(path + ' : not a directory') + err(path + " : not a directory") else: theme_name = os.path.basename(os.path.normpath(path)) theme_path = os.path.join(_THEMES_PATH, theme_name) exists = os.path.exists(theme_path) if exists and not u: - err(path + ' : already exists') + err(path + " : already exists") elif exists: remove(theme_name, v) install(path, v) else: if v: - print("Copying '{p}' to '{t}' ...".format(p=path, - t=theme_path)) + print("Copying '{p}' to '{t}' ...".format(p=path, t=theme_path)) try: shutil.copytree(path, theme_path) try: - if os.name == 'posix': + if os.name == "posix": for root, dirs, files in os.walk(theme_path): for d in dirs: dname = os.path.join(root, d) @@ -203,35 +238,41 @@ def install(path, v=False, u=False): fname = os.path.join(root, f) os.chmod(fname, 420) # 0o644 except OSError as e: - err("Cannot change permissions of files " - "or directory in `{r}':\n{e}".format(r=theme_path, - e=str(e)), - die=False) + err( + "Cannot change permissions of files " + "or directory in `{r}':\n{e}".format(r=theme_path, e=str(e)), + die=False, + ) except Exception as e: - err("Cannot copy `{p}' to `{t}':\n{e}".format( - p=path, t=theme_path, e=str(e))) + err( + "Cannot copy `{p}' to `{t}':\n{e}".format( + p=path, t=theme_path, e=str(e) + ) + ) def symlink(path, v=False): """Symbolically link a theme""" if not os.path.exists(path): - err(path + ' : no such file or directory') + err(path + " : no such file or directory") elif not os.path.isdir(path): - err(path + ' : not a directory') + err(path + " : not a directory") else: theme_name = os.path.basename(os.path.normpath(path)) theme_path = os.path.join(_THEMES_PATH, theme_name) if os.path.exists(theme_path): - err(path + ' : already exists') + err(path + " : already exists") else: if v: - print("Linking `{p}' to `{t}' ...".format( - p=path, t=theme_path)) + print("Linking `{p}' to `{t}' ...".format(p=path, t=theme_path)) try: os.symlink(path, theme_path) except Exception as e: - err("Cannot link `{p}' to `{t}':\n{e}".format( - p=path, t=theme_path, e=str(e))) + err( + "Cannot link `{p}' to `{t}':\n{e}".format( + p=path, t=theme_path, e=str(e) + ) + ) def is_broken_link(path): @@ -247,11 +288,11 @@ def clean(v=False): path = os.path.join(_THEMES_PATH, path) if os.path.islink(path) and is_broken_link(path): if v: - print('Removing {}'.format(path)) + print("Removing {}".format(path)) try: os.remove(path) except OSError: - print('Error: cannot remove {}'.format(path)) + print("Error: cannot remove {}".format(path)) else: c += 1 diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index e00b914c..2e8cc953 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -31,17 +31,16 @@ class URLWrapper: @property def slug(self): if self._slug is None: - class_key = '{}_REGEX_SUBSTITUTIONS'.format( - self.__class__.__name__.upper()) + class_key = "{}_REGEX_SUBSTITUTIONS".format(self.__class__.__name__.upper()) regex_subs = self.settings.get( - class_key, - self.settings.get('SLUG_REGEX_SUBSTITUTIONS', [])) - preserve_case = self.settings.get('SLUGIFY_PRESERVE_CASE', False) + class_key, self.settings.get("SLUG_REGEX_SUBSTITUTIONS", []) + ) + preserve_case = self.settings.get("SLUGIFY_PRESERVE_CASE", False) self._slug = slugify( self.name, regex_subs=regex_subs, preserve_case=preserve_case, - use_unicode=self.settings.get('SLUGIFY_USE_UNICODE', False) + use_unicode=self.settings.get("SLUGIFY_USE_UNICODE", False), ) return self._slug @@ -53,26 +52,26 @@ class URLWrapper: def as_dict(self): d = self.__dict__ - d['name'] = self.name - d['slug'] = self.slug + d["name"] = self.name + d["slug"] = self.slug return d def __hash__(self): return hash(self.slug) def _normalize_key(self, key): - class_key = '{}_REGEX_SUBSTITUTIONS'.format( - self.__class__.__name__.upper()) + class_key = "{}_REGEX_SUBSTITUTIONS".format(self.__class__.__name__.upper()) regex_subs = self.settings.get( - class_key, - self.settings.get('SLUG_REGEX_SUBSTITUTIONS', [])) - use_unicode = self.settings.get('SLUGIFY_USE_UNICODE', False) - preserve_case = self.settings.get('SLUGIFY_PRESERVE_CASE', False) + class_key, self.settings.get("SLUG_REGEX_SUBSTITUTIONS", []) + ) + use_unicode = self.settings.get("SLUGIFY_USE_UNICODE", False) + preserve_case = self.settings.get("SLUGIFY_PRESERVE_CASE", False) return slugify( key, regex_subs=regex_subs, preserve_case=preserve_case, - use_unicode=use_unicode) + use_unicode=use_unicode, + ) def __eq__(self, other): if isinstance(other, self.__class__): @@ -99,7 +98,7 @@ class URLWrapper: return self.name def __repr__(self): - return '<{} {}>'.format(type(self).__name__, repr(self._name)) + return "<{} {}>".format(type(self).__name__, repr(self._name)) def _from_settings(self, key, get_page_name=False): """Returns URL information as defined in settings. @@ -114,7 +113,7 @@ class URLWrapper: if isinstance(value, pathlib.Path): value = str(value) if not isinstance(value, str): - logger.warning('%s is set to %s', setting, value) + logger.warning("%s is set to %s", setting, value) return value else: if get_page_name: @@ -122,10 +121,11 @@ class URLWrapper: else: return value.format(**self.as_dict()) - page_name = property(functools.partial(_from_settings, key='URL', - get_page_name=True)) - url = property(functools.partial(_from_settings, key='URL')) - save_as = property(functools.partial(_from_settings, key='SAVE_AS')) + page_name = property( + functools.partial(_from_settings, key="URL", get_page_name=True) + ) + url = property(functools.partial(_from_settings, key="URL")) + save_as = property(functools.partial(_from_settings, key="SAVE_AS")) class Category(URLWrapper): diff --git a/pelican/utils.py b/pelican/utils.py index 09ffcfe6..08a08f7e 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -32,38 +32,37 @@ logger = logging.getLogger(__name__) def sanitised_join(base_directory, *parts): - joined = posixize_path( - os.path.abspath(os.path.join(base_directory, *parts))) + joined = posixize_path(os.path.abspath(os.path.join(base_directory, *parts))) base = posixize_path(os.path.abspath(base_directory)) if not joined.startswith(base): raise RuntimeError( - "Attempted to break out of output directory to {}".format( - joined - ) + "Attempted to break out of output directory to {}".format(joined) ) return joined def strftime(date, date_format): - ''' + """ Enhanced replacement for built-in strftime with zero stripping This works by 'grabbing' possible format strings (those starting with %), formatting them with the date, stripping any leading zeros if - prefix is used and replacing formatted output back. - ''' + """ + def strip_zeros(x): - return x.lstrip('0') or '0' + return x.lstrip("0") or "0" + # includes ISO date parameters added by Python 3.6 - c89_directives = 'aAbBcdfGHIjmMpSUuVwWxXyYzZ%' + c89_directives = "aAbBcdfGHIjmMpSUuVwWxXyYzZ%" # grab candidate format options - format_options = '%[-]?.' + format_options = "%[-]?." candidates = re.findall(format_options, date_format) # replace candidates with placeholders for later % formatting - template = re.sub(format_options, '%s', date_format) + template = re.sub(format_options, "%s", date_format) formatted_candidates = [] for candidate in candidates: @@ -72,7 +71,7 @@ def strftime(date, date_format): # check for '-' prefix if len(candidate) == 3: # '-' prefix - candidate = '%{}'.format(candidate[-1]) + candidate = "%{}".format(candidate[-1]) conversion = strip_zeros else: conversion = None @@ -95,10 +94,10 @@ def strftime(date, date_format): class SafeDatetime(datetime.datetime): - '''Subclass of datetime that works with utf-8 format strings on PY2''' + """Subclass of datetime that works with utf-8 format strings on PY2""" def strftime(self, fmt, safe=True): - '''Uses our custom strftime if supposed to be *safe*''' + """Uses our custom strftime if supposed to be *safe*""" if safe: return strftime(self, fmt) else: @@ -106,22 +105,21 @@ class SafeDatetime(datetime.datetime): class DateFormatter: - '''A date formatter object used as a jinja filter + """A date formatter object used as a jinja filter Uses the `strftime` implementation and makes sure jinja uses the locale defined in LOCALE setting - ''' + """ def __init__(self): self.locale = locale.setlocale(locale.LC_TIME) def __call__(self, date, date_format): - # on OSX, encoding from LC_CTYPE determines the unicode output in PY3 # make sure it's same as LC_TIME - with temporary_locale(self.locale, locale.LC_TIME), \ - temporary_locale(self.locale, locale.LC_CTYPE): - + with temporary_locale(self.locale, locale.LC_TIME), temporary_locale( + self.locale, locale.LC_CTYPE + ): formatted = strftime(date, date_format) return formatted @@ -155,7 +153,7 @@ class memoized: return self.func.__doc__ def __get__(self, obj, objtype): - '''Support instance methods.''' + """Support instance methods.""" fn = partial(self.__call__, obj) fn.cache = self.cache return fn @@ -177,17 +175,16 @@ def deprecated_attribute(old, new, since=None, remove=None, doc=None): Note that the decorator needs a dummy method to attach to, but the content of the dummy method is ignored. """ + def _warn(): - version = '.'.join(str(x) for x in since) - message = ['{} has been deprecated since {}'.format(old, version)] + version = ".".join(str(x) for x in since) + message = ["{} has been deprecated since {}".format(old, version)] if remove: - version = '.'.join(str(x) for x in remove) - message.append( - ' and will be removed by version {}'.format(version)) - message.append('. Use {} instead.'.format(new)) - logger.warning(''.join(message)) - logger.debug(''.join(str(x) for x - in traceback.format_stack())) + version = ".".join(str(x) for x in remove) + message.append(" and will be removed by version {}".format(version)) + message.append(". Use {} instead.".format(new)) + logger.warning("".join(message)) + logger.debug("".join(str(x) for x in traceback.format_stack())) def fget(self): _warn() @@ -208,21 +205,20 @@ def get_date(string): If no format matches the given date, raise a ValueError. """ - string = re.sub(' +', ' ', string) - default = SafeDatetime.now().replace(hour=0, minute=0, - second=0, microsecond=0) + string = re.sub(" +", " ", string) + default = SafeDatetime.now().replace(hour=0, minute=0, second=0, microsecond=0) try: return dateutil.parser.parse(string, default=default) except (TypeError, ValueError): - raise ValueError('{!r} is not a valid date'.format(string)) + raise ValueError("{!r} is not a valid date".format(string)) @contextmanager -def pelican_open(filename, mode='r', strip_crs=(sys.platform == 'win32')): +def pelican_open(filename, mode="r", strip_crs=(sys.platform == "win32")): """Open a file and return its content""" # utf-8-sig will clear any BOM if present - with open(filename, mode, encoding='utf-8-sig') as infile: + with open(filename, mode, encoding="utf-8-sig") as infile: content = infile.read() yield content @@ -244,7 +240,7 @@ def slugify(value, regex_subs=(), preserve_case=False, use_unicode=False): def normalize_unicode(text): # normalize text by compatibility composition # see: https://en.wikipedia.org/wiki/Unicode_equivalence - return unicodedata.normalize('NFKC', text) + return unicodedata.normalize("NFKC", text) # strip tags from value value = Markup(value).striptags() @@ -259,10 +255,8 @@ def slugify(value, regex_subs=(), preserve_case=False, use_unicode=False): # perform regex substitutions for src, dst in regex_subs: value = re.sub( - normalize_unicode(src), - normalize_unicode(dst), - value, - flags=re.IGNORECASE) + normalize_unicode(src), normalize_unicode(dst), value, flags=re.IGNORECASE + ) if not preserve_case: value = value.lower() @@ -283,8 +277,7 @@ def copy(source, destination, ignores=None): """ def walk_error(err): - logger.warning("While copying %s: %s: %s", - source_, err.filename, err.strerror) + logger.warning("While copying %s: %s: %s", source_, err.filename, err.strerror) source_ = os.path.abspath(os.path.expanduser(source)) destination_ = os.path.abspath(os.path.expanduser(destination)) @@ -292,39 +285,40 @@ def copy(source, destination, ignores=None): if ignores is None: ignores = [] - if any(fnmatch.fnmatch(os.path.basename(source), ignore) - for ignore in ignores): - logger.info('Not copying %s due to ignores', source_) + if any(fnmatch.fnmatch(os.path.basename(source), ignore) for ignore in ignores): + logger.info("Not copying %s due to ignores", source_) return if os.path.isfile(source_): dst_dir = os.path.dirname(destination_) if not os.path.exists(dst_dir): - logger.info('Creating directory %s', dst_dir) + logger.info("Creating directory %s", dst_dir) os.makedirs(dst_dir) - logger.info('Copying %s to %s', source_, destination_) + logger.info("Copying %s to %s", source_, destination_) copy_file(source_, destination_) elif os.path.isdir(source_): if not os.path.exists(destination_): - logger.info('Creating directory %s', destination_) + logger.info("Creating directory %s", destination_) os.makedirs(destination_) if not os.path.isdir(destination_): - logger.warning('Cannot copy %s (a directory) to %s (a file)', - source_, destination_) + logger.warning( + "Cannot copy %s (a directory) to %s (a file)", source_, destination_ + ) return for src_dir, subdirs, others in os.walk(source_, followlinks=True): - dst_dir = os.path.join(destination_, - os.path.relpath(src_dir, source_)) + dst_dir = os.path.join(destination_, os.path.relpath(src_dir, source_)) - subdirs[:] = (s for s in subdirs if not any(fnmatch.fnmatch(s, i) - for i in ignores)) - others[:] = (o for o in others if not any(fnmatch.fnmatch(o, i) - for i in ignores)) + subdirs[:] = ( + s for s in subdirs if not any(fnmatch.fnmatch(s, i) for i in ignores) + ) + others[:] = ( + o for o in others if not any(fnmatch.fnmatch(o, i) for i in ignores) + ) if not os.path.isdir(dst_dir): - logger.info('Creating directory %s', dst_dir) + logger.info("Creating directory %s", dst_dir) # Parent directories are known to exist, so 'mkdir' suffices. os.mkdir(dst_dir) @@ -332,21 +326,24 @@ def copy(source, destination, ignores=None): src_path = os.path.join(src_dir, o) dst_path = os.path.join(dst_dir, o) if os.path.isfile(src_path): - logger.info('Copying %s to %s', src_path, dst_path) + logger.info("Copying %s to %s", src_path, dst_path) copy_file(src_path, dst_path) else: - logger.warning('Skipped copy %s (not a file or ' - 'directory) to %s', - src_path, dst_path) + logger.warning( + "Skipped copy %s (not a file or " "directory) to %s", + src_path, + dst_path, + ) def copy_file(source, destination): - '''Copy a file''' + """Copy a file""" try: shutil.copyfile(source, destination) except OSError as e: - logger.warning("A problem occurred copying file %s to %s; %s", - source, destination, e) + logger.warning( + "A problem occurred copying file %s to %s; %s", source, destination, e + ) def clean_output_dir(path, retention): @@ -367,15 +364,15 @@ def clean_output_dir(path, retention): for filename in os.listdir(path): file = os.path.join(path, filename) if any(filename == retain for retain in retention): - logger.debug("Skipping deletion; %s is on retention list: %s", - filename, file) + logger.debug( + "Skipping deletion; %s is on retention list: %s", filename, file + ) elif os.path.isdir(file): try: shutil.rmtree(file) logger.debug("Deleted directory %s", file) except Exception as e: - logger.error("Unable to delete directory %s; %s", - file, e) + logger.error("Unable to delete directory %s; %s", file, e) elif os.path.isfile(file) or os.path.islink(file): try: os.remove(file) @@ -407,29 +404,31 @@ def posixize_path(rel_path): """Use '/' as path separator, so that source references, like '{static}/foo/bar.jpg' or 'extras/favicon.ico', will work on Windows as well as on Mac and Linux.""" - return rel_path.replace(os.sep, '/') + return rel_path.replace(os.sep, "/") class _HTMLWordTruncator(HTMLParser): - - _word_regex = re.compile(r"{DBC}|(\w[\w'-]*)".format( - # DBC means CJK-like characters. An character can stand for a word. - DBC=("([\u4E00-\u9FFF])|" # CJK Unified Ideographs - "([\u3400-\u4DBF])|" # CJK Unified Ideographs Extension A - "([\uF900-\uFAFF])|" # CJK Compatibility Ideographs - "([\U00020000-\U0002A6DF])|" # CJK Unified Ideographs Extension B - "([\U0002F800-\U0002FA1F])|" # CJK Compatibility Ideographs Supplement - "([\u3040-\u30FF])|" # Hiragana and Katakana - "([\u1100-\u11FF])|" # Hangul Jamo - "([\uAC00-\uD7FF])|" # Hangul Compatibility Jamo - "([\u3130-\u318F])" # Hangul Syllables - )), re.UNICODE) - _word_prefix_regex = re.compile(r'\w', re.U) - _singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area', - 'hr', 'input') + _word_regex = re.compile( + r"{DBC}|(\w[\w'-]*)".format( + # DBC means CJK-like characters. An character can stand for a word. + DBC=( + "([\u4E00-\u9FFF])|" # CJK Unified Ideographs + "([\u3400-\u4DBF])|" # CJK Unified Ideographs Extension A + "([\uF900-\uFAFF])|" # CJK Compatibility Ideographs + "([\U00020000-\U0002A6DF])|" # CJK Unified Ideographs Extension B + "([\U0002F800-\U0002FA1F])|" # CJK Compatibility Ideographs Supplement + "([\u3040-\u30FF])|" # Hiragana and Katakana + "([\u1100-\u11FF])|" # Hangul Jamo + "([\uAC00-\uD7FF])|" # Hangul Compatibility Jamo + "([\u3130-\u318F])" # Hangul Syllables + ) + ), + re.UNICODE, + ) + _word_prefix_regex = re.compile(r"\w", re.U) + _singlets = ("br", "col", "link", "base", "img", "param", "area", "hr", "input") class TruncationCompleted(Exception): - def __init__(self, truncate_at): super().__init__(truncate_at) self.truncate_at = truncate_at @@ -455,7 +454,7 @@ class _HTMLWordTruncator(HTMLParser): line_start = 0 lineno, line_offset = self.getpos() for i in range(lineno - 1): - line_start = self.rawdata.index('\n', line_start) + 1 + line_start = self.rawdata.index("\n", line_start) + 1 return line_start + line_offset def add_word(self, word_end): @@ -482,7 +481,7 @@ class _HTMLWordTruncator(HTMLParser): else: # SGML: An end tag closes, back to the matching start tag, # all unclosed intervening start tags with omitted end tags - del self.open_tags[:i + 1] + del self.open_tags[: i + 1] def handle_data(self, data): word_end = 0 @@ -531,7 +530,7 @@ class _HTMLWordTruncator(HTMLParser): ref_end = offset + len(name) + 1 try: - if self.rawdata[ref_end] == ';': + if self.rawdata[ref_end] == ";": ref_end += 1 except IndexError: # We are at the end of the string and there's no ';' @@ -556,7 +555,7 @@ class _HTMLWordTruncator(HTMLParser): codepoint = entities.name2codepoint[name] char = chr(codepoint) except KeyError: - char = '' + char = "" self._handle_ref(name, char) def handle_charref(self, name): @@ -567,17 +566,17 @@ class _HTMLWordTruncator(HTMLParser): `#x2014`) """ try: - if name.startswith('x'): + if name.startswith("x"): codepoint = int(name[1:], 16) else: codepoint = int(name) char = chr(codepoint) except (ValueError, OverflowError): - char = '' - self._handle_ref('#' + name, char) + char = "" + self._handle_ref("#" + name, char) -def truncate_html_words(s, num, end_text='…'): +def truncate_html_words(s, num, end_text="…"): """Truncates HTML to a certain number of words. (not counting tags and comments). Closes opened tags if they were correctly @@ -588,23 +587,23 @@ def truncate_html_words(s, num, end_text='…'): """ length = int(num) if length <= 0: - return '' + return "" truncator = _HTMLWordTruncator(length) truncator.feed(s) if truncator.truncate_at is None: return s - out = s[:truncator.truncate_at] + out = s[: truncator.truncate_at] if end_text: - out += ' ' + end_text + out += " " + end_text # Close any tags still open for tag in truncator.open_tags: - out += '' % tag + out += "" % tag # Return string return out def process_translations(content_list, translation_id=None): - """ Finds translations and returns them. + """Finds translations and returns them. For each content_list item, populates the 'translations' attribute, and returns a tuple with two lists (index, translations). Index list includes @@ -632,19 +631,23 @@ def process_translations(content_list, translation_id=None): try: content_list.sort(key=attrgetter(*translation_id)) except TypeError: - raise TypeError('Cannot unpack {}, \'translation_id\' must be falsy, a' - ' string or a collection of strings' - .format(translation_id)) + raise TypeError( + "Cannot unpack {}, 'translation_id' must be falsy, a" + " string or a collection of strings".format(translation_id) + ) except AttributeError: - raise AttributeError('Cannot use {} as \'translation_id\', there ' - 'appear to be items without these metadata ' - 'attributes'.format(translation_id)) + raise AttributeError( + "Cannot use {} as 'translation_id', there " + "appear to be items without these metadata " + "attributes".format(translation_id) + ) for id_vals, items in groupby(content_list, attrgetter(*translation_id)): # prepare warning string id_vals = (id_vals,) if len(translation_id) == 1 else id_vals - with_str = 'with' + ', '.join([' {} "{{}}"'] * len(translation_id))\ - .format(*translation_id).format(*id_vals) + with_str = "with" + ", ".join([' {} "{{}}"'] * len(translation_id)).format( + *translation_id + ).format(*id_vals) items = list(items) original_items = get_original_items(items, with_str) @@ -662,24 +665,24 @@ def get_original_items(items, with_str): args = [len(items)] args.extend(extra) args.extend(x.source_path for x in items) - logger.warning('{}: {}'.format(msg, '\n%s' * len(items)), *args) + logger.warning("{}: {}".format(msg, "\n%s" * len(items)), *args) # warn if several items have the same lang - for lang, lang_items in groupby(items, attrgetter('lang')): + for lang, lang_items in groupby(items, attrgetter("lang")): lang_items = list(lang_items) if len(lang_items) > 1: - _warn_source_paths('There are %s items "%s" with lang %s', - lang_items, with_str, lang) + _warn_source_paths( + 'There are %s items "%s" with lang %s', lang_items, with_str, lang + ) # items with `translation` metadata will be used as translations... candidate_items = [ - i for i in items - if i.metadata.get('translation', 'false').lower() == 'false'] + i for i in items if i.metadata.get("translation", "false").lower() == "false" + ] # ...unless all items with that slug are translations if not candidate_items: - _warn_source_paths('All items ("%s") "%s" are translations', - items, with_str) + _warn_source_paths('All items ("%s") "%s" are translations', items, with_str) candidate_items = items # find items with default language @@ -691,13 +694,14 @@ def get_original_items(items, with_str): # warn if there are several original items if len(original_items) > 1: - _warn_source_paths('There are %s original (not translated) items %s', - original_items, with_str) + _warn_source_paths( + "There are %s original (not translated) items %s", original_items, with_str + ) return original_items -def order_content(content_list, order_by='slug'): - """ Sorts content. +def order_content(content_list, order_by="slug"): + """Sorts content. order_by can be a string of an attribute or sorting function. If order_by is defined, content will be ordered by that attribute or sorting function. @@ -713,22 +717,22 @@ def order_content(content_list, order_by='slug'): try: content_list.sort(key=order_by) except Exception: - logger.error('Error sorting with function %s', order_by) + logger.error("Error sorting with function %s", order_by) elif isinstance(order_by, str): - if order_by.startswith('reversed-'): + if order_by.startswith("reversed-"): order_reversed = True - order_by = order_by.replace('reversed-', '', 1) + order_by = order_by.replace("reversed-", "", 1) else: order_reversed = False - if order_by == 'basename': + if order_by == "basename": content_list.sort( - key=lambda x: os.path.basename(x.source_path or ''), - reverse=order_reversed) + key=lambda x: os.path.basename(x.source_path or ""), + reverse=order_reversed, + ) else: try: - content_list.sort(key=attrgetter(order_by), - reverse=order_reversed) + content_list.sort(key=attrgetter(order_by), reverse=order_reversed) except AttributeError: for content in content_list: try: @@ -736,26 +740,31 @@ def order_content(content_list, order_by='slug'): except AttributeError: logger.warning( 'There is no "%s" attribute in "%s". ' - 'Defaulting to slug order.', + "Defaulting to slug order.", order_by, content.get_relative_source_path(), extra={ - 'limit_msg': ('More files are missing ' - 'the needed attribute.') - }) + "limit_msg": ( + "More files are missing " + "the needed attribute." + ) + }, + ) else: logger.warning( - 'Invalid *_ORDER_BY setting (%s). ' - 'Valid options are strings and functions.', order_by) + "Invalid *_ORDER_BY setting (%s). " + "Valid options are strings and functions.", + order_by, + ) return content_list def wait_for_changes(settings_file, reader_class, settings): - content_path = settings.get('PATH', '') - theme_path = settings.get('THEME', '') + content_path = settings.get("PATH", "") + theme_path = settings.get("THEME", "") ignore_files = set( - fnmatch.translate(pattern) for pattern in settings.get('IGNORE_FILES', []) + fnmatch.translate(pattern) for pattern in settings.get("IGNORE_FILES", []) ) candidate_paths = [ @@ -765,7 +774,7 @@ def wait_for_changes(settings_file, reader_class, settings): ] candidate_paths.extend( - os.path.join(content_path, path) for path in settings.get('STATIC_PATHS', []) + os.path.join(content_path, path) for path in settings.get("STATIC_PATHS", []) ) watching_paths = [] @@ -778,11 +787,13 @@ def wait_for_changes(settings_file, reader_class, settings): else: watching_paths.append(path) - return next(watchfiles.watch( - *watching_paths, - watch_filter=watchfiles.DefaultFilter(ignore_entity_patterns=ignore_files), - rust_timeout=0 - )) + return next( + watchfiles.watch( + *watching_paths, + watch_filter=watchfiles.DefaultFilter(ignore_entity_patterns=ignore_files), + rust_timeout=0, + ) + ) def set_date_tzinfo(d, tz_name=None): @@ -811,7 +822,7 @@ def split_all(path): """ if isinstance(path, str): components = [] - path = path.lstrip('/') + path = path.lstrip("/") while path: head, tail = os.path.split(path) if tail: @@ -827,32 +838,30 @@ def split_all(path): return None else: raise TypeError( - '"path" was {}, must be string, None, or pathlib.Path'.format( - type(path) - ) + '"path" was {}, must be string, None, or pathlib.Path'.format(type(path)) ) def is_selected_for_writing(settings, path): - '''Check whether path is selected for writing + """Check whether path is selected for writing according to the WRITE_SELECTED list If WRITE_SELECTED is an empty list (default), any path is selected for writing. - ''' - if settings['WRITE_SELECTED']: - return path in settings['WRITE_SELECTED'] + """ + if settings["WRITE_SELECTED"]: + return path in settings["WRITE_SELECTED"] else: return True def path_to_file_url(path): - '''Convert file-system path to file:// URL''' + """Convert file-system path to file:// URL""" return urllib.parse.urljoin("file://", urllib.request.pathname2url(path)) def maybe_pluralize(count, singular, plural): - ''' + """ Returns a formatted string containing count and plural if count is not 1 Returns count and singular if count is 1 @@ -860,22 +869,22 @@ def maybe_pluralize(count, singular, plural): maybe_pluralize(1, 'Article', 'Articles') -> '1 Article' maybe_pluralize(2, 'Article', 'Articles') -> '2 Articles' - ''' + """ selection = plural if count == 1: selection = singular - return '{} {}'.format(count, selection) + return "{} {}".format(count, selection) @contextmanager def temporary_locale(temp_locale=None, lc_category=locale.LC_ALL): - ''' + """ Enable code to run in a context with a temporary locale Resets the locale back when exiting context. Use tests.support.TestCaseWithCLocale if you want every unit test in a class to use the C locale. - ''' + """ orig_locale = locale.setlocale(lc_category) if temp_locale: locale.setlocale(lc_category, temp_locale) diff --git a/pelican/writers.py b/pelican/writers.py index 632c6b87..ec12d125 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -9,14 +9,18 @@ from markupsafe import Markup from pelican.paginator import Paginator from pelican.plugins import signals -from pelican.utils import (get_relative_path, is_selected_for_writing, - path_to_url, sanitised_join, set_date_tzinfo) +from pelican.utils import ( + get_relative_path, + is_selected_for_writing, + path_to_url, + sanitised_join, + set_date_tzinfo, +) logger = logging.getLogger(__name__) class Writer: - def __init__(self, output_path, settings=None): self.output_path = output_path self.reminder = dict() @@ -25,24 +29,26 @@ class Writer: self._overridden_files = set() # See Content._link_replacer for details - if "RELATIVE_URLS" in self.settings and self.settings['RELATIVE_URLS']: + if "RELATIVE_URLS" in self.settings and self.settings["RELATIVE_URLS"]: self.urljoiner = posix_join else: self.urljoiner = lambda base, url: urljoin( - base if base.endswith('/') else base + '/', str(url)) + base if base.endswith("/") else base + "/", str(url) + ) def _create_new_feed(self, feed_type, feed_title, context): - feed_class = Rss201rev2Feed if feed_type == 'rss' else Atom1Feed + feed_class = Rss201rev2Feed if feed_type == "rss" else Atom1Feed if feed_title: - feed_title = context['SITENAME'] + ' - ' + feed_title + feed_title = context["SITENAME"] + " - " + feed_title else: - feed_title = context['SITENAME'] + feed_title = context["SITENAME"] return feed_class( title=Markup(feed_title).striptags(), - link=(self.site_url + '/'), + link=(self.site_url + "/"), feed_url=self.feed_url, - description=context.get('SITESUBTITLE', ''), - subtitle=context.get('SITESUBTITLE', None)) + description=context.get("SITESUBTITLE", ""), + subtitle=context.get("SITESUBTITLE", None), + ) def _add_item_to_the_feed(self, feed, item): title = Markup(item.title).striptags() @@ -52,7 +58,7 @@ class Writer: # RSS feeds use a single tag called 'description' for both the full # content and the summary content = None - if self.settings.get('RSS_FEED_SUMMARY_ONLY'): + if self.settings.get("RSS_FEED_SUMMARY_ONLY"): description = item.summary else: description = item.get_content(self.site_url) @@ -71,9 +77,9 @@ class Writer: description = None categories = [] - if hasattr(item, 'category'): + if hasattr(item, "category"): categories.append(item.category) - if hasattr(item, 'tags'): + if hasattr(item, "tags"): categories.extend(item.tags) feed.add_item( @@ -83,14 +89,12 @@ class Writer: description=description, content=content, categories=categories or None, - author_name=getattr(item, 'author', ''), - pubdate=set_date_tzinfo( - item.date, self.settings.get('TIMEZONE', None) - ), + author_name=getattr(item, "author", ""), + pubdate=set_date_tzinfo(item.date, self.settings.get("TIMEZONE", None)), updateddate=set_date_tzinfo( - item.modified, self.settings.get('TIMEZONE', None) + item.modified, self.settings.get("TIMEZONE", None) ) - if hasattr(item, 'modified') + if hasattr(item, "modified") else None, ) @@ -102,22 +106,29 @@ class Writer: """ if filename in self._overridden_files: if override: - raise RuntimeError('File %s is set to be overridden twice' - % filename) - logger.info('Skipping %s', filename) + raise RuntimeError("File %s is set to be overridden twice" % filename) + logger.info("Skipping %s", filename) filename = os.devnull elif filename in self._written_files: if override: - logger.info('Overwriting %s', filename) + logger.info("Overwriting %s", filename) else: - raise RuntimeError('File %s is to be overwritten' % filename) + raise RuntimeError("File %s is to be overwritten" % filename) if override: self._overridden_files.add(filename) self._written_files.add(filename) - return open(filename, 'w', encoding=encoding) + return open(filename, "w", encoding=encoding) - def write_feed(self, elements, context, path=None, url=None, - feed_type='atom', override_output=False, feed_title=None): + def write_feed( + self, + elements, + context, + path=None, + url=None, + feed_type="atom", + override_output=False, + feed_title=None, + ): """Generate a feed with the list of articles provided Return the feed. If no path or output_path is specified, just @@ -137,16 +148,15 @@ class Writer: if not is_selected_for_writing(self.settings, path): return - self.site_url = context.get( - 'SITEURL', path_to_url(get_relative_path(path))) + self.site_url = context.get("SITEURL", path_to_url(get_relative_path(path))) - self.feed_domain = context.get('FEED_DOMAIN') + self.feed_domain = context.get("FEED_DOMAIN") self.feed_url = self.urljoiner(self.feed_domain, url or path) feed = self._create_new_feed(feed_type, feed_title, context) # FEED_MAX_ITEMS = None means [:None] to get every element - for element in elements[:self.settings['FEED_MAX_ITEMS']]: + for element in elements[: self.settings["FEED_MAX_ITEMS"]]: self._add_item_to_the_feed(feed, element) signals.feed_generated.send(context, feed=feed) @@ -158,17 +168,25 @@ class Writer: except Exception: pass - with self._open_w(complete_path, 'utf-8', override_output) as fp: - feed.write(fp, 'utf-8') - logger.info('Writing %s', complete_path) + with self._open_w(complete_path, "utf-8", override_output) as fp: + feed.write(fp, "utf-8") + logger.info("Writing %s", complete_path) - signals.feed_written.send( - complete_path, context=context, feed=feed) + signals.feed_written.send(complete_path, context=context, feed=feed) return feed - def write_file(self, name, template, context, relative_urls=False, - paginated=None, template_name=None, override_output=False, - url=None, **kwargs): + def write_file( + self, + name, + template, + context, + relative_urls=False, + paginated=None, + template_name=None, + override_output=False, + url=None, + **kwargs, + ): """Render the template and write the file. :param name: name of the file to output @@ -185,10 +203,13 @@ class Writer: :param **kwargs: additional variables to pass to the templates """ - if name is False or \ - name == "" or \ - not is_selected_for_writing(self.settings, - os.path.join(self.output_path, name)): + if ( + name is False + or name == "" + or not is_selected_for_writing( + self.settings, os.path.join(self.output_path, name) + ) + ): return elif not name: # other stuff, just return for now @@ -197,8 +218,8 @@ class Writer: def _write_file(template, localcontext, output_path, name, override): """Render the template write the file.""" # set localsiteurl for context so that Contents can adjust links - if localcontext['localsiteurl']: - context['localsiteurl'] = localcontext['localsiteurl'] + if localcontext["localsiteurl"]: + context["localsiteurl"] = localcontext["localsiteurl"] output = template.render(localcontext) path = sanitised_join(output_path, name) @@ -207,9 +228,9 @@ class Writer: except Exception: pass - with self._open_w(path, 'utf-8', override=override) as f: + with self._open_w(path, "utf-8", override=override) as f: f.write(output) - logger.info('Writing %s', path) + logger.info("Writing %s", path) # Send a signal to say we're writing a file with some specific # local context. @@ -217,54 +238,66 @@ class Writer: def _get_localcontext(context, name, kwargs, relative_urls): localcontext = context.copy() - localcontext['localsiteurl'] = localcontext.get( - 'localsiteurl', None) + localcontext["localsiteurl"] = localcontext.get("localsiteurl", None) if relative_urls: relative_url = path_to_url(get_relative_path(name)) - localcontext['SITEURL'] = relative_url - localcontext['localsiteurl'] = relative_url - localcontext['output_file'] = name + localcontext["SITEURL"] = relative_url + localcontext["localsiteurl"] = relative_url + localcontext["output_file"] = name localcontext.update(kwargs) return localcontext if paginated is None: - paginated = {key: val for key, val in kwargs.items() - if key in {'articles', 'dates'}} + paginated = { + key: val for key, val in kwargs.items() if key in {"articles", "dates"} + } # pagination - if paginated and template_name in self.settings['PAGINATED_TEMPLATES']: + if paginated and template_name in self.settings["PAGINATED_TEMPLATES"]: # pagination needed - per_page = self.settings['PAGINATED_TEMPLATES'][template_name] \ - or self.settings['DEFAULT_PAGINATION'] + per_page = ( + self.settings["PAGINATED_TEMPLATES"][template_name] + or self.settings["DEFAULT_PAGINATION"] + ) # init paginators - paginators = {key: Paginator(name, url, val, self.settings, - per_page) - for key, val in paginated.items()} + paginators = { + key: Paginator(name, url, val, self.settings, per_page) + for key, val in paginated.items() + } # generated pages, and write for page_num in range(list(paginators.values())[0].num_pages): paginated_kwargs = kwargs.copy() for key in paginators.keys(): paginator = paginators[key] - previous_page = paginator.page(page_num) \ - if page_num > 0 else None + previous_page = paginator.page(page_num) if page_num > 0 else None page = paginator.page(page_num + 1) - next_page = paginator.page(page_num + 2) \ - if page_num + 1 < paginator.num_pages else None + next_page = ( + paginator.page(page_num + 2) + if page_num + 1 < paginator.num_pages + else None + ) paginated_kwargs.update( - {'%s_paginator' % key: paginator, - '%s_page' % key: page, - '%s_previous_page' % key: previous_page, - '%s_next_page' % key: next_page}) + { + "%s_paginator" % key: paginator, + "%s_page" % key: page, + "%s_previous_page" % key: previous_page, + "%s_next_page" % key: next_page, + } + ) localcontext = _get_localcontext( - context, page.save_as, paginated_kwargs, relative_urls) - _write_file(template, localcontext, self.output_path, - page.save_as, override_output) + context, page.save_as, paginated_kwargs, relative_urls + ) + _write_file( + template, + localcontext, + self.output_path, + page.save_as, + override_output, + ) else: # no pagination - localcontext = _get_localcontext( - context, name, kwargs, relative_urls) - _write_file(template, localcontext, self.output_path, name, - override_output) + localcontext = _get_localcontext(context, name, kwargs, relative_urls) + _write_file(template, localcontext, self.output_path, name, override_output) diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py index 1fa7c472..d10254e8 100755 --- a/samples/pelican.conf.py +++ b/samples/pelican.conf.py @@ -1,55 +1,59 @@ -AUTHOR = 'Alexis Métaireau' +AUTHOR = "Alexis Métaireau" SITENAME = "Alexis' log" -SITESUBTITLE = 'A personal blog.' -SITEURL = 'http://blog.notmyidea.org' +SITESUBTITLE = "A personal blog." +SITEURL = "http://blog.notmyidea.org" TIMEZONE = "Europe/Paris" # can be useful in development, but set to False when you're ready to publish RELATIVE_URLS = True -GITHUB_URL = 'http://github.com/ametaireau/' +GITHUB_URL = "http://github.com/ametaireau/" DISQUS_SITENAME = "blog-notmyidea" REVERSE_CATEGORY_ORDER = True LOCALE = "C" DEFAULT_PAGINATION = 4 DEFAULT_DATE = (2012, 3, 2, 14, 1, 1) -FEED_ALL_RSS = 'feeds/all.rss.xml' -CATEGORY_FEED_RSS = 'feeds/{slug}.rss.xml' +FEED_ALL_RSS = "feeds/all.rss.xml" +CATEGORY_FEED_RSS = "feeds/{slug}.rss.xml" -LINKS = (('Biologeek', 'http://biologeek.org'), - ('Filyb', "http://filyb.info/"), - ('Libert-fr', "http://www.libert-fr.com"), - ('N1k0', "http://prendreuncafe.com/blog/"), - ('Tarek Ziadé', "http://ziade.org/blog"), - ('Zubin Mithra', "http://zubin71.wordpress.com/"),) +LINKS = ( + ("Biologeek", "http://biologeek.org"), + ("Filyb", "http://filyb.info/"), + ("Libert-fr", "http://www.libert-fr.com"), + ("N1k0", "http://prendreuncafe.com/blog/"), + ("Tarek Ziadé", "http://ziade.org/blog"), + ("Zubin Mithra", "http://zubin71.wordpress.com/"), +) -SOCIAL = (('twitter', 'http://twitter.com/ametaireau'), - ('lastfm', 'http://lastfm.com/user/akounet'), - ('github', 'http://github.com/ametaireau'),) +SOCIAL = ( + ("twitter", "http://twitter.com/ametaireau"), + ("lastfm", "http://lastfm.com/user/akounet"), + ("github", "http://github.com/ametaireau"), +) # global metadata to all the contents -DEFAULT_METADATA = {'yeah': 'it is'} +DEFAULT_METADATA = {"yeah": "it is"} # path-specific metadata EXTRA_PATH_METADATA = { - 'extra/robots.txt': {'path': 'robots.txt'}, - } + "extra/robots.txt": {"path": "robots.txt"}, +} # static paths will be copied without parsing their contents STATIC_PATHS = [ - 'images', - 'extra/robots.txt', - ] + "images", + "extra/robots.txt", +] # custom page generated with a jinja2 template -TEMPLATE_PAGES = {'pages/jinja2_template.html': 'jinja2_template.html'} +TEMPLATE_PAGES = {"pages/jinja2_template.html": "jinja2_template.html"} # there is no other HTML content -READERS = {'html': None} +READERS = {"html": None} # code blocks with line numbers -PYGMENTS_RST_OPTIONS = {'linenos': 'table'} +PYGMENTS_RST_OPTIONS = {"linenos": "table"} # foobar will not be used, because it's not in caps. All configuration keys # have to be in caps diff --git a/samples/pelican.conf_FR.py b/samples/pelican.conf_FR.py index dc657404..cbca06df 100644 --- a/samples/pelican.conf_FR.py +++ b/samples/pelican.conf_FR.py @@ -1,56 +1,60 @@ -AUTHOR = 'Alexis Métaireau' +AUTHOR = "Alexis Métaireau" SITENAME = "Alexis' log" -SITEURL = 'http://blog.notmyidea.org' +SITEURL = "http://blog.notmyidea.org" TIMEZONE = "Europe/Paris" # can be useful in development, but set to False when you're ready to publish RELATIVE_URLS = True -GITHUB_URL = 'http://github.com/ametaireau/' +GITHUB_URL = "http://github.com/ametaireau/" DISQUS_SITENAME = "blog-notmyidea" PDF_GENERATOR = False REVERSE_CATEGORY_ORDER = True LOCALE = "fr_FR.UTF-8" DEFAULT_PAGINATION = 4 DEFAULT_DATE = (2012, 3, 2, 14, 1, 1) -DEFAULT_DATE_FORMAT = '%d %B %Y' +DEFAULT_DATE_FORMAT = "%d %B %Y" -ARTICLE_URL = 'posts/{date:%Y}/{date:%B}/{date:%d}/{slug}/' -ARTICLE_SAVE_AS = ARTICLE_URL + 'index.html' +ARTICLE_URL = "posts/{date:%Y}/{date:%B}/{date:%d}/{slug}/" +ARTICLE_SAVE_AS = ARTICLE_URL + "index.html" -FEED_ALL_RSS = 'feeds/all.rss.xml' -CATEGORY_FEED_RSS = 'feeds/{slug}.rss.xml' +FEED_ALL_RSS = "feeds/all.rss.xml" +CATEGORY_FEED_RSS = "feeds/{slug}.rss.xml" -LINKS = (('Biologeek', 'http://biologeek.org'), - ('Filyb', "http://filyb.info/"), - ('Libert-fr', "http://www.libert-fr.com"), - ('N1k0', "http://prendreuncafe.com/blog/"), - ('Tarek Ziadé', "http://ziade.org/blog"), - ('Zubin Mithra', "http://zubin71.wordpress.com/"),) +LINKS = ( + ("Biologeek", "http://biologeek.org"), + ("Filyb", "http://filyb.info/"), + ("Libert-fr", "http://www.libert-fr.com"), + ("N1k0", "http://prendreuncafe.com/blog/"), + ("Tarek Ziadé", "http://ziade.org/blog"), + ("Zubin Mithra", "http://zubin71.wordpress.com/"), +) -SOCIAL = (('twitter', 'http://twitter.com/ametaireau'), - ('lastfm', 'http://lastfm.com/user/akounet'), - ('github', 'http://github.com/ametaireau'),) +SOCIAL = ( + ("twitter", "http://twitter.com/ametaireau"), + ("lastfm", "http://lastfm.com/user/akounet"), + ("github", "http://github.com/ametaireau"), +) # global metadata to all the contents -DEFAULT_METADATA = {'yeah': 'it is'} +DEFAULT_METADATA = {"yeah": "it is"} # path-specific metadata EXTRA_PATH_METADATA = { - 'extra/robots.txt': {'path': 'robots.txt'}, - } + "extra/robots.txt": {"path": "robots.txt"}, +} # static paths will be copied without parsing their contents STATIC_PATHS = [ - 'pictures', - 'extra/robots.txt', - ] + "pictures", + "extra/robots.txt", +] # custom page generated with a jinja2 template -TEMPLATE_PAGES = {'pages/jinja2_template.html': 'jinja2_template.html'} +TEMPLATE_PAGES = {"pages/jinja2_template.html": "jinja2_template.html"} # code blocks with line numbers -PYGMENTS_RST_OPTIONS = {'linenos': 'table'} +PYGMENTS_RST_OPTIONS = {"linenos": "table"} # foobar will not be used, because it's not in caps. All configuration keys # have to be in caps From 271f4dd68f58313f20c2b3cf40ddc6256b5b6d69 Mon Sep 17 00:00:00 2001 From: Chris Rose Date: Sun, 29 Oct 2023 09:57:37 -0700 Subject: [PATCH 316/465] Strip trailing whitespace --- .coveragerc | 1 - docs/_static/pelican-logo.svg | 2 +- docs/_static/theme_overrides.css | 1 - pelican/tests/TestPages/draft_page_markdown.md | 2 +- .../content/2012-11-30_md_w_filename_meta#foo-bar.md | 1 - .../content/article_with_markdown_markup_extensions.md | 1 - .../tests/content/article_with_uppercase_metadata.rst | 1 - pelican/tests/content/article_without_category.rst | 1 - pelican/tests/content/bloggerexport.xml | 2 +- pelican/tests/content/empty_with_bom.md | 2 +- pelican/tests/content/wordpress_content_encoded | 1 - pelican/tests/content/wordpressexport.xml | 2 +- pelican/themes/notmyidea/static/css/reset.css | 2 +- pelican/themes/simple/templates/author.html | 1 - pelican/themes/simple/templates/category.html | 1 - samples/content/pages/hidden_page.rst | 1 - samples/content/unbelievable.rst | 10 +++++----- 17 files changed, 11 insertions(+), 21 deletions(-) diff --git a/.coveragerc b/.coveragerc index 2cb24879..fdd2cad6 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,2 @@ [report] omit = pelican/tests/* - diff --git a/docs/_static/pelican-logo.svg b/docs/_static/pelican-logo.svg index 95b947bf..f36b42fa 100644 --- a/docs/_static/pelican-logo.svg +++ b/docs/_static/pelican-logo.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/docs/_static/theme_overrides.css b/docs/_static/theme_overrides.css index 83afc78e..e840ab6b 100644 --- a/docs/_static/theme_overrides.css +++ b/docs/_static/theme_overrides.css @@ -9,4 +9,3 @@ .wy-table-responsive { overflow: visible !important; } - diff --git a/pelican/tests/TestPages/draft_page_markdown.md b/pelican/tests/TestPages/draft_page_markdown.md index fda71868..0f378a55 100644 --- a/pelican/tests/TestPages/draft_page_markdown.md +++ b/pelican/tests/TestPages/draft_page_markdown.md @@ -9,4 +9,4 @@ Used for pelican test The quick brown fox . -This page is a draft \ No newline at end of file +This page is a draft diff --git a/pelican/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md b/pelican/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md index cdccfc8a..8b3c0bcf 100644 --- a/pelican/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md +++ b/pelican/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md @@ -3,4 +3,3 @@ author: Alexis Métaireau Markdown with filename metadata =============================== - diff --git a/pelican/tests/content/article_with_markdown_markup_extensions.md b/pelican/tests/content/article_with_markdown_markup_extensions.md index 6cf56403..7eff4dcb 100644 --- a/pelican/tests/content/article_with_markdown_markup_extensions.md +++ b/pelican/tests/content/article_with_markdown_markup_extensions.md @@ -5,4 +5,3 @@ Title: Test Markdown extensions ## Level1 ### Level2 - diff --git a/pelican/tests/content/article_with_uppercase_metadata.rst b/pelican/tests/content/article_with_uppercase_metadata.rst index e26cdd13..ee79f55a 100644 --- a/pelican/tests/content/article_with_uppercase_metadata.rst +++ b/pelican/tests/content/article_with_uppercase_metadata.rst @@ -3,4 +3,3 @@ This is a super article ! ######################### :Category: Yeah - diff --git a/pelican/tests/content/article_without_category.rst b/pelican/tests/content/article_without_category.rst index ff47f6ef..1cfcba71 100644 --- a/pelican/tests/content/article_without_category.rst +++ b/pelican/tests/content/article_without_category.rst @@ -3,4 +3,3 @@ This is an article without category ! ##################################### This article should be in the DEFAULT_CATEGORY. - diff --git a/pelican/tests/content/bloggerexport.xml b/pelican/tests/content/bloggerexport.xml index 4bc0985a..a3b9cdf2 100644 --- a/pelican/tests/content/bloggerexport.xml +++ b/pelican/tests/content/bloggerexport.xml @@ -1064,4 +1064,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/content/empty_with_bom.md b/pelican/tests/content/empty_with_bom.md index 5f282702..e02abfc9 100644 --- a/pelican/tests/content/empty_with_bom.md +++ b/pelican/tests/content/empty_with_bom.md @@ -1 +1 @@ - \ No newline at end of file + diff --git a/pelican/tests/content/wordpress_content_encoded b/pelican/tests/content/wordpress_content_encoded index da35de3b..eefff1e9 100644 --- a/pelican/tests/content/wordpress_content_encoded +++ b/pelican/tests/content/wordpress_content_encoded @@ -52,4 +52,3 @@ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. - diff --git a/pelican/tests/content/wordpressexport.xml b/pelican/tests/content/wordpressexport.xml index 4f5b3651..81ed7ea3 100644 --- a/pelican/tests/content/wordpressexport.xml +++ b/pelican/tests/content/wordpressexport.xml @@ -838,7 +838,7 @@ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.]]>_edit_last - + A 2nd custom post type also in category 5 http://thisisa.test/?p=177 diff --git a/pelican/themes/notmyidea/static/css/reset.css b/pelican/themes/notmyidea/static/css/reset.css index c88e6196..f5123cf6 100644 --- a/pelican/themes/notmyidea/static/css/reset.css +++ b/pelican/themes/notmyidea/static/css/reset.css @@ -49,4 +49,4 @@ del {text-decoration: line-through;} table { border-collapse: collapse; border-spacing: 0; -} \ No newline at end of file +} diff --git a/pelican/themes/simple/templates/author.html b/pelican/themes/simple/templates/author.html index 64aadffb..c054f8ab 100644 --- a/pelican/themes/simple/templates/author.html +++ b/pelican/themes/simple/templates/author.html @@ -5,4 +5,3 @@ {% block content_title %}

    Articles by {{ author }}

    {% endblock %} - diff --git a/pelican/themes/simple/templates/category.html b/pelican/themes/simple/templates/category.html index f7889d00..da1a8b52 100644 --- a/pelican/themes/simple/templates/category.html +++ b/pelican/themes/simple/templates/category.html @@ -5,4 +5,3 @@ {% block content_title %}

    Articles in the {{ category }} category

    {% endblock %} - diff --git a/samples/content/pages/hidden_page.rst b/samples/content/pages/hidden_page.rst index ab8704ed..b1f52d95 100644 --- a/samples/content/pages/hidden_page.rst +++ b/samples/content/pages/hidden_page.rst @@ -6,4 +6,3 @@ This is a test hidden page This is great for things like error(404) pages Anyone can see this page but it's not linked to anywhere! - diff --git a/samples/content/unbelievable.rst b/samples/content/unbelievable.rst index 209e3557..afa502cf 100644 --- a/samples/content/unbelievable.rst +++ b/samples/content/unbelievable.rst @@ -45,7 +45,7 @@ Testing more sourcecode directives :lineseparator:
    :linespans: foo :nobackground: - + def run(self): self.assert_has_content() try: @@ -76,8 +76,8 @@ Testing even more sourcecode directives .. sourcecode:: python :linenos: table :nowrap: - - + + formatter = self.options and VARIANTS[self.options.keys()[0]] @@ -90,8 +90,8 @@ Even if the default is line numbers, we can override it here .. sourcecode:: python :linenos: none - - + + formatter = self.options and VARIANTS[self.options.keys()[0]] From 805ca9b4a9af8975f4c2f411e162033bf0a1a500 Mon Sep 17 00:00:00 2001 From: boxydog Date: Sun, 29 Oct 2023 22:21:04 +0100 Subject: [PATCH 317/465] Run pre-commit on all files during CI test job --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 59a22862..b1b7c809 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -63,6 +63,8 @@ jobs: pdm install --no-default --dev - name: Run linters run: pdm lint --diff + - name: Run pre-commit checks on all files + uses: pre-commit/action@v3.0.0 docs: name: Build docs From f0aab11a2da8329aab1cb523365adf1441865b49 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Mon, 30 Oct 2023 00:53:15 +0300 Subject: [PATCH 318/465] Force git subprocess in tests to use utf-8 --- pelican/tests/support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tests/support.py b/pelican/tests/support.py index 3e4da785..a395eeaf 100644 --- a/pelican/tests/support.py +++ b/pelican/tests/support.py @@ -232,7 +232,7 @@ def diff_subproc(first, second): '-w', first, second], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - text=True, + encoding="utf-8", ) From 4e438ffe6073c4d67ae8074d02f9604ec6a1041f Mon Sep 17 00:00:00 2001 From: Lioman Date: Mon, 30 Oct 2023 16:04:44 +0100 Subject: [PATCH 319/465] Enable tests to validate dist build contents (#3229) --- .github/workflows/main.yml | 19 +++++- pelican/tests/build_test/conftest.py | 2 +- pelican/tests/build_test/test_build_files.py | 66 ++++++++++++++++++++ pelican/tests/build_test/test_wheel.py | 28 --------- 4 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 pelican/tests/build_test/test_build_files.py delete mode 100644 pelican/tests/build_test/test_wheel.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0127982e..85ee5ccb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,6 +64,23 @@ jobs: - name: Run linters run: pdm lint --diff + build: + name: Test build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pdm-project/setup-pdm@v3 + with: + python-version: 3.9 + cache: true + cache-dependency-path: ./pyproject.toml + - name: Install dependencies + run: pdm install --dev + - name: Build package + run: pdm build + - name: Test build + run: pdm run pytest --check-build=dist pelican/tests/build_test + docs: name: Build docs runs-on: ubuntu-latest @@ -84,7 +101,7 @@ jobs: deploy: name: Deploy environment: Deployment - needs: [test, lint, docs] + needs: [test, lint, docs, build] runs-on: ubuntu-latest if: github.ref=='refs/heads/master' && github.event_name!='pull_request' && github.repository == 'getpelican/pelican' diff --git a/pelican/tests/build_test/conftest.py b/pelican/tests/build_test/conftest.py index 548f7970..b1d1a54b 100644 --- a/pelican/tests/build_test/conftest.py +++ b/pelican/tests/build_test/conftest.py @@ -1,6 +1,6 @@ def pytest_addoption(parser): parser.addoption( - "--check-wheel", + "--check-build", action="store", default=False, help="Check wheel contents.", diff --git a/pelican/tests/build_test/test_build_files.py b/pelican/tests/build_test/test_build_files.py new file mode 100644 index 00000000..2b51d362 --- /dev/null +++ b/pelican/tests/build_test/test_build_files.py @@ -0,0 +1,66 @@ +from re import match +import tarfile +from pathlib import Path +from zipfile import ZipFile + +import pytest + + +@pytest.mark.skipif( + "not config.getoption('--check-build')", + reason="Only run when --check-build is given", +) +def test_wheel_contents(pytestconfig): + """ + This test should test the contents of the wheel to make sure + that everything that is needed is included in the final build + """ + dist_folder = pytestconfig.getoption("--check-build") + wheels = Path(dist_folder).rglob("*.whl") + for wheel_file in wheels: + files_list = ZipFile(wheel_file).namelist() + # Check if theme files are copied to wheel + simple_theme = Path("./pelican/themes/simple/templates") + for x in simple_theme.iterdir(): + assert str(x) in files_list + + # Check if tool templates are copied to wheel + tools = Path("./pelican/tools/templates") + for x in tools.iterdir(): + assert str(x) in files_list + + assert "pelican/tools/templates/tasks.py.jinja2" in files_list + + +@pytest.mark.skipif( + "not config.getoption('--check-build')", + reason="Only run when --check-build is given", +) +@pytest.mark.parametrize( + "expected_file", + [ + ("THANKS"), + ("README.rst"), + ("CONTRIBUTING.rst"), + ("docs/changelog.rst"), + ("samples/"), + ], +) +def test_sdist_contents(pytestconfig, expected_file): + """ + This test should test the contents of the source distribution to make sure + that everything that is needed is included in the final build. + """ + dist_folder = pytestconfig.getoption("--check-build") + sdist_files = Path(dist_folder).rglob("*.tar.gz") + for dist in sdist_files: + files_list = tarfile.open(dist, "r:gz").getnames() + dir_matcher = "" + if expected_file.endswith("/"): + dir_matcher = ".*" + filtered_values = [ + path + for path in files_list + if match(f"^pelican-\d\.\d\.\d/{expected_file}{dir_matcher}$", path) + ] + assert len(filtered_values) > 0 diff --git a/pelican/tests/build_test/test_wheel.py b/pelican/tests/build_test/test_wheel.py deleted file mode 100644 index 8e643981..00000000 --- a/pelican/tests/build_test/test_wheel.py +++ /dev/null @@ -1,28 +0,0 @@ -from pathlib import Path -import pytest -from zipfile import ZipFile - - -@pytest.mark.skipif( - "not config.getoption('--check-wheel')", - reason="Only run when --check-wheel is given", -) -def test_wheel_contents(pytestconfig): - """ - This test, should test the contents of the wheel to make sure, - that everything that is needed is included in the final build - """ - wheel_file = pytestconfig.getoption("--check-wheel") - assert wheel_file.endswith(".whl") - files_list = ZipFile(wheel_file).namelist() - # Check if theme files are copied to wheel - simple_theme = Path("./pelican/themes/simple/templates") - for x in simple_theme.iterdir(): - assert str(x) in files_list - - # Check if tool templates are copied to wheel - tools = Path("./pelican/tools/templates") - for x in tools.iterdir(): - assert str(x) in files_list - - assert "pelican/tools/templates/tasks.py.jinja2" in files_list From 08785f714ffdc10560072f2379cb123c6984254e Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 30 Oct 2023 19:35:59 +0100 Subject: [PATCH 320/465] Remove obsolete linters: Flake8, Black, isort --- pyproject.toml | 4 ---- requirements/style.pip | 2 -- tox.ini | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 requirements/style.pip diff --git a/pyproject.toml b/pyproject.toml index 4e3e712a..a16bb7f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,11 +91,7 @@ dev = [ "pytest-sugar>=0.9.7", "pytest-xdist>=3.3.1", "tox>=4.11.3", - "flake8>=6.1.0", - "flake8-import-order>=0.18.2", "invoke>=2.2.0", - "isort>=5.12.0", - "black>=23.10.1", "ruff>=0.1.3", "tomli>=2.0.1; python_version < \"3.11\"", ] diff --git a/requirements/style.pip b/requirements/style.pip deleted file mode 100644 index f1c82ed0..00000000 --- a/requirements/style.pip +++ /dev/null @@ -1,2 +0,0 @@ -flake8==3.9.2 -flake8-import-order diff --git a/tox.ini b/tox.ini index 361c52dd..f6f45af1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{3.8,3.9,3.10,3.11.3.12},docs,flake8 +envlist = py{3.8,3.9,3.10,3.11.3.12},docs [testenv] basepython = From 3c5799694530d5e038c71b03007eb684e3c280f5 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 30 Oct 2023 19:45:42 +0100 Subject: [PATCH 321/465] Ignore Ruff format commit in the blame view --- .git-blame-ignore-revs | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..7b822fd3 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,3 @@ +# .git-blame-ignore-revs +# Apply code style to project via: ruff format . +cabdb26cee66e1173cf16cb31d3fe5f9fa4392e7 From abae21494dcd69ab8a2b11f8b9e01774dc4f52b1 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 31 Oct 2023 16:50:48 +0100 Subject: [PATCH 322/465] Adjust line length to 88 in EditorConfig --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index edb13c8a..a9c06c97 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,7 +9,7 @@ insert_final_newline = true trim_trailing_whitespace = true [*.py] -max_line_length = 79 +max_line_length = 88 [*.{yml,yaml}] indent_size = 2 From e6a5e2a66520a51f25336201ec876633c267769d Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 1 Nov 2023 09:07:48 +0100 Subject: [PATCH 323/465] Update pre-commit hook versions --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f68521e5..5a73aebc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for info on hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-ast @@ -14,7 +14,7 @@ repos: - id: forbid-new-submodules - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.0 + rev: v0.1.3 hooks: - id: ruff - id: ruff-format From 76650898a648b4ba8d2e34e2d67abbbeafc0776e Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 1 Nov 2023 09:43:21 +0100 Subject: [PATCH 324/465] Update to Markdown 3.5.1 in test requirements --- requirements/test.pip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/test.pip b/requirements/test.pip index 2cf1ea1f..87869e67 100644 --- a/requirements/test.pip +++ b/requirements/test.pip @@ -6,7 +6,7 @@ pytest-xdist[psutil] tzdata # Optional Packages -Markdown==3.4.3 +Markdown==3.5.1 BeautifulSoup4 lxml typogrify From feae8ef41c995cb0d327033aa0d261cfd4eb85df Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Wed, 1 Nov 2023 22:49:15 +0300 Subject: [PATCH 325/465] Provide a plugin_enabled Jinja test for themes --- docs/themes.rst | 17 +++++++ pelican/generators.py | 4 ++ pelican/plugins/_utils.py | 16 +++++++ pelican/tests/test_generators.py | 25 ++++++++++ pelican/tests/test_plugins.py | 82 +++++++++++++++++++++++++++++++- 5 files changed, 143 insertions(+), 1 deletion(-) diff --git a/docs/themes.rst b/docs/themes.rst index 51b5b0d5..2df717e4 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -140,6 +140,23 @@ your date according to the locale given in your settings:: .. _datetime: https://docs.python.org/3/library/datetime.html#datetime-objects .. _strftime: https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior +Checking Loaded Plugins +----------------------- + +Pelican provides a ``plugin_enabled`` Jinja test for checking if a certain plugin +is enabled. This test accepts a plugin name as a string and will return a boolean. +Namespace Plugins can be specified with their full name (``pelican.plugins.plugin_name``) +or their short name (``plugin_name``). The following example uses ``webassets`` plugin +to minify CSS if it is enabled and falls back to regular CSS otherwise:: + + {% if "webassets" is plugin_enabled %} + {% assets filters="cssmin", output="css/style.min.css", "css/style.scss" %} + + {% endassets %} + {% else %} + + {% endif %} + index.html ---------- diff --git a/pelican/generators.py b/pelican/generators.py index 0bbb7268..3b5ca9e4 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -20,6 +20,7 @@ from jinja2 import ( from pelican.cache import FileStampDataCacher from pelican.contents import Article, Page, Static from pelican.plugins import signals +from pelican.plugins._utils import plugin_enabled from pelican.readers import Readers from pelican.utils import ( DateFormatter, @@ -102,6 +103,9 @@ class Generator: # get custom Jinja tests from user settings custom_tests = self.settings["JINJA_TESTS"] + self.env.tests["plugin_enabled"] = partial( + plugin_enabled, plugin_list=self.settings["PLUGINS"] + ) self.env.tests.update(custom_tests) signals.generator_init.send(self) diff --git a/pelican/plugins/_utils.py b/pelican/plugins/_utils.py index f0c18f5c..c25f8114 100644 --- a/pelican/plugins/_utils.py +++ b/pelican/plugins/_utils.py @@ -40,6 +40,22 @@ def list_plugins(ns_pkg=None): logger.info("No plugins are installed") +def plugin_enabled(name, plugin_list=None): + if plugin_list is None or not plugin_list: + # no plugins are loaded + return False + + if name in plugin_list: + # search name as is + return True + + if "pelican.plugins.{}".format(name) in plugin_list: + # check if short name is a namespace plugin + return True + + return False + + def load_legacy_plugin(plugin, plugin_paths): if "." in plugin: # it is in a package, try to resolve package first diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 52adb2c9..af6f5b1a 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -1581,6 +1581,31 @@ class TestJinja2Environment(TestCaseWithCLocale): self._test_jinja2_helper(settings, content, expected) + def test_jinja2_filter_plugin_enabled(self): + """JINJA_FILTERS adds custom filters to Jinja2 environment""" + settings = {"PLUGINS": ["legacy_plugin", "pelican.plugins.ns_plugin"]} + jinja_template = ( + "{plugin}: " + "{{% if '{plugin}' is plugin_enabled %}}yes" + "{{% else %}}no{{% endif %}}" + ) + content = " / ".join( + ( + jinja_template.format(plugin="ns_plugin"), + jinja_template.format(plugin="pelican.plugins.ns_plugin"), + jinja_template.format(plugin="legacy_plugin"), + jinja_template.format(plugin="unknown"), + ) + ) + expected = ( + "ns_plugin: yes / " + "pelican.plugins.ns_plugin: yes / " + "legacy_plugin: yes / " + "unknown: no" + ) + + self._test_jinja2_helper(settings, content, expected) + def test_jinja2_test(self): """JINJA_TESTS adds custom tests to Jinja2 environment""" content = "foo {{ foo is custom_test }}, bar {{ bar is custom_test }}" diff --git a/pelican/tests/test_plugins.py b/pelican/tests/test_plugins.py index 4f02022c..ccce684e 100644 --- a/pelican/tests/test_plugins.py +++ b/pelican/tests/test_plugins.py @@ -2,7 +2,12 @@ import os from contextlib import contextmanager import pelican.tests.dummy_plugins.normal_plugin.normal_plugin as normal_plugin -from pelican.plugins._utils import get_namespace_plugins, get_plugin_name, load_plugins +from pelican.plugins._utils import ( + get_namespace_plugins, + get_plugin_name, + load_plugins, + plugin_enabled, +) from pelican.tests.support import unittest @@ -183,3 +188,78 @@ class PluginTest(unittest.TestCase): get_plugin_name(NoopPlugin()), "PluginTest.test_get_plugin_name..NoopPlugin", ) + + def test_plugin_enabled(self): + def get_plugin_names(plugins): + return [get_plugin_name(p) for p in plugins] + + with tmp_namespace_path(self._NS_PLUGIN_FOLDER): + # with no `PLUGINS` setting, load namespace plugins + SETTINGS = {} + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertTrue(plugin_enabled("ns_plugin", plugins)) + self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertFalse(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # disable namespace plugins with `PLUGINS = []` + SETTINGS = {"PLUGINS": []} + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertFalse(plugin_enabled("ns_plugin", plugins)) + self.assertFalse(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertFalse(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # with `PLUGINS`, load only specified plugins + + # normal plugin + SETTINGS = { + "PLUGINS": ["normal_plugin"], + "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER], + } + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertFalse(plugin_enabled("ns_plugin", plugins)) + self.assertFalse(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertTrue(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # normal submodule/subpackage plugins + SETTINGS = { + "PLUGINS": [ + "normal_submodule_plugin.subplugin", + "normal_submodule_plugin.subpackage.subpackage", + ], + "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER], + } + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertFalse(plugin_enabled("ns_plugin", plugins)) + self.assertFalse(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertFalse(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # namespace plugin short + SETTINGS = {"PLUGINS": ["ns_plugin"]} + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertTrue(plugin_enabled("ns_plugin", plugins)) + self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertFalse(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # namespace plugin long + SETTINGS = {"PLUGINS": ["pelican.plugins.ns_plugin"]} + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertTrue(plugin_enabled("ns_plugin", plugins)) + self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertFalse(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # normal and namespace plugin + SETTINGS = { + "PLUGINS": ["normal_plugin", "ns_plugin"], + "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER], + } + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertTrue(plugin_enabled("ns_plugin", plugins)) + self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertTrue(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) From 49aef30dab372a041acf097207033666bdd48a3e Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Wed, 1 Nov 2023 23:19:26 +0300 Subject: [PATCH 326/465] add sphinxext-opengraph to pyproject dev requirements --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index a16bb7f8..d9fe1a33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,6 +82,7 @@ dev = [ "markdown>=3.5", "typogrify>=2.0.7", "sphinx>=7.1.2", + "sphinxext-opengraph>=0.9.0", "furo>=2023.9.10", "livereload>=2.6.3", "psutil>=5.9.6", From 32b72123f051dd23bf481eed26ab947f66a22663 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 2 Nov 2023 14:09:51 +0100 Subject: [PATCH 327/465] Modify wording slightly --- docs/themes.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/themes.rst b/docs/themes.rst index 2df717e4..2e01ec8e 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -144,10 +144,10 @@ Checking Loaded Plugins ----------------------- Pelican provides a ``plugin_enabled`` Jinja test for checking if a certain plugin -is enabled. This test accepts a plugin name as a string and will return a boolean. -Namespace Plugins can be specified with their full name (``pelican.plugins.plugin_name``) -or their short name (``plugin_name``). The following example uses ``webassets`` plugin -to minify CSS if it is enabled and falls back to regular CSS otherwise:: +is enabled. This test accepts a plugin name as a string and will return a Boolean. +Namespace plugins can be specified by full name (``pelican.plugins.plugin_name``) +or short name (``plugin_name``). The following example uses the ``webassets`` plugin +to minify CSS if the plugin is enabled and otherwise falls back to regular CSS:: {% if "webassets" is plugin_enabled %} {% assets filters="cssmin", output="css/style.min.css", "css/style.scss" %} From 8a8b952ecb83c7b7e2cd8d331a8dff23d319f1ac Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Fri, 3 Nov 2023 01:05:12 +0300 Subject: [PATCH 328/465] preserve connection order in blinker --- docs/plugins.rst | 10 ++++++---- pelican/plugins/signals.py | 6 +++++- pelican/tests/test_plugins.py | 21 +++++++++++++++++++++ pyproject.toml | 3 ++- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index db7e00b4..22e1bcc6 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -94,9 +94,12 @@ which you map the signals to your plugin logic. Let's take a simple example:: your ``register`` callable or they will be garbage-collected before the signal is emitted. -If multiple plugins connect to the same signal, there is no way to guarantee or -control in which order the plugins will be executed. This is a limitation -inherited from Blinker_, the dependency Pelican uses to implement signals. +If multiple plugins connect to the same signal, plugins will be executed in the +order they are connected. With ``PLUGINS`` setting, order will be as defined in +the setting. If you rely on auto-discovered namespace plugins, no ``PLUGINS`` +setting, they will be connected in the same order they are discovered (same +order as ``pelican-plugins`` output). If you want to specify the order +explicitly, disable auto-discovery by defining ``PLUGINS`` in the desired order. Namespace plugin structure -------------------------- @@ -341,4 +344,3 @@ custom article, using the ``article_generator_pretaxonomy`` signal:: .. _Pip: https://pip.pypa.io/ .. _pelican-plugins bug #314: https://github.com/getpelican/pelican-plugins/issues/314 -.. _Blinker: https://pythonhosted.org/blinker/ diff --git a/pelican/plugins/signals.py b/pelican/plugins/signals.py index ff129cb4..27177367 100644 --- a/pelican/plugins/signals.py +++ b/pelican/plugins/signals.py @@ -1,4 +1,8 @@ -from blinker import signal +from blinker import signal, Signal +from ordered_set import OrderedSet + +# Signals will call functions in the order of connection, i.e. plugin order +Signal.set_class = OrderedSet # Run-level signals: diff --git a/pelican/tests/test_plugins.py b/pelican/tests/test_plugins.py index ccce684e..55fa8a6a 100644 --- a/pelican/tests/test_plugins.py +++ b/pelican/tests/test_plugins.py @@ -8,6 +8,7 @@ from pelican.plugins._utils import ( load_plugins, plugin_enabled, ) +from pelican.plugins.signals import signal from pelican.tests.support import unittest @@ -263,3 +264,23 @@ class PluginTest(unittest.TestCase): self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins)) self.assertTrue(plugin_enabled("normal_plugin", plugins)) self.assertFalse(plugin_enabled("unknown", plugins)) + + def test_blinker_is_ordered(self): + """ensure that call order is connetion order""" + dummy_signal = signal("dummpy_signal") + + functions = [] + expected = [] + for i in range(50): + # function appends value of i to a list + def func(input, i=i): + input.append(i) + + functions.append(func) + # we expect functions to be run in the connection order + dummy_signal.connect(func) + expected.append(i) + + input = [] + dummy_signal.send(input) + self.assertEqual(input, expected) diff --git a/pyproject.toml b/pyproject.toml index d9fe1a33..816a25f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,10 +29,11 @@ classifiers = [ ] requires-python = ">=3.8.1,<4.0" dependencies = [ - "blinker>=1.6.3", + "blinker>=1.7.0", "docutils>=0.20.1", "feedgenerator>=2.1.0", "jinja2>=3.1.2", + "ordered-set>=4.1.0", "pygments>=2.16.1", "python-dateutil>=2.8.2", "rich>=13.6.0", From 451b094a940ab28060784a1030b84bd39a2a523d Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Sat, 4 Nov 2023 00:54:21 +0300 Subject: [PATCH 329/465] remove social icons from notmyidea theme redistribution of these icons may not be compatible with AGPL --- pelican/tests/output/basic/theme/css/main.css | 33 ------------------ .../basic/theme/images/icons/aboutme.png | Bin 411 -> 0 bytes .../basic/theme/images/icons/bitbucket.png | Bin 3178 -> 0 bytes .../basic/theme/images/icons/delicious.png | Bin 827 -> 0 bytes .../basic/theme/images/icons/facebook.png | Bin 150 -> 0 bytes .../basic/theme/images/icons/github.png | Bin 606 -> 0 bytes .../basic/theme/images/icons/gitorious.png | Bin 223 -> 0 bytes .../basic/theme/images/icons/gittip.png | Bin 402 -> 0 bytes .../theme/images/icons/google-groups.png | Bin 420 -> 0 bytes .../basic/theme/images/icons/google-plus.png | Bin 511 -> 0 bytes .../basic/theme/images/icons/hackernews.png | Bin 2771 -> 0 bytes .../basic/theme/images/icons/lastfm.png | Bin 840 -> 0 bytes .../basic/theme/images/icons/linkedin.png | Bin 625 -> 0 bytes .../basic/theme/images/icons/reddit.png | Bin 458 -> 0 bytes .../output/basic/theme/images/icons/rss.png | Bin 751 -> 0 bytes .../basic/theme/images/icons/slideshare.png | Bin 435 -> 0 bytes .../basic/theme/images/icons/speakerdeck.png | Bin 580 -> 0 bytes .../theme/images/icons/stackoverflow.png | Bin 414 -> 0 bytes .../basic/theme/images/icons/twitter.png | Bin 416 -> 0 bytes .../output/basic/theme/images/icons/vimeo.png | Bin 349 -> 0 bytes .../basic/theme/images/icons/youtube.png | Bin 316 -> 0 bytes .../tests/output/custom/theme/css/main.css | 33 ------------------ .../custom/theme/images/icons/aboutme.png | Bin 411 -> 0 bytes .../custom/theme/images/icons/bitbucket.png | Bin 3178 -> 0 bytes .../custom/theme/images/icons/delicious.png | Bin 827 -> 0 bytes .../custom/theme/images/icons/facebook.png | Bin 150 -> 0 bytes .../custom/theme/images/icons/github.png | Bin 606 -> 0 bytes .../custom/theme/images/icons/gitorious.png | Bin 223 -> 0 bytes .../custom/theme/images/icons/gittip.png | Bin 402 -> 0 bytes .../theme/images/icons/google-groups.png | Bin 420 -> 0 bytes .../custom/theme/images/icons/google-plus.png | Bin 511 -> 0 bytes .../custom/theme/images/icons/hackernews.png | Bin 2771 -> 0 bytes .../custom/theme/images/icons/lastfm.png | Bin 840 -> 0 bytes .../custom/theme/images/icons/linkedin.png | Bin 625 -> 0 bytes .../custom/theme/images/icons/reddit.png | Bin 458 -> 0 bytes .../output/custom/theme/images/icons/rss.png | Bin 751 -> 0 bytes .../custom/theme/images/icons/slideshare.png | Bin 435 -> 0 bytes .../custom/theme/images/icons/speakerdeck.png | Bin 580 -> 0 bytes .../theme/images/icons/stackoverflow.png | Bin 414 -> 0 bytes .../custom/theme/images/icons/twitter.png | Bin 416 -> 0 bytes .../custom/theme/images/icons/vimeo.png | Bin 349 -> 0 bytes .../custom/theme/images/icons/youtube.png | Bin 316 -> 0 bytes .../output/custom_locale/theme/css/main.css | 33 ------------------ .../theme/images/icons/aboutme.png | Bin 411 -> 0 bytes .../theme/images/icons/bitbucket.png | Bin 3178 -> 0 bytes .../theme/images/icons/delicious.png | Bin 827 -> 0 bytes .../theme/images/icons/facebook.png | Bin 150 -> 0 bytes .../theme/images/icons/github.png | Bin 606 -> 0 bytes .../theme/images/icons/gitorious.png | Bin 223 -> 0 bytes .../theme/images/icons/gittip.png | Bin 402 -> 0 bytes .../theme/images/icons/google-groups.png | Bin 420 -> 0 bytes .../theme/images/icons/google-plus.png | Bin 511 -> 0 bytes .../theme/images/icons/hackernews.png | Bin 2771 -> 0 bytes .../theme/images/icons/lastfm.png | Bin 840 -> 0 bytes .../theme/images/icons/linkedin.png | Bin 625 -> 0 bytes .../theme/images/icons/reddit.png | Bin 458 -> 0 bytes .../custom_locale/theme/images/icons/rss.png | Bin 751 -> 0 bytes .../theme/images/icons/slideshare.png | Bin 435 -> 0 bytes .../theme/images/icons/speakerdeck.png | Bin 580 -> 0 bytes .../theme/images/icons/stackoverflow.png | Bin 414 -> 0 bytes .../theme/images/icons/twitter.png | Bin 416 -> 0 bytes .../theme/images/icons/vimeo.png | Bin 349 -> 0 bytes .../theme/images/icons/youtube.png | Bin 316 -> 0 bytes pelican/themes/notmyidea/static/css/main.css | 33 ------------------ .../notmyidea/static/images/icons/aboutme.png | Bin 411 -> 0 bytes .../static/images/icons/bitbucket.png | Bin 3178 -> 0 bytes .../static/images/icons/delicious.png | Bin 827 -> 0 bytes .../static/images/icons/facebook.png | Bin 150 -> 0 bytes .../notmyidea/static/images/icons/github.png | Bin 606 -> 0 bytes .../static/images/icons/gitorious.png | Bin 223 -> 0 bytes .../notmyidea/static/images/icons/gittip.png | Bin 402 -> 0 bytes .../static/images/icons/google-groups.png | Bin 420 -> 0 bytes .../static/images/icons/google-plus.png | Bin 511 -> 0 bytes .../static/images/icons/hackernews.png | Bin 2771 -> 0 bytes .../notmyidea/static/images/icons/lastfm.png | Bin 840 -> 0 bytes .../static/images/icons/linkedin.png | Bin 625 -> 0 bytes .../notmyidea/static/images/icons/reddit.png | Bin 458 -> 0 bytes .../notmyidea/static/images/icons/rss.png | Bin 751 -> 0 bytes .../static/images/icons/slideshare.png | Bin 435 -> 0 bytes .../static/images/icons/speakerdeck.png | Bin 580 -> 0 bytes .../static/images/icons/stackoverflow.png | Bin 414 -> 0 bytes .../notmyidea/static/images/icons/twitter.png | Bin 416 -> 0 bytes .../notmyidea/static/images/icons/vimeo.png | Bin 349 -> 0 bytes .../notmyidea/static/images/icons/youtube.png | Bin 316 -> 0 bytes 84 files changed, 132 deletions(-) delete mode 100644 pelican/tests/output/basic/theme/images/icons/aboutme.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/bitbucket.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/delicious.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/facebook.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/github.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/gitorious.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/gittip.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/google-groups.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/google-plus.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/hackernews.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/lastfm.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/linkedin.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/reddit.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/rss.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/slideshare.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/speakerdeck.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/stackoverflow.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/twitter.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/vimeo.png delete mode 100644 pelican/tests/output/basic/theme/images/icons/youtube.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/aboutme.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/bitbucket.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/delicious.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/facebook.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/github.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/gitorious.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/gittip.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/google-groups.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/google-plus.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/hackernews.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/lastfm.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/linkedin.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/reddit.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/rss.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/slideshare.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/speakerdeck.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/stackoverflow.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/twitter.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/vimeo.png delete mode 100644 pelican/tests/output/custom/theme/images/icons/youtube.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/aboutme.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/bitbucket.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/delicious.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/facebook.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/github.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/gitorious.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/gittip.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/google-groups.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/google-plus.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/hackernews.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/lastfm.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/linkedin.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/reddit.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/rss.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/slideshare.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/speakerdeck.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/stackoverflow.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/twitter.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/vimeo.png delete mode 100644 pelican/tests/output/custom_locale/theme/images/icons/youtube.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/aboutme.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/bitbucket.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/delicious.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/facebook.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/github.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/gitorious.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/gittip.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/google-groups.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/google-plus.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/hackernews.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/lastfm.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/linkedin.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/reddit.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/rss.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/slideshare.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/speakerdeck.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/stackoverflow.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/twitter.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/vimeo.png delete mode 100644 pelican/themes/notmyidea/static/images/icons/youtube.png diff --git a/pelican/tests/output/basic/theme/css/main.css b/pelican/tests/output/basic/theme/css/main.css index a4aa51a1..49de03d9 100644 --- a/pelican/tests/output/basic/theme/css/main.css +++ b/pelican/tests/output/basic/theme/css/main.css @@ -322,39 +322,6 @@ div.figure p.caption, figure p.caption { /* margin provided by figure */ max-width: 175px; } - #extras div[class='social'] a { - background-repeat: no-repeat; - background-position: 3px 6px; - padding-left: 25px; - } - - /* Icons */ - .social a[href*='about.me'] {background-image: url('../images/icons/aboutme.png');} - .social a[href*='bitbucket.org'] {background-image: url('../images/icons/bitbucket.png');} - .social a[href*='delicious.com'] {background-image: url('../images/icons/delicious.png');} - .social a[href*='facebook.com'] {background-image: url('../images/icons/facebook.png');} - .social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.png');} - .social a[href*='github.com'], - .social a[href*='git.io'] { - background-image: url('../images/icons/github.png'); - background-size: 16px 16px; - } - .social a[href*='gittip.com'] {background-image: url('../images/icons/gittip.png');} - .social a[href*='plus.google.com'] {background-image: url('../images/icons/google-plus.png');} - .social a[href*='groups.google.com'] {background-image: url('../images/icons/google-groups.png');} - .social a[href*='news.ycombinator.com'], - .social a[href*='hackernewsers.com'] {background-image: url('../images/icons/hackernews.png');} - .social a[href*='last.fm'], .social a[href*='lastfm.'] {background-image: url('../images/icons/lastfm.png');} - .social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');} - .social a[href*='reddit.com'] {background-image: url('../images/icons/reddit.png');} - .social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');} - .social a[href*='slideshare.net'] {background-image: url('../images/icons/slideshare.png');} - .social a[href*='speakerdeck.com'] {background-image: url('../images/icons/speakerdeck.png');} - .social a[href*='stackoverflow.com'] {background-image: url('../images/icons/stackoverflow.png');} - .social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');} - .social a[href*='vimeo.com'] {background-image: url('../images/icons/vimeo.png');} - .social a[href*='youtube.com'] {background-image: url('../images/icons/youtube.png');} - /* About *****************/ diff --git a/pelican/tests/output/basic/theme/images/icons/aboutme.png b/pelican/tests/output/basic/theme/images/icons/aboutme.png deleted file mode 100644 index 600110f2f63811ebea40af1b943fded0275b6a39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 411 zcmV;M0c8G(P)UqK~#7FjgmEk6+sXNzkTlh65QS0-8I48iI50)i1CK$|J3O zMSiddRs94I-RZ%BpPVQlG-*wE)^@pKur#bMkyS)ii4hvVB0PPwtX?$Lk2sgu3yxdr zMHBD`H`aw%Ysl&kiS1h=1)tbo!~Ye`fCpjdO!(yU+ZHs&^bQiZX+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@*Jzj349X=?9_$gI2kIcwdunCZtMPOnPe)w(oZZmY=P7Ks9%FQb&p8>E zTGTu^G&~wGL&GC!#VuGydpI#q;n4F`-2p*w5hggKWfvm1-Ht!-D0%;*r0sX&mtTmS z=#a1o5c3rNC06S#lEEUl4ZT(7hJXc%h^4bgLQ=J-kX6=dv2qWx(KFxw<| z8p~uAa?0C01wO*eV=5{z+2r5xRmC+O#kC#3;ww#bTsi?_p2FbJ$cF2&dW*;dEIS`r z-{$NC7APVX0b-s4bL>@`-m2iuJf@Y`cU}9I16ZJlSOkdq3%}wjQF(Q}Mc#EUA+xj< z?7W)*1-w0hpaS1pO{?AaFnQ`>1)C;df$n>lghe2z(B3_8<9lvWK~rg6XL?CXWL7;$ zD`|ly6*S-encvkr7*yZ~ue};=vMZZ!#$EfG^I9zdEKo!aRp=d<^B=Mdj&Pkt)pJqZACTW#!hM@D5IKDqq;%eMz!tbv+awqP8IHt=!XVS zsZ`n>$2lgOwFdh8dv^BsZ>()0em94EiuYK~#M+HZNki~hy!n`DO0RgV7kx83G-6kVzbw2peZ^$T0XF+WR zfC!}kbTrMykUD-`pyq(>!2GmFl&qi|8BD#Yimm6&NW;(o1*9a20zlURQu0~F;$lpk zC^lANi08=`Jt9})*0WlBM`^dLR$E*Uq-h9fXc|BQRMFBY=8w1tql8shMigC06q$^` z3o;x$Brcc5Vx|EggvrrZTv~ueFR9S5n?9&@2ra;ZlJa{y#y*#;tE-3i@BjIXTFqx< zWQM^b2eIvt(ZSC+IB^l#ZO?Jct~%A$Zme8|ylK!@D6=^GmdF`z&B`yo_%8qUJKqup zI`#T8-~s^<(cM|*r>_LNwibZ|C;c5580x>Z^zql9MEVifl5$XO00aq!?ra*2Oy6Bcd7s& z4E&Wd&NypYm|1Hm3>yT#OOnPY6%hC_U@Jle48x$&@MzjDei*iPb+`A(ty}jT{_VFp0@p^D9%vpdEdoQ&A~UVr|G*1s z*N$#`bZl0nsoeDaV0>!ow??I6E} zTD@V0|CC##a5uK^=-$j1%KE$Sep%nY|D6y3VDFw|_#0Q*b=;R(Ji7n@002ovPDHLk FV1ku*k$3ca`voa6f0k zeB{69k@N|?Km7UBZZ))YUHu=w|Mw?(E)O$?WixY_FPL7s$Pm-8fstds@^ATL%bUet yZgfGChJzsUtA%$MR;8+S88GxaEa+z7`Ew3v5re0zpUXO@geCxdur~kz diff --git a/pelican/tests/output/basic/theme/images/icons/github.png b/pelican/tests/output/basic/theme/images/icons/github.png deleted file mode 100644 index 5d9109deab2b3669c845845515f4d53ab5d6daa6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 606 zcmV-k0-^nhP)3g@6&i?1|w)NXSf84|8m&o+0 zUbR{k48>ZU#v6!wqi1V1r%EE$MYAT@gfEZ`ebJgG`3x?P75EOx(Rb}pK9^g49TLfP zG|6;$SGbAH zWw$>tFUyCTTU=}VL5$UQcykD>ruLPAMktgpS2)vHd2`;>>D~O#r0q^piwN%{Eu7xl ze_3-g6EyPr+mZ( z$~zD9`4D^$F>WOyU!f<&c%QI`>dHS@;3~xO7I+5=MISM;>bsLPrcC8T?Gyg2EhYt{Z#{?9|afq=U$pRKgVkaa6A5hOW zm~E0;qoMsELHiVUdq+qHW_!=pLlCuB&nsXkrlThcr&yqB2Ez}bh}WQodCMG=62#-T(jq diff --git a/pelican/tests/output/basic/theme/images/icons/gitorious.png b/pelican/tests/output/basic/theme/images/icons/gitorious.png deleted file mode 100644 index a6705d0f462b505be4d9a5a372743d206c141a11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!O@L2`D?^o)dRwqYcev@q#DMvQ z*(*AHH`kW0om8=NO8&uVRmbKn*uQ@5(H+Zf?%R0x*ww37FTJ_{>eZ{eAD{pK|9`68 zWgnn9ah@)YAr*|ZCyp{U2QWBn{C>Cgf8^^ph2sKmrZ@0Db>47eliif`GZN3kOR|4E zp1HSr>(=i-J2K=SnYPGB*}QnjI%8UJ(Sx8gTe~DWM4fr0!yB diff --git a/pelican/tests/output/basic/theme/images/icons/gittip.png b/pelican/tests/output/basic/theme/images/icons/gittip.png deleted file mode 100644 index b9f67aaa32a0acd166d7271b3dbf9113717d3f00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMfoB*E?S0J6}W0T-*4J5$~5D8`b z*u=V-n;Wh0%9W86%yoICmD^QXBJ+TuMeduj_mynB29_Kl1X*A2_&{r~^}-@kve zdYW2FGLG%r8Ru!aX~lxyzki)LushDvqC7Rcx2`zG-R$h~L;wH%fBfKXl#6k)zx}VD zKk}k|sxxDHYKs!Qtrkt~nbKapYTmR2Z<_=!t4|TTzXRRJSrX(I%%C#k)~^Ns|6P3T zwz~@CcuyC{kP61s^QK8h9R!*aGdC-5mQ6dy==6QR=MEp?G>PQz`~2?eFFMS(r+K1g zp!&qb32C!ZG_9s->U*tRla<=$@$cY?|5H+adAMETx@7!MusHjBPl?KXziQ+6?1D=V ze?2)-gt;N$>Wd8<>nsYyl^Lt{Fxgl%o_?@o{T-$+24C7lzi+*sf1s9QvY5Z$XS=Iy lUyMDUTPOc|vvcNeU4iM^B0ctPia>WVc)I$ztaD0e0syFMv7G<_ diff --git a/pelican/tests/output/basic/theme/images/icons/google-groups.png b/pelican/tests/output/basic/theme/images/icons/google-groups.png deleted file mode 100644 index bbd0a0fd41295e94081103efe82742a5c6411765..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 420 zcmV;V0bBlwP)wr?kW++e@xB+qT~SFYEF=HvR&ieDr#i-_xj} zRHo4+e;!+Noelr;cc?77Y;{c*CNr6Zes7m1^J^+M9sV--D^!{k1_1#;6aW?Y?FKU> z$wUFckfr-O)R-Be!K8BMN$?ljo<0akOpD)OEKI~9+x!b$boZC|3lK-`{C{xHEuU!d zCn!ydf)v}GF)B$#1SpIDL5ep&8ms%oIbw(Zf<>Eu zV{!X$x!~%@{^9sNR&mx%AN_fQnat#ZJ3j~K-$A9Q{GPt1rQRSFm;ow5sT2?@^QR4r zg$anDgeCx$iqb6h2iG6)ockYoL!0v;mgd!$p2~_@_rOO_1Na`l%Wwj6hQdz( O0000oJ)blrcs(C@6ijHhGjL zrFRi_0z}#gh+z_?jx?ulJ6ZEDq_MICdrx(I>4q_hBh(7t5P3nx z6N?eUwA{x^#MIz^w4#;vg#Q`{-|sTaZm+z|gUicmu%q_q_{S4K=37O~-YZFy$l zCeshS!0W4y4-;Rs{O>veK>w%APq})#_~VzlN~kv#Z!>>U^FI6esN%Y~QuIs1d#~iY zS5C<<$DO!3cexVMKA`VP&KrXDnvg`iD8?-qH)mw8u{$e0Ls5IrFnpzz@8B4^@ijQjs{a4Z+iYWeG zt$5N5AMVx+R~iI;l~A9E$d3ce%4xvm2W_r2NEe$slb&w_>f!(Z002ovPDHLkV1lnz B@09=m diff --git a/pelican/tests/output/basic/theme/images/icons/hackernews.png b/pelican/tests/output/basic/theme/images/icons/hackernews.png deleted file mode 100644 index 8e05e3ee207e114a96f3811f46f68d17d52fe80b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2771 zcmV;^3M}=BP)X+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@QN3Mg3?1|NsB>i~#tuXaD5J_o76e z$B4WD001vZL_t(|0b{@m1Qi$r!3;wKW??Xc(Sbz(%wQEZ41g$d6oINX6oxVw1@JNO Z0sz-j0)=#JDs2D&002ovPDHLkV1kM1SuX$p diff --git a/pelican/tests/output/basic/theme/images/icons/lastfm.png b/pelican/tests/output/basic/theme/images/icons/lastfm.png deleted file mode 100644 index 2eedd2daa1429ba026c0b3c81f200c7df55d199a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 840 zcmV-O1GoH%P)KCJu$;;6i zN)?b2a3_#VfTsWs$6EHV5TGNlK%}w4u2N|o9mJtskW6u)FfMY|?6HLq0N~tSmCYiQ z2itn^l^G`qTae2D0xESFZh|%tXmDcRU;!-yQXzzdgu?j^sE-_`#F)I#a0AjAIORAv z?nua{5j_L2Xf^mv7?p>M&VsISuyh@C_u_&8Tdjpsq*G;#gR3rt3(wqpYJX|(pqnm- zVi_(t9gaQ}dWUeQ9S7b0nAMvJMjCC()%*h2T()P6`Q<<4KoQZ3;E)b@mqq9E^qzbJ^&kHt zb@z3sXYQl9ZY#2oD@eP!I~#uc8*V^AwupI;L7D|Jvi|(8qu0 z2LL$zBz)6?fx+w8bl#l{x;rm&{_hjrZqU60&R?r z_q^a%r%rJ@Z+x)Fd-;e{U-Iff*T}z9(5p5g3wcDXPJR3rG$+nsWZfoYCWAb*gT_Bo z7fg}|L9J&75r5f0ZeI>gIn5MAw<$RbKrq)K4wK5le| zR(k=@SlVyBWV*`avXNJ2NP$f#q_(8G+6X;MtJy>;4`+;2 znIjMBZmisPo~Hw3I)yO?atTx+i^wMNq>KkRoaf=R#1u=&Mhm*)MMoa?yQ=3?Z|U=o zU9OHh!itfmFwG$0g0M5@al}`~v399M8f%oORSV+K_KnG|j?V;kMf)2sF5LyF&y{nJ Sd8X6=0000 diff --git a/pelican/tests/output/basic/theme/images/icons/linkedin.png b/pelican/tests/output/basic/theme/images/icons/linkedin.png deleted file mode 100644 index 06a88016f191d1f7596e5d159eb9e3ef276b6ad4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 625 zcmV-%0*?KOP)NJD$VFG5yq%r(;8zR}7Ydty&~ue@oPv z!go{cG`vvH)n(hYdJfur_4kM7-}`N$BLW71f|-O65QELNn>4;$_2zi#L7VQdUf*VG zc0B*xR8@Sfo>nmHN}+MDnqn5J#;f9g$TZW=OS!!C<4lU-jh|=cs;0XYW~;nhJEE0r zdMWpJ#bDNMT&}cGH>t7P-yM4W=hFCXaLv`alDWtkCwY0;!z58#z z#&T71?c1;2yXPjW|3Lt-K|fa>vgM24P7nS%my|*-?u{?k1z4y`Q@qLr7+I)}xOVW2 zJ3sxy0Crrjx2{T+o3K*EU*7;Otu&GVZhd>`xi2Rl`h0wRu?9M0pr|e$T%t()-9X(G zM?fxx1SUyc#XyCAwXc5_{TIPFaU$>TzBm6pmt%Vs1;7F00000 LNkvXXu0mjfHfAMI diff --git a/pelican/tests/output/basic/theme/images/icons/reddit.png b/pelican/tests/output/basic/theme/images/icons/reddit.png deleted file mode 100644 index d826d3e746a18b712db7a68746c0c523af802fe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458 zcmV;*0X6=KP)rn|{qWNU7Dgl~R~ zWNK}Sjg6(Fp|rEIpPrq2fPh0AJ zxy#GTfq{X3etxmY-Bn&^YHMt-t*g_~&)L`2qqxp^h?r+{fMacVpMN&`0001qNklWKP}*}Is^$| zQ}1hfWmjT2av-PYqbE!!n)oua2c5|Qn7a$5l#p|w6z7}`F{d?z0T5WQglfOLER+{E--Hu?5@wDUpV?F@=Mkg+ zN{1=uqDlzrnkbYp)J3q!I?Vn|_~M zVqi53FAJQ^xo7*4+R80OflZT^0WscTaBuO7ca6A_aA-H9M3Y@{DSLGBL)| zTym{{me}!*Z3962jmy>w<<#@)IsY9R%I3sYbB>~@mM@C5Ks~s4nX-}TW8S>D*g7F! hew!yYJnD{z;D0NLV2ni3qF(?2002ovPDHLkV1k{La&G_t diff --git a/pelican/tests/output/basic/theme/images/icons/slideshare.png b/pelican/tests/output/basic/theme/images/icons/slideshare.png deleted file mode 100644 index 9cbe8588e24976076e5fc3898fa97856122f6fa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 435 zcmV;k0ZjghP)YNn`_&)?H$|J+jM18jcAeNE5840mI#38{rmUny@9DE z#Xg@8y@9DE0*}W7yWQR+m>Mc@yWPA^!~YvlZXQ7Ti(&Z??RignZicC$0;ki-+teNz zN*Qw@h$6fTDiChi(;mQdV>IdC=QLyP(`3?fGmSD0E706p2ko0(Fn89&YPG`Qa4@$> zdhWPhszyk06L$T`#EcuEoIB7dp?k0q>OF&bXs#{9!Ot=1fBXlJ={G`9+@@d!27`fI z*^>PO`g==w@1W}FBwRnS8UK$h$DgmS5K-O+o6QEJ(Z~waYBd=2dOa)_3-qUlQ$o(J zM$*}Z*m~g-OePa_IvoOeR-jNQz{PlZ6lKDV2#PRA1rmt_T+Fw|P-fhVCCq=C0AY>_ z#9}czIyxxk)H|>~8V7Uy1sn}2#;nIlxE)_f_XF?+t*xyTxm-@(N8ncJn41xc#e6|8 dAc8(0=Pod^EtOP(ARYh!002ovPDHLkV1k4J!&(3U diff --git a/pelican/tests/output/basic/theme/images/icons/speakerdeck.png b/pelican/tests/output/basic/theme/images/icons/speakerdeck.png deleted file mode 100644 index 7281ec48b41095180a83d71eaf0b00c85116965b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 580 zcmV-K0=xZ*P)K92#E{?_PJ+8Tth@W(+a>28uk~_0`h`<(yY|`h z@3?AaV0?I7E@5C8LL5^B7?_#m1O5GLKL6{LaQ3ZN%$$19RhM^ilM4(QGb91Pk}#td z%#5L?Rn9&W7zkqrdeL$z%L))wjb8cZK2wtEL}mE*O&6H3%~&#zdHx68i}xJQZ+34dIdgm^E12V4($L!Sax!Q)0op z%%0y@llS*=IU_?ugf;+>a;DdHv`xcYwwSu^B5bgwWG8rbaUIq4<$P}6+C9-V(ZSGy!i4XI zBkkxgITji#coxBn7F*@>CvnH5Im-dI=x{a@nz|+mZz(oB8CqTLuD=F=p|2qrpWMi5G S=mtXo0000YRoeQcY09>#1xxKwS5F z8mlP6JGx3wQgxp~4T-?1`Rz0oPN3$iK>QzwU%sBkR`YrWi#XVjbZs+WxcmSb0KzU< z4R|<_pXuFnCY5*7ncLn>WBm*@=rz!g=9fLZ0#9?*eV!!i{{b4*NYE=UCUZ&x4QhHf zjrr}HsjUBBPiJ}aY9eRTvkFsE%bi<+hJ_q$)(X1QD;kN@ z$%`wz`8QVB1Z}9WYXRb&Kzt8~{{vn2e{DI?z%r-*E6ZK%Ff``oJb76HYP@>p``(Y0Zs!*!BjN>0I%aNVnQ8)X9Pc^Y zIsS936mToi>>TyPEkcc+2 z`5!z^ZFZ_O#v*`YvS&^RN+0_pGFvTxh}Lp2wqGagAdVyz;6#9udAenJvhWwC3QjN} ztg*<~69%xm!G(wSxK?V6H7%K70|sCIX&%<6rLrHmSw|10dx_{@95hn1(i_a}5^hC>VT?7YQHpjC*P~ez&J%_J0000< KMNUMnLSTY<*|#qM diff --git a/pelican/tests/output/basic/theme/images/icons/vimeo.png b/pelican/tests/output/basic/theme/images/icons/vimeo.png deleted file mode 100644 index 4b9d72127c574237090a2f5f2f7eb7be94a2b62e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 349 zcmV-j0iyniP)K~#7FV*mo~S^J1YP}SCZ?(wd@Mi2p&f%E_|kV7PbYe1Nxz5O|y=nOth{(bt=TS6pPua;?J=e5) zBGkM)Pk5GtRBXBib(QPnRVW%@oC6PDyoRVi_QQr_=YS?+52c0sPeBv`1LxxFPe2CH z8{$R%ID<81>CXSCz@?@iUpz?mZ$MU^)He_47^%Sg0P#sgK~#7Fl~6T81W^oq2cX(5aX1R@6<62}xdAmT6>b%79aG~T97?pp zUBiU8?wcQZ@ytL(4yKyhs>_L_h@KZq&4RGqy&kuUuOC&N_n)+)st0x|+@BHFkRd@&PPEwb z&tB6>xX7cYComKsCfH0jmXR=>tbysq2Gd0WOmZB{y88Z3Wj9^}w-Gbm)y>{S4W9T7 zfp3d!a2x}~TiXWa<}BOO6S3ho2QJ^`nhs+IjSU!vHHb*~-y}#B5s^RLsrl%w&%5IQ O0000UqK~#7FjgmEk6+sXNzkTlh65QS0-8I48iI50)i1CK$|J3O zMSiddRs94I-RZ%BpPVQlG-*wE)^@pKur#bMkyS)ii4hvVB0PPwtX?$Lk2sgu3yxdr zMHBD`H`aw%Ysl&kiS1h=1)tbo!~Ye`fCpjdO!(yU+ZHs&^bQiZX+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@*Jzj349X=?9_$gI2kIcwdunCZtMPOnPe)w(oZZmY=P7Ks9%FQb&p8>E zTGTu^G&~wGL&GC!#VuGydpI#q;n4F`-2p*w5hggKWfvm1-Ht!-D0%;*r0sX&mtTmS z=#a1o5c3rNC06S#lEEUl4ZT(7hJXc%h^4bgLQ=J-kX6=dv2qWx(KFxw<| z8p~uAa?0C01wO*eV=5{z+2r5xRmC+O#kC#3;ww#bTsi?_p2FbJ$cF2&dW*;dEIS`r z-{$NC7APVX0b-s4bL>@`-m2iuJf@Y`cU}9I16ZJlSOkdq3%}wjQF(Q}Mc#EUA+xj< z?7W)*1-w0hpaS1pO{?AaFnQ`>1)C;df$n>lghe2z(B3_8<9lvWK~rg6XL?CXWL7;$ zD`|ly6*S-encvkr7*yZ~ue};=vMZZ!#$EfG^I9zdEKo!aRp=d<^B=Mdj&Pkt)pJqZACTW#!hM@D5IKDqq;%eMz!tbv+awqP8IHt=!XVS zsZ`n>$2lgOwFdh8dv^BsZ>()0em94EiuYK~#M+HZNki~hy!n`DO0RgV7kx83G-6kVzbw2peZ^$T0XF+WR zfC!}kbTrMykUD-`pyq(>!2GmFl&qi|8BD#Yimm6&NW;(o1*9a20zlURQu0~F;$lpk zC^lANi08=`Jt9})*0WlBM`^dLR$E*Uq-h9fXc|BQRMFBY=8w1tql8shMigC06q$^` z3o;x$Brcc5Vx|EggvrrZTv~ueFR9S5n?9&@2ra;ZlJa{y#y*#;tE-3i@BjIXTFqx< zWQM^b2eIvt(ZSC+IB^l#ZO?Jct~%A$Zme8|ylK!@D6=^GmdF`z&B`yo_%8qUJKqup zI`#T8-~s^<(cM|*r>_LNwibZ|C;c5580x>Z^zql9MEVifl5$XO00aq!?ra*2Oy6Bcd7s& z4E&Wd&NypYm|1Hm3>yT#OOnPY6%hC_U@Jle48x$&@MzjDei*iPb+`A(ty}jT{_VFp0@p^D9%vpdEdoQ&A~UVr|G*1s z*N$#`bZl0nsoeDaV0>!ow??I6E} zTD@V0|CC##a5uK^=-$j1%KE$Sep%nY|D6y3VDFw|_#0Q*b=;R(Ji7n@002ovPDHLk FV1ku*k$3ca`voa6f0k zeB{69k@N|?Km7UBZZ))YUHu=w|Mw?(E)O$?WixY_FPL7s$Pm-8fstds@^ATL%bUet yZgfGChJzsUtA%$MR;8+S88GxaEa+z7`Ew3v5re0zpUXO@geCxdur~kz diff --git a/pelican/tests/output/custom/theme/images/icons/github.png b/pelican/tests/output/custom/theme/images/icons/github.png deleted file mode 100644 index 5d9109deab2b3669c845845515f4d53ab5d6daa6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 606 zcmV-k0-^nhP)3g@6&i?1|w)NXSf84|8m&o+0 zUbR{k48>ZU#v6!wqi1V1r%EE$MYAT@gfEZ`ebJgG`3x?P75EOx(Rb}pK9^g49TLfP zG|6;$SGbAH zWw$>tFUyCTTU=}VL5$UQcykD>ruLPAMktgpS2)vHd2`;>>D~O#r0q^piwN%{Eu7xl ze_3-g6EyPr+mZ( z$~zD9`4D^$F>WOyU!f<&c%QI`>dHS@;3~xO7I+5=MISM;>bsLPrcC8T?Gyg2EhYt{Z#{?9|afq=U$pRKgVkaa6A5hOW zm~E0;qoMsELHiVUdq+qHW_!=pLlCuB&nsXkrlThcr&yqB2Ez}bh}WQodCMG=62#-T(jq diff --git a/pelican/tests/output/custom/theme/images/icons/gitorious.png b/pelican/tests/output/custom/theme/images/icons/gitorious.png deleted file mode 100644 index a6705d0f462b505be4d9a5a372743d206c141a11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!O@L2`D?^o)dRwqYcev@q#DMvQ z*(*AHH`kW0om8=NO8&uVRmbKn*uQ@5(H+Zf?%R0x*ww37FTJ_{>eZ{eAD{pK|9`68 zWgnn9ah@)YAr*|ZCyp{U2QWBn{C>Cgf8^^ph2sKmrZ@0Db>47eliif`GZN3kOR|4E zp1HSr>(=i-J2K=SnYPGB*}QnjI%8UJ(Sx8gTe~DWM4fr0!yB diff --git a/pelican/tests/output/custom/theme/images/icons/gittip.png b/pelican/tests/output/custom/theme/images/icons/gittip.png deleted file mode 100644 index b9f67aaa32a0acd166d7271b3dbf9113717d3f00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMfoB*E?S0J6}W0T-*4J5$~5D8`b z*u=V-n;Wh0%9W86%yoICmD^QXBJ+TuMeduj_mynB29_Kl1X*A2_&{r~^}-@kve zdYW2FGLG%r8Ru!aX~lxyzki)LushDvqC7Rcx2`zG-R$h~L;wH%fBfKXl#6k)zx}VD zKk}k|sxxDHYKs!Qtrkt~nbKapYTmR2Z<_=!t4|TTzXRRJSrX(I%%C#k)~^Ns|6P3T zwz~@CcuyC{kP61s^QK8h9R!*aGdC-5mQ6dy==6QR=MEp?G>PQz`~2?eFFMS(r+K1g zp!&qb32C!ZG_9s->U*tRla<=$@$cY?|5H+adAMETx@7!MusHjBPl?KXziQ+6?1D=V ze?2)-gt;N$>Wd8<>nsYyl^Lt{Fxgl%o_?@o{T-$+24C7lzi+*sf1s9QvY5Z$XS=Iy lUyMDUTPOc|vvcNeU4iM^B0ctPia>WVc)I$ztaD0e0syFMv7G<_ diff --git a/pelican/tests/output/custom/theme/images/icons/google-groups.png b/pelican/tests/output/custom/theme/images/icons/google-groups.png deleted file mode 100644 index bbd0a0fd41295e94081103efe82742a5c6411765..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 420 zcmV;V0bBlwP)wr?kW++e@xB+qT~SFYEF=HvR&ieDr#i-_xj} zRHo4+e;!+Noelr;cc?77Y;{c*CNr6Zes7m1^J^+M9sV--D^!{k1_1#;6aW?Y?FKU> z$wUFckfr-O)R-Be!K8BMN$?ljo<0akOpD)OEKI~9+x!b$boZC|3lK-`{C{xHEuU!d zCn!ydf)v}GF)B$#1SpIDL5ep&8ms%oIbw(Zf<>Eu zV{!X$x!~%@{^9sNR&mx%AN_fQnat#ZJ3j~K-$A9Q{GPt1rQRSFm;ow5sT2?@^QR4r zg$anDgeCx$iqb6h2iG6)ockYoL!0v;mgd!$p2~_@_rOO_1Na`l%Wwj6hQdz( O0000oJ)blrcs(C@6ijHhGjL zrFRi_0z}#gh+z_?jx?ulJ6ZEDq_MICdrx(I>4q_hBh(7t5P3nx z6N?eUwA{x^#MIz^w4#;vg#Q`{-|sTaZm+z|gUicmu%q_q_{S4K=37O~-YZFy$l zCeshS!0W4y4-;Rs{O>veK>w%APq})#_~VzlN~kv#Z!>>U^FI6esN%Y~QuIs1d#~iY zS5C<<$DO!3cexVMKA`VP&KrXDnvg`iD8?-qH)mw8u{$e0Ls5IrFnpzz@8B4^@ijQjs{a4Z+iYWeG zt$5N5AMVx+R~iI;l~A9E$d3ce%4xvm2W_r2NEe$slb&w_>f!(Z002ovPDHLkV1lnz B@09=m diff --git a/pelican/tests/output/custom/theme/images/icons/hackernews.png b/pelican/tests/output/custom/theme/images/icons/hackernews.png deleted file mode 100644 index 8e05e3ee207e114a96f3811f46f68d17d52fe80b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2771 zcmV;^3M}=BP)X+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@QN3Mg3?1|NsB>i~#tuXaD5J_o76e z$B4WD001vZL_t(|0b{@m1Qi$r!3;wKW??Xc(Sbz(%wQEZ41g$d6oINX6oxVw1@JNO Z0sz-j0)=#JDs2D&002ovPDHLkV1kM1SuX$p diff --git a/pelican/tests/output/custom/theme/images/icons/lastfm.png b/pelican/tests/output/custom/theme/images/icons/lastfm.png deleted file mode 100644 index 2eedd2daa1429ba026c0b3c81f200c7df55d199a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 840 zcmV-O1GoH%P)KCJu$;;6i zN)?b2a3_#VfTsWs$6EHV5TGNlK%}w4u2N|o9mJtskW6u)FfMY|?6HLq0N~tSmCYiQ z2itn^l^G`qTae2D0xESFZh|%tXmDcRU;!-yQXzzdgu?j^sE-_`#F)I#a0AjAIORAv z?nua{5j_L2Xf^mv7?p>M&VsISuyh@C_u_&8Tdjpsq*G;#gR3rt3(wqpYJX|(pqnm- zVi_(t9gaQ}dWUeQ9S7b0nAMvJMjCC()%*h2T()P6`Q<<4KoQZ3;E)b@mqq9E^qzbJ^&kHt zb@z3sXYQl9ZY#2oD@eP!I~#uc8*V^AwupI;L7D|Jvi|(8qu0 z2LL$zBz)6?fx+w8bl#l{x;rm&{_hjrZqU60&R?r z_q^a%r%rJ@Z+x)Fd-;e{U-Iff*T}z9(5p5g3wcDXPJR3rG$+nsWZfoYCWAb*gT_Bo z7fg}|L9J&75r5f0ZeI>gIn5MAw<$RbKrq)K4wK5le| zR(k=@SlVyBWV*`avXNJ2NP$f#q_(8G+6X;MtJy>;4`+;2 znIjMBZmisPo~Hw3I)yO?atTx+i^wMNq>KkRoaf=R#1u=&Mhm*)MMoa?yQ=3?Z|U=o zU9OHh!itfmFwG$0g0M5@al}`~v399M8f%oORSV+K_KnG|j?V;kMf)2sF5LyF&y{nJ Sd8X6=0000 diff --git a/pelican/tests/output/custom/theme/images/icons/linkedin.png b/pelican/tests/output/custom/theme/images/icons/linkedin.png deleted file mode 100644 index 06a88016f191d1f7596e5d159eb9e3ef276b6ad4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 625 zcmV-%0*?KOP)NJD$VFG5yq%r(;8zR}7Ydty&~ue@oPv z!go{cG`vvH)n(hYdJfur_4kM7-}`N$BLW71f|-O65QELNn>4;$_2zi#L7VQdUf*VG zc0B*xR8@Sfo>nmHN}+MDnqn5J#;f9g$TZW=OS!!C<4lU-jh|=cs;0XYW~;nhJEE0r zdMWpJ#bDNMT&}cGH>t7P-yM4W=hFCXaLv`alDWtkCwY0;!z58#z z#&T71?c1;2yXPjW|3Lt-K|fa>vgM24P7nS%my|*-?u{?k1z4y`Q@qLr7+I)}xOVW2 zJ3sxy0Crrjx2{T+o3K*EU*7;Otu&GVZhd>`xi2Rl`h0wRu?9M0pr|e$T%t()-9X(G zM?fxx1SUyc#XyCAwXc5_{TIPFaU$>TzBm6pmt%Vs1;7F00000 LNkvXXu0mjfHfAMI diff --git a/pelican/tests/output/custom/theme/images/icons/reddit.png b/pelican/tests/output/custom/theme/images/icons/reddit.png deleted file mode 100644 index d826d3e746a18b712db7a68746c0c523af802fe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458 zcmV;*0X6=KP)rn|{qWNU7Dgl~R~ zWNK}Sjg6(Fp|rEIpPrq2fPh0AJ zxy#GTfq{X3etxmY-Bn&^YHMt-t*g_~&)L`2qqxp^h?r+{fMacVpMN&`0001qNklWKP}*}Is^$| zQ}1hfWmjT2av-PYqbE!!n)oua2c5|Qn7a$5l#p|w6z7}`F{d?z0T5WQglfOLER+{E--Hu?5@wDUpV?F@=Mkg+ zN{1=uqDlzrnkbYp)J3q!I?Vn|_~M zVqi53FAJQ^xo7*4+R80OflZT^0WscTaBuO7ca6A_aA-H9M3Y@{DSLGBL)| zTym{{me}!*Z3962jmy>w<<#@)IsY9R%I3sYbB>~@mM@C5Ks~s4nX-}TW8S>D*g7F! hew!yYJnD{z;D0NLV2ni3qF(?2002ovPDHLkV1k{La&G_t diff --git a/pelican/tests/output/custom/theme/images/icons/slideshare.png b/pelican/tests/output/custom/theme/images/icons/slideshare.png deleted file mode 100644 index 9cbe8588e24976076e5fc3898fa97856122f6fa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 435 zcmV;k0ZjghP)YNn`_&)?H$|J+jM18jcAeNE5840mI#38{rmUny@9DE z#Xg@8y@9DE0*}W7yWQR+m>Mc@yWPA^!~YvlZXQ7Ti(&Z??RignZicC$0;ki-+teNz zN*Qw@h$6fTDiChi(;mQdV>IdC=QLyP(`3?fGmSD0E706p2ko0(Fn89&YPG`Qa4@$> zdhWPhszyk06L$T`#EcuEoIB7dp?k0q>OF&bXs#{9!Ot=1fBXlJ={G`9+@@d!27`fI z*^>PO`g==w@1W}FBwRnS8UK$h$DgmS5K-O+o6QEJ(Z~waYBd=2dOa)_3-qUlQ$o(J zM$*}Z*m~g-OePa_IvoOeR-jNQz{PlZ6lKDV2#PRA1rmt_T+Fw|P-fhVCCq=C0AY>_ z#9}czIyxxk)H|>~8V7Uy1sn}2#;nIlxE)_f_XF?+t*xyTxm-@(N8ncJn41xc#e6|8 dAc8(0=Pod^EtOP(ARYh!002ovPDHLkV1k4J!&(3U diff --git a/pelican/tests/output/custom/theme/images/icons/speakerdeck.png b/pelican/tests/output/custom/theme/images/icons/speakerdeck.png deleted file mode 100644 index 7281ec48b41095180a83d71eaf0b00c85116965b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 580 zcmV-K0=xZ*P)K92#E{?_PJ+8Tth@W(+a>28uk~_0`h`<(yY|`h z@3?AaV0?I7E@5C8LL5^B7?_#m1O5GLKL6{LaQ3ZN%$$19RhM^ilM4(QGb91Pk}#td z%#5L?Rn9&W7zkqrdeL$z%L))wjb8cZK2wtEL}mE*O&6H3%~&#zdHx68i}xJQZ+34dIdgm^E12V4($L!Sax!Q)0op z%%0y@llS*=IU_?ugf;+>a;DdHv`xcYwwSu^B5bgwWG8rbaUIq4<$P}6+C9-V(ZSGy!i4XI zBkkxgITji#coxBn7F*@>CvnH5Im-dI=x{a@nz|+mZz(oB8CqTLuD=F=p|2qrpWMi5G S=mtXo0000YRoeQcY09>#1xxKwS5F z8mlP6JGx3wQgxp~4T-?1`Rz0oPN3$iK>QzwU%sBkR`YrWi#XVjbZs+WxcmSb0KzU< z4R|<_pXuFnCY5*7ncLn>WBm*@=rz!g=9fLZ0#9?*eV!!i{{b4*NYE=UCUZ&x4QhHf zjrr}HsjUBBPiJ}aY9eRTvkFsE%bi<+hJ_q$)(X1QD;kN@ z$%`wz`8QVB1Z}9WYXRb&Kzt8~{{vn2e{DI?z%r-*E6ZK%Ff``oJb76HYP@>p``(Y0Zs!*!BjN>0I%aNVnQ8)X9Pc^Y zIsS936mToi>>TyPEkcc+2 z`5!z^ZFZ_O#v*`YvS&^RN+0_pGFvTxh}Lp2wqGagAdVyz;6#9udAenJvhWwC3QjN} ztg*<~69%xm!G(wSxK?V6H7%K70|sCIX&%<6rLrHmSw|10dx_{@95hn1(i_a}5^hC>VT?7YQHpjC*P~ez&J%_J0000< KMNUMnLSTY<*|#qM diff --git a/pelican/tests/output/custom/theme/images/icons/vimeo.png b/pelican/tests/output/custom/theme/images/icons/vimeo.png deleted file mode 100644 index 4b9d72127c574237090a2f5f2f7eb7be94a2b62e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 349 zcmV-j0iyniP)K~#7FV*mo~S^J1YP}SCZ?(wd@Mi2p&f%E_|kV7PbYe1Nxz5O|y=nOth{(bt=TS6pPua;?J=e5) zBGkM)Pk5GtRBXBib(QPnRVW%@oC6PDyoRVi_QQr_=YS?+52c0sPeBv`1LxxFPe2CH z8{$R%ID<81>CXSCz@?@iUpz?mZ$MU^)He_47^%Sg0P#sgK~#7Fl~6T81W^oq2cX(5aX1R@6<62}xdAmT6>b%79aG~T97?pp zUBiU8?wcQZ@ytL(4yKyhs>_L_h@KZq&4RGqy&kuUuOC&N_n)+)st0x|+@BHFkRd@&PPEwb z&tB6>xX7cYComKsCfH0jmXR=>tbysq2Gd0WOmZB{y88Z3Wj9^}w-Gbm)y>{S4W9T7 zfp3d!a2x}~TiXWa<}BOO6S3ho2QJ^`nhs+IjSU!vHHb*~-y}#B5s^RLsrl%w&%5IQ O0000UqK~#7FjgmEk6+sXNzkTlh65QS0-8I48iI50)i1CK$|J3O zMSiddRs94I-RZ%BpPVQlG-*wE)^@pKur#bMkyS)ii4hvVB0PPwtX?$Lk2sgu3yxdr zMHBD`H`aw%Ysl&kiS1h=1)tbo!~Ye`fCpjdO!(yU+ZHs&^bQiZX+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@*Jzj349X=?9_$gI2kIcwdunCZtMPOnPe)w(oZZmY=P7Ks9%FQb&p8>E zTGTu^G&~wGL&GC!#VuGydpI#q;n4F`-2p*w5hggKWfvm1-Ht!-D0%;*r0sX&mtTmS z=#a1o5c3rNC06S#lEEUl4ZT(7hJXc%h^4bgLQ=J-kX6=dv2qWx(KFxw<| z8p~uAa?0C01wO*eV=5{z+2r5xRmC+O#kC#3;ww#bTsi?_p2FbJ$cF2&dW*;dEIS`r z-{$NC7APVX0b-s4bL>@`-m2iuJf@Y`cU}9I16ZJlSOkdq3%}wjQF(Q}Mc#EUA+xj< z?7W)*1-w0hpaS1pO{?AaFnQ`>1)C;df$n>lghe2z(B3_8<9lvWK~rg6XL?CXWL7;$ zD`|ly6*S-encvkr7*yZ~ue};=vMZZ!#$EfG^I9zdEKo!aRp=d<^B=Mdj&Pkt)pJqZACTW#!hM@D5IKDqq;%eMz!tbv+awqP8IHt=!XVS zsZ`n>$2lgOwFdh8dv^BsZ>()0em94EiuYK~#M+HZNki~hy!n`DO0RgV7kx83G-6kVzbw2peZ^$T0XF+WR zfC!}kbTrMykUD-`pyq(>!2GmFl&qi|8BD#Yimm6&NW;(o1*9a20zlURQu0~F;$lpk zC^lANi08=`Jt9})*0WlBM`^dLR$E*Uq-h9fXc|BQRMFBY=8w1tql8shMigC06q$^` z3o;x$Brcc5Vx|EggvrrZTv~ueFR9S5n?9&@2ra;ZlJa{y#y*#;tE-3i@BjIXTFqx< zWQM^b2eIvt(ZSC+IB^l#ZO?Jct~%A$Zme8|ylK!@D6=^GmdF`z&B`yo_%8qUJKqup zI`#T8-~s^<(cM|*r>_LNwibZ|C;c5580x>Z^zql9MEVifl5$XO00aq!?ra*2Oy6Bcd7s& z4E&Wd&NypYm|1Hm3>yT#OOnPY6%hC_U@Jle48x$&@MzjDei*iPb+`A(ty}jT{_VFp0@p^D9%vpdEdoQ&A~UVr|G*1s z*N$#`bZl0nsoeDaV0>!ow??I6E} zTD@V0|CC##a5uK^=-$j1%KE$Sep%nY|D6y3VDFw|_#0Q*b=;R(Ji7n@002ovPDHLk FV1ku*k$3ca`voa6f0k zeB{69k@N|?Km7UBZZ))YUHu=w|Mw?(E)O$?WixY_FPL7s$Pm-8fstds@^ATL%bUet yZgfGChJzsUtA%$MR;8+S88GxaEa+z7`Ew3v5re0zpUXO@geCxdur~kz diff --git a/pelican/tests/output/custom_locale/theme/images/icons/github.png b/pelican/tests/output/custom_locale/theme/images/icons/github.png deleted file mode 100644 index 5d9109deab2b3669c845845515f4d53ab5d6daa6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 606 zcmV-k0-^nhP)3g@6&i?1|w)NXSf84|8m&o+0 zUbR{k48>ZU#v6!wqi1V1r%EE$MYAT@gfEZ`ebJgG`3x?P75EOx(Rb}pK9^g49TLfP zG|6;$SGbAH zWw$>tFUyCTTU=}VL5$UQcykD>ruLPAMktgpS2)vHd2`;>>D~O#r0q^piwN%{Eu7xl ze_3-g6EyPr+mZ( z$~zD9`4D^$F>WOyU!f<&c%QI`>dHS@;3~xO7I+5=MISM;>bsLPrcC8T?Gyg2EhYt{Z#{?9|afq=U$pRKgVkaa6A5hOW zm~E0;qoMsELHiVUdq+qHW_!=pLlCuB&nsXkrlThcr&yqB2Ez}bh}WQodCMG=62#-T(jq diff --git a/pelican/tests/output/custom_locale/theme/images/icons/gitorious.png b/pelican/tests/output/custom_locale/theme/images/icons/gitorious.png deleted file mode 100644 index a6705d0f462b505be4d9a5a372743d206c141a11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!O@L2`D?^o)dRwqYcev@q#DMvQ z*(*AHH`kW0om8=NO8&uVRmbKn*uQ@5(H+Zf?%R0x*ww37FTJ_{>eZ{eAD{pK|9`68 zWgnn9ah@)YAr*|ZCyp{U2QWBn{C>Cgf8^^ph2sKmrZ@0Db>47eliif`GZN3kOR|4E zp1HSr>(=i-J2K=SnYPGB*}QnjI%8UJ(Sx8gTe~DWM4fr0!yB diff --git a/pelican/tests/output/custom_locale/theme/images/icons/gittip.png b/pelican/tests/output/custom_locale/theme/images/icons/gittip.png deleted file mode 100644 index b9f67aaa32a0acd166d7271b3dbf9113717d3f00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMfoB*E?S0J6}W0T-*4J5$~5D8`b z*u=V-n;Wh0%9W86%yoICmD^QXBJ+TuMeduj_mynB29_Kl1X*A2_&{r~^}-@kve zdYW2FGLG%r8Ru!aX~lxyzki)LushDvqC7Rcx2`zG-R$h~L;wH%fBfKXl#6k)zx}VD zKk}k|sxxDHYKs!Qtrkt~nbKapYTmR2Z<_=!t4|TTzXRRJSrX(I%%C#k)~^Ns|6P3T zwz~@CcuyC{kP61s^QK8h9R!*aGdC-5mQ6dy==6QR=MEp?G>PQz`~2?eFFMS(r+K1g zp!&qb32C!ZG_9s->U*tRla<=$@$cY?|5H+adAMETx@7!MusHjBPl?KXziQ+6?1D=V ze?2)-gt;N$>Wd8<>nsYyl^Lt{Fxgl%o_?@o{T-$+24C7lzi+*sf1s9QvY5Z$XS=Iy lUyMDUTPOc|vvcNeU4iM^B0ctPia>WVc)I$ztaD0e0syFMv7G<_ diff --git a/pelican/tests/output/custom_locale/theme/images/icons/google-groups.png b/pelican/tests/output/custom_locale/theme/images/icons/google-groups.png deleted file mode 100644 index bbd0a0fd41295e94081103efe82742a5c6411765..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 420 zcmV;V0bBlwP)wr?kW++e@xB+qT~SFYEF=HvR&ieDr#i-_xj} zRHo4+e;!+Noelr;cc?77Y;{c*CNr6Zes7m1^J^+M9sV--D^!{k1_1#;6aW?Y?FKU> z$wUFckfr-O)R-Be!K8BMN$?ljo<0akOpD)OEKI~9+x!b$boZC|3lK-`{C{xHEuU!d zCn!ydf)v}GF)B$#1SpIDL5ep&8ms%oIbw(Zf<>Eu zV{!X$x!~%@{^9sNR&mx%AN_fQnat#ZJ3j~K-$A9Q{GPt1rQRSFm;ow5sT2?@^QR4r zg$anDgeCx$iqb6h2iG6)ockYoL!0v;mgd!$p2~_@_rOO_1Na`l%Wwj6hQdz( O0000oJ)blrcs(C@6ijHhGjL zrFRi_0z}#gh+z_?jx?ulJ6ZEDq_MICdrx(I>4q_hBh(7t5P3nx z6N?eUwA{x^#MIz^w4#;vg#Q`{-|sTaZm+z|gUicmu%q_q_{S4K=37O~-YZFy$l zCeshS!0W4y4-;Rs{O>veK>w%APq})#_~VzlN~kv#Z!>>U^FI6esN%Y~QuIs1d#~iY zS5C<<$DO!3cexVMKA`VP&KrXDnvg`iD8?-qH)mw8u{$e0Ls5IrFnpzz@8B4^@ijQjs{a4Z+iYWeG zt$5N5AMVx+R~iI;l~A9E$d3ce%4xvm2W_r2NEe$slb&w_>f!(Z002ovPDHLkV1lnz B@09=m diff --git a/pelican/tests/output/custom_locale/theme/images/icons/hackernews.png b/pelican/tests/output/custom_locale/theme/images/icons/hackernews.png deleted file mode 100644 index 8e05e3ee207e114a96f3811f46f68d17d52fe80b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2771 zcmV;^3M}=BP)X+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@QN3Mg3?1|NsB>i~#tuXaD5J_o76e z$B4WD001vZL_t(|0b{@m1Qi$r!3;wKW??Xc(Sbz(%wQEZ41g$d6oINX6oxVw1@JNO Z0sz-j0)=#JDs2D&002ovPDHLkV1kM1SuX$p diff --git a/pelican/tests/output/custom_locale/theme/images/icons/lastfm.png b/pelican/tests/output/custom_locale/theme/images/icons/lastfm.png deleted file mode 100644 index 2eedd2daa1429ba026c0b3c81f200c7df55d199a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 840 zcmV-O1GoH%P)KCJu$;;6i zN)?b2a3_#VfTsWs$6EHV5TGNlK%}w4u2N|o9mJtskW6u)FfMY|?6HLq0N~tSmCYiQ z2itn^l^G`qTae2D0xESFZh|%tXmDcRU;!-yQXzzdgu?j^sE-_`#F)I#a0AjAIORAv z?nua{5j_L2Xf^mv7?p>M&VsISuyh@C_u_&8Tdjpsq*G;#gR3rt3(wqpYJX|(pqnm- zVi_(t9gaQ}dWUeQ9S7b0nAMvJMjCC()%*h2T()P6`Q<<4KoQZ3;E)b@mqq9E^qzbJ^&kHt zb@z3sXYQl9ZY#2oD@eP!I~#uc8*V^AwupI;L7D|Jvi|(8qu0 z2LL$zBz)6?fx+w8bl#l{x;rm&{_hjrZqU60&R?r z_q^a%r%rJ@Z+x)Fd-;e{U-Iff*T}z9(5p5g3wcDXPJR3rG$+nsWZfoYCWAb*gT_Bo z7fg}|L9J&75r5f0ZeI>gIn5MAw<$RbKrq)K4wK5le| zR(k=@SlVyBWV*`avXNJ2NP$f#q_(8G+6X;MtJy>;4`+;2 znIjMBZmisPo~Hw3I)yO?atTx+i^wMNq>KkRoaf=R#1u=&Mhm*)MMoa?yQ=3?Z|U=o zU9OHh!itfmFwG$0g0M5@al}`~v399M8f%oORSV+K_KnG|j?V;kMf)2sF5LyF&y{nJ Sd8X6=0000 diff --git a/pelican/tests/output/custom_locale/theme/images/icons/linkedin.png b/pelican/tests/output/custom_locale/theme/images/icons/linkedin.png deleted file mode 100644 index 06a88016f191d1f7596e5d159eb9e3ef276b6ad4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 625 zcmV-%0*?KOP)NJD$VFG5yq%r(;8zR}7Ydty&~ue@oPv z!go{cG`vvH)n(hYdJfur_4kM7-}`N$BLW71f|-O65QELNn>4;$_2zi#L7VQdUf*VG zc0B*xR8@Sfo>nmHN}+MDnqn5J#;f9g$TZW=OS!!C<4lU-jh|=cs;0XYW~;nhJEE0r zdMWpJ#bDNMT&}cGH>t7P-yM4W=hFCXaLv`alDWtkCwY0;!z58#z z#&T71?c1;2yXPjW|3Lt-K|fa>vgM24P7nS%my|*-?u{?k1z4y`Q@qLr7+I)}xOVW2 zJ3sxy0Crrjx2{T+o3K*EU*7;Otu&GVZhd>`xi2Rl`h0wRu?9M0pr|e$T%t()-9X(G zM?fxx1SUyc#XyCAwXc5_{TIPFaU$>TzBm6pmt%Vs1;7F00000 LNkvXXu0mjfHfAMI diff --git a/pelican/tests/output/custom_locale/theme/images/icons/reddit.png b/pelican/tests/output/custom_locale/theme/images/icons/reddit.png deleted file mode 100644 index d826d3e746a18b712db7a68746c0c523af802fe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458 zcmV;*0X6=KP)rn|{qWNU7Dgl~R~ zWNK}Sjg6(Fp|rEIpPrq2fPh0AJ zxy#GTfq{X3etxmY-Bn&^YHMt-t*g_~&)L`2qqxp^h?r+{fMacVpMN&`0001qNklWKP}*}Is^$| zQ}1hfWmjT2av-PYqbE!!n)oua2c5|Qn7a$5l#p|w6z7}`F{d?z0T5WQglfOLER+{E--Hu?5@wDUpV?F@=Mkg+ zN{1=uqDlzrnkbYp)J3q!I?Vn|_~M zVqi53FAJQ^xo7*4+R80OflZT^0WscTaBuO7ca6A_aA-H9M3Y@{DSLGBL)| zTym{{me}!*Z3962jmy>w<<#@)IsY9R%I3sYbB>~@mM@C5Ks~s4nX-}TW8S>D*g7F! hew!yYJnD{z;D0NLV2ni3qF(?2002ovPDHLkV1k{La&G_t diff --git a/pelican/tests/output/custom_locale/theme/images/icons/slideshare.png b/pelican/tests/output/custom_locale/theme/images/icons/slideshare.png deleted file mode 100644 index 9cbe8588e24976076e5fc3898fa97856122f6fa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 435 zcmV;k0ZjghP)YNn`_&)?H$|J+jM18jcAeNE5840mI#38{rmUny@9DE z#Xg@8y@9DE0*}W7yWQR+m>Mc@yWPA^!~YvlZXQ7Ti(&Z??RignZicC$0;ki-+teNz zN*Qw@h$6fTDiChi(;mQdV>IdC=QLyP(`3?fGmSD0E706p2ko0(Fn89&YPG`Qa4@$> zdhWPhszyk06L$T`#EcuEoIB7dp?k0q>OF&bXs#{9!Ot=1fBXlJ={G`9+@@d!27`fI z*^>PO`g==w@1W}FBwRnS8UK$h$DgmS5K-O+o6QEJ(Z~waYBd=2dOa)_3-qUlQ$o(J zM$*}Z*m~g-OePa_IvoOeR-jNQz{PlZ6lKDV2#PRA1rmt_T+Fw|P-fhVCCq=C0AY>_ z#9}czIyxxk)H|>~8V7Uy1sn}2#;nIlxE)_f_XF?+t*xyTxm-@(N8ncJn41xc#e6|8 dAc8(0=Pod^EtOP(ARYh!002ovPDHLkV1k4J!&(3U diff --git a/pelican/tests/output/custom_locale/theme/images/icons/speakerdeck.png b/pelican/tests/output/custom_locale/theme/images/icons/speakerdeck.png deleted file mode 100644 index 7281ec48b41095180a83d71eaf0b00c85116965b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 580 zcmV-K0=xZ*P)K92#E{?_PJ+8Tth@W(+a>28uk~_0`h`<(yY|`h z@3?AaV0?I7E@5C8LL5^B7?_#m1O5GLKL6{LaQ3ZN%$$19RhM^ilM4(QGb91Pk}#td z%#5L?Rn9&W7zkqrdeL$z%L))wjb8cZK2wtEL}mE*O&6H3%~&#zdHx68i}xJQZ+34dIdgm^E12V4($L!Sax!Q)0op z%%0y@llS*=IU_?ugf;+>a;DdHv`xcYwwSu^B5bgwWG8rbaUIq4<$P}6+C9-V(ZSGy!i4XI zBkkxgITji#coxBn7F*@>CvnH5Im-dI=x{a@nz|+mZz(oB8CqTLuD=F=p|2qrpWMi5G S=mtXo0000YRoeQcY09>#1xxKwS5F z8mlP6JGx3wQgxp~4T-?1`Rz0oPN3$iK>QzwU%sBkR`YrWi#XVjbZs+WxcmSb0KzU< z4R|<_pXuFnCY5*7ncLn>WBm*@=rz!g=9fLZ0#9?*eV!!i{{b4*NYE=UCUZ&x4QhHf zjrr}HsjUBBPiJ}aY9eRTvkFsE%bi<+hJ_q$)(X1QD;kN@ z$%`wz`8QVB1Z}9WYXRb&Kzt8~{{vn2e{DI?z%r-*E6ZK%Ff``oJb76HYP@>p``(Y0Zs!*!BjN>0I%aNVnQ8)X9Pc^Y zIsS936mToi>>TyPEkcc+2 z`5!z^ZFZ_O#v*`YvS&^RN+0_pGFvTxh}Lp2wqGagAdVyz;6#9udAenJvhWwC3QjN} ztg*<~69%xm!G(wSxK?V6H7%K70|sCIX&%<6rLrHmSw|10dx_{@95hn1(i_a}5^hC>VT?7YQHpjC*P~ez&J%_J0000< KMNUMnLSTY<*|#qM diff --git a/pelican/tests/output/custom_locale/theme/images/icons/vimeo.png b/pelican/tests/output/custom_locale/theme/images/icons/vimeo.png deleted file mode 100644 index 4b9d72127c574237090a2f5f2f7eb7be94a2b62e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 349 zcmV-j0iyniP)K~#7FV*mo~S^J1YP}SCZ?(wd@Mi2p&f%E_|kV7PbYe1Nxz5O|y=nOth{(bt=TS6pPua;?J=e5) zBGkM)Pk5GtRBXBib(QPnRVW%@oC6PDyoRVi_QQr_=YS?+52c0sPeBv`1LxxFPe2CH z8{$R%ID<81>CXSCz@?@iUpz?mZ$MU^)He_47^%Sg0P#sgK~#7Fl~6T81W^oq2cX(5aX1R@6<62}xdAmT6>b%79aG~T97?pp zUBiU8?wcQZ@ytL(4yKyhs>_L_h@KZq&4RGqy&kuUuOC&N_n)+)st0x|+@BHFkRd@&PPEwb z&tB6>xX7cYComKsCfH0jmXR=>tbysq2Gd0WOmZB{y88Z3Wj9^}w-Gbm)y>{S4W9T7 zfp3d!a2x}~TiXWa<}BOO6S3ho2QJ^`nhs+IjSU!vHHb*~-y}#B5s^RLsrl%w&%5IQ O0000UqK~#7FjgmEk6+sXNzkTlh65QS0-8I48iI50)i1CK$|J3O zMSiddRs94I-RZ%BpPVQlG-*wE)^@pKur#bMkyS)ii4hvVB0PPwtX?$Lk2sgu3yxdr zMHBD`H`aw%Ysl&kiS1h=1)tbo!~Ye`fCpjdO!(yU+ZHs&^bQiZX+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@*Jzj349X=?9_$gI2kIcwdunCZtMPOnPe)w(oZZmY=P7Ks9%FQb&p8>E zTGTu^G&~wGL&GC!#VuGydpI#q;n4F`-2p*w5hggKWfvm1-Ht!-D0%;*r0sX&mtTmS z=#a1o5c3rNC06S#lEEUl4ZT(7hJXc%h^4bgLQ=J-kX6=dv2qWx(KFxw<| z8p~uAa?0C01wO*eV=5{z+2r5xRmC+O#kC#3;ww#bTsi?_p2FbJ$cF2&dW*;dEIS`r z-{$NC7APVX0b-s4bL>@`-m2iuJf@Y`cU}9I16ZJlSOkdq3%}wjQF(Q}Mc#EUA+xj< z?7W)*1-w0hpaS1pO{?AaFnQ`>1)C;df$n>lghe2z(B3_8<9lvWK~rg6XL?CXWL7;$ zD`|ly6*S-encvkr7*yZ~ue};=vMZZ!#$EfG^I9zdEKo!aRp=d<^B=Mdj&Pkt)pJqZACTW#!hM@D5IKDqq;%eMz!tbv+awqP8IHt=!XVS zsZ`n>$2lgOwFdh8dv^BsZ>()0em94EiuYK~#M+HZNki~hy!n`DO0RgV7kx83G-6kVzbw2peZ^$T0XF+WR zfC!}kbTrMykUD-`pyq(>!2GmFl&qi|8BD#Yimm6&NW;(o1*9a20zlURQu0~F;$lpk zC^lANi08=`Jt9})*0WlBM`^dLR$E*Uq-h9fXc|BQRMFBY=8w1tql8shMigC06q$^` z3o;x$Brcc5Vx|EggvrrZTv~ueFR9S5n?9&@2ra;ZlJa{y#y*#;tE-3i@BjIXTFqx< zWQM^b2eIvt(ZSC+IB^l#ZO?Jct~%A$Zme8|ylK!@D6=^GmdF`z&B`yo_%8qUJKqup zI`#T8-~s^<(cM|*r>_LNwibZ|C;c5580x>Z^zql9MEVifl5$XO00aq!?ra*2Oy6Bcd7s& z4E&Wd&NypYm|1Hm3>yT#OOnPY6%hC_U@Jle48x$&@MzjDei*iPb+`A(ty}jT{_VFp0@p^D9%vpdEdoQ&A~UVr|G*1s z*N$#`bZl0nsoeDaV0>!ow??I6E} zTD@V0|CC##a5uK^=-$j1%KE$Sep%nY|D6y3VDFw|_#0Q*b=;R(Ji7n@002ovPDHLk FV1ku*k$3ca`voa6f0k zeB{69k@N|?Km7UBZZ))YUHu=w|Mw?(E)O$?WixY_FPL7s$Pm-8fstds@^ATL%bUet yZgfGChJzsUtA%$MR;8+S88GxaEa+z7`Ew3v5re0zpUXO@geCxdur~kz diff --git a/pelican/themes/notmyidea/static/images/icons/github.png b/pelican/themes/notmyidea/static/images/icons/github.png deleted file mode 100644 index 5d9109deab2b3669c845845515f4d53ab5d6daa6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 606 zcmV-k0-^nhP)3g@6&i?1|w)NXSf84|8m&o+0 zUbR{k48>ZU#v6!wqi1V1r%EE$MYAT@gfEZ`ebJgG`3x?P75EOx(Rb}pK9^g49TLfP zG|6;$SGbAH zWw$>tFUyCTTU=}VL5$UQcykD>ruLPAMktgpS2)vHd2`;>>D~O#r0q^piwN%{Eu7xl ze_3-g6EyPr+mZ( z$~zD9`4D^$F>WOyU!f<&c%QI`>dHS@;3~xO7I+5=MISM;>bsLPrcC8T?Gyg2EhYt{Z#{?9|afq=U$pRKgVkaa6A5hOW zm~E0;qoMsELHiVUdq+qHW_!=pLlCuB&nsXkrlThcr&yqB2Ez}bh}WQodCMG=62#-T(jq diff --git a/pelican/themes/notmyidea/static/images/icons/gitorious.png b/pelican/themes/notmyidea/static/images/icons/gitorious.png deleted file mode 100644 index a6705d0f462b505be4d9a5a372743d206c141a11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!O@L2`D?^o)dRwqYcev@q#DMvQ z*(*AHH`kW0om8=NO8&uVRmbKn*uQ@5(H+Zf?%R0x*ww37FTJ_{>eZ{eAD{pK|9`68 zWgnn9ah@)YAr*|ZCyp{U2QWBn{C>Cgf8^^ph2sKmrZ@0Db>47eliif`GZN3kOR|4E zp1HSr>(=i-J2K=SnYPGB*}QnjI%8UJ(Sx8gTe~DWM4fr0!yB diff --git a/pelican/themes/notmyidea/static/images/icons/gittip.png b/pelican/themes/notmyidea/static/images/icons/gittip.png deleted file mode 100644 index b9f67aaa32a0acd166d7271b3dbf9113717d3f00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMfoB*E?S0J6}W0T-*4J5$~5D8`b z*u=V-n;Wh0%9W86%yoICmD^QXBJ+TuMeduj_mynB29_Kl1X*A2_&{r~^}-@kve zdYW2FGLG%r8Ru!aX~lxyzki)LushDvqC7Rcx2`zG-R$h~L;wH%fBfKXl#6k)zx}VD zKk}k|sxxDHYKs!Qtrkt~nbKapYTmR2Z<_=!t4|TTzXRRJSrX(I%%C#k)~^Ns|6P3T zwz~@CcuyC{kP61s^QK8h9R!*aGdC-5mQ6dy==6QR=MEp?G>PQz`~2?eFFMS(r+K1g zp!&qb32C!ZG_9s->U*tRla<=$@$cY?|5H+adAMETx@7!MusHjBPl?KXziQ+6?1D=V ze?2)-gt;N$>Wd8<>nsYyl^Lt{Fxgl%o_?@o{T-$+24C7lzi+*sf1s9QvY5Z$XS=Iy lUyMDUTPOc|vvcNeU4iM^B0ctPia>WVc)I$ztaD0e0syFMv7G<_ diff --git a/pelican/themes/notmyidea/static/images/icons/google-groups.png b/pelican/themes/notmyidea/static/images/icons/google-groups.png deleted file mode 100644 index bbd0a0fd41295e94081103efe82742a5c6411765..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 420 zcmV;V0bBlwP)wr?kW++e@xB+qT~SFYEF=HvR&ieDr#i-_xj} zRHo4+e;!+Noelr;cc?77Y;{c*CNr6Zes7m1^J^+M9sV--D^!{k1_1#;6aW?Y?FKU> z$wUFckfr-O)R-Be!K8BMN$?ljo<0akOpD)OEKI~9+x!b$boZC|3lK-`{C{xHEuU!d zCn!ydf)v}GF)B$#1SpIDL5ep&8ms%oIbw(Zf<>Eu zV{!X$x!~%@{^9sNR&mx%AN_fQnat#ZJ3j~K-$A9Q{GPt1rQRSFm;ow5sT2?@^QR4r zg$anDgeCx$iqb6h2iG6)ockYoL!0v;mgd!$p2~_@_rOO_1Na`l%Wwj6hQdz( O0000oJ)blrcs(C@6ijHhGjL zrFRi_0z}#gh+z_?jx?ulJ6ZEDq_MICdrx(I>4q_hBh(7t5P3nx z6N?eUwA{x^#MIz^w4#;vg#Q`{-|sTaZm+z|gUicmu%q_q_{S4K=37O~-YZFy$l zCeshS!0W4y4-;Rs{O>veK>w%APq})#_~VzlN~kv#Z!>>U^FI6esN%Y~QuIs1d#~iY zS5C<<$DO!3cexVMKA`VP&KrXDnvg`iD8?-qH)mw8u{$e0Ls5IrFnpzz@8B4^@ijQjs{a4Z+iYWeG zt$5N5AMVx+R~iI;l~A9E$d3ce%4xvm2W_r2NEe$slb&w_>f!(Z002ovPDHLkV1lnz B@09=m diff --git a/pelican/themes/notmyidea/static/images/icons/hackernews.png b/pelican/themes/notmyidea/static/images/icons/hackernews.png deleted file mode 100644 index 8e05e3ee207e114a96f3811f46f68d17d52fe80b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2771 zcmV;^3M}=BP)X+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@QN3Mg3?1|NsB>i~#tuXaD5J_o76e z$B4WD001vZL_t(|0b{@m1Qi$r!3;wKW??Xc(Sbz(%wQEZ41g$d6oINX6oxVw1@JNO Z0sz-j0)=#JDs2D&002ovPDHLkV1kM1SuX$p diff --git a/pelican/themes/notmyidea/static/images/icons/lastfm.png b/pelican/themes/notmyidea/static/images/icons/lastfm.png deleted file mode 100644 index 2eedd2daa1429ba026c0b3c81f200c7df55d199a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 840 zcmV-O1GoH%P)KCJu$;;6i zN)?b2a3_#VfTsWs$6EHV5TGNlK%}w4u2N|o9mJtskW6u)FfMY|?6HLq0N~tSmCYiQ z2itn^l^G`qTae2D0xESFZh|%tXmDcRU;!-yQXzzdgu?j^sE-_`#F)I#a0AjAIORAv z?nua{5j_L2Xf^mv7?p>M&VsISuyh@C_u_&8Tdjpsq*G;#gR3rt3(wqpYJX|(pqnm- zVi_(t9gaQ}dWUeQ9S7b0nAMvJMjCC()%*h2T()P6`Q<<4KoQZ3;E)b@mqq9E^qzbJ^&kHt zb@z3sXYQl9ZY#2oD@eP!I~#uc8*V^AwupI;L7D|Jvi|(8qu0 z2LL$zBz)6?fx+w8bl#l{x;rm&{_hjrZqU60&R?r z_q^a%r%rJ@Z+x)Fd-;e{U-Iff*T}z9(5p5g3wcDXPJR3rG$+nsWZfoYCWAb*gT_Bo z7fg}|L9J&75r5f0ZeI>gIn5MAw<$RbKrq)K4wK5le| zR(k=@SlVyBWV*`avXNJ2NP$f#q_(8G+6X;MtJy>;4`+;2 znIjMBZmisPo~Hw3I)yO?atTx+i^wMNq>KkRoaf=R#1u=&Mhm*)MMoa?yQ=3?Z|U=o zU9OHh!itfmFwG$0g0M5@al}`~v399M8f%oORSV+K_KnG|j?V;kMf)2sF5LyF&y{nJ Sd8X6=0000 diff --git a/pelican/themes/notmyidea/static/images/icons/linkedin.png b/pelican/themes/notmyidea/static/images/icons/linkedin.png deleted file mode 100644 index 06a88016f191d1f7596e5d159eb9e3ef276b6ad4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 625 zcmV-%0*?KOP)NJD$VFG5yq%r(;8zR}7Ydty&~ue@oPv z!go{cG`vvH)n(hYdJfur_4kM7-}`N$BLW71f|-O65QELNn>4;$_2zi#L7VQdUf*VG zc0B*xR8@Sfo>nmHN}+MDnqn5J#;f9g$TZW=OS!!C<4lU-jh|=cs;0XYW~;nhJEE0r zdMWpJ#bDNMT&}cGH>t7P-yM4W=hFCXaLv`alDWtkCwY0;!z58#z z#&T71?c1;2yXPjW|3Lt-K|fa>vgM24P7nS%my|*-?u{?k1z4y`Q@qLr7+I)}xOVW2 zJ3sxy0Crrjx2{T+o3K*EU*7;Otu&GVZhd>`xi2Rl`h0wRu?9M0pr|e$T%t()-9X(G zM?fxx1SUyc#XyCAwXc5_{TIPFaU$>TzBm6pmt%Vs1;7F00000 LNkvXXu0mjfHfAMI diff --git a/pelican/themes/notmyidea/static/images/icons/reddit.png b/pelican/themes/notmyidea/static/images/icons/reddit.png deleted file mode 100644 index d826d3e746a18b712db7a68746c0c523af802fe0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458 zcmV;*0X6=KP)rn|{qWNU7Dgl~R~ zWNK}Sjg6(Fp|rEIpPrq2fPh0AJ zxy#GTfq{X3etxmY-Bn&^YHMt-t*g_~&)L`2qqxp^h?r+{fMacVpMN&`0001qNklWKP}*}Is^$| zQ}1hfWmjT2av-PYqbE!!n)oua2c5|Qn7a$5l#p|w6z7}`F{d?z0T5WQglfOLER+{E--Hu?5@wDUpV?F@=Mkg+ zN{1=uqDlzrnkbYp)J3q!I?Vn|_~M zVqi53FAJQ^xo7*4+R80OflZT^0WscTaBuO7ca6A_aA-H9M3Y@{DSLGBL)| zTym{{me}!*Z3962jmy>w<<#@)IsY9R%I3sYbB>~@mM@C5Ks~s4nX-}TW8S>D*g7F! hew!yYJnD{z;D0NLV2ni3qF(?2002ovPDHLkV1k{La&G_t diff --git a/pelican/themes/notmyidea/static/images/icons/slideshare.png b/pelican/themes/notmyidea/static/images/icons/slideshare.png deleted file mode 100644 index 9cbe8588e24976076e5fc3898fa97856122f6fa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 435 zcmV;k0ZjghP)YNn`_&)?H$|J+jM18jcAeNE5840mI#38{rmUny@9DE z#Xg@8y@9DE0*}W7yWQR+m>Mc@yWPA^!~YvlZXQ7Ti(&Z??RignZicC$0;ki-+teNz zN*Qw@h$6fTDiChi(;mQdV>IdC=QLyP(`3?fGmSD0E706p2ko0(Fn89&YPG`Qa4@$> zdhWPhszyk06L$T`#EcuEoIB7dp?k0q>OF&bXs#{9!Ot=1fBXlJ={G`9+@@d!27`fI z*^>PO`g==w@1W}FBwRnS8UK$h$DgmS5K-O+o6QEJ(Z~waYBd=2dOa)_3-qUlQ$o(J zM$*}Z*m~g-OePa_IvoOeR-jNQz{PlZ6lKDV2#PRA1rmt_T+Fw|P-fhVCCq=C0AY>_ z#9}czIyxxk)H|>~8V7Uy1sn}2#;nIlxE)_f_XF?+t*xyTxm-@(N8ncJn41xc#e6|8 dAc8(0=Pod^EtOP(ARYh!002ovPDHLkV1k4J!&(3U diff --git a/pelican/themes/notmyidea/static/images/icons/speakerdeck.png b/pelican/themes/notmyidea/static/images/icons/speakerdeck.png deleted file mode 100644 index 7281ec48b41095180a83d71eaf0b00c85116965b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 580 zcmV-K0=xZ*P)K92#E{?_PJ+8Tth@W(+a>28uk~_0`h`<(yY|`h z@3?AaV0?I7E@5C8LL5^B7?_#m1O5GLKL6{LaQ3ZN%$$19RhM^ilM4(QGb91Pk}#td z%#5L?Rn9&W7zkqrdeL$z%L))wjb8cZK2wtEL}mE*O&6H3%~&#zdHx68i}xJQZ+34dIdgm^E12V4($L!Sax!Q)0op z%%0y@llS*=IU_?ugf;+>a;DdHv`xcYwwSu^B5bgwWG8rbaUIq4<$P}6+C9-V(ZSGy!i4XI zBkkxgITji#coxBn7F*@>CvnH5Im-dI=x{a@nz|+mZz(oB8CqTLuD=F=p|2qrpWMi5G S=mtXo0000YRoeQcY09>#1xxKwS5F z8mlP6JGx3wQgxp~4T-?1`Rz0oPN3$iK>QzwU%sBkR`YrWi#XVjbZs+WxcmSb0KzU< z4R|<_pXuFnCY5*7ncLn>WBm*@=rz!g=9fLZ0#9?*eV!!i{{b4*NYE=UCUZ&x4QhHf zjrr}HsjUBBPiJ}aY9eRTvkFsE%bi<+hJ_q$)(X1QD;kN@ z$%`wz`8QVB1Z}9WYXRb&Kzt8~{{vn2e{DI?z%r-*E6ZK%Ff``oJb76HYP@>p``(Y0Zs!*!BjN>0I%aNVnQ8)X9Pc^Y zIsS936mToi>>TyPEkcc+2 z`5!z^ZFZ_O#v*`YvS&^RN+0_pGFvTxh}Lp2wqGagAdVyz;6#9udAenJvhWwC3QjN} ztg*<~69%xm!G(wSxK?V6H7%K70|sCIX&%<6rLrHmSw|10dx_{@95hn1(i_a}5^hC>VT?7YQHpjC*P~ez&J%_J0000< KMNUMnLSTY<*|#qM diff --git a/pelican/themes/notmyidea/static/images/icons/vimeo.png b/pelican/themes/notmyidea/static/images/icons/vimeo.png deleted file mode 100644 index 4b9d72127c574237090a2f5f2f7eb7be94a2b62e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 349 zcmV-j0iyniP)K~#7FV*mo~S^J1YP}SCZ?(wd@Mi2p&f%E_|kV7PbYe1Nxz5O|y=nOth{(bt=TS6pPua;?J=e5) zBGkM)Pk5GtRBXBib(QPnRVW%@oC6PDyoRVi_QQr_=YS?+52c0sPeBv`1LxxFPe2CH z8{$R%ID<81>CXSCz@?@iUpz?mZ$MU^)He_47^%Sg0P#sgK~#7Fl~6T81W^oq2cX(5aX1R@6<62}xdAmT6>b%79aG~T97?pp zUBiU8?wcQZ@ytL(4yKyhs>_L_h@KZq&4RGqy&kuUuOC&N_n)+)st0x|+@BHFkRd@&PPEwb z&tB6>xX7cYComKsCfH0jmXR=>tbysq2Gd0WOmZB{y88Z3Wj9^}w-Gbm)y>{S4W9T7 zfp3d!a2x}~TiXWa<}BOO6S3ho2QJ^`nhs+IjSU!vHHb*~-y}#B5s^RLsrl%w&%5IQ O0000 Date: Sat, 4 Nov 2023 01:00:51 +0300 Subject: [PATCH 330/465] add notmyidea font license --- .../theme/fonts/Yanone_Kaffeesatz_LICENSE.txt | 93 +++++++++++++++++++ .../theme/fonts/Yanone_Kaffeesatz_LICENSE.txt | 93 +++++++++++++++++++ .../theme/fonts/Yanone_Kaffeesatz_LICENSE.txt | 93 +++++++++++++++++++ .../fonts/Yanone_Kaffeesatz_LICENSE.txt | 93 +++++++++++++++++++ 4 files changed, 372 insertions(+) create mode 100644 pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt create mode 100644 pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt create mode 100644 pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt create mode 100644 pelican/themes/notmyidea/static/fonts/Yanone_Kaffeesatz_LICENSE.txt diff --git a/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt b/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt new file mode 100644 index 00000000..309fd710 --- /dev/null +++ b/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2010 The Yanone Kaffeesatz Project Authors (https://github.com/alexeiva/yanone-kaffeesatz) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt b/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt new file mode 100644 index 00000000..309fd710 --- /dev/null +++ b/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2010 The Yanone Kaffeesatz Project Authors (https://github.com/alexeiva/yanone-kaffeesatz) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt b/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt new file mode 100644 index 00000000..309fd710 --- /dev/null +++ b/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2010 The Yanone Kaffeesatz Project Authors (https://github.com/alexeiva/yanone-kaffeesatz) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/pelican/themes/notmyidea/static/fonts/Yanone_Kaffeesatz_LICENSE.txt b/pelican/themes/notmyidea/static/fonts/Yanone_Kaffeesatz_LICENSE.txt new file mode 100644 index 00000000..c70bcad3 --- /dev/null +++ b/pelican/themes/notmyidea/static/fonts/Yanone_Kaffeesatz_LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2010 The Yanone Kaffeesatz Project Authors (https://github.com/alexeiva/yanone-kaffeesatz) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. From 6059675d55d0abbb2bcba73f365d88473fb8029f Mon Sep 17 00:00:00 2001 From: Paolo Melchiorre Date: Sat, 11 Nov 2023 14:10:08 +0100 Subject: [PATCH 331/465] Fix #3233 -- Simple theme classless semantic HTML --- .gitignore | 1 + docs/settings.rst | 6 ++++ .../custom/author/alexis-metaireau.html | 12 ++++--- .../custom/author/alexis-metaireau2.html | 16 ++++++---- .../custom/author/alexis-metaireau3.html | 12 ++++--- pelican/tests/output/custom/index.html | 12 ++++--- pelican/tests/output/custom/index2.html | 16 ++++++---- pelican/tests/output/custom/index3.html | 12 ++++--- .../author/alexis-metaireau.html | 12 ++++--- .../author/alexis-metaireau2.html | 16 ++++++---- .../author/alexis-metaireau3.html | 12 ++++--- pelican/tests/output/custom_locale/index.html | 12 ++++--- .../tests/output/custom_locale/index2.html | 16 ++++++---- .../tests/output/custom_locale/index3.html | 12 ++++--- pelican/themes/simple/templates/archives.html | 2 +- pelican/themes/simple/templates/article.html | 32 +++++++++---------- pelican/themes/simple/templates/author.html | 2 +- pelican/themes/simple/templates/authors.html | 2 +- pelican/themes/simple/templates/base.html | 18 +++++++---- .../themes/simple/templates/categories.html | 2 +- pelican/themes/simple/templates/category.html | 2 +- pelican/themes/simple/templates/index.html | 25 +++++++-------- pelican/themes/simple/templates/page.html | 8 ++++- .../themes/simple/templates/pagination.html | 16 ++++++---- .../simple/templates/period_archives.html | 2 +- pelican/themes/simple/templates/tag.html | 2 +- pelican/themes/simple/templates/tags.html | 2 +- 27 files changed, 163 insertions(+), 119 deletions(-) diff --git a/.gitignore b/.gitignore index b27f3eb9..473efea2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ samples/output *.pem *.lock .pdm-python +.venv diff --git a/docs/settings.rst b/docs/settings.rst index a7768514..88a32d23 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -1231,6 +1231,12 @@ Following are example ways to specify your preferred theme:: # Specify a customized theme, via absolute path THEME = "/home/myuser/projects/mysite/themes/mycustomtheme" +The built-in ``simple`` theme can be customized using the following settings. + +.. data:: STYLESHEET_URL + + The URL of the stylesheet to use. + The built-in ``notmyidea`` theme can make good use of the following settings. Feel free to use them in your themes as well. diff --git a/pelican/tests/output/custom/author/alexis-metaireau.html b/pelican/tests/output/custom/author/alexis-metaireau.html index a9c73e6b..aef8c6e6 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau.html +++ b/pelican/tests/output/custom/author/alexis-metaireau.html @@ -120,11 +120,13 @@

    There are comments.

    -

    - Page 1 / 3 - » - -

    +
    diff --git a/pelican/tests/output/custom/author/alexis-metaireau2.html b/pelican/tests/output/custom/author/alexis-metaireau2.html index 41f00605..8d17eed5 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom/author/alexis-metaireau2.html @@ -133,13 +133,15 @@ YEAH !

    There are comments.

    -

    - - « - Page 2 / 3 - » - -

    +
    diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index 45a5f0d1..48fe75ba 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -85,11 +85,13 @@ pelican.conf, it will …

    There are comments.

    -

    - - « - Page 3 / 3 -

    +
    diff --git a/pelican/tests/output/custom/index.html b/pelican/tests/output/custom/index.html index ceb6e4f5..6c4d7a94 100644 --- a/pelican/tests/output/custom/index.html +++ b/pelican/tests/output/custom/index.html @@ -120,11 +120,13 @@

    There are comments.

    -

    - Page 1 / 3 - » - -

    +
    diff --git a/pelican/tests/output/custom/index2.html b/pelican/tests/output/custom/index2.html index ddd4e96e..043f8c33 100644 --- a/pelican/tests/output/custom/index2.html +++ b/pelican/tests/output/custom/index2.html @@ -133,13 +133,15 @@ YEAH !

    There are comments.

    -

    - - « - Page 2 / 3 - » - -

    +
    diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index 698139a0..f8ebaac0 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -85,11 +85,13 @@ pelican.conf, it will …

    There are comments.

    -

    - - « - Page 3 / 3 -

    +
    diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau.html b/pelican/tests/output/custom_locale/author/alexis-metaireau.html index 41a21a98..df76ccf6 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau.html @@ -120,11 +120,13 @@

    There are comments.

    -

    - Page 1 / 3 - » - -

    +
    diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html index 6412784f..42a929d0 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html @@ -133,13 +133,15 @@ YEAH !

    There are comments.

    -

    - - « - Page 2 / 3 - » - -

    +
    diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html index 2679b0a6..941cdc46 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html @@ -85,11 +85,13 @@ pelican.conf, it will …

    There are comments.

    -

    - - « - Page 3 / 3 -

    +
    diff --git a/pelican/tests/output/custom_locale/index.html b/pelican/tests/output/custom_locale/index.html index 4a661093..054011cc 100644 --- a/pelican/tests/output/custom_locale/index.html +++ b/pelican/tests/output/custom_locale/index.html @@ -120,11 +120,13 @@

    There are comments.

    -

    - Page 1 / 3 - » - -

    +
    diff --git a/pelican/tests/output/custom_locale/index2.html b/pelican/tests/output/custom_locale/index2.html index 60fee085..fa2c4d2d 100644 --- a/pelican/tests/output/custom_locale/index2.html +++ b/pelican/tests/output/custom_locale/index2.html @@ -133,13 +133,15 @@ YEAH !

    There are comments.

    -

    - - « - Page 2 / 3 - » - -

    +
    diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html index 76597e82..71ccb4b1 100644 --- a/pelican/tests/output/custom_locale/index3.html +++ b/pelican/tests/output/custom_locale/index3.html @@ -85,11 +85,13 @@ pelican.conf, it will …

    There are comments.

    -

    - - « - Page 3 / 3 -

    +
    diff --git a/pelican/themes/simple/templates/archives.html b/pelican/themes/simple/templates/archives.html index b7754c45..c7fb2127 100644 --- a/pelican/themes/simple/templates/archives.html +++ b/pelican/themes/simple/templates/archives.html @@ -3,7 +3,7 @@ {% block title %}{{ SITENAME|striptags }} - Archives{% endblock %} {% block content %} -

    Archives for {{ SITENAME }}

    +

    Archives for {{ SITENAME }}

    {% for article in dates %} diff --git a/pelican/themes/simple/templates/article.html b/pelican/themes/simple/templates/article.html index a17f2759..07e5534d 100644 --- a/pelican/themes/simple/templates/article.html +++ b/pelican/themes/simple/templates/article.html @@ -22,44 +22,44 @@ {% endblock %} {% block content %} +
    -

    +

    {{ article.title }}

    + title="Permalink to {{ article.title|striptags }}">{{ article.title }} {% import 'translations.html' as translations with context %} {{ translations.translations_for(article) }}
    -
    -
    {% endblock %} diff --git a/pelican/themes/simple/templates/author.html b/pelican/themes/simple/templates/author.html index c054f8ab..9b30dfe2 100644 --- a/pelican/themes/simple/templates/author.html +++ b/pelican/themes/simple/templates/author.html @@ -3,5 +3,5 @@ {% block title %}{{ SITENAME|striptags }} - Articles by {{ author }}{% endblock %} {% block content_title %} -

    Articles by {{ author }}

    +

    Articles by {{ author }}

    {% endblock %} diff --git a/pelican/themes/simple/templates/authors.html b/pelican/themes/simple/templates/authors.html index 9b80b499..01b4f6f1 100644 --- a/pelican/themes/simple/templates/authors.html +++ b/pelican/themes/simple/templates/authors.html @@ -3,7 +3,7 @@ {% block title %}{{ SITENAME|striptags }} - Authors{% endblock %} {% block content %} -

    Authors on {{ SITENAME }}

    +

    Authors on {{ SITENAME }}

      {% for author, articles in authors|sort %}
    • {{ author }} ({{ articles|count }})
    • diff --git a/pelican/themes/simple/templates/base.html b/pelican/themes/simple/templates/base.html index 94a16930..e006cba1 100644 --- a/pelican/themes/simple/templates/base.html +++ b/pelican/themes/simple/templates/base.html @@ -6,6 +6,12 @@ + {% if SITESUBTITLE %} + + {% endif %} + {% if STYLESHEET_URL %} + + {% endif %} {% if FEED_ALL_ATOM %} {% endif %} @@ -35,32 +41,32 @@
      -

      {{ SITENAME }}{% if SITESUBTITLE %} {{ SITESUBTITLE }}{% endif %}

      -
      +

      {{ SITENAME }}

      {% if SITESUBTITLE %}

      {{ SITESUBTITLE }}

      {% endif %}
      +
      {% block content %} {% endblock %}
      -
      +
      Proudly powered by Pelican, which takes great advantage of Python. -
      +
      diff --git a/pelican/themes/simple/templates/categories.html b/pelican/themes/simple/templates/categories.html index f099e88f..7da19fb4 100644 --- a/pelican/themes/simple/templates/categories.html +++ b/pelican/themes/simple/templates/categories.html @@ -3,7 +3,7 @@ {% block title %}{{ SITENAME|striptags }} - Categories{% endblock %} {% block content %} -

      Categories on {{ SITENAME }}

      +

      Categories on {{ SITENAME }}

        {% for category, articles in categories|sort %}
      • {{ category }} ({{ articles|count }})
      • diff --git a/pelican/themes/simple/templates/category.html b/pelican/themes/simple/templates/category.html index da1a8b52..16525fb2 100644 --- a/pelican/themes/simple/templates/category.html +++ b/pelican/themes/simple/templates/category.html @@ -3,5 +3,5 @@ {% block title %}{{ SITENAME|striptags }} - {{ category }} category{% endblock %} {% block content_title %} -

        Articles in the {{ category }} category

        +

        Articles in the {{ category }} category

        {% endblock %} diff --git a/pelican/themes/simple/templates/index.html b/pelican/themes/simple/templates/index.html index ab4bc345..c9837b54 100644 --- a/pelican/themes/simple/templates/index.html +++ b/pelican/themes/simple/templates/index.html @@ -1,28 +1,27 @@ {% extends "base.html" %} {% block content %} -
        {% block content_title %}

        All articles

        {% endblock %} -
          + {% for article in articles_page.object_list %} -
        1. + + {% endfor %} -
        + {% if articles_page.has_other_pages() %} {% include 'pagination.html' %} {% endif %} -
        + {% endblock content %} diff --git a/pelican/themes/simple/templates/page.html b/pelican/themes/simple/templates/page.html index eea816a9..38452d1d 100644 --- a/pelican/themes/simple/templates/page.html +++ b/pelican/themes/simple/templates/page.html @@ -13,15 +13,21 @@ {% endblock %} {% block content %} -

        {{ page.title }}

        +
        +
        +

        {{ page.title }}

        +
        {% import 'translations.html' as translations with context %} {{ translations.translations_for(page) }} {{ page.content }} {% if page.modified %} +

        Last updated: {{ page.locale_modified }}

        +
        {% endif %} +
        {% endblock %} diff --git a/pelican/themes/simple/templates/pagination.html b/pelican/themes/simple/templates/pagination.html index 588f130c..45e7f167 100644 --- a/pelican/themes/simple/templates/pagination.html +++ b/pelican/themes/simple/templates/pagination.html @@ -1,15 +1,17 @@ {% if DEFAULT_PAGINATION %} {% set first_page = articles_paginator.page(1) %} {% set last_page = articles_paginator.page(articles_paginator.num_pages) %} -

        +

        {% endif %} diff --git a/pelican/themes/simple/templates/period_archives.html b/pelican/themes/simple/templates/period_archives.html index 9cdc354d..595def50 100644 --- a/pelican/themes/simple/templates/period_archives.html +++ b/pelican/themes/simple/templates/period_archives.html @@ -3,7 +3,7 @@ {% block title %}{{ SITENAME|striptags }} - {{ period | reverse | join(' ') }} archives{% endblock %} {% block content %} -

        Archives for {{ period | reverse | join(' ') }}

        +

        Archives for {{ period | reverse | join(' ') }}

        {% for article in dates %} diff --git a/pelican/themes/simple/templates/tag.html b/pelican/themes/simple/templates/tag.html index 59725a05..f9b71f48 100644 --- a/pelican/themes/simple/templates/tag.html +++ b/pelican/themes/simple/templates/tag.html @@ -3,5 +3,5 @@ {% block title %}{{ SITENAME|striptags }} - {{ tag }} tag{% endblock %} {% block content_title %} -

        Articles tagged with {{ tag }}

        +

        Articles tagged with {{ tag }}

        {% endblock %} diff --git a/pelican/themes/simple/templates/tags.html b/pelican/themes/simple/templates/tags.html index 92c142d2..6b5c6b1c 100644 --- a/pelican/themes/simple/templates/tags.html +++ b/pelican/themes/simple/templates/tags.html @@ -3,7 +3,7 @@ {% block title %}{{ SITENAME|striptags }} - Tags{% endblock %} {% block content %} -

        Tags for {{ SITENAME }}

        +

        Tags for {{ SITENAME }}

          {% for tag, articles in tags|sort %}
        • {{ tag }} ({{ articles|count }})
        • From 39ff56a08260f057538cbb2e39e147bffacdf357 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 12 Nov 2023 13:38:30 +0100 Subject: [PATCH 332/465] Update development dependencies --- .pre-commit-config.yaml | 2 +- pyproject.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5a73aebc..333bc3c0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: forbid-new-submodules - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.3 + rev: v0.1.5 hooks: - id: ruff - id: ruff-format diff --git a/pyproject.toml b/pyproject.toml index 816a25f3..03ba81b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,7 +80,7 @@ dev = [ "BeautifulSoup4>=4.12.2", "jinja2>=3.1.2", "lxml>=4.9.3", - "markdown>=3.5", + "markdown>=3.5.1", "typogrify>=2.0.7", "sphinx>=7.1.2", "sphinxext-opengraph>=0.9.0", @@ -91,10 +91,10 @@ dev = [ "pytest>=7.4.3", "pytest-cov>=4.1.0", "pytest-sugar>=0.9.7", - "pytest-xdist>=3.3.1", + "pytest-xdist>=3.4.0", "tox>=4.11.3", "invoke>=2.2.0", - "ruff>=0.1.3", + "ruff>=0.1.5", "tomli>=2.0.1; python_version < \"3.11\"", ] From 903ce3ce33d614dfaa39a55db96b7ec44fc7df43 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 12 Nov 2023 13:41:38 +0100 Subject: [PATCH 333/465] Pin Furo doc theme version We override its page.html template with our own, so it is better to manually and explicity upgrade rather than have a future version of the Furo theme potentially break the documentation build. --- pyproject.toml | 2 +- requirements/docs.pip | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 03ba81b4..d40e6bce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ dev = [ "typogrify>=2.0.7", "sphinx>=7.1.2", "sphinxext-opengraph>=0.9.0", - "furo>=2023.9.10", + "furo==2023.9.10", "livereload>=2.6.3", "psutil>=5.9.6", "pygments>=2.16.1", diff --git a/requirements/docs.pip b/requirements/docs.pip index 961a6473..7b0f37cc 100644 --- a/requirements/docs.pip +++ b/requirements/docs.pip @@ -1,5 +1,5 @@ -sphinx<6.0 +sphinx sphinxext-opengraph -furo +furo==2023.9.10 livereload tomli;python_version<"3.11" From ecd598f293161a52564aa6e8dfdcc8284dc93970 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 12 Nov 2023 13:53:02 +0100 Subject: [PATCH 334/465] Update code base for Python 3.8 and above Result of: pipx run pyupgrade --py38-plus pelican/**/*.py --- pelican/__init__.py | 6 ++---- pelican/contents.py | 2 +- pelican/paginator.py | 2 +- pelican/plugins/_utils.py | 8 +++---- pelican/readers.py | 16 +++++++------- pelican/settings.py | 6 +++--- pelican/tests/build_test/test_build_files.py | 2 +- pelican/tests/support.py | 2 +- pelican/tests/test_readers.py | 4 +--- pelican/tests/test_settings.py | 2 +- pelican/tools/pelican_import.py | 8 +++---- pelican/tools/pelican_quickstart.py | 22 ++++++++++---------- pelican/tools/pelican_themes.py | 12 +++++------ pelican/urlwrappers.py | 8 +++---- pelican/utils.py | 22 +++++++++----------- 15 files changed, 58 insertions(+), 64 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index a0ff4989..25c493b9 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -273,7 +273,7 @@ class PrintSettings(argparse.Action): ) ) else: - console.print("\n{} is not a recognized setting.".format(setting)) + console.print(f"\n{setting} is not a recognized setting.") break else: # No argument was given to --print-settings, so print all settings @@ -611,9 +611,7 @@ def listen(server, port, output, excqueue=None): return try: - console.print( - "Serving site at: http://{}:{} - Tap CTRL-C to stop".format(server, port) - ) + console.print(f"Serving site at: http://{server}:{port} - Tap CTRL-C to stop") httpd.serve_forever() except Exception as e: if excqueue is not None: diff --git a/pelican/contents.py b/pelican/contents.py index f99e6426..474e5bbf 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -235,7 +235,7 @@ class Content: def _expand_settings(self, key, klass=None): if not klass: klass = self.__class__.__name__ - fq_key = ("{}_{}".format(klass, key)).upper() + fq_key = (f"{klass}_{key}").upper() return str(self.settings[fq_key]).format(**self.url_format) def get_url_setting(self, key): diff --git a/pelican/paginator.py b/pelican/paginator.py index 930c915b..e1d50881 100644 --- a/pelican/paginator.py +++ b/pelican/paginator.py @@ -81,7 +81,7 @@ class Page: self.settings = settings def __repr__(self): - return "".format(self.number, self.paginator.num_pages) + return f"" def has_next(self): return self.number < self.paginator.num_pages diff --git a/pelican/plugins/_utils.py b/pelican/plugins/_utils.py index c25f8114..805ed049 100644 --- a/pelican/plugins/_utils.py +++ b/pelican/plugins/_utils.py @@ -49,7 +49,7 @@ def plugin_enabled(name, plugin_list=None): # search name as is return True - if "pelican.plugins.{}".format(name) in plugin_list: + if f"pelican.plugins.{name}" in plugin_list: # check if short name is a namespace plugin return True @@ -68,7 +68,7 @@ def load_legacy_plugin(plugin, plugin_paths): # If failed, try to find it in normal importable locations spec = importlib.util.find_spec(plugin) if spec is None: - raise ImportError("Cannot import plugin `{}`".format(plugin)) + raise ImportError(f"Cannot import plugin `{plugin}`") else: # Avoid loading the same plugin twice if spec.name in sys.modules: @@ -106,8 +106,8 @@ def load_plugins(settings): # try to find in namespace plugins if plugin in namespace_plugins: plugin = namespace_plugins[plugin] - elif "pelican.plugins.{}".format(plugin) in namespace_plugins: - plugin = namespace_plugins["pelican.plugins.{}".format(plugin)] + elif f"pelican.plugins.{plugin}" in namespace_plugins: + plugin = namespace_plugins[f"pelican.plugins.{plugin}"] # try to import it else: try: diff --git a/pelican/readers.py b/pelican/readers.py index 5033c0bd..60b9765a 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -401,7 +401,7 @@ class HTMLReader(BaseReader): self._in_body = False self._in_top_level = True elif self._in_body: - self._data_buffer += "".format(escape(tag)) + self._data_buffer += f"" def handle_startendtag(self, tag, attrs): if tag == "meta" and self._in_head: @@ -410,28 +410,28 @@ class HTMLReader(BaseReader): self._data_buffer += self.build_tag(tag, attrs, True) def handle_comment(self, data): - self._data_buffer += "".format(data) + self._data_buffer += f"" def handle_data(self, data): self._data_buffer += data def handle_entityref(self, data): - self._data_buffer += "&{};".format(data) + self._data_buffer += f"&{data};" def handle_charref(self, data): - self._data_buffer += "&#{};".format(data) + self._data_buffer += f"&#{data};" def build_tag(self, tag, attrs, close_tag): - result = "<{}".format(escape(tag)) + result = f"<{escape(tag)}" for k, v in attrs: result += " " + escape(k) if v is not None: # If the attribute value contains a double quote, surround # with single quotes, otherwise use double quotes. if '"' in v: - result += "='{}'".format(escape(v, quote=False)) + result += f"='{escape(v, quote=False)}'" else: - result += '="{}"'.format(escape(v, quote=False)) + result += f'="{escape(v, quote=False)}"' if close_tag: return result + " />" return result + ">" @@ -439,7 +439,7 @@ class HTMLReader(BaseReader): def _handle_meta_tag(self, attrs): name = self._attr_value(attrs, "name") if name is None: - attr_list = ['{}="{}"'.format(k, v) for k, v in attrs] + attr_list = [f'{k}="{v}"' for k, v in attrs] attr_serialized = ", ".join(attr_list) logger.warning( "Meta tag in file %s does not have a 'name' " diff --git a/pelican/settings.py b/pelican/settings.py index 2c84b6f0..4a4f2901 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -265,7 +265,7 @@ def _printf_s_to_format_field(printf_string, format_field): format_field ) if result.format(**{format_field: TEST_STRING}) != expected: - raise ValueError("Failed to safely replace %s with {{{}}}".format(format_field)) + raise ValueError(f"Failed to safely replace %s with {{{format_field}}}") return result @@ -350,9 +350,9 @@ def handle_deprecated_settings(settings): ), ]: if old in settings: - message = "The {} setting has been removed in favor of {}".format(old, new) + message = f"The {old} setting has been removed in favor of {new}" if doc: - message += ", see {} for details".format(doc) + message += f", see {doc} for details" logger.warning(message) # PAGINATED_DIRECT_TEMPLATES -> PAGINATED_TEMPLATES diff --git a/pelican/tests/build_test/test_build_files.py b/pelican/tests/build_test/test_build_files.py index 2b51d362..9aad990d 100644 --- a/pelican/tests/build_test/test_build_files.py +++ b/pelican/tests/build_test/test_build_files.py @@ -61,6 +61,6 @@ def test_sdist_contents(pytestconfig, expected_file): filtered_values = [ path for path in files_list - if match(f"^pelican-\d\.\d\.\d/{expected_file}{dir_matcher}$", path) + if match(rf"^pelican-\d\.\d\.\d/{expected_file}{dir_matcher}$", path) ] assert len(filtered_values) > 0 diff --git a/pelican/tests/support.py b/pelican/tests/support.py index e3813849..16060441 100644 --- a/pelican/tests/support.py +++ b/pelican/tests/support.py @@ -133,7 +133,7 @@ def skipIfNoExecutable(executable): res = None if res is None: - return unittest.skip("{} executable not found".format(executable)) + return unittest.skip(f"{executable} executable not found") return lambda func: func diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index cf0f39f1..04049894 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -32,9 +32,7 @@ class ReaderTest(unittest.TestCase): % (key, value, real_value), ) else: - self.fail( - "Expected %s to have value %s, but was not in Dict" % (key, value) - ) + self.fail(f"Expected {key} to have value {value}, but was not in Dict") class TestAssertDictHasSubset(ReaderTest): diff --git a/pelican/tests/test_settings.py b/pelican/tests/test_settings.py index 0e77674d..f370f7eb 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -170,7 +170,7 @@ class TestSettingsConfiguration(unittest.TestCase): def test__printf_s_to_format_field(self): for s in ("%s", "{%s}", "{%s"): - option = "foo/{}/bar.baz".format(s) + option = f"foo/{s}/bar.baz" result = _printf_s_to_format_field(option, "slug") expected = option % "qux" found = result.format(slug="qux") diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 27102f38..681a5c45 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -41,7 +41,7 @@ def decode_wp_content(content, br=True): if start == -1: content = content + pre_part continue - name = "
          ".format(pre_index)
          +            name = f"
          "
                       pre_tags[name] = pre_part[start:] + ""
                       content = content + pre_part[0:start] + name
                       pre_index += 1
          @@ -765,7 +765,7 @@ def download_attachments(output_path, urls):
           
                   if not os.path.exists(full_path):
                       os.makedirs(full_path)
          -        print("downloading {}".format(filename))
          +        print(f"downloading {filename}")
                   try:
                       urlretrieve(url, os.path.join(full_path, filename))
                       locations[url] = os.path.join(localpath, filename)
          @@ -782,7 +782,7 @@ def is_pandoc_needed(in_markup):
           def get_pandoc_version():
               cmd = ["pandoc", "--version"]
               try:
          -        output = subprocess.check_output(cmd, universal_newlines=True)
          +        output = subprocess.check_output(cmd, text=True)
               except (subprocess.CalledProcessError, OSError) as e:
                   logger.warning("Pandoc version unknown: %s", e)
                   return ()
          @@ -898,7 +898,7 @@ def fields2pelican(
                               new_content = decode_wp_content(content)
                           else:
                               paragraphs = content.splitlines()
          -                    paragraphs = ["

          {}

          ".format(p) for p in paragraphs] + paragraphs = [f"

          {p}

          " for p in paragraphs] new_content = "".join(paragraphs) with open(html_filename, "w", encoding="utf-8") as fp: fp.write(new_content) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index fba0c9c3..db00ce70 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -90,9 +90,9 @@ def ask(question, answer=str, default=None, length=None): r = "" while True: if default: - r = input("> {} [{}] ".format(question, default)) + r = input(f"> {question} [{default}] ") else: - r = input("> {} ".format(question)) + r = input(f"> {question} ") r = r.strip() @@ -104,7 +104,7 @@ def ask(question, answer=str, default=None, length=None): print("You must enter something") else: if length and len(r) != length: - print("Entry must be {} characters long".format(length)) + print(f"Entry must be {length} characters long") else: break @@ -114,11 +114,11 @@ def ask(question, answer=str, default=None, length=None): r = None while True: if default is True: - r = input("> {} (Y/n) ".format(question)) + r = input(f"> {question} (Y/n) ") elif default is False: - r = input("> {} (y/N) ".format(question)) + r = input(f"> {question} (y/N) ") else: - r = input("> {} (y/n) ".format(question)) + r = input(f"> {question} (y/n) ") r = r.strip().lower() @@ -138,9 +138,9 @@ def ask(question, answer=str, default=None, length=None): r = None while True: if default: - r = input("> {} [{}] ".format(question, default)) + r = input(f"> {question} [{default}] ") else: - r = input("> {} ".format(question)) + r = input(f"> {question} ") r = r.strip() @@ -180,7 +180,7 @@ def render_jinja_template(tmpl_name: str, tmpl_vars: Mapping, target_path: str): _template = _jinja_env.get_template(tmpl_name) fd.write(_template.render(**tmpl_vars)) except OSError as e: - print("Error: {}".format(e)) + print(f"Error: {e}") def main(): @@ -376,12 +376,12 @@ needed by Pelican. try: os.makedirs(os.path.join(CONF["basedir"], "content")) except OSError as e: - print("Error: {}".format(e)) + print(f"Error: {e}") try: os.makedirs(os.path.join(CONF["basedir"], "output")) except OSError as e: - print("Error: {}".format(e)) + print(f"Error: {e}") conf_python = dict() for key, value in CONF.items(): diff --git a/pelican/tools/pelican_themes.py b/pelican/tools/pelican_themes.py index 4069f99b..c5b49b9f 100755 --- a/pelican/tools/pelican_themes.py +++ b/pelican/tools/pelican_themes.py @@ -58,7 +58,7 @@ def main(): "-V", "--version", action="version", - version="pelican-themes v{}".format(__version__), + version=f"pelican-themes v{__version__}", help="Print the version of this script", ) @@ -224,7 +224,7 @@ def install(path, v=False, u=False): install(path, v) else: if v: - print("Copying '{p}' to '{t}' ...".format(p=path, t=theme_path)) + print(f"Copying '{path}' to '{theme_path}' ...") try: shutil.copytree(path, theme_path) @@ -264,7 +264,7 @@ def symlink(path, v=False): err(path + " : already exists") else: if v: - print("Linking `{p}' to `{t}' ...".format(p=path, t=theme_path)) + print(f"Linking `{path}' to `{theme_path}' ...") try: os.symlink(path, theme_path) except Exception as e: @@ -288,12 +288,12 @@ def clean(v=False): path = os.path.join(_THEMES_PATH, path) if os.path.islink(path) and is_broken_link(path): if v: - print("Removing {}".format(path)) + print(f"Removing {path}") try: os.remove(path) except OSError: - print("Error: cannot remove {}".format(path)) + print(f"Error: cannot remove {path}") else: c += 1 - print("\nRemoved {} broken links".format(c)) + print(f"\nRemoved {c} broken links") diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index 2e8cc953..6d705d4c 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -31,7 +31,7 @@ class URLWrapper: @property def slug(self): if self._slug is None: - class_key = "{}_REGEX_SUBSTITUTIONS".format(self.__class__.__name__.upper()) + class_key = f"{self.__class__.__name__.upper()}_REGEX_SUBSTITUTIONS" regex_subs = self.settings.get( class_key, self.settings.get("SLUG_REGEX_SUBSTITUTIONS", []) ) @@ -60,7 +60,7 @@ class URLWrapper: return hash(self.slug) def _normalize_key(self, key): - class_key = "{}_REGEX_SUBSTITUTIONS".format(self.__class__.__name__.upper()) + class_key = f"{self.__class__.__name__.upper()}_REGEX_SUBSTITUTIONS" regex_subs = self.settings.get( class_key, self.settings.get("SLUG_REGEX_SUBSTITUTIONS", []) ) @@ -98,7 +98,7 @@ class URLWrapper: return self.name def __repr__(self): - return "<{} {}>".format(type(self).__name__, repr(self._name)) + return f"<{type(self).__name__} {repr(self._name)}>" def _from_settings(self, key, get_page_name=False): """Returns URL information as defined in settings. @@ -108,7 +108,7 @@ class URLWrapper: "cat/{slug}" Useful for pagination. """ - setting = "{}_{}".format(self.__class__.__name__.upper(), key) + setting = f"{self.__class__.__name__.upper()}_{key}" value = self.settings[setting] if isinstance(value, pathlib.Path): value = str(value) diff --git a/pelican/utils.py b/pelican/utils.py index 08a08f7e..02ffb9d1 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -35,9 +35,7 @@ def sanitised_join(base_directory, *parts): joined = posixize_path(os.path.abspath(os.path.join(base_directory, *parts))) base = posixize_path(os.path.abspath(base_directory)) if not joined.startswith(base): - raise RuntimeError( - "Attempted to break out of output directory to {}".format(joined) - ) + raise RuntimeError(f"Attempted to break out of output directory to {joined}") return joined @@ -71,7 +69,7 @@ def strftime(date, date_format): # check for '-' prefix if len(candidate) == 3: # '-' prefix - candidate = "%{}".format(candidate[-1]) + candidate = f"%{candidate[-1]}" conversion = strip_zeros else: conversion = None @@ -178,11 +176,11 @@ def deprecated_attribute(old, new, since=None, remove=None, doc=None): def _warn(): version = ".".join(str(x) for x in since) - message = ["{} has been deprecated since {}".format(old, version)] + message = [f"{old} has been deprecated since {version}"] if remove: version = ".".join(str(x) for x in remove) - message.append(" and will be removed by version {}".format(version)) - message.append(". Use {} instead.".format(new)) + message.append(f" and will be removed by version {version}") + message.append(f". Use {new} instead.") logger.warning("".join(message)) logger.debug("".join(str(x) for x in traceback.format_stack())) @@ -210,7 +208,7 @@ def get_date(string): try: return dateutil.parser.parse(string, default=default) except (TypeError, ValueError): - raise ValueError("{!r} is not a valid date".format(string)) + raise ValueError(f"{string!r} is not a valid date") @contextmanager @@ -763,9 +761,9 @@ def order_content(content_list, order_by="slug"): def wait_for_changes(settings_file, reader_class, settings): content_path = settings.get("PATH", "") theme_path = settings.get("THEME", "") - ignore_files = set( + ignore_files = { fnmatch.translate(pattern) for pattern in settings.get("IGNORE_FILES", []) - ) + } candidate_paths = [ settings_file, @@ -838,7 +836,7 @@ def split_all(path): return None else: raise TypeError( - '"path" was {}, must be string, None, or pathlib.Path'.format(type(path)) + f'"path" was {type(path)}, must be string, None, or pathlib.Path' ) @@ -873,7 +871,7 @@ def maybe_pluralize(count, singular, plural): selection = plural if count == 1: selection = singular - return "{} {}".format(count, selection) + return f"{count} {selection}" @contextmanager From db241feaa445375dc05e189e69287000ffe5fa8e Mon Sep 17 00:00:00 2001 From: Paolo Melchiorre Date: Sun, 12 Nov 2023 15:06:02 +0100 Subject: [PATCH 335/465] Fix #2888 -- Apply ruff and pyupgrade to templates --- pelican/tools/templates/pelicanconf.py.jinja2 | 22 ++-- pelican/tools/templates/publishconf.py.jinja2 | 11 +- pelican/tools/templates/tasks.py.jinja2 | 113 ++++++++++-------- 3 files changed, 82 insertions(+), 64 deletions(-) diff --git a/pelican/tools/templates/pelicanconf.py.jinja2 b/pelican/tools/templates/pelicanconf.py.jinja2 index 1112ac88..d2e92d4b 100644 --- a/pelican/tools/templates/pelicanconf.py.jinja2 +++ b/pelican/tools/templates/pelicanconf.py.jinja2 @@ -1,8 +1,8 @@ AUTHOR = {{author}} SITENAME = {{sitename}} -SITEURL = '' +SITEURL = "" -PATH = 'content' +PATH = "content" TIMEZONE = {{timezone}} @@ -16,16 +16,20 @@ AUTHOR_FEED_ATOM = None AUTHOR_FEED_RSS = None # Blogroll -LINKS = (('Pelican', 'https://getpelican.com/'), - ('Python.org', 'https://www.python.org/'), - ('Jinja2', 'https://palletsprojects.com/p/jinja/'), - ('You can modify those links in your config file', '#'),) +LINKS = ( + ("Pelican", "https://getpelican.com/"), + ("Python.org", "https://www.python.org/"), + ("Jinja2", "https://palletsprojects.com/p/jinja/"), + ("You can modify those links in your config file", "#"), +) # Social widget -SOCIAL = (('You can add links in your config file', '#'), - ('Another social link', '#'),) +SOCIAL = ( + ("You can add links in your config file", "#"), + ("Another social link", "#"), +) DEFAULT_PAGINATION = {{default_pagination}} # Uncomment following line if you want document-relative URLs when developing -#RELATIVE_URLS = True +# RELATIVE_URLS = True diff --git a/pelican/tools/templates/publishconf.py.jinja2 b/pelican/tools/templates/publishconf.py.jinja2 index e119222c..301e4dfa 100755 --- a/pelican/tools/templates/publishconf.py.jinja2 +++ b/pelican/tools/templates/publishconf.py.jinja2 @@ -3,19 +3,20 @@ import os import sys + sys.path.append(os.curdir) from pelicanconf import * # If your site is available via HTTPS, make sure SITEURL begins with https:// -SITEURL = '{{siteurl}}' +SITEURL = "{{siteurl}}" RELATIVE_URLS = False -FEED_ALL_ATOM = 'feeds/all.atom.xml' -CATEGORY_FEED_ATOM = 'feeds/{slug}.atom.xml' +FEED_ALL_ATOM = "feeds/all.atom.xml" +CATEGORY_FEED_ATOM = "feeds/{slug}.atom.xml" DELETE_OUTPUT_DIRECTORY = True # Following items are often useful when publishing -#DISQUS_SITENAME = "" -#GOOGLE_ANALYTICS = "" +# DISQUS_SITENAME = "" +# GOOGLE_ANALYTICS = "" diff --git a/pelican/tools/templates/tasks.py.jinja2 b/pelican/tools/templates/tasks.py.jinja2 index f3caed56..1a0f02d1 100644 --- a/pelican/tools/templates/tasks.py.jinja2 +++ b/pelican/tools/templates/tasks.py.jinja2 @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import os import shlex import shutil @@ -14,61 +12,66 @@ from pelican.server import ComplexHTTPRequestHandler, RootedHTTPServer from pelican.settings import DEFAULT_CONFIG, get_settings_from_file OPEN_BROWSER_ON_SERVE = True -SETTINGS_FILE_BASE = 'pelicanconf.py' +SETTINGS_FILE_BASE = "pelicanconf.py" SETTINGS = {} SETTINGS.update(DEFAULT_CONFIG) LOCAL_SETTINGS = get_settings_from_file(SETTINGS_FILE_BASE) SETTINGS.update(LOCAL_SETTINGS) CONFIG = { - 'settings_base': SETTINGS_FILE_BASE, - 'settings_publish': 'publishconf.py', + "settings_base": SETTINGS_FILE_BASE, + "settings_publish": "publishconf.py", # Output path. Can be absolute or relative to tasks.py. Default: 'output' - 'deploy_path': SETTINGS['OUTPUT_PATH'], + "deploy_path": SETTINGS["OUTPUT_PATH"], {% if ssh %} # Remote server configuration - 'ssh_user': '{{ssh_user}}', - 'ssh_host': '{{ssh_host}}', - 'ssh_port': '{{ssh_port}}', - 'ssh_path': '{{ssh_target_dir}}', + "ssh_user": "{{ssh_user}}", + "ssh_host": "{{ssh_host}}", + "ssh_port": "{{ssh_port}}", + "ssh_path": "{{ssh_target_dir}}", {% endif %} {% if cloudfiles %} # Rackspace Cloud Files configuration settings - 'cloudfiles_username': '{{cloudfiles_username}}', - 'cloudfiles_api_key': '{{cloudfiles_api_key}}', - 'cloudfiles_container': '{{cloudfiles_container}}', + "cloudfiles_username": "{{cloudfiles_username}}", + "cloudfiles_api_key": "{{cloudfiles_api_key}}", + "cloudfiles_container": "{{cloudfiles_container}}", {% endif %} {% if github %} # Github Pages configuration - 'github_pages_branch': '{{github_pages_branch}}', - 'commit_message': "'Publish site on {}'".format(datetime.date.today().isoformat()), + "github_pages_branch": "{{github_pages_branch}}", + "commit_message": f"'Publish site on {datetime.date.today().isoformat()}'", {% endif %} # Host and port for `serve` - 'host': 'localhost', - 'port': 8000, + "host": "localhost", + "port": 8000, } + @task def clean(c): """Remove generated files""" - if os.path.isdir(CONFIG['deploy_path']): - shutil.rmtree(CONFIG['deploy_path']) - os.makedirs(CONFIG['deploy_path']) + if os.path.isdir(CONFIG["deploy_path"]): + shutil.rmtree(CONFIG["deploy_path"]) + os.makedirs(CONFIG["deploy_path"]) + @task def build(c): """Build local version of site""" - pelican_run('-s {settings_base}'.format(**CONFIG)) + pelican_run("-s {settings_base}".format(**CONFIG)) + @task def rebuild(c): """`build` with the delete switch""" - pelican_run('-d -s {settings_base}'.format(**CONFIG)) + pelican_run("-d -s {settings_base}".format(**CONFIG)) + @task def regenerate(c): """Automatically regenerate site upon file modification""" - pelican_run('-r -s {settings_base}'.format(**CONFIG)) + pelican_run("-r -s {settings_base}".format(**CONFIG)) + @task def serve(c): @@ -78,28 +81,32 @@ def serve(c): allow_reuse_address = True server = AddressReuseTCPServer( - CONFIG['deploy_path'], - (CONFIG['host'], CONFIG['port']), - ComplexHTTPRequestHandler) + CONFIG["deploy_path"], + (CONFIG["host"], CONFIG["port"]), + ComplexHTTPRequestHandler, + ) if OPEN_BROWSER_ON_SERVE: # Open site in default browser import webbrowser + webbrowser.open("http://{host}:{port}".format(**CONFIG)) - sys.stderr.write('Serving at {host}:{port} ...\n'.format(**CONFIG)) + sys.stderr.write("Serving at {host}:{port} ...\n".format(**CONFIG)) server.serve_forever() + @task def reserve(c): """`build`, then `serve`""" build(c) serve(c) + @task def preview(c): """Build production version of site""" - pelican_run('-s {settings_publish}'.format(**CONFIG)) + pelican_run("-s {settings_publish}".format(**CONFIG)) @task def livereload(c): @@ -107,25 +114,25 @@ def livereload(c): from livereload import Server def cached_build(): - cmd = '-s {settings_base} -e CACHE_CONTENT=true LOAD_CONTENT_CACHE=true' + cmd = "-s {settings_base} -e CACHE_CONTENT=true LOAD_CONTENT_CACHE=true" pelican_run(cmd.format(**CONFIG)) cached_build() server = Server() - theme_path = SETTINGS['THEME'] + theme_path = SETTINGS["THEME"] watched_globs = [ - CONFIG['settings_base'], - '{}/templates/**/*.html'.format(theme_path), + CONFIG["settings_base"], + f"{theme_path}/templates/**/*.html", ] - content_file_extensions = ['.md', '.rst'] + content_file_extensions = [".md", ".rst"] for extension in content_file_extensions: - content_glob = '{0}/**/*{1}'.format(SETTINGS['PATH'], extension) + content_glob = "{}/**/*{}".format(SETTINGS["PATH"], extension) watched_globs.append(content_glob) - static_file_extensions = ['.css', '.js'] + static_file_extensions = [".css", ".js"] for extension in static_file_extensions: - static_file_glob = '{0}/static/**/*{1}'.format(theme_path, extension) + static_file_glob = f"{theme_path}/static/**/*{extension}" watched_globs.append(static_file_glob) for glob in watched_globs: @@ -134,43 +141,49 @@ def livereload(c): if OPEN_BROWSER_ON_SERVE: # Open site in default browser import webbrowser + webbrowser.open("http://{host}:{port}".format(**CONFIG)) - server.serve(host=CONFIG['host'], port=CONFIG['port'], root=CONFIG['deploy_path']) + server.serve(host=CONFIG["host"], port=CONFIG["port"], root=CONFIG["deploy_path"]) {% if cloudfiles %} @task def cf_upload(c): """Publish to Rackspace Cloud Files""" rebuild(c) - with cd(CONFIG['deploy_path']): - c.run('swift -v -A https://auth.api.rackspacecloud.com/v1.0 ' - '-U {cloudfiles_username} ' - '-K {cloudfiles_api_key} ' - 'upload -c {cloudfiles_container} .'.format(**CONFIG)) + with cd(CONFIG["deploy_path"]): + c.run( + "swift -v -A https://auth.api.rackspacecloud.com/v1.0 " + "-U {cloudfiles_username} " + "-K {cloudfiles_api_key} " + "upload -c {cloudfiles_container} .".format(**CONFIG) + ) {% endif %} @task def publish(c): """Publish to production via rsync""" - pelican_run('-s {settings_publish}'.format(**CONFIG)) + pelican_run("-s {settings_publish}".format(**CONFIG)) c.run( 'rsync --delete --exclude ".DS_Store" -pthrvz -c ' '-e "ssh -p {ssh_port}" ' - '{} {ssh_user}@{ssh_host}:{ssh_path}'.format( - CONFIG['deploy_path'].rstrip('/') + '/', - **CONFIG)) + "{} {ssh_user}@{ssh_host}:{ssh_path}".format( + CONFIG["deploy_path"].rstrip("/") + "/", **CONFIG + ) + ) {% if github %} @task def gh_pages(c): """Publish to GitHub Pages""" preview(c) - c.run('ghp-import -b {github_pages_branch} ' - '-m {commit_message} ' - '{deploy_path} -p'.format(**CONFIG)) + c.run( + "ghp-import -b {github_pages_branch} " + "-m {commit_message} " + "{deploy_path} -p".format(**CONFIG) + ) {% endif %} def pelican_run(cmd): - cmd += ' ' + program.core.remainder # allows to pass-through args to pelican + cmd += " " + program.core.remainder # allows to pass-through args to pelican pelican_main(shlex.split(cmd)) From 2238dcab077eac83fb6855b2ab4914cb05882ca3 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 12 Nov 2023 15:53:13 +0100 Subject: [PATCH 336/465] Ignore code format commits from `git blame` --- .git-blame-ignore-revs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 7b822fd3..0d92c9d9 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,3 +1,7 @@ # .git-blame-ignore-revs # Apply code style to project via: ruff format . cabdb26cee66e1173cf16cb31d3fe5f9fa4392e7 +# Upgrade code base for Python 3.8 and above +ecd598f293161a52564aa6e8dfdcc8284dc93970 +# Apply Ruff and pyupgrade to Jinja templates +db241feaa445375dc05e189e69287000ffe5fa8e From 0c5d63c69ee53d1891644a6b97e96b36e5d8721e Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 12 Nov 2023 17:11:23 +0100 Subject: [PATCH 337/465] Update documentation related to contributing --- CONTRIBUTING.rst | 28 ++++++++++------------------ docs/contribute.rst | 4 +--- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index c1175aa4..4faace91 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -79,6 +79,10 @@ don't spend a lot of time working on something that would be rejected for a known reason. Consider also whether your new feature might be better suited as a ':pelican-doc:`plugins` — you can `ask for help`_ to make that determination. +Also, if you intend to submit a pull request to address something for which there +is no existing issue, there is no need to create a new issue and then immediately +submit a pull request that closes it. You can submit the pull request by itself. + Using Git and GitHub -------------------- @@ -87,7 +91,8 @@ Using Git and GitHub * **Don't put multiple unrelated fixes/features in the same branch / pull request.** For example, if you're working on a new feature and find a bugfix that doesn't *require* your new feature, **make a new distinct branch and pull - request** for the bugfix. + request** for the bugfix. Similarly, any proposed changes to code style + formatting should be in a completely separate pull request. * Add a ``RELEASE.md`` file in the root of the project that contains the release type (major, minor, patch) and a summary of the changes that will be used as the release changelog entry. For example:: @@ -106,15 +111,8 @@ Using Git and GitHub detailed explanation (when relevant). * `Squash your commits`_ to eliminate merge commits and ensure a clean and readable commit history. -* If you have previously filed a GitHub issue and want to contribute code that - addresses that issue, **please use** ``hub pull-request`` instead of using - GitHub's web UI to submit the pull request. This isn't an absolute - requirement, but makes the maintainers' lives much easier! Specifically: - `install hub `_ and then run - `hub pull-request -i [ISSUE] `_ - to turn your GitHub issue into a pull request containing your code. * After you have issued a pull request, the continuous integration (CI) system - will run the test suite for all supported Python versions and check for PEP8 + will run the test suite on all supported Python versions and check for code style compliance. If any of these checks fail, you should fix them. (If tests fail on the CI system but seem to pass locally, ensure that local test runs aren't skipping any tests.) @@ -122,13 +120,7 @@ Using Git and GitHub Contribution quality standards ------------------------------ -* Adhere to `PEP8 coding standards`_. This can be eased via the `pycodestyle - `_ or `flake8 - `_ tools, the latter of which in - particular will give you some useful hints about ways in which the - code/formatting can be improved. We try to keep line length within the - 79-character maximum specified by PEP8. Because that can sometimes compromise - readability, the hard/enforced maximum is 88 characters. +* Adhere to the project's code style standards. See: `Development Environment`_ * Ensure your code is compatible with the `officially-supported Python releases`_. * Add docs and tests for your changes. Undocumented and untested features will not be accepted. @@ -142,6 +134,6 @@ need assistance or have any questions about these guidelines. .. _`Create a new branch`: https://github.com/getpelican/pelican/wiki/Git-Tips#making-your-changes .. _`Squash your commits`: https://github.com/getpelican/pelican/wiki/Git-Tips#squashing-commits .. _`Git Tips`: https://github.com/getpelican/pelican/wiki/Git-Tips -.. _`PEP8 coding standards`: https://www.python.org/dev/peps/pep-0008/ .. _`ask for help`: `How to get help`_ -.. _`officially-supported Python releases`: https://devguide.python.org/#status-of-python-branches +.. _`Development Environment`: https://docs.getpelican.com/en/latest/contribute.html#setting-up-the-development-environment +.. _`officially-supported Python releases`: https://devguide.python.org/versions/#versions diff --git a/docs/contribute.rst b/docs/contribute.rst index 33a62064..6a5a417e 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -46,7 +46,6 @@ Install the needed dependencies and set up the project:: python -m pip install invoke invoke setup - python -m pip install -e ~/projects/pelican Your local environment should now be ready to go! @@ -159,8 +158,7 @@ check for code style compliance via:: If style violations are found, many of them can be addressed automatically via:: - invoke black - invoke isort + invoke format If style violations are found even after running the above auto-formatters, you will need to make additional manual changes until ``invoke lint`` no longer From 86d689851793e61b0cee1fe0bf72c8ebc991b6c1 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Sun, 12 Nov 2023 19:43:26 +0300 Subject: [PATCH 338/465] remove WRITE_SELECTED Implementation is buggy and unreliable. Therefore, it is better to remove the functionality until a robust implementation is added. --- docs/faq.rst | 4 ---- docs/publish.rst | 12 ------------ docs/settings.rst | 22 ---------------------- pelican/__init__.py | 11 ----------- pelican/settings.py | 14 +++++++------- pelican/tests/test_pelican.py | 23 ----------------------- pelican/utils.py | 13 ------------- pelican/writers.py | 12 +----------- 8 files changed, 8 insertions(+), 103 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index c065b4ed..cecc1157 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -217,10 +217,6 @@ changed. A simple solution is to make ``rsync`` use the ``--checksum`` option, which will make it compare the file checksums in a much faster way than Pelican would. -When only several specific output files are of interest (e.g. when working on -some specific page or the theme templates), the ``WRITE_SELECTED`` option may -help, see :ref:`writing_only_selected_content`. - How to process only a subset of all articles? ============================================= diff --git a/docs/publish.rst b/docs/publish.rst index f5ebfff5..e687b65a 100644 --- a/docs/publish.rst +++ b/docs/publish.rst @@ -18,18 +18,6 @@ folder, using the default theme to produce a simple site. The default theme consists of very simple HTML without styling and is provided so folks may use it as a basis for creating their own themes. -When working on a single article or page, it is possible to generate only the -file that corresponds to that content. To do this, use the ``--write-selected`` -argument, like so:: - - pelican --write-selected output/posts/my-post-title.html - -Note that you must specify the path to the generated *output* file — not the -source content. To determine the output file name and location, use the -``--debug`` flag. If desired, ``--write-selected`` can take a comma-separated -list of paths or can be configured as a setting. (See: -:ref:`writing_only_selected_content`) - You can also tell Pelican to watch for your modifications, instead of manually re-running it every time you want to see your changes. To enable this, run the ``pelican`` command with the ``-r`` or ``--autoreload`` option. On non-Windows diff --git a/docs/settings.rst b/docs/settings.rst index 88a32d23..e9edffde 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -362,13 +362,6 @@ Basic settings If ``True``, load unmodified content from caches. -.. data:: WRITE_SELECTED = [] - - If this list is not empty, **only** output files with their paths in this - list are written. Paths should be either absolute or relative to the current - Pelican working directory. For possible use cases see - :ref:`writing_only_selected_content`. - .. data:: FORMATTED_FIELDS = ['summary'] A list of metadata fields containing reST/Markdown content to be parsed and @@ -1400,21 +1393,6 @@ modification times of the generated ``*.html`` files will always change. Therefore, ``rsync``-based uploading may benefit from the ``--checksum`` option. -.. _writing_only_selected_content: - - -Writing only selected content -============================= - -When only working on a single article or page, or making tweaks to your theme, -it is often desirable to generate and review your work as quickly as possible. -In such cases, generating and writing the entire site output is often -unnecessary. By specifying only the desired files as output paths in the -``WRITE_SELECTED`` list, **only** those files will be written. This list can be -also specified on the command line using the ``--write-selected`` option, which -accepts a comma-separated list of output file paths. By default this list is -empty, so all output is written. See :ref:`site_generation` for more details. - Example settings ================ diff --git a/pelican/__init__.py b/pelican/__init__.py index 25c493b9..a25f5624 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -434,15 +434,6 @@ def parse_arguments(argv=None): help="Ignore content cache " "from previous runs by not loading cache files.", ) - parser.add_argument( - "-w", - "--write-selected", - type=str, - dest="selected_paths", - default=None, - help="Comma separated list of selected paths to write", - ) - parser.add_argument( "--fatal", metavar="errors|warnings", @@ -527,8 +518,6 @@ def get_config(args): config["LOAD_CONTENT_CACHE"] = False if args.cache_path: config["CACHE_PATH"] = args.cache_path - if args.selected_paths: - config["WRITE_SELECTED"] = args.selected_paths.split(",") if args.relative_paths: config["RELATIVE_URLS"] = args.relative_paths if args.port is not None: diff --git a/pelican/settings.py b/pelican/settings.py index 4a4f2901..33ec210a 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -169,7 +169,6 @@ DEFAULT_CONFIG = { "GZIP_CACHE": True, "CHECK_MODIFIED_METHOD": "mtime", "LOAD_CONTENT_CACHE": False, - "WRITE_SELECTED": [], "FORMATTED_FIELDS": ["summary"], "PORT": 8000, "BIND": "127.0.0.1", @@ -557,6 +556,13 @@ def handle_deprecated_settings(settings): ) settings[old] = settings[new] + # Warn if removed WRITE_SELECTED is present + if "WRITE_SELECTED" in settings: + logger.warning( + "WRITE_SELECTED is present in settings but this functionality was removed. " + "It will have no effect." + ) + return settings @@ -585,12 +591,6 @@ def configure_settings(settings): else: raise Exception("Could not find the theme %s" % settings["THEME"]) - # make paths selected for writing absolute if necessary - settings["WRITE_SELECTED"] = [ - os.path.abspath(path) - for path in settings.get("WRITE_SELECTED", DEFAULT_CONFIG["WRITE_SELECTED"]) - ] - # standardize strings to lowercase strings for key in ["DEFAULT_LANG"]: if key in settings: diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 3c0c0572..075f55eb 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -202,29 +202,6 @@ class TestPelican(LoggedTestCase): for file in ["a_stylesheet", "a_template"]: self.assertTrue(os.path.exists(os.path.join(theme_output, file))) - def test_write_only_selected(self): - """Test that only the selected files are written""" - settings = read_settings( - path=None, - override={ - "PATH": INPUT_PATH, - "OUTPUT_PATH": self.temp_path, - "CACHE_PATH": self.temp_cache, - "WRITE_SELECTED": [ - os.path.join(self.temp_path, "oh-yeah.html"), - os.path.join(self.temp_path, "categories.html"), - ], - "LOCALE": locale.normalize("en_US"), - }, - ) - pelican = Pelican(settings=settings) - logger = logging.getLogger() - orig_level = logger.getEffectiveLevel() - logger.setLevel(logging.INFO) - mute(True)(pelican.run)() - logger.setLevel(orig_level) - self.assertLogCountEqual(count=2, msg="Writing .*", level=logging.INFO) - def test_cyclic_intersite_links_no_warnings(self): settings = read_settings( path=None, diff --git a/pelican/utils.py b/pelican/utils.py index 02ffb9d1..eda53d3f 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -840,19 +840,6 @@ def split_all(path): ) -def is_selected_for_writing(settings, path): - """Check whether path is selected for writing - according to the WRITE_SELECTED list - - If WRITE_SELECTED is an empty list (default), - any path is selected for writing. - """ - if settings["WRITE_SELECTED"]: - return path in settings["WRITE_SELECTED"] - else: - return True - - def path_to_file_url(path): """Convert file-system path to file:// URL""" return urllib.parse.urljoin("file://", urllib.request.pathname2url(path)) diff --git a/pelican/writers.py b/pelican/writers.py index ec12d125..d405fc88 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -11,7 +11,6 @@ from pelican.paginator import Paginator from pelican.plugins import signals from pelican.utils import ( get_relative_path, - is_selected_for_writing, path_to_url, sanitised_join, set_date_tzinfo, @@ -145,9 +144,6 @@ class Writer: name should be skipped to keep that one) :param feed_title: the title of the feed.o """ - if not is_selected_for_writing(self.settings, path): - return - self.site_url = context.get("SITEURL", path_to_url(get_relative_path(path))) self.feed_domain = context.get("FEED_DOMAIN") @@ -203,13 +199,7 @@ class Writer: :param **kwargs: additional variables to pass to the templates """ - if ( - name is False - or name == "" - or not is_selected_for_writing( - self.settings, os.path.join(self.output_path, name) - ) - ): + if name is False or name == "": return elif not name: # other stuff, just return for now From 7ca66ee9d0ac672e88c845d347457be7ad4def41 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 12 Nov 2023 18:03:36 +0100 Subject: [PATCH 339/465] Prepare release --- RELEASE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..33991cb6 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,21 @@ +Release type: minor + +* Upgrade code to new minimum supported Python version: 3.8 +* Settings support for ``pathlib.Path`` `(#2758) `_ +* Various improvements to Simple theme (`#2976 `_ & `#3234 `_) +* Use Furo as Sphinx documentation theme `(#3023) `_ +* Default to 100 articles maximum in feeds `(#3127) `_ +* Add ``period_archives common context`` variable `(#3148) `_ +* Use ``watchfiles`` as the file-watching backend `(#3151) `_ +* Add GitHub Actions workflow for GitHub Pages `(#3189) `_ +* Allow dataclasses in settings `(#3204) `_ +* Switch build tool to PDM instead of Setuptools/Poetry `(#3220) `_ +* Provide a ``plugin_enabled`` Jinja test for themes `(#3235) `_ +* Preserve connection order in Blinker `(#3238) `_ +* Remove social icons from default ``notmyidea`` theme `(#3240) `_ +* Remove unreliable ``WRITE_SELECTED`` feature `(#3243) `_ +* Importer: Report broken embedded video links when importing from Tumblr `(#3177) `_ +* Importer: Remove newline addition when iterating Photo post types `(#3178) `_ +* Importer: Force timestamp conversion in Tumblr importer to be UTC with offset `(#3221) `_ +* Importer: Use tempfile for intermediate HTML file for Pandoc `(#3221) `_ +* Switch linters to Ruff `(#3223) `_ From 6cd707a66893a7ed509d982639cd4e7a2bc8cf9c Mon Sep 17 00:00:00 2001 From: botpub <52496925+botpub@users.noreply.github.com> Date: Sun, 12 Nov 2023 17:44:45 +0000 Subject: [PATCH 340/465] Release Pelican 4.9.0 --- RELEASE.md | 21 --------------------- docs/changelog.rst | 23 +++++++++++++++++++++++ pyproject.toml | 2 +- 3 files changed, 24 insertions(+), 22 deletions(-) delete mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 33991cb6..00000000 --- a/RELEASE.md +++ /dev/null @@ -1,21 +0,0 @@ -Release type: minor - -* Upgrade code to new minimum supported Python version: 3.8 -* Settings support for ``pathlib.Path`` `(#2758) `_ -* Various improvements to Simple theme (`#2976 `_ & `#3234 `_) -* Use Furo as Sphinx documentation theme `(#3023) `_ -* Default to 100 articles maximum in feeds `(#3127) `_ -* Add ``period_archives common context`` variable `(#3148) `_ -* Use ``watchfiles`` as the file-watching backend `(#3151) `_ -* Add GitHub Actions workflow for GitHub Pages `(#3189) `_ -* Allow dataclasses in settings `(#3204) `_ -* Switch build tool to PDM instead of Setuptools/Poetry `(#3220) `_ -* Provide a ``plugin_enabled`` Jinja test for themes `(#3235) `_ -* Preserve connection order in Blinker `(#3238) `_ -* Remove social icons from default ``notmyidea`` theme `(#3240) `_ -* Remove unreliable ``WRITE_SELECTED`` feature `(#3243) `_ -* Importer: Report broken embedded video links when importing from Tumblr `(#3177) `_ -* Importer: Remove newline addition when iterating Photo post types `(#3178) `_ -* Importer: Force timestamp conversion in Tumblr importer to be UTC with offset `(#3221) `_ -* Importer: Use tempfile for intermediate HTML file for Pandoc `(#3221) `_ -* Switch linters to Ruff `(#3223) `_ diff --git a/docs/changelog.rst b/docs/changelog.rst index 88353ed4..98da5b20 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,29 @@ Release history ############### +4.9.0 - 2023-11-12 +================== + +* Upgrade code to new minimum supported Python version: 3.8 +* Settings support for ``pathlib.Path`` `(#2758) `_ +* Various improvements to Simple theme (`#2976 `_ & `#3234 `_) +* Use Furo as Sphinx documentation theme `(#3023) `_ +* Default to 100 articles maximum in feeds `(#3127) `_ +* Add ``period_archives common context`` variable `(#3148) `_ +* Use ``watchfiles`` as the file-watching backend `(#3151) `_ +* Add GitHub Actions workflow for GitHub Pages `(#3189) `_ +* Allow dataclasses in settings `(#3204) `_ +* Switch build tool to PDM instead of Setuptools/Poetry `(#3220) `_ +* Provide a ``plugin_enabled`` Jinja test for themes `(#3235) `_ +* Preserve connection order in Blinker `(#3238) `_ +* Remove social icons from default ``notmyidea`` theme `(#3240) `_ +* Remove unreliable ``WRITE_SELECTED`` feature `(#3243) `_ +* Importer: Report broken embedded video links when importing from Tumblr `(#3177) `_ +* Importer: Remove newline addition when iterating Photo post types `(#3178) `_ +* Importer: Force timestamp conversion in Tumblr importer to be UTC with offset `(#3221) `_ +* Importer: Use tempfile for intermediate HTML file for Pandoc `(#3221) `_ +* Switch linters to Ruff `(#3223) `_ + 4.8.0 - 2022-07-11 ================== diff --git a/pyproject.toml b/pyproject.toml index d40e6bce..e0e118f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "pelican" authors = [{ name = "Justin Mayer", email = "authors@getpelican.com" }] description = "Static site generator supporting Markdown and reStructuredText" -version = "4.8.0" +version = "4.9.0" license = { text = "AGPLv3" } readme = "README.rst" keywords = ["static site generator", "static sites", "ssg"] From 1d0fd456e875d743516b56458ab9767511f3e5d3 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Mon, 13 Nov 2023 12:18:41 -0700 Subject: [PATCH 341/465] Add `tzdata` requirement on Windows --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index e0e118f5..cf3c23c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ dependencies = [ "unidecode>=1.3.7", "backports-zoneinfo>=0.2.1; python_version < \"3.9\"", "watchfiles>=0.21.0", + "tzdata; sys_platform == 'win32'", ] [project.optional-dependencies] From a2525f7db442e99c4210c07aed9e4a04afb494be Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Mon, 13 Nov 2023 14:42:29 -0700 Subject: [PATCH 342/465] Remove `tzdata` from testing requirements. Should be installed by pelican directly, if needed, based on OS. --- requirements/test.pip | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements/test.pip b/requirements/test.pip index 87869e67..8eb1029f 100644 --- a/requirements/test.pip +++ b/requirements/test.pip @@ -3,7 +3,6 @@ Pygments==2.14.0 pytest pytest-cov pytest-xdist[psutil] -tzdata # Optional Packages Markdown==3.5.1 From f510b4b21f8c7d707eb42d6261d910e327b71bc0 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 15 Nov 2023 17:52:41 +0100 Subject: [PATCH 343/465] Remove reference to non-existent requirements file --- requirements/developer.pip | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements/developer.pip b/requirements/developer.pip index 5c2f5a69..58b2a1dc 100644 --- a/requirements/developer.pip +++ b/requirements/developer.pip @@ -1,3 +1,2 @@ -r test.pip -r docs.pip --r style.pip From 76f7343b6149e300dc014fd9128f6f4214099b91 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 15 Nov 2023 18:08:10 +0100 Subject: [PATCH 344/465] Prepare release --- RELEASE.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..b61180cb --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,3 @@ +Release type: patch + +* Ensure ``tzdata`` dependency is installed on Windows From 7194cf579571dbbca85faad5bace71627c56152d Mon Sep 17 00:00:00 2001 From: botpub <52496925+botpub@users.noreply.github.com> Date: Wed, 15 Nov 2023 17:16:23 +0000 Subject: [PATCH 345/465] Release Pelican 4.9.1 --- RELEASE.md | 3 --- docs/changelog.rst | 5 +++++ pyproject.toml | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) delete mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index b61180cb..00000000 --- a/RELEASE.md +++ /dev/null @@ -1,3 +0,0 @@ -Release type: patch - -* Ensure ``tzdata`` dependency is installed on Windows diff --git a/docs/changelog.rst b/docs/changelog.rst index 98da5b20..5ef19b17 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Release history ############### +4.9.1 - 2023-11-15 +================== + +* Ensure ``tzdata`` dependency is installed on Windows + 4.9.0 - 2023-11-12 ================== diff --git a/pyproject.toml b/pyproject.toml index cf3c23c0..c8bbe985 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "pelican" authors = [{ name = "Justin Mayer", email = "authors@getpelican.com" }] description = "Static site generator supporting Markdown and reStructuredText" -version = "4.9.0" +version = "4.9.1" license = { text = "AGPLv3" } readme = "README.rst" keywords = ["static site generator", "static sites", "ssg"] From d9b2bc3a4ea9f11aea58180aaaea69cc95db1f8c Mon Sep 17 00:00:00 2001 From: Vivek Bharadwaj <67718556+vbharadwaj-bk@users.noreply.github.com> Date: Sun, 19 Nov 2023 01:48:13 -0800 Subject: [PATCH 346/465] Add GH pages action to fix file permissions (#3248) --- .github/workflows/github_pages.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/github_pages.yml b/.github/workflows/github_pages.yml index ccf172b4..7eae170a 100644 --- a/.github/workflows/github_pages.yml +++ b/.github/workflows/github_pages.yml @@ -44,6 +44,11 @@ jobs: --settings "${{ inputs.settings }}" \ --extra-settings SITEURL='"${{ steps.pages.outputs.base_url }}"' \ --output "${{ inputs.output-path }}" + - name: Fix permissions + run: | + chmod -c -R +rX "${{ inputs.output-path }}" | while read line; do + echo "::warning title=Invalid file permissions automatically fixed::$line" + done - name: Upload artifact uses: actions/upload-pages-artifact@v2 with: From 7466b13e0a6c2f0a19ee7b8640adfbd8a7a8ec9e Mon Sep 17 00:00:00 2001 From: Salar Nosrati-Ershad Date: Wed, 22 Nov 2023 22:54:30 +0330 Subject: [PATCH 347/465] fix: keep newline at the end of the file in tools As referenced in Jinja documentation about whitespace control: > To keep single trailing newlines, configure Jinja to > `keep_trailing_newline` I added this to our Jinja environment to keep EOL new line in tools scripts --- RELEASE.md | 3 +++ pelican/tools/pelican_quickstart.py | 1 + 2 files changed, 4 insertions(+) create mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..7881aeac --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,3 @@ +Release type: patch + +Keep the newline at the end of the file in generating tools scripts diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index db00ce70..a4dc98e1 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -44,6 +44,7 @@ _TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templ _jinja_env = Environment( loader=FileSystemLoader(_TEMPLATES_DIR), trim_blocks=True, + keep_trailing_newline=True, ) From 4ed5c0d5b87e7711e779be6a26c4a1d9ad21aeaa Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Sat, 25 Nov 2023 20:57:40 -0700 Subject: [PATCH 348/465] Log the original calling location, rather than the wrapper function --- pelican/log.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pelican/log.py b/pelican/log.py index 0d2b6a3f..befecbf1 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -85,13 +85,15 @@ class FatalLogger(LimitLogger): warnings_fatal = False errors_fatal = False + # adding `stacklevel=2` means that the displayed filename and line number + # will match the "original" calling location, rather than the wrapper here def warning(self, *args, **kwargs): - super().warning(*args, **kwargs) + super().warning(*args, stacklevel=2, **kwargs) if FatalLogger.warnings_fatal: raise RuntimeError("Warning encountered") def error(self, *args, **kwargs): - super().error(*args, **kwargs) + super().error(*args, stacklevel=2, **kwargs) if FatalLogger.errors_fatal: raise RuntimeError("Error encountered") From 61eacffa90760c1323aaac15dc6829412e08d2a8 Mon Sep 17 00:00:00 2001 From: Paolo Melchiorre Date: Tue, 28 Nov 2023 21:38:26 +0100 Subject: [PATCH 349/465] Update tox and GitHub pipeline --- .github/workflows/github_pages.yml | 2 +- .github/workflows/main.yml | 18 +++++++++--------- tox.ini | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/github_pages.yml b/.github/workflows/github_pages.yml index 7eae170a..eb9e955f 100644 --- a/.github/workflows/github_pages.yml +++ b/.github/workflows/github_pages.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c29a08c2..cd646522 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: python: "3.9" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v4 with: @@ -52,10 +52,10 @@ jobs: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: pdm-project/setup-pdm@v3 with: - python-version: 3.9 + python-version: "3.11" cache: true cache-dependency-path: ./pyproject.toml - name: Install dependencies @@ -70,10 +70,10 @@ jobs: name: Test build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: pdm-project/setup-pdm@v3 with: - python-version: 3.9 + python-version: "3.11" cache: true cache-dependency-path: ./pyproject.toml - name: Install dependencies @@ -88,11 +88,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" cache: "pip" cache-dependency-path: "**/requirements/*" - name: Install tox @@ -117,14 +117,14 @@ jobs: id-token: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: token: ${{ secrets.GH_TOKEN }} - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" - name: Check release id: check_release diff --git a/tox.ini b/tox.ini index f6f45af1..61e8908a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{3.8,3.9,3.10,3.11.3.12},docs +envlist = py{3.8,3.9,3.10,3.11,3.12},docs [testenv] basepython = @@ -18,7 +18,7 @@ commands = pytest -s --cov=pelican pelican [testenv:docs] -basepython = python3.9 +basepython = python3.11 deps = -rrequirements/docs.pip changedir = docs From 8626d5bd85da049e7ca7828a785d08e02b736aa1 Mon Sep 17 00:00:00 2001 From: Raphael Das Gupta Date: Fri, 22 Dec 2023 15:56:57 +0100 Subject: [PATCH 350/465] docs: update URL to AsciiDoc website https://www.methods.co.nz/asciidoc/ gives a SSL certificate warning and a 404 (page not found) error. https://asciidoc.org is the new official website for the AsciiDoc file format. (It's also what https://en.wikipedia.org/wiki/AsciiDoc links to.) --- docs/content.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content.rst b/docs/content.rst index cacacea9..8a5d9b32 100644 --- a/docs/content.rst +++ b/docs/content.rst @@ -631,7 +631,7 @@ are not included by default in tag, category, and author indexes, nor in the main article feed. This has the effect of creating an "unlisted" post. .. _W3C ISO 8601: https://www.w3.org/TR/NOTE-datetime -.. _AsciiDoc: https://www.methods.co.nz/asciidoc/ +.. _AsciiDoc: https://asciidoc.org .. _Pelican Plugins: https://github.com/pelican-plugins .. _pelican-plugins: https://github.com/getpelican/pelican-plugins .. _Python-Markdown: https://github.com/Python-Markdown/markdown From f0beb81a973f44ed1c8704984bc325b5f4df095c Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Sun, 14 Jan 2024 13:45:51 -0700 Subject: [PATCH 351/465] Better error logging if a plugin refuses to load --- pelican/__init__.py | 3 ++- pelican/log.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index a25f5624..40251887 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -80,7 +80,8 @@ class Pelican: plugin.register() self.plugins.append(plugin) except Exception as e: - logger.error("Cannot register plugin `%s`\n%s", name, e) + logger.error("Cannot register plugin `%s`\n%s", name, e, stacklevel=3) + print(e.stacktrace) self.settings["PLUGINS"] = [get_plugin_name(p) for p in self.plugins] diff --git a/pelican/log.py b/pelican/log.py index befecbf1..6a8fcdf1 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -88,12 +88,16 @@ class FatalLogger(LimitLogger): # adding `stacklevel=2` means that the displayed filename and line number # will match the "original" calling location, rather than the wrapper here def warning(self, *args, **kwargs): - super().warning(*args, stacklevel=2, **kwargs) + if "stacklevel" not in kwargs.keys(): + kwargs["stacklevel"] = 2 + super().warning(*args, **kwargs) if FatalLogger.warnings_fatal: raise RuntimeError("Warning encountered") def error(self, *args, **kwargs): - super().error(*args, stacklevel=2, **kwargs) + if "stacklevel" not in kwargs.keys(): + kwargs["stacklevel"] = 2 + super().error(*args, **kwargs) if FatalLogger.errors_fatal: raise RuntimeError("Error encountered") From f69e2cca6b5d26c8a6b2f3f4444a2c3de2e2d202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ricks?= Date: Sun, 17 Dec 2023 13:56:33 +0100 Subject: [PATCH 352/465] Add type hints for settings module Types make it easier to understand the code and improve autocompletion in IDEs. --- pelican/settings.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/pelican/settings.py b/pelican/settings.py index 33ec210a..29051ddb 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -8,11 +8,13 @@ import re import sys from os.path import isabs from pathlib import Path +from types import ModuleType +from typing import Any, Dict, Optional from pelican.log import LimitFilter -def load_source(name, path): +def load_source(name: str, path: str) -> ModuleType: spec = importlib.util.spec_from_file_location(name, path) mod = importlib.util.module_from_spec(spec) sys.modules[name] = mod @@ -22,6 +24,8 @@ def load_source(name, path): logger = logging.getLogger(__name__) +Settings = Dict[str, Any] + DEFAULT_THEME = os.path.join( os.path.dirname(os.path.abspath(__file__)), "themes", "notmyidea" ) @@ -177,7 +181,9 @@ DEFAULT_CONFIG = { PYGMENTS_RST_OPTIONS = None -def read_settings(path=None, override=None): +def read_settings( + path: Optional[str] = None, override: Optional[Settings] = None +) -> Settings: settings = override or {} if path: @@ -221,7 +227,7 @@ def read_settings(path=None, override=None): return settings -def get_settings_from_module(module=None): +def get_settings_from_module(module: Optional[ModuleType] = None) -> Settings: """Loads settings from a module, returns a dictionary.""" context = {} @@ -230,7 +236,7 @@ def get_settings_from_module(module=None): return context -def get_settings_from_file(path): +def get_settings_from_file(path: str) -> Settings: """Loads settings from a file path, returning a dict.""" name, ext = os.path.splitext(os.path.basename(path)) @@ -238,7 +244,7 @@ def get_settings_from_file(path): return get_settings_from_module(module) -def get_jinja_environment(settings): +def get_jinja_environment(settings: Settings) -> Settings: """Sets the environment for Jinja""" jinja_env = settings.setdefault( @@ -253,7 +259,7 @@ def get_jinja_environment(settings): return settings -def _printf_s_to_format_field(printf_string, format_field): +def _printf_s_to_format_field(printf_string: str, format_field: str) -> str: """Tries to replace %s with {format_field} in the provided printf_string. Raises ValueError in case of failure. """ @@ -269,7 +275,7 @@ def _printf_s_to_format_field(printf_string, format_field): return result -def handle_deprecated_settings(settings): +def handle_deprecated_settings(settings: Settings) -> Settings: """Converts deprecated settings and issues warnings. Issues an exception if both old and new setting is specified. """ @@ -566,7 +572,7 @@ def handle_deprecated_settings(settings): return settings -def configure_settings(settings): +def configure_settings(settings: Settings) -> Settings: """Provide optimizations, error checking, and warnings for the given settings. Also, specify the log messages to be ignored. From bf4fd679a5322433cc4313a80cac49d4ed6c348f Mon Sep 17 00:00:00 2001 From: boxydog <93335439+boxydog@users.noreply.github.com> Date: Mon, 15 Jan 2024 03:43:19 -0600 Subject: [PATCH 353/465] Document how to import posts from Medium (#3262) --- docs/importer.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/importer.rst b/docs/importer.rst index 997a4632..08092984 100644 --- a/docs/importer.rst +++ b/docs/importer.rst @@ -26,6 +26,12 @@ not be converted (as Pelican also supports Markdown). manually, or use a plugin such as `More Categories`_ that enables multiple categories per article. +.. note:: + + Imported pages may contain links to images that still point to the original site. + So you might want to download those images into your local content and manually + re-link them from the relevant pages of your site. + Dependencies ============ @@ -121,6 +127,15 @@ For WordPress:: $ pelican-import --wpfile -o ~/output ~/posts.xml +For Medium (an example of using an RSS feed): + + $ python -m pip install feedparser + $ pelican-import --feed https://medium.com/feed/@username + +.. note:: + + The RSS feed may only return the most recent posts — not all of them. + Tests ===== From 5e6dba73acfd6a85560d82870d1cda9d184c3cb5 Mon Sep 17 00:00:00 2001 From: Salar Nosrati-Ershad Date: Mon, 15 Jan 2024 13:33:54 +0330 Subject: [PATCH 354/465] Add Github Pages commit message variable (#3250) --- pelican/tools/templates/Makefile.jinja2 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pelican/tools/templates/Makefile.jinja2 b/pelican/tools/templates/Makefile.jinja2 index 93ab1aa7..1e9dbff5 100644 --- a/pelican/tools/templates/Makefile.jinja2 +++ b/pelican/tools/templates/Makefile.jinja2 @@ -37,6 +37,7 @@ DROPBOX_DIR={{dropbox_dir}} {% endif %} {% if github %} GITHUB_PAGES_BRANCH={{github_pages_branch}} +GITHUB_PAGES_COMMIT_MESSAGE=Generate Pelican site {% endif %} @@ -161,7 +162,7 @@ cf_upload: publish {% if github %} {% set upload = upload + ["github"] %} github: publish - ghp-import -m "Generate Pelican site" -b $(GITHUB_PAGES_BRANCH) "$(OUTPUTDIR)" + ghp-import -m "$(GITHUB_PAGES_COMMIT_MESSAGE)" -b $(GITHUB_PAGES_BRANCH) "$(OUTPUTDIR)" git push origin $(GITHUB_PAGES_BRANCH) {% endif %} From b1cb6c7326e32afba373113b86d823d46f94a812 Mon Sep 17 00:00:00 2001 From: Salar Nosrati-Ershad Date: Mon, 15 Jan 2024 13:40:12 +0330 Subject: [PATCH 355/465] Use `--no-jekyll` flag when invoking `ghp-import` (#3259) --- pelican/tools/templates/Makefile.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/templates/Makefile.jinja2 b/pelican/tools/templates/Makefile.jinja2 index 1e9dbff5..67571b47 100644 --- a/pelican/tools/templates/Makefile.jinja2 +++ b/pelican/tools/templates/Makefile.jinja2 @@ -162,7 +162,7 @@ cf_upload: publish {% if github %} {% set upload = upload + ["github"] %} github: publish - ghp-import -m "$(GITHUB_PAGES_COMMIT_MESSAGE)" -b $(GITHUB_PAGES_BRANCH) "$(OUTPUTDIR)" + ghp-import -m "$(GITHUB_PAGES_COMMIT_MESSAGE)" -b $(GITHUB_PAGES_BRANCH) "$(OUTPUTDIR)" --no-jekyll git push origin $(GITHUB_PAGES_BRANCH) {% endif %} From d6a33f1d21a8cbb34b584895554147ad97e97a72 Mon Sep 17 00:00:00 2001 From: boxydog Date: Fri, 1 Dec 2023 11:27:16 -0600 Subject: [PATCH 356/465] Medium post importer (from medium export) --- docs/content.rst | 4 +- docs/importer.rst | 13 +- pelican/tests/content/medium_post_content.txt | 4 + ...2017-04-21_-medium-post--d1bf01d62ba3.html | 72 ++++++++ pelican/tests/test_generators.py | 37 +++- pelican/tests/test_importer.py | 83 +++++++++ pelican/tools/pelican_import.py | 165 +++++++++++++++++- 7 files changed, 357 insertions(+), 21 deletions(-) create mode 100644 pelican/tests/content/medium_post_content.txt create mode 100644 pelican/tests/content/medium_posts/2017-04-21_-medium-post--d1bf01d62ba3.html diff --git a/docs/content.rst b/docs/content.rst index cacacea9..46db1140 100644 --- a/docs/content.rst +++ b/docs/content.rst @@ -439,8 +439,8 @@ For **Markdown**, one must rely on an extension. For example, using the `mdx_inc Importing an existing site ========================== -It is possible to import your site from WordPress, Tumblr, Dotclear, and RSS -feeds using a simple script. See :ref:`import`. +It is possible to import your site from several other blogging sites +(like WordPress, Tumblr, ..) using a simple script. See :ref:`import`. Translations ============ diff --git a/docs/importer.rst b/docs/importer.rst index 997a4632..093ef465 100644 --- a/docs/importer.rst +++ b/docs/importer.rst @@ -11,6 +11,7 @@ software to reStructuredText or Markdown. The supported import formats are: - Blogger XML export - Dotclear export +- Medium export - Tumblr API - WordPress XML export - RSS/Atom feed @@ -65,6 +66,7 @@ Optional arguments -h, --help Show this help message and exit --blogger Blogger XML export (default: False) --dotclear Dotclear export (default: False) + --medium Medium export (default: False) --tumblr Tumblr API (default: False) --wpfile WordPress XML export (default: False) --feed Feed to parse (default: False) @@ -80,8 +82,7 @@ Optional arguments (default: False) --filter-author Import only post from the specified author --strip-raw Strip raw HTML code that can't be converted to markup - such as flash embeds or iframes (wordpress import - only) (default: False) + such as flash embeds or iframes (default: False) --wp-custpost Put wordpress custom post types in directories. If used with --dir-cat option directories will be created as "/post_type/category/" (wordpress import only) @@ -113,6 +114,14 @@ For Dotclear:: $ pelican-import --dotclear -o ~/output ~/backup.txt +For Medium:: + + $ pelican-import --medium -o ~/output ~/medium-export/posts/ + +The Medium export is a zip file. Unzip it, and point this tool to the +"posts" subdirectory. For more information on how to export, see +https://help.medium.com/hc/en-us/articles/115004745787-Export-your-account-data. + For Tumblr:: $ pelican-import --tumblr -o ~/output --blogname= diff --git a/pelican/tests/content/medium_post_content.txt b/pelican/tests/content/medium_post_content.txt new file mode 100644 index 00000000..5e21881c --- /dev/null +++ b/pelican/tests/content/medium_post_content.txt @@ -0,0 +1,4 @@ + +

          Title header

          A paragraph of content.

          Paragraph number two.

          A list:

          1. One.
          2. Two.
          3. Three.

          A link: link text.

          Header 2

          A block quote:

          quote words strong words

          after blockquote

          A figure caption.

          A final note: Cross-Validated has sometimes been helpful.


          Next: Next post +

          +

          By User Name on .

          Canonical link

          Exported from Medium on December 1, 2023.

          diff --git a/pelican/tests/content/medium_posts/2017-04-21_-medium-post--d1bf01d62ba3.html b/pelican/tests/content/medium_posts/2017-04-21_-medium-post--d1bf01d62ba3.html new file mode 100644 index 00000000..02d272dc --- /dev/null +++ b/pelican/tests/content/medium_posts/2017-04-21_-medium-post--d1bf01d62ba3.html @@ -0,0 +1,72 @@ +A title diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index af6f5b1a..8c257b55 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -264,6 +264,7 @@ class TestArticlesGenerator(unittest.TestCase): def test_generate_context(self): articles_expected = [ + ["A title", "published", "medium_posts", "article"], ["Article title", "published", "Default", "article"], [ "Article with markdown and summary metadata multi", @@ -391,13 +392,24 @@ class TestArticlesGenerator(unittest.TestCase): # terms of process order will define the name for that category categories = [cat.name for cat, _ in self.generator.categories] categories_alternatives = ( - sorted(["Default", "TestCategory", "Yeah", "test", "指導書"]), - sorted(["Default", "TestCategory", "yeah", "test", "指導書"]), + sorted( + ["Default", "TestCategory", "medium_posts", "Yeah", "test", "指導書"] + ), + sorted( + ["Default", "TestCategory", "medium_posts", "yeah", "test", "指導書"] + ), ) self.assertIn(sorted(categories), categories_alternatives) # test for slug categories = [cat.slug for cat, _ in self.generator.categories] - categories_expected = ["default", "testcategory", "yeah", "test", "zhi-dao-shu"] + categories_expected = [ + "default", + "testcategory", + "medium_posts", + "yeah", + "test", + "zhi-dao-shu", + ] self.assertEqual(sorted(categories), sorted(categories_expected)) def test_do_not_use_folder_as_category(self): @@ -549,7 +561,8 @@ class TestArticlesGenerator(unittest.TestCase): granularity: {period["period"] for period in periods} for granularity, periods in period_archives.items() } - expected = {"year": {(1970,), (2010,), (2012,), (2014,)}} + self.maxDiff = None + expected = {"year": {(1970,), (2010,), (2012,), (2014,), (2017,)}} self.assertEqual(expected, abbreviated_archives) # Month archives enabled: @@ -570,7 +583,7 @@ class TestArticlesGenerator(unittest.TestCase): for granularity, periods in period_archives.items() } expected = { - "year": {(1970,), (2010,), (2012,), (2014,)}, + "year": {(1970,), (2010,), (2012,), (2014,), (2017,)}, "month": { (1970, "January"), (2010, "December"), @@ -578,6 +591,7 @@ class TestArticlesGenerator(unittest.TestCase): (2012, "November"), (2012, "October"), (2014, "February"), + (2017, "April"), }, } self.assertEqual(expected, abbreviated_archives) @@ -602,7 +616,7 @@ class TestArticlesGenerator(unittest.TestCase): for granularity, periods in period_archives.items() } expected = { - "year": {(1970,), (2010,), (2012,), (2014,)}, + "year": {(1970,), (2010,), (2012,), (2014,), (2017,)}, "month": { (1970, "January"), (2010, "December"), @@ -610,6 +624,7 @@ class TestArticlesGenerator(unittest.TestCase): (2012, "November"), (2012, "October"), (2014, "February"), + (2017, "April"), }, "day": { (1970, "January", 1), @@ -619,6 +634,7 @@ class TestArticlesGenerator(unittest.TestCase): (2012, "October", 30), (2012, "October", 31), (2014, "February", 9), + (2017, "April", 21), }, } self.assertEqual(expected, abbreviated_archives) @@ -836,8 +852,12 @@ class TestArticlesGenerator(unittest.TestCase): categories = sorted([category.name for category, _ in generator.categories]) categories_expected = [ - sorted(["Default", "TestCategory", "yeah", "test", "指導書"]), - sorted(["Default", "TestCategory", "Yeah", "test", "指導書"]), + sorted( + ["Default", "TestCategory", "medium_posts", "yeah", "test", "指導書"] + ), + sorted( + ["Default", "TestCategory", "medium_posts", "Yeah", "test", "指導書"] + ), ] self.assertIn(categories, categories_expected) @@ -864,6 +884,7 @@ class TestArticlesGenerator(unittest.TestCase): generator.generate_context() expected = [ + "A title", "An Article With Code Block To Test Typogrify Ignore", "Article title", "Article with Nonconformant HTML meta tags", diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index 05ef5bbd..916c1183 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -21,6 +21,10 @@ from pelican.tools.pelican_import import ( get_attachments, tumblr2fields, wp2fields, + mediumpost2fields, + mediumposts2fields, + strip_medium_post_content, + medium_slug, ) from pelican.utils import path_to_file_url, slugify @@ -708,3 +712,82 @@ class TestTumblrImporter(TestCaseWithCLocale): posts, posts, ) + + +class TestMediumImporter(TestCaseWithCLocale): + def setUp(self): + super().setUp() + self.test_content_root = "pelican/tests/content" + # The content coming out of parsing is similar, but not the same. + # Beautiful soup rearranges the order of attributes, for example. + # So, we keep a copy of the content for the test. + content_filename = f"{self.test_content_root}/medium_post_content.txt" + with open(content_filename, encoding="utf-8") as the_content_file: + # Many editors and scripts add a final newline, so live with that + # in our test + the_content = the_content_file.read() + assert the_content[-1] == "\n" + the_content = the_content[:-1] + self.post_tuple = ( + "A title", + the_content, + # slug: + "2017-04-21-medium-post", + "2017-04-21 17:11", + "User Name", + None, + (), + "published", + "article", + "html", + ) + + def test_mediumpost2field(self): + """Parse one post""" + post_filename = f"{self.test_content_root}/medium_posts/2017-04-21_-medium-post--d1bf01d62ba3.html" + val = mediumpost2fields(post_filename) + self.assertEqual(self.post_tuple, val, val) + + def test_mediumposts2field(self): + """Parse all posts in an export directory""" + posts = [ + fields + for fields in mediumposts2fields(f"{self.test_content_root}/medium_posts") + ] + self.assertEqual(1, len(posts)) + self.assertEqual(self.post_tuple, posts[0]) + + def test_strip_content(self): + """Strip out unhelpful tags""" + html_doc = ( + "
          This keeps lots of tags, but not " + "the
          section
          tags
          " + ) + soup = BeautifulSoup(html_doc, "html.parser") + self.assertEqual( + "This keeps lots of tags, but not the section tags", + strip_medium_post_content(soup), + ) + + def test_medium_slug(self): + # Remove hex stuff at the end + self.assertEqual( + "2017-04-27_A-long-title", + medium_slug( + "medium-export/posts/2017-04-27_A-long-title--2971442227dd.html" + ), + ) + # Remove "--DRAFT" at the end + self.assertEqual( + "2017-04-27_A-long-title", + medium_slug("medium-export/posts/2017-04-27_A-long-title--DRAFT.html"), + ) + # Remove both (which happens) + self.assertEqual( + "draft_How-to-do", medium_slug("draft_How-to-do--DRAFT--87225c81dddd.html") + ) + # If no hex stuff, leave it alone + self.assertEqual( + "2017-04-27_A-long-title", + medium_slug("medium-export/posts/2017-04-27_A-long-title.html"), + ) diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 681a5c45..eb343860 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -15,6 +15,8 @@ from urllib.error import URLError from urllib.parse import quote, urlparse, urlsplit, urlunsplit from urllib.request import urlretrieve +import dateutil.parser + # because logging.setLoggerClass has to be called before logging.getLogger from pelican.log import init from pelican.settings import DEFAULT_CONFIG @@ -114,19 +116,25 @@ def decode_wp_content(content, br=True): return content -def xml_to_soup(xml): - """Opens an xml file""" +def _import_bs4(): + """Import and return bs4, otherwise sys.exit.""" try: - from bs4 import BeautifulSoup + import bs4 except ImportError: error = ( 'Missing dependency "BeautifulSoup4" and "lxml" required to ' "import XML files." ) sys.exit(error) + return bs4 + + +def file_to_soup(xml, features="xml"): + """Reads a file, returns soup.""" + bs4 = _import_bs4() with open(xml, encoding="utf-8") as infile: xmlfile = infile.read() - soup = BeautifulSoup(xmlfile, "xml") + soup = bs4.BeautifulSoup(xmlfile, features) return soup @@ -140,7 +148,7 @@ def get_filename(post_name, post_id): def wp2fields(xml, wp_custpost=False): """Opens a wordpress XML file, and yield Pelican fields""" - soup = xml_to_soup(xml) + soup = file_to_soup(xml) items = soup.rss.channel.findAll("item") for item in items: if item.find("status").string in ["publish", "draft"]: @@ -210,7 +218,7 @@ def wp2fields(xml, wp_custpost=False): def blogger2fields(xml): """Opens a blogger XML file, and yield Pelican fields""" - soup = xml_to_soup(xml) + soup = file_to_soup(xml) entries = soup.feed.findAll("entry") for entry in entries: raw_kind = entry.find( @@ -536,6 +544,133 @@ def tumblr2fields(api_key, blogname): posts = _get_tumblr_posts(api_key, blogname, offset) +def strip_medium_post_content(soup) -> str: + """Strip some tags and attributes from medium post content. + + For example, the 'section' and 'div' tags cause trouble while rendering. + + The problem with these tags is you can get a section divider (--------------) + that is not between two pieces of content. For example: + + Some text. + + .. container:: section-divider + + -------------- + + .. container:: section-content + + More content. + + In this case, pandoc complains: "Unexpected section title or transition." + + Also, the "id" and "name" attributes in tags cause similar problems. They show + up in .rst as extra junk that separates transitions. + """ + # Remove tags + # section and div cause problems + # footer also can cause problems, and has nothing we want to keep + # See https://stackoverflow.com/a/8439761 + invalid_tags = ["section", "div", "footer"] + for tag in invalid_tags: + for match in soup.findAll(tag): + match.replaceWithChildren() + + # Remove attributes + # See https://stackoverflow.com/a/9045719 + invalid_attributes = ["name", "id", "class"] + bs4 = _import_bs4() + for tag in soup.descendants: + if isinstance(tag, bs4.element.Tag): + tag.attrs = { + key: value + for key, value in tag.attrs.items() + if key not in invalid_attributes + } + + # Get the string of all content, keeping other tags + all_content = "".join(str(element) for element in soup.contents) + return all_content + + +def mediumpost2fields(filepath: str) -> tuple: + """Take an HTML post from a medium export, return Pelican fields.""" + + soup = file_to_soup(filepath, "html.parser") + if not soup: + raise ValueError(f"{filepath} could not be parsed by beautifulsoup") + kind = "article" + + content = soup.find("section", class_="e-content") + if not content: + raise ValueError(f"{filepath}: Post has no content") + + title = soup.find("title").string or "" + + raw_date = soup.find("time", class_="dt-published") + date = None + if raw_date: + # This datetime can include timezone, e.g., "2017-04-21T17:11:55.799Z" + # python before 3.11 can't parse the timezone using datetime.fromisoformat + # See also https://docs.python.org/3.10/library/datetime.html#datetime.datetime.fromisoformat + # "This does not support parsing arbitrary ISO 8601 strings" + # So, we use dateutil.parser, which can handle it. + date_object = dateutil.parser.parse(raw_date.attrs["datetime"]) + date = date_object.strftime("%Y-%m-%d %H:%M") + status = "published" + else: + status = "draft" + author = soup.find("a", class_="p-author h-card") + if author: + author = author.string + + # Now that we're done with classes, we can strip the content + content = strip_medium_post_content(content) + + # medium HTML export doesn't have tag or category + # RSS feed has tags, but it doesn't have all the posts. + tags = () + + slug = medium_slug(filepath) + + # TODO: make the fields a python dataclass + return ( + title, + content, + slug, + date, + author, + None, + tags, + status, + kind, + "html", + ) + + +def medium_slug(filepath: str) -> str: + """Make the filepath of a medium exported file into a slug.""" + # slug: filename without extension + slug = os.path.basename(filepath) + slug = os.path.splitext(slug)[0] + # A medium export filename looks like date_-title-...html + # But, RST doesn't like "_-" (see https://github.com/sphinx-doc/sphinx/issues/4350) + # so get rid of it + slug = slug.replace("_-", "-") + # drop the hex string medium puts on the end of the filename, why keep it. + # e.g., "-a8a8a8a8" or "---a9a9a9a9" + # also: drafts don't need "--DRAFT" + slug = re.sub(r"((-)+([0-9a-f]+|DRAFT))+$", "", slug) + return slug + + +def mediumposts2fields(medium_export_dir: str): + """Take HTML posts in a medium export directory, and yield Pelican fields.""" + for file in os.listdir(medium_export_dir): + filename = os.fsdecode(file) + yield mediumpost2fields(os.path.join(medium_export_dir, filename)) + + def feed2fields(file): """Read a feed and yield pelican fields""" import feedparser @@ -711,7 +846,7 @@ def get_attachments(xml): """returns a dictionary of posts that have attachments with a list of the attachment_urls """ - soup = xml_to_soup(xml) + soup = file_to_soup(xml) items = soup.rss.channel.findAll("item") names = {} attachments = [] @@ -837,6 +972,9 @@ def fields2pelican( posts_require_pandoc.append(filename) slug = not disable_slugs and filename or None + assert slug is None or filename == os.path.basename( + filename + ), f"filename is not a basename: {filename}" if wp_attach and attachments: try: @@ -984,6 +1122,9 @@ def main(): parser.add_argument( "--dotclear", action="store_true", dest="dotclear", help="Dotclear export" ) + parser.add_argument( + "--medium", action="store_true", dest="medium", help="Medium export" + ) parser.add_argument( "--tumblr", action="store_true", dest="tumblr", help="Tumblr export" ) @@ -1069,6 +1210,8 @@ def main(): input_type = "blogger" elif args.dotclear: input_type = "dotclear" + elif args.medium: + input_type = "medium" elif args.tumblr: input_type = "tumblr" elif args.wpfile: @@ -1077,8 +1220,8 @@ def main(): input_type = "feed" else: error = ( - "You must provide either --blogger, --dotclear, " - "--tumblr, --wpfile or --feed options" + "You must provide one of --blogger, --dotclear, " + "--medium, --tumblr, --wpfile or --feed options" ) exit(error) @@ -1097,12 +1240,16 @@ def main(): fields = blogger2fields(args.input) elif input_type == "dotclear": fields = dc2fields(args.input) + elif input_type == "medium": + fields = mediumposts2fields(args.input) elif input_type == "tumblr": fields = tumblr2fields(args.input, args.blogname) elif input_type == "wordpress": fields = wp2fields(args.input, args.wp_custpost or False) elif input_type == "feed": fields = feed2fields(args.input) + else: + raise ValueError(f"Unhandled input_type {input_type}") if args.wp_attach: attachments = get_attachments(args.input) From fbe81a971a8f96eae6a13aee4471468f31cbf194 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 17 Jan 2024 09:48:05 +0100 Subject: [PATCH 357/465] Delete RELEASE.md --- RELEASE.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 RELEASE.md diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 7881aeac..00000000 --- a/RELEASE.md +++ /dev/null @@ -1,3 +0,0 @@ -Release type: patch - -Keep the newline at the end of the file in generating tools scripts From d39dd9b85f0309e4101e74a270fd2ce97f051a84 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Sun, 21 Jan 2024 22:52:56 -0700 Subject: [PATCH 358/465] Resolve inter-site links in summaries. c.f. https://github.com/getpelican/pelican/issues/3265 c.f. https://github.com/MinchinWeb/minchin.pelican.plugins.summary/issues/5 --- pelican/__init__.py | 7 +++++-- pelican/contents.py | 17 +++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index a25f5624..1a3090f8 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -120,12 +120,15 @@ class Pelican: if hasattr(p, "generate_context"): p.generate_context() + # for plugins that create/edit the summary + logger.debug("Signal all_generators_finalized.send()") + signals.all_generators_finalized.send(generators) + + # update links in the summary, etc for p in generators: if hasattr(p, "refresh_metadata_intersite_links"): p.refresh_metadata_intersite_links() - signals.all_generators_finalized.send(generators) - writer = self._get_writer() for p in generators: diff --git a/pelican/contents.py b/pelican/contents.py index 474e5bbf..27b8bbc3 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -520,12 +520,17 @@ class Content: # _summary is an internal variable that some plugins may be writing to, # so ensure changes to it are picked up - if ( - "summary" in self.settings["FORMATTED_FIELDS"] - and "summary" in self.metadata - ): - self._summary = self._update_content(self._summary, self.get_siteurl()) - self.metadata["summary"] = self._summary + if "summary" in self.settings["FORMATTED_FIELDS"]: + if hasattr(self, "_summary"): + self.metadata["summary"] = self._summary + + if "summary" in self.metadata: + self.metadata["summary"] = self._update_content( + self.metadata["summary"], self.get_siteurl() + ) + + if hasattr(self, "_summary") and "summary" in self.metadata: + self._summary = self.metadata["summary"] class Page(Content): From 2fa5c515b0232ce212a3d83827de88b01deaa598 Mon Sep 17 00:00:00 2001 From: namori <157323136+nam-ori@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:43:07 +0100 Subject: [PATCH 359/465] Feeds - Update generators.py to fix a bug with slugs (#3279) --- pelican/generators.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index 3b5ca9e4..076c8d38 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -384,8 +384,8 @@ class ArticlesGenerator(CachingGenerator): str(self.settings["CATEGORY_FEED_ATOM"]).format(slug=cat.slug), self.settings.get( "CATEGORY_FEED_ATOM_URL", - str(self.settings["CATEGORY_FEED_ATOM"]).format(slug=cat.slug), - ), + str(self.settings["CATEGORY_FEED_ATOM"]), + ).format(slug=cat.slug), feed_title=cat.name, ) @@ -396,8 +396,8 @@ class ArticlesGenerator(CachingGenerator): str(self.settings["CATEGORY_FEED_RSS"]).format(slug=cat.slug), self.settings.get( "CATEGORY_FEED_RSS_URL", - str(self.settings["CATEGORY_FEED_RSS"]).format(slug=cat.slug), - ), + str(self.settings["CATEGORY_FEED_RSS"]), + ).format(slug=cat.slug), feed_title=cat.name, feed_type="rss", ) @@ -410,8 +410,8 @@ class ArticlesGenerator(CachingGenerator): str(self.settings["AUTHOR_FEED_ATOM"]).format(slug=auth.slug), self.settings.get( "AUTHOR_FEED_ATOM_URL", - str(self.settings["AUTHOR_FEED_ATOM"]).format(slug=auth.slug), - ), + str(self.settings["AUTHOR_FEED_ATOM"]), + ).format(slug=auth.slug), feed_title=auth.name, ) @@ -422,8 +422,8 @@ class ArticlesGenerator(CachingGenerator): str(self.settings["AUTHOR_FEED_RSS"]).format(slug=auth.slug), self.settings.get( "AUTHOR_FEED_RSS_URL", - str(self.settings["AUTHOR_FEED_RSS"]).format(slug=auth.slug), - ), + str(self.settings["AUTHOR_FEED_RSS"]), + ).format(slug=auth.slug), feed_title=auth.name, feed_type="rss", ) @@ -437,8 +437,8 @@ class ArticlesGenerator(CachingGenerator): str(self.settings["TAG_FEED_ATOM"]).format(slug=tag.slug), self.settings.get( "TAG_FEED_ATOM_URL", - str(self.settings["TAG_FEED_ATOM"]).format(slug=tag.slug), - ), + str(self.settings["TAG_FEED_ATOM"]), + ).format(slug=tag.slug), feed_title=tag.name, ) @@ -449,8 +449,8 @@ class ArticlesGenerator(CachingGenerator): str(self.settings["TAG_FEED_RSS"]).format(slug=tag.slug), self.settings.get( "TAG_FEED_RSS_URL", - str(self.settings["TAG_FEED_RSS"]).format(slug=tag.slug), - ), + str(self.settings["TAG_FEED_RSS"]), + ).format(slug=tag.slug), feed_title=tag.name, feed_type="rss", ) @@ -471,10 +471,8 @@ class ArticlesGenerator(CachingGenerator): str(self.settings["TRANSLATION_FEED_ATOM"]).format(lang=lang), self.settings.get( "TRANSLATION_FEED_ATOM_URL", - str(self.settings["TRANSLATION_FEED_ATOM"]).format( - lang=lang - ), - ), + str(self.settings["TRANSLATION_FEED_ATOM"]), + ).format(lang=lang), ) if self.settings.get("TRANSLATION_FEED_RSS"): writer.write_feed( From 3a662ace031a20d15f4933c028b3fffd1b588430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ricks?= Date: Thu, 18 Jan 2024 17:17:29 +0100 Subject: [PATCH 360/465] Add type hints for contents module Types make it easier to understand the code and improve autocompletion in IDEs. --- pelican/contents.py | 95 +++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 474e5bbf..82be8f73 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -6,7 +6,8 @@ import os import re from datetime import timezone from html import unescape -from urllib.parse import unquote, urljoin, urlparse, urlunparse +from typing import Any, Dict, Optional, Set, Tuple +from urllib.parse import ParseResult, unquote, urljoin, urlparse, urlunparse try: from zoneinfo import ZoneInfo @@ -15,7 +16,7 @@ except ModuleNotFoundError: from pelican.plugins import signals -from pelican.settings import DEFAULT_CONFIG +from pelican.settings import DEFAULT_CONFIG, Settings from pelican.utils import ( deprecated_attribute, memoized, @@ -44,12 +45,20 @@ class Content: """ + default_template: Optional[str] = None + mandatory_properties: Tuple[str, ...] = () + @deprecated_attribute(old="filename", new="source_path", since=(3, 2, 0)) def filename(): return None def __init__( - self, content, metadata=None, settings=None, source_path=None, context=None + self, + content: str, + metadata: Optional[Dict[str, Any]] = None, + settings: Optional[Settings] = None, + source_path: Optional[str] = None, + context: Optional[Dict[Any, Any]] = None, ): if metadata is None: metadata = {} @@ -156,10 +165,10 @@ class Content: signals.content_object_init.send(self) - def __str__(self): + def __str__(self) -> str: return self.source_path or repr(self) - def _has_valid_mandatory_properties(self): + def _has_valid_mandatory_properties(self) -> bool: """Test mandatory properties are set.""" for prop in self.mandatory_properties: if not hasattr(self, prop): @@ -169,7 +178,7 @@ class Content: return False return True - def _has_valid_save_as(self): + def _has_valid_save_as(self) -> bool: """Return true if save_as doesn't write outside output path, false otherwise.""" try: @@ -190,7 +199,7 @@ class Content: return True - def _has_valid_status(self): + def _has_valid_status(self) -> bool: if hasattr(self, "allowed_statuses"): if self.status not in self.allowed_statuses: logger.error( @@ -204,7 +213,7 @@ class Content: # if undefined we allow all return True - def is_valid(self): + def is_valid(self) -> bool: """Validate Content""" # Use all() to not short circuit and get results of all validations return all( @@ -216,7 +225,7 @@ class Content: ) @property - def url_format(self): + def url_format(self) -> Dict[str, Any]: """Returns the URL, formatted with the proper values""" metadata = copy.copy(self.metadata) path = self.metadata.get("path", self.get_relative_source_path()) @@ -232,19 +241,19 @@ class Content: ) return metadata - def _expand_settings(self, key, klass=None): + def _expand_settings(self, key: str, klass: Optional[str] = None) -> str: if not klass: klass = self.__class__.__name__ fq_key = (f"{klass}_{key}").upper() return str(self.settings[fq_key]).format(**self.url_format) - def get_url_setting(self, key): + def get_url_setting(self, key: str) -> str: if hasattr(self, "override_" + key): return getattr(self, "override_" + key) key = key if self.in_default_lang else "lang_%s" % key return self._expand_settings(key) - def _link_replacer(self, siteurl, m): + def _link_replacer(self, siteurl: str, m: re.Match) -> str: what = m.group("what") value = urlparse(m.group("value")) path = value.path @@ -272,15 +281,15 @@ class Content: # XXX Put this in a different location. if what in {"filename", "static", "attach"}: - def _get_linked_content(key, url): + def _get_linked_content(key: str, url: ParseResult) -> Optional[Content]: nonlocal value - def _find_path(path): + def _find_path(path: str) -> Optional[Content]: if path.startswith("/"): path = path[1:] else: # relative to the source path of this content - path = self.get_relative_source_path( + path = self.get_relative_source_path( # type: ignore os.path.join(self.relative_dir, path) ) return self._context[key].get(path, None) @@ -324,7 +333,7 @@ class Content: linked_content = _get_linked_content(key, value) if linked_content: if what == "attach": - linked_content.attach_to(self) + linked_content.attach_to(self) # type: ignore origin = joiner(siteurl, linked_content.url) origin = origin.replace("\\", "/") # for Windows paths. else: @@ -359,7 +368,7 @@ class Content: return "".join((m.group("markup"), m.group("quote"), origin, m.group("quote"))) - def _get_intrasite_link_regex(self): + def _get_intrasite_link_regex(self) -> re.Pattern: intrasite_link_regex = self.settings["INTRASITE_LINK_REGEX"] regex = r""" (?P<[^\>]+ # match tag with all url-value attributes @@ -370,7 +379,7 @@ class Content: (?P=quote)""".format(intrasite_link_regex) return re.compile(regex, re.X) - def _update_content(self, content, siteurl): + def _update_content(self, content: str, siteurl: str) -> str: """Update the content attribute. Change all the relative paths of the content to relative paths @@ -386,7 +395,7 @@ class Content: hrefs = self._get_intrasite_link_regex() return hrefs.sub(lambda m: self._link_replacer(siteurl, m), content) - def get_static_links(self): + def get_static_links(self) -> Set[str]: static_links = set() hrefs = self._get_intrasite_link_regex() for m in hrefs.finditer(self._content): @@ -402,15 +411,15 @@ class Content: path = self.get_relative_source_path( os.path.join(self.relative_dir, path) ) - path = path.replace("%20", " ") + path = path.replace("%20", " ") # type: ignore static_links.add(path) return static_links - def get_siteurl(self): + def get_siteurl(self) -> str: return self._context.get("localsiteurl", "") @memoized - def get_content(self, siteurl): + def get_content(self, siteurl: str) -> str: if hasattr(self, "_get_content"): content = self._get_content() else: @@ -418,11 +427,11 @@ class Content: return self._update_content(content, siteurl) @property - def content(self): + def content(self) -> str: return self.get_content(self.get_siteurl()) @memoized - def get_summary(self, siteurl): + def get_summary(self, siteurl: str) -> str: """Returns the summary of an article. This is based on the summary metadata if set, otherwise truncate the @@ -441,10 +450,10 @@ class Content: ) @property - def summary(self): + def summary(self) -> str: return self.get_summary(self.get_siteurl()) - def _get_summary(self): + def _get_summary(self) -> str: """deprecated function to access summary""" logger.warning( @@ -454,34 +463,36 @@ class Content: return self.summary @summary.setter - def summary(self, value): + def summary(self, value: str): """Dummy function""" pass @property - def status(self): + def status(self) -> str: return self._status @status.setter - def status(self, value): + def status(self, value: str) -> None: # TODO maybe typecheck self._status = value.lower() @property - def url(self): + def url(self) -> str: return self.get_url_setting("url") @property - def save_as(self): + def save_as(self) -> str: return self.get_url_setting("save_as") - def _get_template(self): + def _get_template(self) -> str: if hasattr(self, "template") and self.template is not None: return self.template else: return self.default_template - def get_relative_source_path(self, source_path=None): + def get_relative_source_path( + self, source_path: Optional[str] = None + ) -> Optional[str]: """Return the relative path (from the content path) to the given source_path. @@ -501,7 +512,7 @@ class Content: ) @property - def relative_dir(self): + def relative_dir(self) -> str: return posixize_path( os.path.dirname( os.path.relpath( @@ -511,7 +522,7 @@ class Content: ) ) - def refresh_metadata_intersite_links(self): + def refresh_metadata_intersite_links(self) -> None: for key in self.settings["FORMATTED_FIELDS"]: if key in self.metadata and key != "summary": value = self._update_content(self.metadata[key], self.get_siteurl()) @@ -534,7 +545,7 @@ class Page(Content): default_status = "published" default_template = "page" - def _expand_settings(self, key): + def _expand_settings(self, key: str) -> str: klass = "draft_page" if self.status == "draft" else None return super()._expand_settings(key, klass) @@ -561,7 +572,7 @@ class Article(Content): if not hasattr(self, "date") and self.status == "draft": self.date = datetime.datetime.max.replace(tzinfo=self.timezone) - def _expand_settings(self, key): + def _expand_settings(self, key: str) -> str: klass = "draft" if self.status == "draft" else "article" return super()._expand_settings(key, klass) @@ -571,7 +582,7 @@ class Static(Content): default_status = "published" default_template = None - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self._output_location_referenced = False @@ -588,18 +599,18 @@ class Static(Content): return None @property - def url(self): + def url(self) -> str: # Note when url has been referenced, so we can avoid overriding it. self._output_location_referenced = True return super().url @property - def save_as(self): + def save_as(self) -> str: # Note when save_as has been referenced, so we can avoid overriding it. self._output_location_referenced = True return super().save_as - def attach_to(self, content): + def attach_to(self, content: Content) -> None: """Override our output directory with that of the given content object.""" # Determine our file's new output path relative to the linking @@ -624,7 +635,7 @@ class Static(Content): new_url = path_to_url(new_save_as) - def _log_reason(reason): + def _log_reason(reason: str) -> None: logger.warning( "The {attach} link in %s cannot relocate " "%s because %s. Falling back to " From e4807316ae9338f05701a70d216687a94fb796d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ricks?= Date: Thu, 18 Jan 2024 09:18:00 +0100 Subject: [PATCH 361/465] Add type hints for utils module Types make it easier to understand the code and improve autocompletion in IDEs. --- pelican/utils.py | 143 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 46 deletions(-) diff --git a/pelican/utils.py b/pelican/utils.py index eda53d3f..5f161667 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import datetime import fnmatch import locale @@ -16,6 +18,21 @@ from html import entities from html.parser import HTMLParser from itertools import groupby from operator import attrgetter +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Collection, + Dict, + Generator, + Iterable, + List, + Optional, + Sequence, + Tuple, + Type, + Union, +) import dateutil.parser @@ -27,11 +44,15 @@ from markupsafe import Markup import watchfiles +if TYPE_CHECKING: + from pelican.contents import Content + from pelican.readers import Readers + from pelican.settings import Settings logger = logging.getLogger(__name__) -def sanitised_join(base_directory, *parts): +def sanitised_join(base_directory: str, *parts: str) -> str: joined = posixize_path(os.path.abspath(os.path.join(base_directory, *parts))) base = posixize_path(os.path.abspath(base_directory)) if not joined.startswith(base): @@ -40,7 +61,7 @@ def sanitised_join(base_directory, *parts): return joined -def strftime(date, date_format): +def strftime(date: datetime.datetime, date_format: str) -> str: """ Enhanced replacement for built-in strftime with zero stripping @@ -109,10 +130,10 @@ class DateFormatter: defined in LOCALE setting """ - def __init__(self): + def __init__(self) -> None: self.locale = locale.setlocale(locale.LC_TIME) - def __call__(self, date, date_format): + def __call__(self, date: datetime.datetime, date_format: str) -> str: # on OSX, encoding from LC_CTYPE determines the unicode output in PY3 # make sure it's same as LC_TIME with temporary_locale(self.locale, locale.LC_TIME), temporary_locale( @@ -131,11 +152,11 @@ class memoized: """ - def __init__(self, func): + def __init__(self, func: Callable) -> None: self.func = func - self.cache = {} + self.cache: Dict[Any, Any] = {} - def __call__(self, *args): + def __call__(self, *args) -> Any: if not isinstance(args, Hashable): # uncacheable. a list, for instance. # better to not cache than blow up. @@ -147,17 +168,23 @@ class memoized: self.cache[args] = value return value - def __repr__(self): + def __repr__(self) -> Optional[str]: return self.func.__doc__ - def __get__(self, obj, objtype): + def __get__(self, obj: Any, objtype): """Support instance methods.""" fn = partial(self.__call__, obj) fn.cache = self.cache return fn -def deprecated_attribute(old, new, since=None, remove=None, doc=None): +def deprecated_attribute( + old: str, + new: str, + since: Tuple[int, ...], + remove: Optional[Tuple[int, ...]] = None, + doc: Optional[str] = None, +): """Attribute deprecation decorator for gentle upgrades For example: @@ -198,7 +225,7 @@ def deprecated_attribute(old, new, since=None, remove=None, doc=None): return decorator -def get_date(string): +def get_date(string: str) -> datetime.datetime: """Return a datetime object from a string. If no format matches the given date, raise a ValueError. @@ -212,7 +239,9 @@ def get_date(string): @contextmanager -def pelican_open(filename, mode="r", strip_crs=(sys.platform == "win32")): +def pelican_open( + filename: str, mode: str = "r", strip_crs: bool = (sys.platform == "win32") +) -> Generator[str, None, None]: """Open a file and return its content""" # utf-8-sig will clear any BOM if present @@ -221,7 +250,12 @@ def pelican_open(filename, mode="r", strip_crs=(sys.platform == "win32")): yield content -def slugify(value, regex_subs=(), preserve_case=False, use_unicode=False): +def slugify( + value: str, + regex_subs: Iterable[Tuple[str, str]] = (), + preserve_case: bool = False, + use_unicode: bool = False, +) -> str: """ Normalizes string, converts to lowercase, removes non-alpha characters, and converts spaces to hyphens. @@ -233,9 +267,10 @@ def slugify(value, regex_subs=(), preserve_case=False, use_unicode=False): """ import unicodedata + import unidecode - def normalize_unicode(text): + def normalize_unicode(text: str) -> str: # normalize text by compatibility composition # see: https://en.wikipedia.org/wiki/Unicode_equivalence return unicodedata.normalize("NFKC", text) @@ -262,7 +297,9 @@ def slugify(value, regex_subs=(), preserve_case=False, use_unicode=False): return value.strip() -def copy(source, destination, ignores=None): +def copy( + source: str, destination: str, ignores: Optional[Iterable[str]] = None +) -> None: """Recursively copy source into destination. If source is a file, destination has to be a file as well. @@ -334,7 +371,7 @@ def copy(source, destination, ignores=None): ) -def copy_file(source, destination): +def copy_file(source: str, destination: str) -> None: """Copy a file""" try: shutil.copyfile(source, destination) @@ -344,7 +381,7 @@ def copy_file(source, destination): ) -def clean_output_dir(path, retention): +def clean_output_dir(path: str, retention: Iterable[str]) -> None: """Remove all files from output directory except those in retention list""" if not os.path.exists(path): @@ -381,24 +418,24 @@ def clean_output_dir(path, retention): logger.error("Unable to delete %s, file type unknown", file) -def get_relative_path(path): +def get_relative_path(path: str) -> str: """Return the relative path from the given path to the root path.""" components = split_all(path) - if len(components) <= 1: + if components is None or len(components) <= 1: return os.curdir else: parents = [os.pardir] * (len(components) - 1) return os.path.join(*parents) -def path_to_url(path): +def path_to_url(path: str) -> str: """Return the URL corresponding to a given path.""" if path is not None: path = posixize_path(path) return path -def posixize_path(rel_path): +def posixize_path(rel_path: str) -> str: """Use '/' as path separator, so that source references, like '{static}/foo/bar.jpg' or 'extras/favicon.ico', will work on Windows as well as on Mac and Linux.""" @@ -427,20 +464,20 @@ class _HTMLWordTruncator(HTMLParser): _singlets = ("br", "col", "link", "base", "img", "param", "area", "hr", "input") class TruncationCompleted(Exception): - def __init__(self, truncate_at): + def __init__(self, truncate_at: int) -> None: super().__init__(truncate_at) self.truncate_at = truncate_at - def __init__(self, max_words): + def __init__(self, max_words: int) -> None: super().__init__(convert_charrefs=False) self.max_words = max_words self.words_found = 0 self.open_tags = [] self.last_word_end = None - self.truncate_at = None + self.truncate_at: Optional[int] = None - def feed(self, *args, **kwargs): + def feed(self, *args, **kwargs) -> None: try: super().feed(*args, **kwargs) except self.TruncationCompleted as exc: @@ -448,29 +485,29 @@ class _HTMLWordTruncator(HTMLParser): else: self.truncate_at = None - def getoffset(self): + def getoffset(self) -> int: line_start = 0 lineno, line_offset = self.getpos() for i in range(lineno - 1): line_start = self.rawdata.index("\n", line_start) + 1 return line_start + line_offset - def add_word(self, word_end): + def add_word(self, word_end: int) -> None: self.words_found += 1 self.last_word_end = None if self.words_found == self.max_words: raise self.TruncationCompleted(word_end) - def add_last_word(self): + def add_last_word(self) -> None: if self.last_word_end is not None: self.add_word(self.last_word_end) - def handle_starttag(self, tag, attrs): + def handle_starttag(self, tag: str, attrs: Any) -> None: self.add_last_word() if tag not in self._singlets: self.open_tags.insert(0, tag) - def handle_endtag(self, tag): + def handle_endtag(self, tag: str) -> None: self.add_last_word() try: i = self.open_tags.index(tag) @@ -481,7 +518,7 @@ class _HTMLWordTruncator(HTMLParser): # all unclosed intervening start tags with omitted end tags del self.open_tags[: i + 1] - def handle_data(self, data): + def handle_data(self, data: str) -> None: word_end = 0 offset = self.getoffset() @@ -499,7 +536,7 @@ class _HTMLWordTruncator(HTMLParser): if word_end < len(data): self.add_last_word() - def _handle_ref(self, name, char): + def _handle_ref(self, name: str, char: str) -> None: """ Called by handle_entityref() or handle_charref() when a ref like `—`, `—`, or `—` is found. @@ -543,7 +580,7 @@ class _HTMLWordTruncator(HTMLParser): else: self.add_last_word() - def handle_entityref(self, name): + def handle_entityref(self, name: str) -> None: """ Called when an entity ref like '—' is found @@ -556,7 +593,7 @@ class _HTMLWordTruncator(HTMLParser): char = "" self._handle_ref(name, char) - def handle_charref(self, name): + def handle_charref(self, name: str) -> None: """ Called when a char ref like '—' or '—' is found @@ -574,7 +611,7 @@ class _HTMLWordTruncator(HTMLParser): self._handle_ref("#" + name, char) -def truncate_html_words(s, num, end_text="…"): +def truncate_html_words(s: str, num: int, end_text: str = "…") -> str: """Truncates HTML to a certain number of words. (not counting tags and comments). Closes opened tags if they were correctly @@ -600,7 +637,10 @@ def truncate_html_words(s, num, end_text="…"): return out -def process_translations(content_list, translation_id=None): +def process_translations( + content_list: List[Content], + translation_id: Optional[Union[str, Collection[str]]] = None, +) -> Tuple[List[Content], List[Content]]: """Finds translations and returns them. For each content_list item, populates the 'translations' attribute, and @@ -658,7 +698,7 @@ def process_translations(content_list, translation_id=None): return index, translations -def get_original_items(items, with_str): +def get_original_items(items: List[Content], with_str: str) -> List[Content]: def _warn_source_paths(msg, items, *extra): args = [len(items)] args.extend(extra) @@ -698,7 +738,10 @@ def get_original_items(items, with_str): return original_items -def order_content(content_list, order_by="slug"): +def order_content( + content_list: List[Content], + order_by: Union[str, Callable[[Content], Any], None] = "slug", +) -> List[Content]: """Sorts content. order_by can be a string of an attribute or sorting function. If order_by @@ -758,7 +801,11 @@ def order_content(content_list, order_by="slug"): return content_list -def wait_for_changes(settings_file, reader_class, settings): +def wait_for_changes( + settings_file: str, + reader_class: Type["Readers"], + settings: "Settings", +): content_path = settings.get("PATH", "") theme_path = settings.get("THEME", "") ignore_files = { @@ -788,13 +835,15 @@ def wait_for_changes(settings_file, reader_class, settings): return next( watchfiles.watch( *watching_paths, - watch_filter=watchfiles.DefaultFilter(ignore_entity_patterns=ignore_files), + watch_filter=watchfiles.DefaultFilter(ignore_entity_patterns=ignore_files), # type: ignore rust_timeout=0, ) ) -def set_date_tzinfo(d, tz_name=None): +def set_date_tzinfo( + d: datetime.datetime, tz_name: Optional[str] = None +) -> datetime.datetime: """Set the timezone for dates that don't have tzinfo""" if tz_name and not d.tzinfo: timezone = ZoneInfo(tz_name) @@ -805,11 +854,11 @@ def set_date_tzinfo(d, tz_name=None): return d -def mkdir_p(path): +def mkdir_p(path: str) -> None: os.makedirs(path, exist_ok=True) -def split_all(path): +def split_all(path: Union[str, pathlib.Path, None]) -> Optional[Sequence[str]]: """Split a path into a list of components While os.path.split() splits a single component off the back of @@ -840,12 +889,12 @@ def split_all(path): ) -def path_to_file_url(path): +def path_to_file_url(path: str) -> str: """Convert file-system path to file:// URL""" return urllib.parse.urljoin("file://", urllib.request.pathname2url(path)) -def maybe_pluralize(count, singular, plural): +def maybe_pluralize(count: int, singular: str, plural: str) -> str: """ Returns a formatted string containing count and plural if count is not 1 Returns count and singular if count is 1 @@ -862,7 +911,9 @@ def maybe_pluralize(count, singular, plural): @contextmanager -def temporary_locale(temp_locale=None, lc_category=locale.LC_ALL): +def temporary_locale( + temp_locale: Optional[str] = None, lc_category: int = locale.LC_ALL +) -> Generator[None, None, None]: """ Enable code to run in a context with a temporary locale Resets the locale back when exiting context. From c36ab075269771834b5e05e4d1586d050743d457 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Fri, 26 Jan 2024 16:31:22 -0700 Subject: [PATCH 362/465] write back to `._summary` --- pelican/contents.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 27b8bbc3..e0629e2a 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -519,7 +519,7 @@ class Content: setattr(self, key.lower(), value) # _summary is an internal variable that some plugins may be writing to, - # so ensure changes to it are picked up + # so ensure changes to it are picked up, and write summary back to it if "summary" in self.settings["FORMATTED_FIELDS"]: if hasattr(self, "_summary"): self.metadata["summary"] = self._summary @@ -528,8 +528,6 @@ class Content: self.metadata["summary"] = self._update_content( self.metadata["summary"], self.get_siteurl() ) - - if hasattr(self, "_summary") and "summary" in self.metadata: self._summary = self.metadata["summary"] From f1f2ceccc757d9743dde39f626eccf05e3e9a5b0 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Sat, 27 Jan 2024 10:47:54 -0700 Subject: [PATCH 363/465] Warning/error logging: be explicit in how the `stacklevel` variable is handled --- pelican/log.py | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/pelican/log.py b/pelican/log.py index 6a8fcdf1..ef49d280 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -85,19 +85,39 @@ class FatalLogger(LimitLogger): warnings_fatal = False errors_fatal = False - # adding `stacklevel=2` means that the displayed filename and line number - # will match the "original" calling location, rather than the wrapper here - def warning(self, *args, **kwargs): - if "stacklevel" not in kwargs.keys(): - kwargs["stacklevel"] = 2 - super().warning(*args, **kwargs) + def warning(self, *args, stacklevel=1, **kwargs): + """ + Displays a logging warning. + + Wrapping it here allows Pelican to filter warnings, and conditionally + make warnings fatal. + + Args: + stacklevel (int): the stacklevel that would be used to display the + calling location, except for this function. Adjusting the + stacklevel allows you to see the "true" calling location of the + warning, rather than this wrapper location. + """ + stacklevel += 1 + super().warning(*args, stacklevel=stacklevel, **kwargs) if FatalLogger.warnings_fatal: raise RuntimeError("Warning encountered") - def error(self, *args, **kwargs): - if "stacklevel" not in kwargs.keys(): - kwargs["stacklevel"] = 2 - super().error(*args, **kwargs) + def error(self, *args, stacklevel=1, **kwargs): + """ + Displays a logging error. + + Wrapping it here allows Pelican to filter errors, and conditionally + make errors non-fatal. + + Args: + stacklevel (int): the stacklevel that would be used to display the + calling location, except for this function. Adjusting the + stacklevel allows you to see the "true" calling location of the + error, rather than this wrapper location. + """ + stacklevel += 1 + super().error(*args, stacklevel=stacklevel, **kwargs) if FatalLogger.errors_fatal: raise RuntimeError("Error encountered") From 1f14606f8339385c5176ba05adca4664a3ad8868 Mon Sep 17 00:00:00 2001 From: MinchinWeb Date: Sat, 27 Jan 2024 10:51:35 -0700 Subject: [PATCH 364/465] On failing to load a plugin, show the stacktrace is pelican is run in debug mode --- pelican/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 40251887..68f3e553 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -80,8 +80,14 @@ class Pelican: plugin.register() self.plugins.append(plugin) except Exception as e: - logger.error("Cannot register plugin `%s`\n%s", name, e, stacklevel=3) - print(e.stacktrace) + logger.error( + "Cannot register plugin `%s`\n%s", + name, + e, + stacklevel=2, + ) + if self.settings.get("DEBUG", False): + console.print_exception() self.settings["PLUGINS"] = [get_plugin_name(p) for p in self.plugins] From 7c7c9355b6c27122dbff6446cd366017f81eb0f2 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 12 Mar 2024 11:57:46 +0100 Subject: [PATCH 365/465] Pin Ruff to major semantic version 0.1.x Upgrading to 0.3.0+ requires code style changes to the code base. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c8bbe985..eb1884a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,7 +95,7 @@ dev = [ "pytest-xdist>=3.4.0", "tox>=4.11.3", "invoke>=2.2.0", - "ruff>=0.1.5", + "ruff>=0.1.5,<0.2.0", "tomli>=2.0.1; python_version < \"3.11\"", ] From 74541381848f1d65ec64463469b5980ba0646617 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 12 Mar 2024 12:05:09 +0100 Subject: [PATCH 366/465] Update `setup-python` & `setup-pdm` GitHub Actions --- .github/workflows/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cd646522..8cd63cc7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} cache: "pip" @@ -53,7 +53,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: pdm-project/setup-pdm@v3 + - uses: pdm-project/setup-pdm@v4 with: python-version: "3.11" cache: true @@ -71,7 +71,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: pdm-project/setup-pdm@v3 + - uses: pdm-project/setup-pdm@v4 with: python-version: "3.11" cache: true @@ -90,7 +90,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.11" cache: "pip" @@ -122,7 +122,7 @@ jobs: token: ${{ secrets.GH_TOKEN }} - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.11" From fabc40927750f52f11f27695e89ff76c0863a79f Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 12 Mar 2024 12:18:11 +0100 Subject: [PATCH 367/465] Update more GitHub Actions to resolve warnings --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8cd63cc7..4c0127df 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,7 +64,7 @@ jobs: - name: Run linters run: pdm lint --diff - name: Run pre-commit checks on all files - uses: pre-commit/action@v3.0.0 + uses: pre-commit/action@v3.0.1 build: name: Test build @@ -100,7 +100,7 @@ jobs: - name: Check run: tox -e docs - name: cache the docs for inspection - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docs path: docs/_build/html/ From b87308cfaaa269c44784cda69855ecaf298f9f5e Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 27 Mar 2024 08:25:48 +0100 Subject: [PATCH 368/465] Update Ruff dependency version --- .pre-commit-config.yaml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 333bc3c0..d6cfac07 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: forbid-new-submodules - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.5 + rev: v0.1.15 hooks: - id: ruff - id: ruff-format diff --git a/pyproject.toml b/pyproject.toml index eb1884a9..2f7d677c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,7 +95,7 @@ dev = [ "pytest-xdist>=3.4.0", "tox>=4.11.3", "invoke>=2.2.0", - "ruff>=0.1.5,<0.2.0", + "ruff>=0.1.15,<0.2.0", "tomli>=2.0.1; python_version < \"3.11\"", ] From 94bcd41f27d7f38a9dbd0847c6166e91a66d2090 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 27 Mar 2024 08:26:55 +0100 Subject: [PATCH 369/465] Ignore Sphinx 7.2.x package install warnings Sphinx 7.2+ requires Python 3.9+, which results in annoying warnings since we still support Python 3.8.x. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 2f7d677c..3ca06df4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,7 @@ changelog-header = "###############" version-header = "=" [tool.pdm] +ignore_package_warnings = ["sphinx"] [tool.pdm.scripts] docbuild = "invoke docbuild" From 666b962eb67369a2de45362b08c74524b5d49c57 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Fri, 19 Apr 2024 08:51:07 -0700 Subject: [PATCH 370/465] workaround Turkish locale issue --- pelican/utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pelican/utils.py b/pelican/utils.py index 5f161667..86698aee 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -132,6 +132,10 @@ class DateFormatter: def __init__(self) -> None: self.locale = locale.setlocale(locale.LC_TIME) + # python has issue with Turkish_Türkiye.1254 locale, replace it to + # something accepted: Turkish + if self.locale == "Turkish_Türkiye.1254": + self.locale = "Turkish" def __call__(self, date: datetime.datetime, date_format: str) -> str: # on OSX, encoding from LC_CTYPE determines the unicode output in PY3 @@ -922,6 +926,10 @@ def temporary_locale( class to use the C locale. """ orig_locale = locale.setlocale(lc_category) + # python has issue with Turkish_Türkiye.1254 locale, replace it to + # something accepted: Turkish + if orig_locale == "Turkish_Türkiye.1254": + orig_locale = "Turkish" if temp_locale: locale.setlocale(lc_category, temp_locale) yield From 0b5934a1fa56bb7ca9b0b0011e35f9043afc6ce9 Mon Sep 17 00:00:00 2001 From: GiovanH Date: Fri, 19 Apr 2024 13:54:27 -0500 Subject: [PATCH 371/465] Feature: Add setting to append `ref` parameter to links in feeds (#3249) --- docs/settings.rst | 5 +++++ pelican/settings.py | 1 + pelican/writers.py | 3 +++ 3 files changed, 9 insertions(+) diff --git a/docs/settings.rst b/docs/settings.rst index e9edffde..4ae608c6 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -1008,6 +1008,11 @@ the ``TAG_FEED_ATOM`` and ``TAG_FEED_RSS`` settings: to ``False``, the full content will be included instead. This setting doesn't affect Atom feeds, only RSS ones. +.. data:: FEED_APPEND_REF = False + + If set to ``True``, ``?ref=feed`` will be appended to links in generated + feeds for the purpose of referrer tracking. + If you don't want to generate some or any of these feeds, set the above variables to ``None``. diff --git a/pelican/settings.py b/pelican/settings.py index 29051ddb..47457ec1 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -52,6 +52,7 @@ DEFAULT_CONFIG = { "TRANSLATION_FEED_ATOM": "feeds/all-{lang}.atom.xml", "FEED_MAX_ITEMS": 100, "RSS_FEED_SUMMARY_ONLY": True, + "FEED_APPEND_REF": False, "SITEURL": "", "SITENAME": "A Pelican Blog", "DISPLAY_PAGES_ON_MENU": True, diff --git a/pelican/writers.py b/pelican/writers.py index d405fc88..1c41b4ee 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -53,6 +53,9 @@ class Writer: title = Markup(item.title).striptags() link = self.urljoiner(self.site_url, item.url) + if self.settings["FEED_APPEND_REF"]: + link = link + "?ref=feed" + if isinstance(feed, Rss201rev2Feed): # RSS feeds use a single tag called 'description' for both the full # content and the summary From e4d7f0a9d95cf2816e4c0b2b59849f49766e05dc Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 29 May 2024 07:28:22 +0200 Subject: [PATCH 372/465] Docs: GitHub Pages workflow not officially supported --- .github/workflows/github_pages.yml | 1 + docs/tips.rst | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github_pages.yml b/.github/workflows/github_pages.yml index eb9e955f..f4a01b92 100644 --- a/.github/workflows/github_pages.yml +++ b/.github/workflows/github_pages.yml @@ -1,3 +1,4 @@ +# Workflow for publishing to GitHub Pages. **NO OFFICIAL SUPPORT PROVIDED.** name: Deploy to GitHub Pages on: workflow_call: diff --git a/docs/tips.rst b/docs/tips.rst index 904e5ee7..3344900d 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -126,8 +126,9 @@ branch of your GitHub repository:: Publishing to GitHub Pages Using a Custom GitHub Actions Workflow ----------------------------------------------------------------- -Pelican comes with a `custom workflow `_ -for publishing a Pelican site. To use it: +Pelican-powered sites can be published to GitHub Pages via a `custom workflow +`_. +**No official support is provided** for this community-submitted workflow. To use it: 1. Enable GitHub Pages in your repo: go to **Settings → Pages** and choose **GitHub Actions** for the **Source** setting. From 1001dcb6096d315bc460571c5f43ea88a33e1e18 Mon Sep 17 00:00:00 2001 From: boxydog Date: Wed, 29 May 2024 16:36:20 -0500 Subject: [PATCH 373/465] Fix test_deprecated_attribute failures in github tests --- pelican/tests/test_utils.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 22dd8e38..bd3f0d00 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -23,9 +23,17 @@ from pelican.tests.support import ( from pelican.writers import Writer -class TestUtils(LoggedTestCase): +class ClassDeprAttr: _new_attribute = "new_value" + @utils.deprecated_attribute( + old="_old_attribute", new="_new_attribute", since=(3, 1, 0), remove=(4, 1, 3) + ) + def _old_attribute(): + return None + + +class TestUtils(LoggedTestCase): def setUp(self): super().setUp() self.temp_output = mkdtemp(prefix="pelicantests.") @@ -34,15 +42,10 @@ class TestUtils(LoggedTestCase): super().tearDown() shutil.rmtree(self.temp_output) - @utils.deprecated_attribute( - old="_old_attribute", new="_new_attribute", since=(3, 1, 0), remove=(4, 1, 3) - ) - def _old_attribute(): - return None - def test_deprecated_attribute(self): - value = self._old_attribute - self.assertEqual(value, self._new_attribute) + test_class = ClassDeprAttr() + value = test_class._old_attribute + self.assertEqual(value, test_class._new_attribute) self.assertLogCountEqual( count=1, msg=( From 4f46fedd731664547fc2b5e61f1d8d2c157d2dd3 Mon Sep 17 00:00:00 2001 From: boxydog Date: Thu, 30 May 2024 09:02:23 -0500 Subject: [PATCH 374/465] More Ruff checks, and make it fix --- .pre-commit-config.yaml | 2 +- pyproject.toml | 99 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d6cfac07..0e65a965 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: rev: v0.1.15 hooks: - id: ruff + args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - args: ["--check"] exclude: ^pelican/tests/output/ diff --git a/pyproject.toml b/pyproject.toml index 3ca06df4..39694ffc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,3 +111,102 @@ source-includes = [ [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" + + +[tool.ruff.lint] +# see https://docs.astral.sh/ruff/configuration/#using-pyprojecttoml +# "F" contains autoflake, see https://github.com/astral-sh/ruff/issues/1647 +# add more rules +select = [ + # default Ruff checkers as of ruff 0.1.3: E4, E7, E9, F + "E4", + "E7", + "E9", + "F", # pyflakes + + # the rest in alphabetical order: + # TODO: "A", # flake8-builtins + # TODO: "ARG", # flake8-unused-arguments + "B", # flake8-bugbear + # TODO: "BLE", # flake8-blind-except + # TODO: Do I want "COM", # flake8-commas + "C4", # flake8-comprehensions + # TODO: "DJ", # flake8-django + # TODO: "DTZ", # flake8-datetimez + # TODO: "EM", # flake8-errmsg + "EXE", # flake8-executable + # TODO: "FURB", # refurb + # TODO: "FBT", # flake8-boolean-trap + # TODO: "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "INP", # flake8-no-pep420 + # TODO: "INT", # flake8-gettext + "ISC", # flake8-implicit-str-concat + # TODO: "LOG", # flake8-logging + "PERF", # perflint + "PIE", # flake8-pie + "PL", # pylint + "PYI", # flake8-pyi + # TODO: "RET", # flake8-return + "RSE", # flake8-raise + "RUF", + # TODO: "SIM", # flake8-simplify + "SLF", # flake8-self + "SLOT", # flake8-slots + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "Q", # flake8-quotes + "TCH", # flake8-type-checking + "T10", # flake8-debugger + "T20", # flake8-print + # TODO: "S", # flake8-bandit + "YTT", # flake8-2020 + # TODO: add more flake8 rules + ] + +ignore = [ + # suppression in order of # of violations in Dec 2023: + "B007", # unused-loop-control-variable + "T201", # print + "PLW2901", # redefined-loop-name + "SLF001", # private-member-access + "RUF001", # ambiguous-unicode-character-string + "PLR2004", # magic-value-comparison + "PLR0912", # too-many-branches + "PLR0913", # too-many-arguments + "RUF005", # collection-literal-concatenation + "RUF012", # mutable-class-default + "PLR0915", # too-many-statements + "INP001", # implicit-namespace-package + "RUF015", # unnecessary-iterable-allocation-for-first-element + "PLR1722", # sys-exit-alias + "ISC001", # single-line-implicit-string-concatenation + "C408", # unnecessary-collection-call + "B904", # raise-without-from-inside-except + "UP007", # use `|` operator for union type annotations (PEP 604) + "UP031", # printf-string-formatting + "PLR5501", # collapsible-else-if + "PERF203", # try-except-in-loop + "B006", # mutable-argument-default + "PLR1714", # repeated-equality-comparison + "PERF401", # manual-list-comprehension + # TODO: these only have one violation each in Dec 2023: + "SLOT000", # no-slots-in-str-subclass + "PYI024", # collections-named-tuple + "PLW0603", # global-statement + "PIE800", # unnecessary-spread + "ISC003", # explicit-string-concatenation + "EXE002", # shebang-missing-executable-file + "C401", # unnecessary-generator-set + "C416", # unnecessary `list` comprehension + "B028", # no-explicit-stacklevel + "B008", # function-call-in-default-argument +] + +[tool.ruff.lint.extend-per-file-ignores] + +"pelican/__init__.py" = [ + # allow imports after a call to a function, see the file + "E402" +] From 6d8597addb17d5fa3027ead91427939e8e4e89ec Mon Sep 17 00:00:00 2001 From: boxydog Date: Thu, 30 May 2024 09:05:36 -0500 Subject: [PATCH 375/465] The ruff and ruff-format fixes --- pelican/__init__.py | 8 ++--- pelican/__main__.py | 1 - pelican/contents.py | 13 ++++--- pelican/plugins/_utils.py | 1 - pelican/plugins/signals.py | 2 +- pelican/readers.py | 4 +-- pelican/rstdirectives.py | 1 - pelican/settings.py | 6 ++-- pelican/tests/build_test/test_build_files.py | 2 +- pelican/tests/support.py | 4 +-- pelican/tests/test_cache.py | 1 - pelican/tests/test_contents.py | 2 -- pelican/tests/test_generators.py | 2 +- pelican/tests/test_importer.py | 14 ++++---- pelican/tests/test_paginator.py | 1 - pelican/tests/test_plugins.py | 2 +- pelican/tests/test_readers.py | 1 - pelican/tests/test_settings.py | 1 - pelican/tools/pelican_import.py | 1 - pelican/tools/pelican_quickstart.py | 6 ++-- pelican/tools/pelican_themes.py | 14 ++------ pelican/urlwrappers.py | 2 +- pelican/utils.py | 37 +++++++++----------- pelican/writers.py | 1 - 24 files changed, 48 insertions(+), 79 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 2fd69f1b..aef4b124 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -19,10 +19,10 @@ __path__ = extend_path(__path__, __name__) # pelican.log has to be the first pelican module to be loaded # because logging.setLoggerClass has to be called before logging.getLogger -from pelican.log import console +from pelican.log import console # noqa: I001 from pelican.log import init as init_logging from pelican.generators import ( - ArticlesGenerator, # noqa: I100 + ArticlesGenerator, PagesGenerator, SourceFileGenerator, StaticGenerator, @@ -354,8 +354,8 @@ def parse_arguments(argv=None): "--settings", dest="settings", help="The settings of the application, this is " - "automatically set to {} if a file exists with this " - "name.".format(DEFAULT_CONFIG_NAME), + f"automatically set to {DEFAULT_CONFIG_NAME} if a file exists with this " + "name.", ) parser.add_argument( diff --git a/pelican/__main__.py b/pelican/__main__.py index 17aead3b..41a1f712 100644 --- a/pelican/__main__.py +++ b/pelican/__main__.py @@ -4,6 +4,5 @@ python -m pelican module entry point to run via python -m from . import main - if __name__ == "__main__": main() diff --git a/pelican/contents.py b/pelican/contents.py index c640df69..9532c523 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -17,6 +17,9 @@ except ModuleNotFoundError: from pelican.plugins import signals from pelican.settings import DEFAULT_CONFIG, Settings + +# Import these so that they're available when you import from pelican.contents. +from pelican.urlwrappers import Author, Category, Tag, URLWrapper # NOQA from pelican.utils import ( deprecated_attribute, memoized, @@ -28,9 +31,6 @@ from pelican.utils import ( truncate_html_words, ) -# Import these so that they're available when you import from pelican.contents. -from pelican.urlwrappers import Author, Category, Tag, URLWrapper # NOQA - logger = logging.getLogger(__name__) @@ -370,13 +370,13 @@ class Content: def _get_intrasite_link_regex(self) -> re.Pattern: intrasite_link_regex = self.settings["INTRASITE_LINK_REGEX"] - regex = r""" + regex = rf""" (?P<[^\>]+ # match tag with all url-value attributes (?:href|src|poster|data|cite|formaction|action|content)\s*=\s*) (?P["\']) # require value to be quoted - (?P{}(?P.*?)) # the url value - (?P=quote)""".format(intrasite_link_regex) + (?P{intrasite_link_regex}(?P.*?)) # the url value + (?P=quote)""" return re.compile(regex, re.X) def _update_content(self, content: str, siteurl: str) -> str: @@ -465,7 +465,6 @@ class Content: @summary.setter def summary(self, value: str): """Dummy function""" - pass @property def status(self) -> str: diff --git a/pelican/plugins/_utils.py b/pelican/plugins/_utils.py index 805ed049..9dfc8f81 100644 --- a/pelican/plugins/_utils.py +++ b/pelican/plugins/_utils.py @@ -6,7 +6,6 @@ import logging import pkgutil import sys - logger = logging.getLogger(__name__) diff --git a/pelican/plugins/signals.py b/pelican/plugins/signals.py index 27177367..c36f595d 100644 --- a/pelican/plugins/signals.py +++ b/pelican/plugins/signals.py @@ -1,4 +1,4 @@ -from blinker import signal, Signal +from blinker import Signal, signal from ordered_set import OrderedSet # Signals will call functions in the order of connection, i.e. plugin order diff --git a/pelican/readers.py b/pelican/readers.py index 60b9765a..e9b07582 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -22,7 +22,7 @@ from pelican.utils import get_date, pelican_open, posixize_path try: from markdown import Markdown except ImportError: - Markdown = False # NOQA + Markdown = False # Metadata processors have no way to discard an unwanted value, so we have # them return this value instead to signal that it should be discarded later. @@ -607,8 +607,8 @@ class Readers(FileStampDataCacher): # eventually filter the content with typogrify if asked so if self.settings["TYPOGRIFY"]: - from typogrify.filters import typogrify import smartypants + from typogrify.filters import typogrify typogrify_dashes = self.settings["TYPOGRIFY_DASHES"] if typogrify_dashes == "oldschool": diff --git a/pelican/rstdirectives.py b/pelican/rstdirectives.py index 0a549424..41bfc3d2 100644 --- a/pelican/rstdirectives.py +++ b/pelican/rstdirectives.py @@ -2,7 +2,6 @@ import re from docutils import nodes, utils from docutils.parsers.rst import Directive, directives, roles - from pygments import highlight from pygments.formatters import HtmlFormatter from pygments.lexers import TextLexer, get_lexer_by_name diff --git a/pelican/settings.py b/pelican/settings.py index 47457ec1..2cd6fb00 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -267,9 +267,7 @@ def _printf_s_to_format_field(printf_string: str, format_field: str) -> str: TEST_STRING = "PELICAN_PRINTF_S_DEPRECATION" expected = printf_string % TEST_STRING - result = printf_string.replace("{", "{{").replace("}", "}}") % "{{{}}}".format( - format_field - ) + result = printf_string.replace("{", "{{").replace("}", "}}") % f"{{{format_field}}}" if result.format(**{format_field: TEST_STRING}) != expected: raise ValueError(f"Failed to safely replace %s with {{{format_field}}}") @@ -412,7 +410,7 @@ def handle_deprecated_settings(settings: Settings) -> Settings: ) logger.warning(message) if old_values.get("SLUG"): - for f in {"CATEGORY", "TAG"}: + for f in ("CATEGORY", "TAG"): if old_values.get(f): old_values[f] = old_values["SLUG"] + old_values[f] old_values["AUTHOR"] = old_values.get("AUTHOR", []) diff --git a/pelican/tests/build_test/test_build_files.py b/pelican/tests/build_test/test_build_files.py index 9aad990d..c80253db 100644 --- a/pelican/tests/build_test/test_build_files.py +++ b/pelican/tests/build_test/test_build_files.py @@ -1,6 +1,6 @@ -from re import match import tarfile from pathlib import Path +from re import match from zipfile import ZipFile import pytest diff --git a/pelican/tests/support.py b/pelican/tests/support.py index 16060441..f43468b2 100644 --- a/pelican/tests/support.py +++ b/pelican/tests/support.py @@ -261,9 +261,7 @@ class LoggedTestCase(unittest.TestCase): self.assertEqual( actual, count, - msg="expected {} occurrences of {!r}, but found {}".format( - count, msg, actual - ), + msg=f"expected {count} occurrences of {msg!r}, but found {actual}", ) diff --git a/pelican/tests/test_cache.py b/pelican/tests/test_cache.py index 6dc91b2c..a1bbc559 100644 --- a/pelican/tests/test_cache.py +++ b/pelican/tests/test_cache.py @@ -6,7 +6,6 @@ from unittest.mock import MagicMock from pelican.generators import ArticlesGenerator, PagesGenerator from pelican.tests.support import get_context, get_settings, unittest - CUR_DIR = os.path.dirname(__file__) CONTENT_DIR = os.path.join(CUR_DIR, "content") diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 9dc7b70d..89219029 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -13,7 +13,6 @@ from pelican.settings import DEFAULT_CONFIG from pelican.tests.support import LoggedTestCase, get_context, get_settings, unittest from pelican.utils import path_to_url, posixize_path, truncate_html_words - # generate one paragraph, enclosed with

          TEST_CONTENT = str(generate_lorem_ipsum(n=1)) TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False) @@ -297,7 +296,6 @@ class TestPage(TestBase): def test_signal(self): def receiver_test_function(sender): receiver_test_function.has_been_called = True - pass receiver_test_function.has_been_called = False diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 8c257b55..920d9061 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -13,11 +13,11 @@ from pelican.generators import ( TemplatePagesGenerator, ) from pelican.tests.support import ( + TestCaseWithCLocale, can_symlink, get_context, get_settings, unittest, - TestCaseWithCLocale, ) from pelican.writers import Writer diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index 916c1183..469184cd 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -5,11 +5,11 @@ from unittest.mock import patch from pelican.settings import DEFAULT_CONFIG from pelican.tests.support import ( + TestCaseWithCLocale, mute, skipIfNoExecutable, temporary_folder, unittest, - TestCaseWithCLocale, ) from pelican.tools.pelican_import import ( blogger2fields, @@ -19,12 +19,12 @@ from pelican.tools.pelican_import import ( download_attachments, fields2pelican, get_attachments, - tumblr2fields, - wp2fields, + medium_slug, mediumpost2fields, mediumposts2fields, strip_medium_post_content, - medium_slug, + tumblr2fields, + wp2fields, ) from pelican.utils import path_to_file_url, slugify @@ -41,7 +41,7 @@ WORDPRESS_DECODED_CONTENT_SAMPLE = os.path.join( try: from bs4 import BeautifulSoup except ImportError: - BeautifulSoup = False # NOQA + BeautifulSoup = False try: import bs4.builder._lxml as LXML @@ -532,9 +532,7 @@ class TestWordpressXMLAttachements(TestCaseWithCLocale): self.assertEqual(self.attachments[post], {expected_invalid}) else: self.fail( - "all attachments should match to a " "filename or None, {}".format( - post - ) + "all attachments should match to a " f"filename or None, {post}" ) def test_download_attachments(self): diff --git a/pelican/tests/test_paginator.py b/pelican/tests/test_paginator.py index 2160421f..6a7dbe02 100644 --- a/pelican/tests/test_paginator.py +++ b/pelican/tests/test_paginator.py @@ -7,7 +7,6 @@ from pelican.paginator import Paginator from pelican.settings import DEFAULT_CONFIG from pelican.tests.support import get_settings, unittest - # generate one paragraph, enclosed with

          TEST_CONTENT = str(generate_lorem_ipsum(n=1)) TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False) diff --git a/pelican/tests/test_plugins.py b/pelican/tests/test_plugins.py index 55fa8a6a..69a0384c 100644 --- a/pelican/tests/test_plugins.py +++ b/pelican/tests/test_plugins.py @@ -1,7 +1,6 @@ import os from contextlib import contextmanager -import pelican.tests.dummy_plugins.normal_plugin.normal_plugin as normal_plugin from pelican.plugins._utils import ( get_namespace_plugins, get_plugin_name, @@ -9,6 +8,7 @@ from pelican.plugins._utils import ( plugin_enabled, ) from pelican.plugins.signals import signal +from pelican.tests.dummy_plugins.normal_plugin import normal_plugin from pelican.tests.support import unittest diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index 04049894..e49c8b74 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -5,7 +5,6 @@ from pelican import readers from pelican.tests.support import get_settings, unittest from pelican.utils import SafeDatetime - CUR_DIR = os.path.dirname(__file__) CONTENT_PATH = os.path.join(CUR_DIR, "content") diff --git a/pelican/tests/test_settings.py b/pelican/tests/test_settings.py index f370f7eb..84f7a5c9 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -3,7 +3,6 @@ import locale import os from os.path import abspath, dirname, join - from pelican.settings import ( DEFAULT_CONFIG, DEFAULT_THEME, diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index eb343860..3e1f31db 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -22,7 +22,6 @@ from pelican.log import init from pelican.settings import DEFAULT_CONFIG from pelican.utils import SafeDatetime, slugify - logger = logging.getLogger(__name__) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index a4dc98e1..c00a252c 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -169,7 +169,7 @@ def ask_timezone(question, default, tzurl): r = tz_dict[r] break else: - print("Please enter a valid time zone:\n" " (check [{}])".format(tzurl)) + print("Please enter a valid time zone:\n" f" (check [{tzurl}])") return r @@ -205,14 +205,14 @@ def main(): args = parser.parse_args() print( - """Welcome to pelican-quickstart v{v}. + f"""Welcome to pelican-quickstart v{__version__}. This script will help you create a new Pelican-based website. Please answer the following questions so this script can generate the files needed by Pelican. - """.format(v=__version__) + """ ) project = os.path.join(os.environ.get("VIRTUAL_ENV", os.curdir), ".project") diff --git a/pelican/tools/pelican_themes.py b/pelican/tools/pelican_themes.py index c5b49b9f..fa59b8fd 100755 --- a/pelican/tools/pelican_themes.py +++ b/pelican/tools/pelican_themes.py @@ -240,15 +240,11 @@ def install(path, v=False, u=False): except OSError as e: err( "Cannot change permissions of files " - "or directory in `{r}':\n{e}".format(r=theme_path, e=str(e)), + f"or directory in `{theme_path}':\n{e!s}", die=False, ) except Exception as e: - err( - "Cannot copy `{p}' to `{t}':\n{e}".format( - p=path, t=theme_path, e=str(e) - ) - ) + err(f"Cannot copy `{path}' to `{theme_path}':\n{e!s}") def symlink(path, v=False): @@ -268,11 +264,7 @@ def symlink(path, v=False): try: os.symlink(path, theme_path) except Exception as e: - err( - "Cannot link `{p}' to `{t}':\n{e}".format( - p=path, t=theme_path, e=str(e) - ) - ) + err(f"Cannot link `{path}' to `{theme_path}':\n{e!s}") def is_broken_link(path): diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index 6d705d4c..4ed385f9 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -98,7 +98,7 @@ class URLWrapper: return self.name def __repr__(self): - return f"<{type(self).__name__} {repr(self._name)}>" + return f"<{type(self).__name__} {self._name!r}>" def _from_settings(self, key, get_page_name=False): """Returns URL information as defined in settings. diff --git a/pelican/utils.py b/pelican/utils.py index 86698aee..7017c458 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -23,14 +23,10 @@ from typing import ( Any, Callable, Collection, - Dict, Generator, Iterable, - List, Optional, Sequence, - Tuple, - Type, Union, ) @@ -40,9 +36,8 @@ try: from zoneinfo import ZoneInfo except ModuleNotFoundError: from backports.zoneinfo import ZoneInfo -from markupsafe import Markup - import watchfiles +from markupsafe import Markup if TYPE_CHECKING: from pelican.contents import Content @@ -158,7 +153,7 @@ class memoized: def __init__(self, func: Callable) -> None: self.func = func - self.cache: Dict[Any, Any] = {} + self.cache: dict[Any, Any] = {} def __call__(self, *args) -> Any: if not isinstance(args, Hashable): @@ -185,8 +180,8 @@ class memoized: def deprecated_attribute( old: str, new: str, - since: Tuple[int, ...], - remove: Optional[Tuple[int, ...]] = None, + since: tuple[int, ...], + remove: Optional[tuple[int, ...]] = None, doc: Optional[str] = None, ): """Attribute deprecation decorator for gentle upgrades @@ -256,7 +251,7 @@ def pelican_open( def slugify( value: str, - regex_subs: Iterable[Tuple[str, str]] = (), + regex_subs: Iterable[tuple[str, str]] = (), preserve_case: bool = False, use_unicode: bool = False, ) -> str: @@ -642,9 +637,9 @@ def truncate_html_words(s: str, num: int, end_text: str = "…") -> str: def process_translations( - content_list: List[Content], + content_list: list[Content], translation_id: Optional[Union[str, Collection[str]]] = None, -) -> Tuple[List[Content], List[Content]]: +) -> tuple[list[Content], list[Content]]: """Finds translations and returns them. For each content_list item, populates the 'translations' attribute, and @@ -674,14 +669,14 @@ def process_translations( content_list.sort(key=attrgetter(*translation_id)) except TypeError: raise TypeError( - "Cannot unpack {}, 'translation_id' must be falsy, a" - " string or a collection of strings".format(translation_id) + f"Cannot unpack {translation_id}, 'translation_id' must be falsy, a" + " string or a collection of strings" ) except AttributeError: raise AttributeError( - "Cannot use {} as 'translation_id', there " + f"Cannot use {translation_id} as 'translation_id', there " "appear to be items without these metadata " - "attributes".format(translation_id) + "attributes" ) for id_vals, items in groupby(content_list, attrgetter(*translation_id)): @@ -702,7 +697,7 @@ def process_translations( return index, translations -def get_original_items(items: List[Content], with_str: str) -> List[Content]: +def get_original_items(items: list[Content], with_str: str) -> list[Content]: def _warn_source_paths(msg, items, *extra): args = [len(items)] args.extend(extra) @@ -743,9 +738,9 @@ def get_original_items(items: List[Content], with_str: str) -> List[Content]: def order_content( - content_list: List[Content], + content_list: list[Content], order_by: Union[str, Callable[[Content], Any], None] = "slug", -) -> List[Content]: +) -> list[Content]: """Sorts content. order_by can be a string of an attribute or sorting function. If order_by @@ -807,8 +802,8 @@ def order_content( def wait_for_changes( settings_file: str, - reader_class: Type["Readers"], - settings: "Settings", + reader_class: type[Readers], + settings: Settings, ): content_path = settings.get("PATH", "") theme_path = settings.get("THEME", "") diff --git a/pelican/writers.py b/pelican/writers.py index 1c41b4ee..746811e1 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -4,7 +4,6 @@ from posixpath import join as posix_join from urllib.parse import urljoin from feedgenerator import Atom1Feed, Rss201rev2Feed, get_tag_uri - from markupsafe import Markup from pelican.paginator import Paginator From c46063cfc39dcde030f30ffaaf8896899805c5c8 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 30 May 2024 16:42:09 +0200 Subject: [PATCH 376/465] Ignore latest Ruff fixes in `git blame` --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 0d92c9d9..bccdab76 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -5,3 +5,5 @@ cabdb26cee66e1173cf16cb31d3fe5f9fa4392e7 ecd598f293161a52564aa6e8dfdcc8284dc93970 # Apply Ruff and pyupgrade to Jinja templates db241feaa445375dc05e189e69287000ffe5fa8e +# Change pre-commit to run ruff and ruff-format with fixes +6d8597addb17d5fa3027ead91427939e8e4e89ec From 39c964450ce5e1dd2987561f475374790f02c6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Thu, 30 May 2024 17:13:27 +0200 Subject: [PATCH 377/465] Choose logging handler via `--log-handler` CLI option (#3293) --- pelican/__init__.py | 16 +++++++++++++++- pelican/log.py | 9 +++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index aef4b124..8a880584 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -19,7 +19,7 @@ __path__ = extend_path(__path__, __name__) # pelican.log has to be the first pelican module to be loaded # because logging.setLoggerClass has to be called before logging.getLogger -from pelican.log import console # noqa: I001 +from pelican.log import console, DEFAULT_LOG_HANDLER # noqa: I001 from pelican.log import init as init_logging from pelican.generators import ( ArticlesGenerator, @@ -455,6 +455,17 @@ def parse_arguments(argv=None): ), ) + LOG_HANDLERS = {"plain": None, "rich": DEFAULT_LOG_HANDLER} + parser.add_argument( + "--log-handler", + default="rich", + choices=LOG_HANDLERS, + help=( + "Which handler to use to format log messages. " + "The `rich` handler prints output in columns." + ), + ) + parser.add_argument( "--logs-dedup-min-level", default="WARNING", @@ -509,6 +520,8 @@ def parse_arguments(argv=None): if args.bind is not None and not args.listen: logger.warning("--bind without --listen has no effect") + args.log_handler = LOG_HANDLERS[args.log_handler] + return args @@ -631,6 +644,7 @@ def main(argv=None): level=args.verbosity, fatal=args.fatal, name=__name__, + handler=args.log_handler, logs_dedup_min_level=logs_dedup_min_level, ) diff --git a/pelican/log.py b/pelican/log.py index ef49d280..edf2f182 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -126,11 +126,13 @@ logging.setLoggerClass(FatalLogger) # force root logger to be of our preferred class logging.getLogger().__class__ = FatalLogger +DEFAULT_LOG_HANDLER = RichHandler(console=console) + def init( level=None, fatal="", - handler=RichHandler(console=console), + handler=DEFAULT_LOG_HANDLER, name=None, logs_dedup_min_level=None, ): @@ -139,7 +141,10 @@ def init( LOG_FORMAT = "%(message)s" logging.basicConfig( - level=level, format=LOG_FORMAT, datefmt="[%H:%M:%S]", handlers=[handler] + level=level, + format=LOG_FORMAT, + datefmt="[%H:%M:%S]", + handlers=[handler] if handler else [], ) logger = logging.getLogger(name) From 800f22b3ba2139c267d44c45b681461294262b7d Mon Sep 17 00:00:00 2001 From: boxydog Date: Thu, 30 May 2024 10:49:57 -0500 Subject: [PATCH 378/465] Upgrade ruff to v0.4.6, pre-commit-hooks to v4.6.0 --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e65a965..626ea299 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for info on hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: check-ast @@ -14,7 +14,7 @@ repos: - id: forbid-new-submodules - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.15 + rev: v0.4.6 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] From 0bd02c00c078fe041b65fbf4eab13601bb42676d Mon Sep 17 00:00:00 2001 From: boxydog Date: Thu, 30 May 2024 10:53:38 -0500 Subject: [PATCH 379/465] Ruff v0.4.6 auto-fixes --- pelican/__init__.py | 10 +--------- pelican/settings.py | 5 +---- pelican/tests/test_generators.py | 12 ++++++------ pelican/utils.py | 18 +++++++++--------- pelican/writers.py | 2 +- 5 files changed, 18 insertions(+), 29 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 8a880584..d63f88c3 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -193,15 +193,7 @@ class Pelican: ) console.print( - "Done: Processed {}, {}, {}, {}, {} and {} in {:.2f} seconds.".format( - pluralized_articles, - pluralized_drafts, - pluralized_hidden_articles, - pluralized_pages, - pluralized_hidden_pages, - pluralized_draft_pages, - time.time() - start_time, - ) + f"Done: Processed {pluralized_articles}, {pluralized_drafts}, {pluralized_hidden_articles}, {pluralized_pages}, {pluralized_hidden_pages} and {pluralized_draft_pages} in {time.time() - start_time:.2f} seconds." ) def _get_generator_classes(self): diff --git a/pelican/settings.py b/pelican/settings.py index 2cd6fb00..0585b3be 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -322,10 +322,7 @@ def handle_deprecated_settings(settings: Settings) -> Settings: "EXTRA_TEMPLATES_PATHS is deprecated use " "THEME_TEMPLATES_OVERRIDES instead." ) - if ( - "THEME_TEMPLATES_OVERRIDES" in settings - and settings["THEME_TEMPLATES_OVERRIDES"] - ): + if settings.get("THEME_TEMPLATES_OVERRIDES"): raise Exception( "Setting both EXTRA_TEMPLATES_PATHS and " "THEME_TEMPLATES_OVERRIDES is not permitted. Please move to " diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 920d9061..e739e180 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -597,9 +597,9 @@ class TestArticlesGenerator(unittest.TestCase): self.assertEqual(expected, abbreviated_archives) # Day archives enabled: - settings[ - "DAY_ARCHIVE_SAVE_AS" - ] = "posts/{date:%Y}/{date:%b}/{date:%d}/index.html" + settings["DAY_ARCHIVE_SAVE_AS"] = ( + "posts/{date:%Y}/{date:%b}/{date:%d}/index.html" + ) settings["DAY_ARCHIVE_URL"] = "posts/{date:%Y}/{date:%b}/{date:%d}/" context = get_context(settings) generator = ArticlesGenerator( @@ -737,9 +737,9 @@ class TestArticlesGenerator(unittest.TestCase): all_articles=generator.articles, ) - settings[ - "DAY_ARCHIVE_SAVE_AS" - ] = "posts/{date:%Y}/{date:%b}/{date:%d}/index.html" + settings["DAY_ARCHIVE_SAVE_AS"] = ( + "posts/{date:%Y}/{date:%b}/{date:%d}/index.html" + ) settings["DAY_ARCHIVE_URL"] = "posts/{date:%Y}/{date:%b}/{date:%d}/" context = get_context(settings) generator = ArticlesGenerator( diff --git a/pelican/utils.py b/pelican/utils.py index 7017c458..78e3e807 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -446,15 +446,15 @@ class _HTMLWordTruncator(HTMLParser): r"{DBC}|(\w[\w'-]*)".format( # DBC means CJK-like characters. An character can stand for a word. DBC=( - "([\u4E00-\u9FFF])|" # CJK Unified Ideographs - "([\u3400-\u4DBF])|" # CJK Unified Ideographs Extension A - "([\uF900-\uFAFF])|" # CJK Compatibility Ideographs - "([\U00020000-\U0002A6DF])|" # CJK Unified Ideographs Extension B - "([\U0002F800-\U0002FA1F])|" # CJK Compatibility Ideographs Supplement - "([\u3040-\u30FF])|" # Hiragana and Katakana - "([\u1100-\u11FF])|" # Hangul Jamo - "([\uAC00-\uD7FF])|" # Hangul Compatibility Jamo - "([\u3130-\u318F])" # Hangul Syllables + "([\u4e00-\u9fff])|" # CJK Unified Ideographs + "([\u3400-\u4dbf])|" # CJK Unified Ideographs Extension A + "([\uf900-\ufaff])|" # CJK Compatibility Ideographs + "([\U00020000-\U0002a6df])|" # CJK Unified Ideographs Extension B + "([\U0002f800-\U0002fa1f])|" # CJK Compatibility Ideographs Supplement + "([\u3040-\u30ff])|" # Hiragana and Katakana + "([\u1100-\u11ff])|" # Hangul Jamo + "([\uac00-\ud7ff])|" # Hangul Compatibility Jamo + "([\u3130-\u318f])" # Hangul Syllables ) ), re.UNICODE, diff --git a/pelican/writers.py b/pelican/writers.py index 746811e1..cce01b58 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -27,7 +27,7 @@ class Writer: self._overridden_files = set() # See Content._link_replacer for details - if "RELATIVE_URLS" in self.settings and self.settings["RELATIVE_URLS"]: + if self.settings.get("RELATIVE_URLS"): self.urljoiner = posix_join else: self.urljoiner = lambda base, url: urljoin( From 54ac03fca6f1d999f61d2f4119dafe156e1a24d8 Mon Sep 17 00:00:00 2001 From: boxydog Date: Thu, 30 May 2024 10:56:06 -0500 Subject: [PATCH 380/465] change ruff version in pyproject.toml --- .pre-commit-config.yaml | 1 + pyproject.toml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 626ea299..a3e7ab17 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,7 @@ repos: - id: forbid-new-submodules - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit + # ruff version should match the one in pyproject.toml rev: v0.4.6 hooks: - id: ruff diff --git a/pyproject.toml b/pyproject.toml index 39694ffc..c84c35b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,7 +96,8 @@ dev = [ "pytest-xdist>=3.4.0", "tox>=4.11.3", "invoke>=2.2.0", - "ruff>=0.1.15,<0.2.0", + # ruff version should match the one in .pre-commit-config.yaml + "ruff==0.4.6", "tomli>=2.0.1; python_version < \"3.11\"", ] From 9d46a94d6d1643258af6246706c0e82c86deb1b1 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 30 May 2024 19:15:56 +0200 Subject: [PATCH 381/465] Ignore Ruff 0.4.x fixes in `git blame` --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index bccdab76..2809b658 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -7,3 +7,5 @@ ecd598f293161a52564aa6e8dfdcc8284dc93970 db241feaa445375dc05e189e69287000ffe5fa8e # Change pre-commit to run ruff and ruff-format with fixes 6d8597addb17d5fa3027ead91427939e8e4e89ec +# Upgrade Ruff from 0.1.x to 0.4.x +0bd02c00c078fe041b65fbf4eab13601bb42676d From b6d3b6589935f8e2ea6d01e2ff6f8ee3a308af8c Mon Sep 17 00:00:00 2001 From: boxydog Date: Thu, 30 May 2024 10:33:40 -0500 Subject: [PATCH 382/465] More ruff checks --- pyproject.toml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c84c35b0..5546d1cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -190,19 +190,10 @@ ignore = [ "PLR5501", # collapsible-else-if "PERF203", # try-except-in-loop "B006", # mutable-argument-default - "PLR1714", # repeated-equality-comparison - "PERF401", # manual-list-comprehension # TODO: these only have one violation each in Dec 2023: "SLOT000", # no-slots-in-str-subclass "PYI024", # collections-named-tuple - "PLW0603", # global-statement "PIE800", # unnecessary-spread - "ISC003", # explicit-string-concatenation - "EXE002", # shebang-missing-executable-file - "C401", # unnecessary-generator-set - "C416", # unnecessary `list` comprehension - "B028", # no-explicit-stacklevel - "B008", # function-call-in-default-argument ] [tool.ruff.lint.extend-per-file-ignores] From 9d30c5608a58d202b1c02d55651e6ac746bfb173 Mon Sep 17 00:00:00 2001 From: boxydog Date: Thu, 30 May 2024 10:33:50 -0500 Subject: [PATCH 383/465] Code changes for more ruff checks --- pelican/server.py | 7 +++---- pelican/settings.py | 2 +- pelican/tests/test_importer.py | 29 ++++++++++++++--------------- pelican/tests/test_testsuite.py | 2 +- samples/pelican.conf.py | 0 5 files changed, 19 insertions(+), 21 deletions(-) mode change 100755 => 100644 samples/pelican.conf.py diff --git a/pelican/server.py b/pelican/server.py index 61729bf1..71cd2d81 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -32,19 +32,18 @@ def parse_arguments(): "--cert", default="./cert.pem", nargs="?", - help="Path to certificate file. " + "Relative to current directory", + help="Path to certificate file. Relative to current directory", ) parser.add_argument( "--key", default="./key.pem", nargs="?", - help="Path to certificate key file. " + "Relative to current directory", + help="Path to certificate key file. Relative to current directory", ) parser.add_argument( "--path", default=".", - help="Path to pelican source directory to serve. " - + "Relative to current directory", + help="Path to pelican source directory to serve. Relative to current directory", ) return parser.parse_args() diff --git a/pelican/settings.py b/pelican/settings.py index 0585b3be..ea6fae77 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -223,7 +223,7 @@ def read_settings( # parameters to docutils directive handlers, so we have to have a # variable here that we'll import from within Pygments.run (see # rstdirectives.py) to see what the user defaults were. - global PYGMENTS_RST_OPTIONS + global PYGMENTS_RST_OPTIONS # noqa: PLW0603 PYGMENTS_RST_OPTIONS = settings.get("PYGMENTS_RST_OPTIONS", None) return settings diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index 469184cd..e69e321f 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -152,11 +152,12 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): def test_dircat(self): silent_f2p = mute(True)(fields2pelican) - test_posts = [] - for post in self.posts: - # check post kind - if len(post[5]) > 0: # Has a category - test_posts.append(post) + test_posts = [ + post + for post in self.posts + # check post has a category + if len(post[5]) > 0 + ] with temporary_folder() as temp: fnames = list(silent_f2p(test_posts, "markdown", temp, dircat=True)) subs = DEFAULT_CONFIG["SLUG_REGEX_SUBSTITUTIONS"] @@ -185,7 +186,7 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): kind, format, ) in self.posts: - if kind == "page" or kind == "article": + if kind in {"page", "article"}: pass else: pages_data.append((title, fname)) @@ -206,7 +207,7 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): kind, format, ) in self.custposts: - if kind == "article" or kind == "page": + if kind in {"page", "article"}: pass else: cust_data.append((title, kind)) @@ -266,11 +267,12 @@ class TestWordpressXmlImporter(TestCaseWithCLocale): def test_wp_custpost_true_dirpage_false(self): # pages should only be put in their own directory when dirpage = True silent_f2p = mute(True)(fields2pelican) - test_posts = [] - for post in self.custposts: + test_posts = [ + post + for post in self.custposts # check post kind - if post[8] == "page": - test_posts.append(post) + if post[8] == "page" + ] with temporary_folder() as temp: fnames = list( silent_f2p( @@ -748,10 +750,7 @@ class TestMediumImporter(TestCaseWithCLocale): def test_mediumposts2field(self): """Parse all posts in an export directory""" - posts = [ - fields - for fields in mediumposts2fields(f"{self.test_content_root}/medium_posts") - ] + posts = list(mediumposts2fields(f"{self.test_content_root}/medium_posts")) self.assertEqual(1, len(posts)) self.assertEqual(self.post_tuple, posts[0]) diff --git a/pelican/tests/test_testsuite.py b/pelican/tests/test_testsuite.py index a9a0c200..938d2bdf 100644 --- a/pelican/tests/test_testsuite.py +++ b/pelican/tests/test_testsuite.py @@ -6,4 +6,4 @@ from pelican.tests.support import unittest class TestSuiteTest(unittest.TestCase): def test_error_on_warning(self): with self.assertRaises(UserWarning): - warnings.warn("test warning") + warnings.warn("test warning") # noqa: B028 diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py old mode 100755 new mode 100644 From 144b2edf88026a764b1978f4cb0f27e150d1334e Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 30 May 2024 19:40:45 +0200 Subject: [PATCH 384/465] Ignore more Ruff fixes in `git blame` --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 2809b658..fddd0764 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -9,3 +9,5 @@ db241feaa445375dc05e189e69287000ffe5fa8e 6d8597addb17d5fa3027ead91427939e8e4e89ec # Upgrade Ruff from 0.1.x to 0.4.x 0bd02c00c078fe041b65fbf4eab13601bb42676d +# Apply more Ruff checks to code +9d30c5608a58d202b1c02d55651e6ac746bfb173 From 3624bcdbf41f570471d24d59dc7ccb24f536e3fe Mon Sep 17 00:00:00 2001 From: boxydog Date: Thu, 30 May 2024 13:18:03 -0500 Subject: [PATCH 385/465] More ruff fixes: stop ignoring C408, UP007, PLR5501, B006 --- pyproject.toml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5546d1cf..b7e39a4a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -182,14 +182,17 @@ ignore = [ "INP001", # implicit-namespace-package "RUF015", # unnecessary-iterable-allocation-for-first-element "PLR1722", # sys-exit-alias + # ruff-format wants us to ignore ISC001. I don't love that, but okay. + # "warning: The following rules may cause conflicts when used with the formatter: + # `ISC001`. To avoid unexpected behavior, we recommend disabling these rules, + # either by removing them from the `select` or `extend-select` configuration, + # or adding them to the `ignore` configuration." "ISC001", # single-line-implicit-string-concatenation - "C408", # unnecessary-collection-call "B904", # raise-without-from-inside-except - "UP007", # use `|` operator for union type annotations (PEP 604) "UP031", # printf-string-formatting - "PLR5501", # collapsible-else-if + # PERF203 has minimal performance impact, and you have to catch the exception + # inside the loop if you want to ignore it, so let's ignore PERF203. "PERF203", # try-except-in-loop - "B006", # mutable-argument-default # TODO: these only have one violation each in Dec 2023: "SLOT000", # no-slots-in-str-subclass "PYI024", # collections-named-tuple From 7577dd7603f7cb3a09922d1edb65b6eafb6e2ac7 Mon Sep 17 00:00:00 2001 From: boxydog Date: Thu, 30 May 2024 13:21:12 -0500 Subject: [PATCH 386/465] More ruff fixes in files: stop ignoring C408, UP007, PLR5501, B006 --- pelican/__init__.py | 6 +++--- pelican/contents.py | 4 ++-- pelican/generators.py | 6 ++++-- pelican/paginator.py | 5 ++--- pelican/readers.py | 4 ++-- pelican/rstdirectives.py | 2 +- pelican/settings.py | 8 +++---- pelican/tests/test_contents.py | 28 ++++++++++++------------ pelican/tests/test_importer.py | 12 +++++------ pelican/tests/test_readers.py | 2 +- pelican/tests/test_utils.py | 4 ++-- pelican/tools/pelican_import.py | 4 ++-- pelican/tools/pelican_quickstart.py | 27 ++++++++++++----------- pelican/urlwrappers.py | 7 +++--- pelican/utils.py | 33 ++++++++++++----------------- pelican/writers.py | 2 +- 16 files changed, 72 insertions(+), 82 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index d63f88c3..ab6b0225 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -417,7 +417,7 @@ def parse_arguments(argv=None): "--relative-urls", dest="relative_paths", action="store_true", - help="Use relative urls in output, " "useful for site development", + help="Use relative urls in output, useful for site development", ) parser.add_argument( @@ -433,7 +433,7 @@ def parse_arguments(argv=None): "--ignore-cache", action="store_true", dest="ignore_cache", - help="Ignore content cache " "from previous runs by not loading cache files.", + help="Ignore content cache from previous runs by not loading cache files.", ) parser.add_argument( @@ -488,7 +488,7 @@ def parse_arguments(argv=None): "-b", "--bind", dest="bind", - help="IP to bind to when serving files via HTTP " "(default: 127.0.0.1)", + help="IP to bind to when serving files via HTTP (default: 127.0.0.1)", ) parser.add_argument( diff --git a/pelican/contents.py b/pelican/contents.py index 9532c523..21c66296 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -72,7 +72,7 @@ class Content: self._context = context self.translations = [] - local_metadata = dict() + local_metadata = {} local_metadata.update(metadata) # set metadata as attributes @@ -357,7 +357,7 @@ class Content: origin = joiner(siteurl, Author(path, self.settings).url) else: logger.warning( - "Replacement Indicator '%s' not recognized, " "skipping replacement", + "Replacement Indicator '%s' not recognized, skipping replacement", what, ) diff --git a/pelican/generators.py b/pelican/generators.py index 076c8d38..fdcc62ce 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -156,7 +156,7 @@ class Generator: return False - def get_files(self, paths, exclude=[], extensions=None): + def get_files(self, paths, exclude=None, extensions=None): """Return a list of files to use, based on rules :param paths: the list pf paths to search (relative to self.path) @@ -164,6 +164,8 @@ class Generator: :param extensions: the list of allowed extensions (if False, all extensions are allowed) """ + if exclude is None: + exclude = [] # backward compatibility for older generators if isinstance(paths, str): paths = [paths] @@ -1068,7 +1070,7 @@ class StaticGenerator(Generator): except OSError as err: if err.errno == errno.EXDEV: # 18: Invalid cross-device link logger.debug( - "Cross-device links not valid. " "Creating symbolic links instead." + "Cross-device links not valid. Creating symbolic links instead." ) self.fallback_to_symlinks = True self._link_staticfile(sc) diff --git a/pelican/paginator.py b/pelican/paginator.py index e1d50881..92cb26b1 100644 --- a/pelican/paginator.py +++ b/pelican/paginator.py @@ -131,9 +131,8 @@ class Page: if not self.has_next(): rule = p break - else: - if p.min_page <= self.number: - rule = p + elif p.min_page <= self.number: + rule = p if not rule: return "" diff --git a/pelican/readers.py b/pelican/readers.py index e9b07582..422f39fc 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -199,7 +199,7 @@ class RstReader(BaseReader): self._language_code = lang_code else: logger.warning( - "Docutils has no localization for '%s'." " Using 'en' instead.", + "Docutils has no localization for '%s'. Using 'en' instead.", lang_code, ) self._language_code = "en" @@ -320,7 +320,7 @@ class MarkdownReader(BaseReader): elif not DUPLICATES_DEFINITIONS_ALLOWED.get(name, True): if len(value) > 1: logger.warning( - "Duplicate definition of `%s` " "for %s. Using first one.", + "Duplicate definition of `%s` for %s. Using first one.", name, self._source_path, ) diff --git a/pelican/rstdirectives.py b/pelican/rstdirectives.py index 41bfc3d2..9022ac83 100644 --- a/pelican/rstdirectives.py +++ b/pelican/rstdirectives.py @@ -78,7 +78,7 @@ class abbreviation(nodes.Inline, nodes.TextElement): pass -def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): +def abbr_role(typ, rawtext, text, lineno, inliner, options=None, content=None): text = utils.unescape(text) m = _abbr_re.search(text) if m is None: diff --git a/pelican/settings.py b/pelican/settings.py index ea6fae77..214d88a3 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -447,7 +447,7 @@ def handle_deprecated_settings(settings: Settings) -> Settings: and not isinstance(settings[key], Path) and "%s" in settings[key] ): - logger.warning("%%s usage in %s is deprecated, use {lang} " "instead.", key) + logger.warning("%%s usage in %s is deprecated, use {lang} instead.", key) try: settings[key] = _printf_s_to_format_field(settings[key], "lang") except ValueError: @@ -470,7 +470,7 @@ def handle_deprecated_settings(settings: Settings) -> Settings: and not isinstance(settings[key], Path) and "%s" in settings[key] ): - logger.warning("%%s usage in %s is deprecated, use {slug} " "instead.", key) + logger.warning("%%s usage in %s is deprecated, use {slug} instead.", key) try: settings[key] = _printf_s_to_format_field(settings[key], "slug") except ValueError: @@ -614,7 +614,7 @@ def configure_settings(settings: Settings) -> Settings: if key in settings and not isinstance(settings[key], types): value = settings.pop(key) logger.warn( - "Detected misconfigured %s (%s), " "falling back to the default (%s)", + "Detected misconfigured %s (%s), falling back to the default (%s)", key, value, DEFAULT_CONFIG[key], @@ -676,7 +676,7 @@ def configure_settings(settings: Settings) -> Settings: if any(settings.get(k) for k in feed_keys): if not settings.get("SITEURL"): logger.warning( - "Feeds generated without SITEURL set properly may" " not be valid" + "Feeds generated without SITEURL set properly may not be valid" ) if "TIMEZONE" not in settings: diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 89219029..d248c3bb 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -314,7 +314,7 @@ class TestPage(TestBase): args["settings"] = settings # Tag - args["content"] = "A simple test, with a " 'link' + args["content"] = 'A simple test, with a link' page = Page(**args) content = page.get_content("http://notmyidea.org") self.assertEqual( @@ -326,9 +326,7 @@ class TestPage(TestBase): ) # Category - args["content"] = ( - "A simple test, with a " 'link' - ) + args["content"] = 'A simple test, with a link' page = Page(**args) content = page.get_content("http://notmyidea.org") self.assertEqual( @@ -350,7 +348,7 @@ class TestPage(TestBase): # Classic intrasite link via filename args["content"] = ( - "A simple test, with a " 'link' + 'A simple test, with a link' ) content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( @@ -401,7 +399,7 @@ class TestPage(TestBase): # also test for summary in metadata parsed = ( - "A simple summary test, with a " 'link' + 'A simple summary test, with a link' ) linked = ( "A simple summary test, with a " @@ -594,7 +592,7 @@ class TestPage(TestBase): # An intrasite link via filename with %20 as a space args["content"] = ( - "A simple test, with a " 'link' + 'A simple test, with a link' ) content = Page(**args).get_content("http://notmyidea.org") self.assertEqual( @@ -834,10 +832,10 @@ class TestStatic(LoggedTestCase): otherdir_settings = self.settings.copy() otherdir_settings.update( - dict( - PAGE_SAVE_AS=os.path.join("otherpages", "{slug}.html"), - PAGE_URL="otherpages/{slug}.html", - ) + { + "PAGE_SAVE_AS": os.path.join("otherpages", "{slug}.html"), + "PAGE_URL": "otherpages/{slug}.html", + } ) otherdir_page = Page( content="other page", @@ -892,7 +890,7 @@ class TestStatic(LoggedTestCase): """ customstatic = Static( content=None, - metadata=dict(save_as="customfoo.jpg", url="customfoo.jpg"), + metadata={"save_as": "customfoo.jpg", "url": "customfoo.jpg"}, settings=self.settings, source_path=os.path.join("dir", "foo.jpg"), context=self.settings.copy(), @@ -1066,9 +1064,9 @@ class TestStatic(LoggedTestCase): static = Static( content=None, - metadata=dict( - status="draft", - ), + metadata={ + "status": "draft", + }, settings=self.settings, source_path=os.path.join("dir", "foo.jpg"), context=self.settings.copy(), diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index e69e321f..46d83432 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -71,7 +71,7 @@ class TestBloggerXmlImporter(TestCaseWithCLocale): ) comment_titles = {x[0] for x in test_posts if x[8] == "comment"} self.assertEqual( - {"Mishka, always a pleasure to read your " "adventures!..."}, comment_titles + {"Mishka, always a pleasure to read your adventures!..."}, comment_titles ) def test_recognise_status_with_correct_filename(self): @@ -478,7 +478,7 @@ class TestBuildHeader(unittest.TestCase): attachments=["output/test1", "output/test2"], ) self.assertEqual( - header, ("test\n####\n" ":attachments: output/test1, " "output/test2\n\n") + header, ("test\n####\n:attachments: output/test1, output/test2\n\n") ) def test_galleries_added_to_markdown_header(self): @@ -521,10 +521,10 @@ class TestWordpressXMLAttachements(TestCaseWithCLocale): self.assertEqual(self.attachments[post], expected) elif post == "with-excerpt": expected_invalid = ( - "http://thisurlisinvalid.notarealdomain/" "not_an_image.jpg" + "http://thisurlisinvalid.notarealdomain/not_an_image.jpg" ) expected_pelikan = ( - "http://en.wikipedia.org/wiki/" "File:Pelikan_Walvis_Bay.jpg" + "http://en.wikipedia.org/wiki/File:Pelikan_Walvis_Bay.jpg" ) self.assertEqual( self.attachments[post], {expected_invalid, expected_pelikan} @@ -533,9 +533,7 @@ class TestWordpressXMLAttachements(TestCaseWithCLocale): expected_invalid = "http://thisurlisinvalid.notarealdomain" self.assertEqual(self.attachments[post], {expected_invalid}) else: - self.fail( - "all attachments should match to a " f"filename or None, {post}" - ) + self.fail(f"all attachments should match to a filename or None, {post}") def test_download_attachments(self): real_file = os.path.join(CUR_DIR, "content/article.rst") diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index e49c8b74..928eb263 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -591,7 +591,7 @@ class MdReaderTest(ReaderTest): "modified": SafeDatetime(2012, 11, 1), "multiline": [ "Line Metadata should be handle properly.", - "See syntax of Meta-Data extension of " "Python Markdown package:", + "See syntax of Meta-Data extension of Python Markdown package:", "If a line is indented by 4 or more spaces,", "that line is assumed to be an additional line of the value", "for the previous keyword.", diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index bd3f0d00..6c25cf45 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -922,14 +922,14 @@ class TestSanitisedJoin(unittest.TestCase): def test_detect_parent_breakout(self): with self.assertRaisesRegex( RuntimeError, - "Attempted to break out of output directory to " "(.*?:)?/foo/test", + "Attempted to break out of output directory to (.*?:)?/foo/test", ): # (.*?:)? accounts for Windows root utils.sanitised_join("/foo/bar", "../test") def test_detect_root_breakout(self): with self.assertRaisesRegex( RuntimeError, - "Attempted to break out of output directory to " "(.*?:)?/test", + "Attempted to break out of output directory to (.*?:)?/test", ): # (.*?:)? accounts for Windows root utils.sanitised_join("/foo/bar", "/test") diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 3e1f31db..c9742d54 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -1095,7 +1095,7 @@ def fields2pelican( if posts_require_pandoc: logger.error( - "Pandoc must be installed to import the following posts:" "\n {}".format( + "Pandoc must be installed to import the following posts:\n {}".format( "\n ".join(posts_require_pandoc) ) ) @@ -1232,7 +1232,7 @@ def main(): exit(error) if args.wp_attach and input_type != "wordpress": - error = "You must be importing a wordpress xml " "to use the --wp-attach option" + error = "You must be importing a wordpress xml to use the --wp-attach option" exit(error) if input_type == "blogger": diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index c00a252c..2f62e4bc 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -103,11 +103,10 @@ def ask(question, answer=str, default=None, length=None): break else: print("You must enter something") + elif length and len(r) != length: + print(f"Entry must be {length} characters long") else: - if length and len(r) != length: - print(f"Entry must be {length} characters long") - else: - break + break return r @@ -169,7 +168,7 @@ def ask_timezone(question, default, tzurl): r = tz_dict[r] break else: - print("Please enter a valid time zone:\n" f" (check [{tzurl}])") + print(f"Please enter a valid time zone:\n (check [{tzurl}])") return r @@ -253,7 +252,7 @@ needed by Pelican. default=True, ): CONF["siteurl"] = ask( - "What is your URL prefix? (see " "above example; no trailing slash)", + "What is your URL prefix? (see above example; no trailing slash)", str, CONF["siteurl"], ) @@ -266,7 +265,7 @@ needed by Pelican. if CONF["with_pagination"]: CONF["default_pagination"] = ask( - "How many articles per page " "do you want?", + "How many articles per page do you want?", int, CONF["default_pagination"], ) @@ -296,7 +295,7 @@ needed by Pelican. "What is your username on that server?", str, CONF["ftp_user"] ) CONF["ftp_target_dir"] = ask( - "Where do you want to put your " "web site on that server?", + "Where do you want to put your web site on that server?", str, CONF["ftp_target_dir"], ) @@ -314,7 +313,7 @@ needed by Pelican. "What is your username on that server?", str, CONF["ssh_user"] ) CONF["ssh_target_dir"] = ask( - "Where do you want to put your " "web site on that server?", + "Where do you want to put your web site on that server?", str, CONF["ssh_target_dir"], ) @@ -338,23 +337,23 @@ needed by Pelican. ) if ask( - "Do you want to upload your website using " "Rackspace Cloud Files?", + "Do you want to upload your website using Rackspace Cloud Files?", answer=bool, default=False, ): CONF["cloudfiles"] = (True,) CONF["cloudfiles_username"] = ask( - "What is your Rackspace " "Cloud username?", + "What is your Rackspace Cloud username?", str, CONF["cloudfiles_username"], ) CONF["cloudfiles_api_key"] = ask( - "What is your Rackspace " "Cloud API key?", + "What is your Rackspace Cloud API key?", str, CONF["cloudfiles_api_key"], ) CONF["cloudfiles_container"] = ask( - "What is the name of your " "Cloud Files container?", + "What is the name of your Cloud Files container?", str, CONF["cloudfiles_container"], ) @@ -384,7 +383,7 @@ needed by Pelican. except OSError as e: print(f"Error: {e}") - conf_python = dict() + conf_python = {} for key, value in CONF.items(): conf_python[key] = repr(value) render_jinja_template("pelicanconf.py.jinja2", conf_python, "pelicanconf.py") diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index 4ed385f9..8023613c 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -115,11 +115,10 @@ class URLWrapper: if not isinstance(value, str): logger.warning("%s is set to %s", setting, value) return value + elif get_page_name: + return os.path.splitext(value)[0].format(**self.as_dict()) else: - if get_page_name: - return os.path.splitext(value)[0].format(**self.as_dict()) - else: - return value.format(**self.as_dict()) + return value.format(**self.as_dict()) page_name = property( functools.partial(_from_settings, key="URL", get_page_name=True) diff --git a/pelican/utils.py b/pelican/utils.py index 78e3e807..b33eaa22 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -25,9 +25,7 @@ from typing import ( Collection, Generator, Iterable, - Optional, Sequence, - Union, ) import dateutil.parser @@ -167,7 +165,7 @@ class memoized: self.cache[args] = value return value - def __repr__(self) -> Optional[str]: + def __repr__(self) -> str | None: return self.func.__doc__ def __get__(self, obj: Any, objtype): @@ -181,8 +179,8 @@ def deprecated_attribute( old: str, new: str, since: tuple[int, ...], - remove: Optional[tuple[int, ...]] = None, - doc: Optional[str] = None, + remove: tuple[int, ...] | None = None, + doc: str | None = None, ): """Attribute deprecation decorator for gentle upgrades @@ -296,9 +294,7 @@ def slugify( return value.strip() -def copy( - source: str, destination: str, ignores: Optional[Iterable[str]] = None -) -> None: +def copy(source: str, destination: str, ignores: Iterable[str] | None = None) -> None: """Recursively copy source into destination. If source is a file, destination has to be a file as well. @@ -364,7 +360,7 @@ def copy( copy_file(src_path, dst_path) else: logger.warning( - "Skipped copy %s (not a file or " "directory) to %s", + "Skipped copy %s (not a file or directory) to %s", src_path, dst_path, ) @@ -474,7 +470,7 @@ class _HTMLWordTruncator(HTMLParser): self.words_found = 0 self.open_tags = [] self.last_word_end = None - self.truncate_at: Optional[int] = None + self.truncate_at: int | None = None def feed(self, *args, **kwargs) -> None: try: @@ -573,11 +569,10 @@ class _HTMLWordTruncator(HTMLParser): if self.last_word_end is None: if self._word_prefix_regex.match(char): self.last_word_end = ref_end + elif self._word_regex.match(char): + self.last_word_end = ref_end else: - if self._word_regex.match(char): - self.last_word_end = ref_end - else: - self.add_last_word() + self.add_last_word() def handle_entityref(self, name: str) -> None: """ @@ -638,7 +633,7 @@ def truncate_html_words(s: str, num: int, end_text: str = "…") -> str: def process_translations( content_list: list[Content], - translation_id: Optional[Union[str, Collection[str]]] = None, + translation_id: str | Collection[str] | None = None, ) -> tuple[list[Content], list[Content]]: """Finds translations and returns them. @@ -739,7 +734,7 @@ def get_original_items(items: list[Content], with_str: str) -> list[Content]: def order_content( content_list: list[Content], - order_by: Union[str, Callable[[Content], Any], None] = "slug", + order_by: str | Callable[[Content], Any] | None = "slug", ) -> list[Content]: """Sorts content. @@ -841,7 +836,7 @@ def wait_for_changes( def set_date_tzinfo( - d: datetime.datetime, tz_name: Optional[str] = None + d: datetime.datetime, tz_name: str | None = None ) -> datetime.datetime: """Set the timezone for dates that don't have tzinfo""" if tz_name and not d.tzinfo: @@ -857,7 +852,7 @@ def mkdir_p(path: str) -> None: os.makedirs(path, exist_ok=True) -def split_all(path: Union[str, pathlib.Path, None]) -> Optional[Sequence[str]]: +def split_all(path: str | pathlib.Path | None) -> Sequence[str] | None: """Split a path into a list of components While os.path.split() splits a single component off the back of @@ -911,7 +906,7 @@ def maybe_pluralize(count: int, singular: str, plural: str) -> str: @contextmanager def temporary_locale( - temp_locale: Optional[str] = None, lc_category: int = locale.LC_ALL + temp_locale: str | None = None, lc_category: int = locale.LC_ALL ) -> Generator[None, None, None]: """ Enable code to run in a context with a temporary locale diff --git a/pelican/writers.py b/pelican/writers.py index cce01b58..683ae30b 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -21,7 +21,7 @@ logger = logging.getLogger(__name__) class Writer: def __init__(self, output_path, settings=None): self.output_path = output_path - self.reminder = dict() + self.reminder = {} self.settings = settings or {} self._written_files = set() self._overridden_files = set() From 3569dede01cb3e7fe0b8813ebb9a8581d7895a93 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 30 May 2024 21:40:27 +0200 Subject: [PATCH 387/465] Ignore more Ruff fixes in `git blame` --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index fddd0764..2d787f0b 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -11,3 +11,5 @@ db241feaa445375dc05e189e69287000ffe5fa8e 0bd02c00c078fe041b65fbf4eab13601bb42676d # Apply more Ruff checks to code 9d30c5608a58d202b1c02d55651e6ac746bfb173 +# Apply yet more Ruff checks to code +7577dd7603f7cb3a09922d1edb65b6eafb6e2ac7 From 308af1912e02d1d6f2cb0a8c765fe58431f95f00 Mon Sep 17 00:00:00 2001 From: boxydog Date: Fri, 31 May 2024 07:21:13 -0500 Subject: [PATCH 388/465] Apply pre-commit filters to pelican/tests/output --- .pre-commit-config.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a3e7ab17..a6179814 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,5 +20,3 @@ repos: - id: ruff args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - -exclude: ^pelican/tests/output/ From 98bdd87dae1974f786971452a3edec51b630db4e Mon Sep 17 00:00:00 2001 From: boxydog Date: Fri, 31 May 2024 07:21:40 -0500 Subject: [PATCH 389/465] Apply pre-commit filters to the files in pelican/tests/output --- pelican/tests/output/basic/a-markdown-powered-article.html | 2 +- pelican/tests/output/basic/archives.html | 2 +- pelican/tests/output/basic/article-1.html | 2 +- pelican/tests/output/basic/article-2.html | 2 +- pelican/tests/output/basic/article-3.html | 2 +- pelican/tests/output/basic/author/alexis-metaireau.html | 2 +- pelican/tests/output/basic/authors.html | 2 +- pelican/tests/output/basic/categories.html | 2 +- pelican/tests/output/basic/category/bar.html | 2 +- pelican/tests/output/basic/category/cat1.html | 2 +- pelican/tests/output/basic/category/misc.html | 2 +- pelican/tests/output/basic/category/yeah.html | 2 +- .../output/basic/drafts/a-draft-article-without-date.html | 4 ++-- pelican/tests/output/basic/drafts/a-draft-article.html | 2 +- pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml | 2 +- pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml | 2 +- pelican/tests/output/basic/feeds/all-en.atom.xml | 2 +- pelican/tests/output/basic/feeds/all-fr.atom.xml | 2 +- pelican/tests/output/basic/feeds/all.atom.xml | 2 +- pelican/tests/output/basic/feeds/bar.atom.xml | 2 +- pelican/tests/output/basic/feeds/cat1.atom.xml | 2 +- pelican/tests/output/basic/feeds/misc.atom.xml | 2 +- pelican/tests/output/basic/feeds/yeah.atom.xml | 2 +- pelican/tests/output/basic/filename_metadata-example.html | 2 +- pelican/tests/output/basic/index.html | 2 +- pelican/tests/output/basic/oh-yeah-fr.html | 2 +- pelican/tests/output/basic/oh-yeah.html | 2 +- pelican/tests/output/basic/override/index.html | 4 ++-- .../tests/output/basic/pages/this-is-a-test-hidden-page.html | 4 ++-- pelican/tests/output/basic/pages/this-is-a-test-page.html | 4 ++-- pelican/tests/output/basic/second-article-fr.html | 2 +- pelican/tests/output/basic/second-article.html | 2 +- pelican/tests/output/basic/tag/bar.html | 2 +- pelican/tests/output/basic/tag/baz.html | 2 +- pelican/tests/output/basic/tag/foo.html | 2 +- pelican/tests/output/basic/tag/foobar.html | 2 +- pelican/tests/output/basic/tag/oh.html | 4 ++-- pelican/tests/output/basic/tag/yeah.html | 2 +- pelican/tests/output/basic/tags.html | 2 +- pelican/tests/output/basic/theme/css/reset.css | 2 +- .../output/basic/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt | 2 +- pelican/tests/output/basic/this-is-a-super-article.html | 2 +- pelican/tests/output/basic/unbelievable.html | 2 +- pelican/tests/output/custom/a-markdown-powered-article.html | 2 +- pelican/tests/output/custom/archives.html | 2 +- pelican/tests/output/custom/article-1.html | 2 +- pelican/tests/output/custom/article-2.html | 2 +- pelican/tests/output/custom/article-3.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau2.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom/authors.html | 2 +- pelican/tests/output/custom/categories.html | 2 +- pelican/tests/output/custom/category/bar.html | 2 +- pelican/tests/output/custom/category/cat1.html | 2 +- pelican/tests/output/custom/category/misc.html | 2 +- pelican/tests/output/custom/category/yeah.html | 2 +- .../output/custom/drafts/a-draft-article-without-date.html | 2 +- pelican/tests/output/custom/drafts/a-draft-article.html | 2 +- pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml | 2 +- pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml | 2 +- pelican/tests/output/custom/feeds/all-en.atom.xml | 2 +- pelican/tests/output/custom/feeds/all-fr.atom.xml | 2 +- pelican/tests/output/custom/feeds/all.atom.xml | 2 +- pelican/tests/output/custom/feeds/all.rss.xml | 2 +- pelican/tests/output/custom/feeds/bar.atom.xml | 2 +- pelican/tests/output/custom/feeds/bar.rss.xml | 2 +- pelican/tests/output/custom/feeds/cat1.atom.xml | 2 +- pelican/tests/output/custom/feeds/cat1.rss.xml | 2 +- pelican/tests/output/custom/feeds/misc.atom.xml | 2 +- pelican/tests/output/custom/feeds/misc.rss.xml | 2 +- pelican/tests/output/custom/feeds/yeah.atom.xml | 2 +- pelican/tests/output/custom/feeds/yeah.rss.xml | 2 +- pelican/tests/output/custom/filename_metadata-example.html | 2 +- pelican/tests/output/custom/index.html | 2 +- pelican/tests/output/custom/index2.html | 2 +- pelican/tests/output/custom/index3.html | 2 +- pelican/tests/output/custom/jinja2_template.html | 2 +- pelican/tests/output/custom/oh-yeah-fr.html | 2 +- pelican/tests/output/custom/oh-yeah.html | 2 +- pelican/tests/output/custom/override/index.html | 4 ++-- .../tests/output/custom/pages/this-is-a-test-hidden-page.html | 4 ++-- pelican/tests/output/custom/pages/this-is-a-test-page.html | 4 ++-- pelican/tests/output/custom/second-article-fr.html | 2 +- pelican/tests/output/custom/second-article.html | 2 +- pelican/tests/output/custom/tag/bar.html | 2 +- pelican/tests/output/custom/tag/baz.html | 2 +- pelican/tests/output/custom/tag/foo.html | 2 +- pelican/tests/output/custom/tag/foobar.html | 2 +- pelican/tests/output/custom/tag/oh.html | 4 ++-- pelican/tests/output/custom/tag/yeah.html | 2 +- pelican/tests/output/custom/tags.html | 2 +- pelican/tests/output/custom/theme/css/reset.css | 2 +- .../output/custom/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt | 2 +- pelican/tests/output/custom/this-is-a-super-article.html | 2 +- pelican/tests/output/custom/unbelievable.html | 2 +- pelican/tests/output/custom_locale/archives.html | 2 +- .../tests/output/custom_locale/author/alexis-metaireau.html | 2 +- .../tests/output/custom_locale/author/alexis-metaireau2.html | 2 +- .../tests/output/custom_locale/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom_locale/authors.html | 2 +- pelican/tests/output/custom_locale/categories.html | 2 +- pelican/tests/output/custom_locale/category/bar.html | 2 +- pelican/tests/output/custom_locale/category/cat1.html | 2 +- pelican/tests/output/custom_locale/category/misc.html | 2 +- pelican/tests/output/custom_locale/category/yeah.html | 2 +- .../custom_locale/drafts/a-draft-article-without-date.html | 2 +- .../tests/output/custom_locale/drafts/a-draft-article.html | 2 +- .../output/custom_locale/feeds/alexis-metaireau.atom.xml | 2 +- .../tests/output/custom_locale/feeds/alexis-metaireau.rss.xml | 2 +- pelican/tests/output/custom_locale/feeds/all-en.atom.xml | 2 +- pelican/tests/output/custom_locale/feeds/all-fr.atom.xml | 2 +- pelican/tests/output/custom_locale/feeds/all.atom.xml | 2 +- pelican/tests/output/custom_locale/feeds/all.rss.xml | 2 +- pelican/tests/output/custom_locale/feeds/bar.atom.xml | 2 +- pelican/tests/output/custom_locale/feeds/bar.rss.xml | 2 +- pelican/tests/output/custom_locale/feeds/cat1.atom.xml | 2 +- pelican/tests/output/custom_locale/feeds/cat1.rss.xml | 2 +- pelican/tests/output/custom_locale/feeds/misc.atom.xml | 2 +- pelican/tests/output/custom_locale/feeds/misc.rss.xml | 2 +- pelican/tests/output/custom_locale/feeds/yeah.atom.xml | 2 +- pelican/tests/output/custom_locale/feeds/yeah.rss.xml | 2 +- pelican/tests/output/custom_locale/index.html | 2 +- pelican/tests/output/custom_locale/index2.html | 2 +- pelican/tests/output/custom_locale/index3.html | 2 +- pelican/tests/output/custom_locale/jinja2_template.html | 2 +- pelican/tests/output/custom_locale/oh-yeah-fr.html | 2 +- pelican/tests/output/custom_locale/override/index.html | 4 ++-- .../custom_locale/pages/this-is-a-test-hidden-page.html | 4 ++-- .../tests/output/custom_locale/pages/this-is-a-test-page.html | 4 ++-- .../posts/2010/décembre/02/this-is-a-super-article/index.html | 2 +- .../posts/2010/octobre/15/unbelievable/index.html | 2 +- .../custom_locale/posts/2010/octobre/20/oh-yeah/index.html | 2 +- .../posts/2011/avril/20/a-markdown-powered-article/index.html | 2 +- .../custom_locale/posts/2011/février/17/article-1/index.html | 2 +- .../custom_locale/posts/2011/février/17/article-2/index.html | 2 +- .../custom_locale/posts/2011/février/17/article-3/index.html | 2 +- .../posts/2012/février/29/second-article/index.html | 2 +- .../2012/novembre/30/filename_metadata-example/index.html | 2 +- pelican/tests/output/custom_locale/second-article-fr.html | 2 +- pelican/tests/output/custom_locale/tag/bar.html | 2 +- pelican/tests/output/custom_locale/tag/baz.html | 2 +- pelican/tests/output/custom_locale/tag/foo.html | 2 +- pelican/tests/output/custom_locale/tag/foobar.html | 2 +- pelican/tests/output/custom_locale/tag/oh.html | 4 ++-- pelican/tests/output/custom_locale/tag/yeah.html | 2 +- pelican/tests/output/custom_locale/tags.html | 2 +- pelican/tests/output/custom_locale/theme/css/reset.css | 2 +- .../custom_locale/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt | 2 +- 149 files changed, 162 insertions(+), 162 deletions(-) diff --git a/pelican/tests/output/basic/a-markdown-powered-article.html b/pelican/tests/output/basic/a-markdown-powered-article.html index 0098ccac..3c2821f1 100644 --- a/pelican/tests/output/basic/a-markdown-powered-article.html +++ b/pelican/tests/output/basic/a-markdown-powered-article.html @@ -65,4 +65,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/archives.html b/pelican/tests/output/basic/archives.html index e3a6c7df..3218fe1d 100644 --- a/pelican/tests/output/basic/archives.html +++ b/pelican/tests/output/basic/archives.html @@ -67,4 +67,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/article-1.html b/pelican/tests/output/basic/article-1.html index 961ad390..91dcff19 100644 --- a/pelican/tests/output/basic/article-1.html +++ b/pelican/tests/output/basic/article-1.html @@ -64,4 +64,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/article-2.html b/pelican/tests/output/basic/article-2.html index e5389d35..e3ad5724 100644 --- a/pelican/tests/output/basic/article-2.html +++ b/pelican/tests/output/basic/article-2.html @@ -64,4 +64,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/article-3.html b/pelican/tests/output/basic/article-3.html index d23e5da2..2ec3f1e7 100644 --- a/pelican/tests/output/basic/article-3.html +++ b/pelican/tests/output/basic/article-3.html @@ -64,4 +64,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/author/alexis-metaireau.html b/pelican/tests/output/basic/author/alexis-metaireau.html index 12e05ec8..d682ab29 100644 --- a/pelican/tests/output/basic/author/alexis-metaireau.html +++ b/pelican/tests/output/basic/author/alexis-metaireau.html @@ -109,4 +109,4 @@ YEAH !

          - \ No newline at end of file + diff --git a/pelican/tests/output/basic/authors.html b/pelican/tests/output/basic/authors.html index cff1360b..86d2249a 100644 --- a/pelican/tests/output/basic/authors.html +++ b/pelican/tests/output/basic/authors.html @@ -49,4 +49,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/categories.html b/pelican/tests/output/basic/categories.html index cc54f4a3..86606fbe 100644 --- a/pelican/tests/output/basic/categories.html +++ b/pelican/tests/output/basic/categories.html @@ -52,4 +52,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/category/bar.html b/pelican/tests/output/basic/category/bar.html index 1f9c0d8d..c4f57bdd 100644 --- a/pelican/tests/output/basic/category/bar.html +++ b/pelican/tests/output/basic/category/bar.html @@ -65,4 +65,4 @@ YEAH !

          - \ No newline at end of file + diff --git a/pelican/tests/output/basic/category/cat1.html b/pelican/tests/output/basic/category/cat1.html index ca47821b..a291aae4 100644 --- a/pelican/tests/output/basic/category/cat1.html +++ b/pelican/tests/output/basic/category/cat1.html @@ -122,4 +122,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/category/misc.html b/pelican/tests/output/basic/category/misc.html index 58490001..fa786910 100644 --- a/pelican/tests/output/basic/category/misc.html +++ b/pelican/tests/output/basic/category/misc.html @@ -133,4 +133,4 @@ pelican.conf, it will …

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/category/yeah.html b/pelican/tests/output/basic/category/yeah.html index 815d42e4..caade72e 100644 --- a/pelican/tests/output/basic/category/yeah.html +++ b/pelican/tests/output/basic/category/yeah.html @@ -73,4 +73,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/drafts/a-draft-article-without-date.html b/pelican/tests/output/basic/drafts/a-draft-article-without-date.html index 5a2d367b..45bc00ec 100644 --- a/pelican/tests/output/basic/drafts/a-draft-article-without-date.html +++ b/pelican/tests/output/basic/drafts/a-draft-article-without-date.html @@ -34,7 +34,7 @@
    - Published: + Published:

    In misc.

    @@ -65,4 +65,4 @@ listed anywhere else.

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/drafts/a-draft-article.html b/pelican/tests/output/basic/drafts/a-draft-article.html index a0bed241..9349064c 100644 --- a/pelican/tests/output/basic/drafts/a-draft-article.html +++ b/pelican/tests/output/basic/drafts/a-draft-article.html @@ -65,4 +65,4 @@ listed anywhere else.

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml index 8f9a85fa..a0306a0b 100644 --- a/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml +++ b/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml @@ -19,4 +19,4 @@ as well as <strong>inline markup</strong>.</p> YEAH !</p> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> </div> - \ No newline at end of file + diff --git a/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml index 1af41d47..d4989286 100644 --- a/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml +++ b/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml @@ -7,4 +7,4 @@ as well as <strong>inline markup</strong>.</p> YEAH !</p> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> </div> -Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0000tag:None,2010-10-20:/oh-yeah.htmlbarohbaryeah \ No newline at end of file +Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0000tag:None,2010-10-20:/oh-yeah.htmlbarohbaryeah diff --git a/pelican/tests/output/basic/feeds/all-en.atom.xml b/pelican/tests/output/basic/feeds/all-en.atom.xml index 9c44c860..49c76c62 100644 --- a/pelican/tests/output/basic/feeds/all-en.atom.xml +++ b/pelican/tests/output/basic/feeds/all-en.atom.xml @@ -71,4 +71,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+00:002010-03-14T00:00:00+00:00tag:None,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/basic/feeds/all-fr.atom.xml b/pelican/tests/output/basic/feeds/all-fr.atom.xml index 4ecf7534..bca3d2cb 100644 --- a/pelican/tests/output/basic/feeds/all-fr.atom.xml +++ b/pelican/tests/output/basic/feeds/all-fr.atom.xml @@ -1,4 +1,4 @@ A Pelican Blog/2012-02-29T00:00:00+00:00Deuxième article2012-02-29T00:00:00+00:002012-02-29T00:00:00+00:00tag:None,2012-02-29:/second-article-fr.html<p>Ceci est un article, en français.</p> Trop bien !2010-10-20T10:14:00+00:002010-10-20T10:14:00+00:00tag:None,2010-10-20:/oh-yeah-fr.html<p>Et voila du contenu en français</p> - \ No newline at end of file + diff --git a/pelican/tests/output/basic/feeds/all.atom.xml b/pelican/tests/output/basic/feeds/all.atom.xml index e9dceb69..b2399afe 100644 --- a/pelican/tests/output/basic/feeds/all.atom.xml +++ b/pelican/tests/output/basic/feeds/all.atom.xml @@ -73,4 +73,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+00:002010-03-14T00:00:00+00:00tag:None,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/basic/feeds/bar.atom.xml b/pelican/tests/output/basic/feeds/bar.atom.xml index edd1170a..93fff29c 100644 --- a/pelican/tests/output/basic/feeds/bar.atom.xml +++ b/pelican/tests/output/basic/feeds/bar.atom.xml @@ -5,4 +5,4 @@ YEAH !</p> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> </div> - \ No newline at end of file + diff --git a/pelican/tests/output/basic/feeds/cat1.atom.xml b/pelican/tests/output/basic/feeds/cat1.atom.xml index 8516b95c..2f054a1c 100644 --- a/pelican/tests/output/basic/feeds/cat1.atom.xml +++ b/pelican/tests/output/basic/feeds/cat1.atom.xml @@ -4,4 +4,4 @@ <a href="/unbelievable.html">a file-relative link to unbelievable</a></p>Article 12011-02-17T00:00:00+00:002011-02-17T00:00:00+00:00tag:None,2011-02-17:/article-1.html<p>Article 1</p> Article 22011-02-17T00:00:00+00:002011-02-17T00:00:00+00:00tag:None,2011-02-17:/article-2.html<p>Article 2</p> Article 32011-02-17T00:00:00+00:002011-02-17T00:00:00+00:00tag:None,2011-02-17:/article-3.html<p>Article 3</p> - \ No newline at end of file + diff --git a/pelican/tests/output/basic/feeds/misc.atom.xml b/pelican/tests/output/basic/feeds/misc.atom.xml index a307ac4e..9127b2a5 100644 --- a/pelican/tests/output/basic/feeds/misc.atom.xml +++ b/pelican/tests/output/basic/feeds/misc.atom.xml @@ -46,4 +46,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+00:002010-03-14T00:00:00+00:00tag:None,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/basic/feeds/yeah.atom.xml b/pelican/tests/output/basic/feeds/yeah.atom.xml index 6f871915..6411bf6d 100644 --- a/pelican/tests/output/basic/feeds/yeah.atom.xml +++ b/pelican/tests/output/basic/feeds/yeah.atom.xml @@ -13,4 +13,4 @@ as well as <strong>inline markup</strong>.</p> </pre> <p>→ And now try with some utf8 hell: ééé</p> </div> - \ No newline at end of file + diff --git a/pelican/tests/output/basic/filename_metadata-example.html b/pelican/tests/output/basic/filename_metadata-example.html index f3ae9cea..30da64fd 100644 --- a/pelican/tests/output/basic/filename_metadata-example.html +++ b/pelican/tests/output/basic/filename_metadata-example.html @@ -64,4 +64,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/index.html b/pelican/tests/output/basic/index.html index fd334d38..db95e29b 100644 --- a/pelican/tests/output/basic/index.html +++ b/pelican/tests/output/basic/index.html @@ -272,4 +272,4 @@ pelican.conf, it will …

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/oh-yeah-fr.html b/pelican/tests/output/basic/oh-yeah-fr.html index 3fdfeae9..388cc283 100644 --- a/pelican/tests/output/basic/oh-yeah-fr.html +++ b/pelican/tests/output/basic/oh-yeah-fr.html @@ -68,4 +68,4 @@ Translations: - \ No newline at end of file + diff --git a/pelican/tests/output/basic/oh-yeah.html b/pelican/tests/output/basic/oh-yeah.html index f8d3d227..186a3c84 100644 --- a/pelican/tests/output/basic/oh-yeah.html +++ b/pelican/tests/output/basic/oh-yeah.html @@ -76,4 +76,4 @@ YEAH !

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/override/index.html b/pelican/tests/output/basic/override/index.html index a74d802f..1b7c404b 100644 --- a/pelican/tests/output/basic/override/index.html +++ b/pelican/tests/output/basic/override/index.html @@ -24,7 +24,7 @@

    Override url/save_as

    - +

    Test page which overrides save_as and url so that this page will be generated at a custom location.

    @@ -48,4 +48,4 @@ at a custom location.

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html index 1c836201..fda32c01 100644 --- a/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html @@ -24,7 +24,7 @@

    This is a test hidden page

    - +

    This is great for things like error(404) pages Anyone can see this page but it's not linked to anywhere!

    @@ -48,4 +48,4 @@ Anyone can see this page but it's not linked to anywhere!

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/pages/this-is-a-test-page.html b/pelican/tests/output/basic/pages/this-is-a-test-page.html index 372eff76..b79f424d 100644 --- a/pelican/tests/output/basic/pages/this-is-a-test-page.html +++ b/pelican/tests/output/basic/pages/this-is-a-test-page.html @@ -24,7 +24,7 @@

    This is a test page

    - +

    Just an image.

    alternate text wrong path since 'images' folder does not exist @@ -49,4 +49,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/second-article-fr.html b/pelican/tests/output/basic/second-article-fr.html index 514c5585..e70f16e5 100644 --- a/pelican/tests/output/basic/second-article-fr.html +++ b/pelican/tests/output/basic/second-article-fr.html @@ -68,4 +68,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/second-article.html b/pelican/tests/output/basic/second-article.html index 365f99cd..4c833a49 100644 --- a/pelican/tests/output/basic/second-article.html +++ b/pelican/tests/output/basic/second-article.html @@ -68,4 +68,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/tag/bar.html b/pelican/tests/output/basic/tag/bar.html index 047d772a..d2824c2e 100644 --- a/pelican/tests/output/basic/tag/bar.html +++ b/pelican/tests/output/basic/tag/bar.html @@ -121,4 +121,4 @@ YEAH !

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/tag/baz.html b/pelican/tests/output/basic/tag/baz.html index 51518620..01cb257e 100644 --- a/pelican/tests/output/basic/tag/baz.html +++ b/pelican/tests/output/basic/tag/baz.html @@ -64,4 +64,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/tag/foo.html b/pelican/tests/output/basic/tag/foo.html index 0214cf26..bdabf131 100644 --- a/pelican/tests/output/basic/tag/foo.html +++ b/pelican/tests/output/basic/tag/foo.html @@ -91,4 +91,4 @@ as well as inline markup.

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/tag/foobar.html b/pelican/tests/output/basic/tag/foobar.html index ab07e87f..00b72790 100644 --- a/pelican/tests/output/basic/tag/foobar.html +++ b/pelican/tests/output/basic/tag/foobar.html @@ -73,4 +73,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/tag/oh.html b/pelican/tests/output/basic/tag/oh.html index f3af2a2f..c675834b 100644 --- a/pelican/tests/output/basic/tag/oh.html +++ b/pelican/tests/output/basic/tag/oh.html @@ -24,7 +24,7 @@

    Oh Oh Oh

    - +

    This page overrides the listening of the articles under the oh tag.

    @@ -47,4 +47,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/tag/yeah.html b/pelican/tests/output/basic/tag/yeah.html index f04affa8..99806970 100644 --- a/pelican/tests/output/basic/tag/yeah.html +++ b/pelican/tests/output/basic/tag/yeah.html @@ -65,4 +65,4 @@ YEAH !

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/tags.html b/pelican/tests/output/basic/tags.html index db5d6634..4d82ca7b 100644 --- a/pelican/tests/output/basic/tags.html +++ b/pelican/tests/output/basic/tags.html @@ -54,4 +54,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/theme/css/reset.css b/pelican/tests/output/basic/theme/css/reset.css index c88e6196..f5123cf6 100644 --- a/pelican/tests/output/basic/theme/css/reset.css +++ b/pelican/tests/output/basic/theme/css/reset.css @@ -49,4 +49,4 @@ del {text-decoration: line-through;} table { border-collapse: collapse; border-spacing: 0; -} \ No newline at end of file +} diff --git a/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt b/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt index 309fd710..c70bcad3 100644 --- a/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt +++ b/pelican/tests/output/basic/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt @@ -18,7 +18,7 @@ with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, +fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The diff --git a/pelican/tests/output/basic/this-is-a-super-article.html b/pelican/tests/output/basic/this-is-a-super-article.html index 6ac68664..52e0bf65 100644 --- a/pelican/tests/output/basic/this-is-a-super-article.html +++ b/pelican/tests/output/basic/this-is-a-super-article.html @@ -82,4 +82,4 @@ - \ No newline at end of file + diff --git a/pelican/tests/output/basic/unbelievable.html b/pelican/tests/output/basic/unbelievable.html index d87c31ea..710c8ff7 100644 --- a/pelican/tests/output/basic/unbelievable.html +++ b/pelican/tests/output/basic/unbelievable.html @@ -96,4 +96,4 @@ pelican.conf, it will have nothing in default.

    - \ No newline at end of file + diff --git a/pelican/tests/output/custom/a-markdown-powered-article.html b/pelican/tests/output/custom/a-markdown-powered-article.html index 422421d8..3cf1deb7 100644 --- a/pelican/tests/output/custom/a-markdown-powered-article.html +++ b/pelican/tests/output/custom/a-markdown-powered-article.html @@ -111,4 +111,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/archives.html b/pelican/tests/output/custom/archives.html index 34c3f4cd..6cf7b82d 100644 --- a/pelican/tests/output/custom/archives.html +++ b/pelican/tests/output/custom/archives.html @@ -95,4 +95,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/article-1.html b/pelican/tests/output/custom/article-1.html index 226489ea..e28fb39b 100644 --- a/pelican/tests/output/custom/article-1.html +++ b/pelican/tests/output/custom/article-1.html @@ -110,4 +110,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/article-2.html b/pelican/tests/output/custom/article-2.html index 1a835849..8c33fe86 100644 --- a/pelican/tests/output/custom/article-2.html +++ b/pelican/tests/output/custom/article-2.html @@ -110,4 +110,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/article-3.html b/pelican/tests/output/custom/article-3.html index c3076e98..15862270 100644 --- a/pelican/tests/output/custom/article-3.html +++ b/pelican/tests/output/custom/article-3.html @@ -110,4 +110,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/author/alexis-metaireau.html b/pelican/tests/output/custom/author/alexis-metaireau.html index aef8c6e6..22a23340 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau.html +++ b/pelican/tests/output/custom/author/alexis-metaireau.html @@ -171,4 +171,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/author/alexis-metaireau2.html b/pelican/tests/output/custom/author/alexis-metaireau2.html index 8d17eed5..e5f7e953 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom/author/alexis-metaireau2.html @@ -186,4 +186,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index 48fe75ba..c5162737 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -136,4 +136,4 @@ pelican.conf, it will …

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/authors.html b/pelican/tests/output/custom/authors.html index de18662e..06919fb7 100644 --- a/pelican/tests/output/custom/authors.html +++ b/pelican/tests/output/custom/authors.html @@ -77,4 +77,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/categories.html b/pelican/tests/output/custom/categories.html index b558d389..34236cdb 100644 --- a/pelican/tests/output/custom/categories.html +++ b/pelican/tests/output/custom/categories.html @@ -80,4 +80,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/category/bar.html b/pelican/tests/output/custom/category/bar.html index 6f3e9f5b..ad1ef940 100644 --- a/pelican/tests/output/custom/category/bar.html +++ b/pelican/tests/output/custom/category/bar.html @@ -93,4 +93,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/category/cat1.html b/pelican/tests/output/custom/category/cat1.html index d9b1e41f..79428cd7 100644 --- a/pelican/tests/output/custom/category/cat1.html +++ b/pelican/tests/output/custom/category/cat1.html @@ -162,4 +162,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html index 7cc5bf9b..16c31252 100644 --- a/pelican/tests/output/custom/category/misc.html +++ b/pelican/tests/output/custom/category/misc.html @@ -173,4 +173,4 @@ pelican.conf, it will …

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/category/yeah.html b/pelican/tests/output/custom/category/yeah.html index 957c76ae..e919ddd5 100644 --- a/pelican/tests/output/custom/category/yeah.html +++ b/pelican/tests/output/custom/category/yeah.html @@ -101,4 +101,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/drafts/a-draft-article-without-date.html b/pelican/tests/output/custom/drafts/a-draft-article-without-date.html index 94d2c8e9..81137fc7 100644 --- a/pelican/tests/output/custom/drafts/a-draft-article-without-date.html +++ b/pelican/tests/output/custom/drafts/a-draft-article-without-date.html @@ -96,4 +96,4 @@ listed anywhere else.

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/drafts/a-draft-article.html b/pelican/tests/output/custom/drafts/a-draft-article.html index c926c14f..260ccaf1 100644 --- a/pelican/tests/output/custom/drafts/a-draft-article.html +++ b/pelican/tests/output/custom/drafts/a-draft-article.html @@ -96,4 +96,4 @@ listed anywhere else.

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml index f6cde37e..5bfb73d0 100644 --- a/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml +++ b/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml @@ -71,4 +71,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+01:002010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml index c13a742b..b5654682 100644 --- a/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml +++ b/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml @@ -26,4 +26,4 @@ YEAH !</p> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will …</p></div>Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:/unbelievable.htmlmiscThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> -Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc \ No newline at end of file +Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc diff --git a/pelican/tests/output/custom/feeds/all-en.atom.xml b/pelican/tests/output/custom/feeds/all-en.atom.xml index 703f56f0..1aac415a 100644 --- a/pelican/tests/output/custom/feeds/all-en.atom.xml +++ b/pelican/tests/output/custom/feeds/all-en.atom.xml @@ -71,4 +71,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+01:002010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom/feeds/all-fr.atom.xml b/pelican/tests/output/custom/feeds/all-fr.atom.xml index 39565ca6..68ebd738 100644 --- a/pelican/tests/output/custom/feeds/all-fr.atom.xml +++ b/pelican/tests/output/custom/feeds/all-fr.atom.xml @@ -1,4 +1,4 @@ Alexis' loghttp://blog.notmyidea.org/2012-02-29T00:00:00+01:00A personal blog.Deuxième article2012-02-29T00:00:00+01:002012-02-29T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-02-29:/second-article-fr.html<p>Ceci est un article, en français.</p> Trop bien !2010-10-20T10:14:00+02:002010-10-20T10:14:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-20:/oh-yeah-fr.html<p>Et voila du contenu en français</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom/feeds/all.atom.xml b/pelican/tests/output/custom/feeds/all.atom.xml index de5e733d..dbe37a32 100644 --- a/pelican/tests/output/custom/feeds/all.atom.xml +++ b/pelican/tests/output/custom/feeds/all.atom.xml @@ -73,4 +73,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+01:002010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom/feeds/all.rss.xml b/pelican/tests/output/custom/feeds/all.rss.xml index a8d984fa..45a8dc58 100644 --- a/pelican/tests/output/custom/feeds/all.rss.xml +++ b/pelican/tests/output/custom/feeds/all.rss.xml @@ -28,4 +28,4 @@ YEAH !</p> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will …</p></div>Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:/unbelievable.htmlmiscThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> -Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc \ No newline at end of file +Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc diff --git a/pelican/tests/output/custom/feeds/bar.atom.xml b/pelican/tests/output/custom/feeds/bar.atom.xml index 002de037..d79aad2d 100644 --- a/pelican/tests/output/custom/feeds/bar.atom.xml +++ b/pelican/tests/output/custom/feeds/bar.atom.xml @@ -5,4 +5,4 @@ YEAH !</p> <img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> </div> - \ No newline at end of file + diff --git a/pelican/tests/output/custom/feeds/bar.rss.xml b/pelican/tests/output/custom/feeds/bar.rss.xml index 53035e71..c993753a 100644 --- a/pelican/tests/output/custom/feeds/bar.rss.xml +++ b/pelican/tests/output/custom/feeds/bar.rss.xml @@ -5,4 +5,4 @@ YEAH !</p> <img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> </div> -Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0200tag:blog.notmyidea.org,2010-10-20:/oh-yeah.htmlbarohbaryeah \ No newline at end of file +Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0200tag:blog.notmyidea.org,2010-10-20:/oh-yeah.htmlbarohbaryeah diff --git a/pelican/tests/output/custom/feeds/cat1.atom.xml b/pelican/tests/output/custom/feeds/cat1.atom.xml index e8ed355b..c44ac595 100644 --- a/pelican/tests/output/custom/feeds/cat1.atom.xml +++ b/pelican/tests/output/custom/feeds/cat1.atom.xml @@ -4,4 +4,4 @@ <a href="http://blog.notmyidea.org/unbelievable.html">a file-relative link to unbelievable</a></p>Article 12011-02-17T00:00:00+01:002011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:/article-1.html<p>Article 1</p> Article 22011-02-17T00:00:00+01:002011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:/article-2.html<p>Article 2</p> Article 32011-02-17T00:00:00+01:002011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:/article-3.html<p>Article 3</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom/feeds/cat1.rss.xml b/pelican/tests/output/custom/feeds/cat1.rss.xml index 9951b293..a6ca0b53 100644 --- a/pelican/tests/output/custom/feeds/cat1.rss.xml +++ b/pelican/tests/output/custom/feeds/cat1.rss.xml @@ -4,4 +4,4 @@ <a href="http://blog.notmyidea.org/unbelievable.html">a file-relative link to unbelievable</a></p>Alexis MétaireauWed, 20 Apr 2011 00:00:00 +0200tag:blog.notmyidea.org,2011-04-20:/a-markdown-powered-article.htmlcat1Article 1http://blog.notmyidea.org/article-1.html<p>Article 1</p> Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:/article-1.htmlcat1Article 2http://blog.notmyidea.org/article-2.html<p>Article 2</p> Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:/article-2.htmlcat1Article 3http://blog.notmyidea.org/article-3.html<p>Article 3</p> -Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:/article-3.htmlcat1 \ No newline at end of file +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:/article-3.htmlcat1 diff --git a/pelican/tests/output/custom/feeds/misc.atom.xml b/pelican/tests/output/custom/feeds/misc.atom.xml index a260f67d..117c2bc2 100644 --- a/pelican/tests/output/custom/feeds/misc.atom.xml +++ b/pelican/tests/output/custom/feeds/misc.atom.xml @@ -46,4 +46,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+01:002010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom/feeds/misc.rss.xml b/pelican/tests/output/custom/feeds/misc.rss.xml index 828eae62..03b3b2fe 100644 --- a/pelican/tests/output/custom/feeds/misc.rss.xml +++ b/pelican/tests/output/custom/feeds/misc.rss.xml @@ -13,4 +13,4 @@ <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will …</p></div>Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:/unbelievable.htmlmiscThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> -Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc \ No newline at end of file +Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc diff --git a/pelican/tests/output/custom/feeds/yeah.atom.xml b/pelican/tests/output/custom/feeds/yeah.atom.xml index 127ab590..0e5e8858 100644 --- a/pelican/tests/output/custom/feeds/yeah.atom.xml +++ b/pelican/tests/output/custom/feeds/yeah.atom.xml @@ -13,4 +13,4 @@ as well as <strong>inline markup</strong>.</p> </pre> <p>→ And now try with some utf8 hell: ééé</p> </div> - \ No newline at end of file + diff --git a/pelican/tests/output/custom/feeds/yeah.rss.xml b/pelican/tests/output/custom/feeds/yeah.rss.xml index 98b820ec..be592f11 100644 --- a/pelican/tests/output/custom/feeds/yeah.rss.xml +++ b/pelican/tests/output/custom/feeds/yeah.rss.xml @@ -1,4 +1,4 @@ Alexis' log - yeahhttp://blog.notmyidea.org/A personal blog.Sun, 17 Nov 2013 23:29:00 +0100This is a super article !http://blog.notmyidea.org/this-is-a-super-article.html<p class="first last">Multi-line metadata should be supported as well as <strong>inline markup</strong>.</p> -Alexis MétaireauThu, 02 Dec 2010 10:14:00 +0100tag:blog.notmyidea.org,2010-12-02:/this-is-a-super-article.htmlyeahfoobarfoobar \ No newline at end of file +Alexis MétaireauThu, 02 Dec 2010 10:14:00 +0100tag:blog.notmyidea.org,2010-12-02:/this-is-a-super-article.htmlyeahfoobarfoobar diff --git a/pelican/tests/output/custom/filename_metadata-example.html b/pelican/tests/output/custom/filename_metadata-example.html index 70d41179..8bc1babb 100644 --- a/pelican/tests/output/custom/filename_metadata-example.html +++ b/pelican/tests/output/custom/filename_metadata-example.html @@ -110,4 +110,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/index.html b/pelican/tests/output/custom/index.html index 6c4d7a94..d97eb4c4 100644 --- a/pelican/tests/output/custom/index.html +++ b/pelican/tests/output/custom/index.html @@ -171,4 +171,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/index2.html b/pelican/tests/output/custom/index2.html index 043f8c33..11943a88 100644 --- a/pelican/tests/output/custom/index2.html +++ b/pelican/tests/output/custom/index2.html @@ -186,4 +186,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index f8ebaac0..72ac69c6 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -136,4 +136,4 @@ pelican.conf, it will …

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/jinja2_template.html b/pelican/tests/output/custom/jinja2_template.html index 27a6df8e..71a9e63a 100644 --- a/pelican/tests/output/custom/jinja2_template.html +++ b/pelican/tests/output/custom/jinja2_template.html @@ -72,4 +72,4 @@ Some text }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/oh-yeah-fr.html b/pelican/tests/output/custom/oh-yeah-fr.html index f30c9b63..555af6c0 100644 --- a/pelican/tests/output/custom/oh-yeah-fr.html +++ b/pelican/tests/output/custom/oh-yeah-fr.html @@ -114,4 +114,4 @@ Translations: }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/oh-yeah.html b/pelican/tests/output/custom/oh-yeah.html index 20b90351..661d0153 100644 --- a/pelican/tests/output/custom/oh-yeah.html +++ b/pelican/tests/output/custom/oh-yeah.html @@ -119,4 +119,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/override/index.html b/pelican/tests/output/custom/override/index.html index 9d1c4f46..09493938 100644 --- a/pelican/tests/output/custom/override/index.html +++ b/pelican/tests/output/custom/override/index.html @@ -28,7 +28,7 @@

    Override url/save_as

    - +

    Test page which overrides save_as and url so that this page will be generated at a custom location.

    @@ -76,4 +76,4 @@ at a custom location.

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html index bd0d03ed..6eb578c9 100644 --- a/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html @@ -28,7 +28,7 @@

    This is a test hidden page

    - +

    This is great for things like error(404) pages Anyone can see this page but it's not linked to anywhere!

    @@ -76,4 +76,4 @@ Anyone can see this page but it's not linked to anywhere!

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/pages/this-is-a-test-page.html b/pelican/tests/output/custom/pages/this-is-a-test-page.html index 16d17c3b..db977694 100644 --- a/pelican/tests/output/custom/pages/this-is-a-test-page.html +++ b/pelican/tests/output/custom/pages/this-is-a-test-page.html @@ -28,7 +28,7 @@

    This is a test page

    - +

    Just an image.

    alternate text wrong path since 'images' folder does not exist @@ -77,4 +77,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/second-article-fr.html b/pelican/tests/output/custom/second-article-fr.html index ff7af42d..27638737 100644 --- a/pelican/tests/output/custom/second-article-fr.html +++ b/pelican/tests/output/custom/second-article-fr.html @@ -114,4 +114,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/second-article.html b/pelican/tests/output/custom/second-article.html index a71df8cf..2ed7c822 100644 --- a/pelican/tests/output/custom/second-article.html +++ b/pelican/tests/output/custom/second-article.html @@ -114,4 +114,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/tag/bar.html b/pelican/tests/output/custom/tag/bar.html index 719ea12a..bc949e11 100644 --- a/pelican/tests/output/custom/tag/bar.html +++ b/pelican/tests/output/custom/tag/bar.html @@ -152,4 +152,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/tag/baz.html b/pelican/tests/output/custom/tag/baz.html index 048dc5ce..bb61b787 100644 --- a/pelican/tests/output/custom/tag/baz.html +++ b/pelican/tests/output/custom/tag/baz.html @@ -110,4 +110,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/tag/foo.html b/pelican/tests/output/custom/tag/foo.html index 637bce0b..4edfe432 100644 --- a/pelican/tests/output/custom/tag/foo.html +++ b/pelican/tests/output/custom/tag/foo.html @@ -122,4 +122,4 @@ as well as inline markup.

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/tag/foobar.html b/pelican/tests/output/custom/tag/foobar.html index 296ee098..cebd52f4 100644 --- a/pelican/tests/output/custom/tag/foobar.html +++ b/pelican/tests/output/custom/tag/foobar.html @@ -101,4 +101,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/tag/oh.html b/pelican/tests/output/custom/tag/oh.html index 4a7ce439..4723075f 100644 --- a/pelican/tests/output/custom/tag/oh.html +++ b/pelican/tests/output/custom/tag/oh.html @@ -28,7 +28,7 @@

    Oh Oh Oh

    - +

    This page overrides the listening of the articles under the oh tag.

    @@ -75,4 +75,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/tag/yeah.html b/pelican/tests/output/custom/tag/yeah.html index 78618480..c3e3bb4a 100644 --- a/pelican/tests/output/custom/tag/yeah.html +++ b/pelican/tests/output/custom/tag/yeah.html @@ -93,4 +93,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/tags.html b/pelican/tests/output/custom/tags.html index 1a8ef572..17feabf9 100644 --- a/pelican/tests/output/custom/tags.html +++ b/pelican/tests/output/custom/tags.html @@ -82,4 +82,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/theme/css/reset.css b/pelican/tests/output/custom/theme/css/reset.css index c88e6196..f5123cf6 100644 --- a/pelican/tests/output/custom/theme/css/reset.css +++ b/pelican/tests/output/custom/theme/css/reset.css @@ -49,4 +49,4 @@ del {text-decoration: line-through;} table { border-collapse: collapse; border-spacing: 0; -} \ No newline at end of file +} diff --git a/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt b/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt index 309fd710..c70bcad3 100644 --- a/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt +++ b/pelican/tests/output/custom/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt @@ -18,7 +18,7 @@ with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, +fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The diff --git a/pelican/tests/output/custom/this-is-a-super-article.html b/pelican/tests/output/custom/this-is-a-super-article.html index ab9dc16f..565d9f3f 100644 --- a/pelican/tests/output/custom/this-is-a-super-article.html +++ b/pelican/tests/output/custom/this-is-a-super-article.html @@ -125,4 +125,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/unbelievable.html b/pelican/tests/output/custom/unbelievable.html index a7ad0efa..6153392e 100644 --- a/pelican/tests/output/custom/unbelievable.html +++ b/pelican/tests/output/custom/unbelievable.html @@ -142,4 +142,4 @@ pelican.conf, it will have nothing in default.

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/archives.html b/pelican/tests/output/custom_locale/archives.html index 25ef1c6c..8b07609d 100644 --- a/pelican/tests/output/custom_locale/archives.html +++ b/pelican/tests/output/custom_locale/archives.html @@ -95,4 +95,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau.html b/pelican/tests/output/custom_locale/author/alexis-metaireau.html index df76ccf6..86ee7416 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau.html @@ -171,4 +171,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html index 42a929d0..cf332f97 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html @@ -186,4 +186,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html index 941cdc46..dff225d2 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html @@ -136,4 +136,4 @@ pelican.conf, it will …

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/authors.html b/pelican/tests/output/custom_locale/authors.html index 0907ff7c..7cac21cc 100644 --- a/pelican/tests/output/custom_locale/authors.html +++ b/pelican/tests/output/custom_locale/authors.html @@ -77,4 +77,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/categories.html b/pelican/tests/output/custom_locale/categories.html index 01dc680a..3b102a03 100644 --- a/pelican/tests/output/custom_locale/categories.html +++ b/pelican/tests/output/custom_locale/categories.html @@ -80,4 +80,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/category/bar.html b/pelican/tests/output/custom_locale/category/bar.html index d3dc29de..644a611c 100644 --- a/pelican/tests/output/custom_locale/category/bar.html +++ b/pelican/tests/output/custom_locale/category/bar.html @@ -93,4 +93,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/category/cat1.html b/pelican/tests/output/custom_locale/category/cat1.html index 5278242c..0fe47339 100644 --- a/pelican/tests/output/custom_locale/category/cat1.html +++ b/pelican/tests/output/custom_locale/category/cat1.html @@ -162,4 +162,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/category/misc.html b/pelican/tests/output/custom_locale/category/misc.html index 82a77efb..b300e98f 100644 --- a/pelican/tests/output/custom_locale/category/misc.html +++ b/pelican/tests/output/custom_locale/category/misc.html @@ -173,4 +173,4 @@ pelican.conf, it will …

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/category/yeah.html b/pelican/tests/output/custom_locale/category/yeah.html index f0dff1b6..afa3c4dd 100644 --- a/pelican/tests/output/custom_locale/category/yeah.html +++ b/pelican/tests/output/custom_locale/category/yeah.html @@ -101,4 +101,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/drafts/a-draft-article-without-date.html b/pelican/tests/output/custom_locale/drafts/a-draft-article-without-date.html index 788f01a3..114aac6c 100644 --- a/pelican/tests/output/custom_locale/drafts/a-draft-article-without-date.html +++ b/pelican/tests/output/custom_locale/drafts/a-draft-article-without-date.html @@ -96,4 +96,4 @@ listed anywhere else.

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/drafts/a-draft-article.html b/pelican/tests/output/custom_locale/drafts/a-draft-article.html index bd54bc56..406fbe36 100644 --- a/pelican/tests/output/custom_locale/drafts/a-draft-article.html +++ b/pelican/tests/output/custom_locale/drafts/a-draft-article.html @@ -96,4 +96,4 @@ listed anywhere else.

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml index d52de6f9..85b63173 100644 --- a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml @@ -71,4 +71,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+01:002010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml index 4d9f5165..f383a84d 100644 --- a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml +++ b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml @@ -26,4 +26,4 @@ YEAH !</p> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will …</p></div>Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:/posts/2010/octobre/15/unbelievable/miscThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> -Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc \ No newline at end of file +Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc diff --git a/pelican/tests/output/custom_locale/feeds/all-en.atom.xml b/pelican/tests/output/custom_locale/feeds/all-en.atom.xml index 974c63b3..974b4279 100644 --- a/pelican/tests/output/custom_locale/feeds/all-en.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/all-en.atom.xml @@ -71,4 +71,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+01:002010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml b/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml index cfd6eeb2..f44b17f2 100644 --- a/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml @@ -1,4 +1,4 @@ Alexis' loghttp://blog.notmyidea.org/2012-02-29T00:00:00+01:00Deuxième article2012-02-29T00:00:00+01:002012-02-29T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-02-29:/second-article-fr.html<p>Ceci est un article, en français.</p> Trop bien !2010-10-20T10:14:00+02:002010-10-20T10:14:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-20:/oh-yeah-fr.html<p>Et voila du contenu en français</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/feeds/all.atom.xml b/pelican/tests/output/custom_locale/feeds/all.atom.xml index e1063591..c2a5ef38 100644 --- a/pelican/tests/output/custom_locale/feeds/all.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/all.atom.xml @@ -73,4 +73,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+01:002010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/feeds/all.rss.xml b/pelican/tests/output/custom_locale/feeds/all.rss.xml index 442fc1ab..9642bef9 100644 --- a/pelican/tests/output/custom_locale/feeds/all.rss.xml +++ b/pelican/tests/output/custom_locale/feeds/all.rss.xml @@ -28,4 +28,4 @@ YEAH !</p> <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will …</p></div>Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:/posts/2010/octobre/15/unbelievable/miscThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> -Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc \ No newline at end of file +Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc diff --git a/pelican/tests/output/custom_locale/feeds/bar.atom.xml b/pelican/tests/output/custom_locale/feeds/bar.atom.xml index d4467ea7..f3d8cc1e 100644 --- a/pelican/tests/output/custom_locale/feeds/bar.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/bar.atom.xml @@ -5,4 +5,4 @@ YEAH !</p> <img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> </div> - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/feeds/bar.rss.xml b/pelican/tests/output/custom_locale/feeds/bar.rss.xml index d17d7703..71130b31 100644 --- a/pelican/tests/output/custom_locale/feeds/bar.rss.xml +++ b/pelican/tests/output/custom_locale/feeds/bar.rss.xml @@ -5,4 +5,4 @@ YEAH !</p> <img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> </div> -Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0200tag:blog.notmyidea.org,2010-10-20:/posts/2010/octobre/20/oh-yeah/barohbaryeah \ No newline at end of file +Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0200tag:blog.notmyidea.org,2010-10-20:/posts/2010/octobre/20/oh-yeah/barohbaryeah diff --git a/pelican/tests/output/custom_locale/feeds/cat1.atom.xml b/pelican/tests/output/custom_locale/feeds/cat1.atom.xml index 87a822e5..9a72b398 100644 --- a/pelican/tests/output/custom_locale/feeds/cat1.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/cat1.atom.xml @@ -4,4 +4,4 @@ <a href="http://blog.notmyidea.org/posts/2010/octobre/15/unbelievable/">a file-relative link to unbelievable</a></p>Article 12011-02-17T00:00:00+01:002011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:/posts/2011/février/17/article-1/<p>Article 1</p> Article 22011-02-17T00:00:00+01:002011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:/posts/2011/février/17/article-2/<p>Article 2</p> Article 32011-02-17T00:00:00+01:002011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:/posts/2011/février/17/article-3/<p>Article 3</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/feeds/cat1.rss.xml b/pelican/tests/output/custom_locale/feeds/cat1.rss.xml index 6b328fda..c16b8092 100644 --- a/pelican/tests/output/custom_locale/feeds/cat1.rss.xml +++ b/pelican/tests/output/custom_locale/feeds/cat1.rss.xml @@ -4,4 +4,4 @@ <a href="http://blog.notmyidea.org/posts/2010/octobre/15/unbelievable/">a file-relative link to unbelievable</a></p>Alexis MétaireauWed, 20 Apr 2011 00:00:00 +0200tag:blog.notmyidea.org,2011-04-20:/posts/2011/avril/20/a-markdown-powered-article/cat1Article 1http://blog.notmyidea.org/posts/2011/f%C3%A9vrier/17/article-1/<p>Article 1</p> Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:/posts/2011/février/17/article-1/cat1Article 2http://blog.notmyidea.org/posts/2011/f%C3%A9vrier/17/article-2/<p>Article 2</p> Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:/posts/2011/février/17/article-2/cat1Article 3http://blog.notmyidea.org/posts/2011/f%C3%A9vrier/17/article-3/<p>Article 3</p> -Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:/posts/2011/février/17/article-3/cat1 \ No newline at end of file +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:/posts/2011/février/17/article-3/cat1 diff --git a/pelican/tests/output/custom_locale/feeds/misc.atom.xml b/pelican/tests/output/custom_locale/feeds/misc.atom.xml index 2e46b473..4898ab84 100644 --- a/pelican/tests/output/custom_locale/feeds/misc.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/misc.atom.xml @@ -46,4 +46,4 @@ pelican.conf, it will have nothing in default.</p> <p>Lovely.</p> </div> The baz tag2010-03-14T00:00:00+01:002010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/feeds/misc.rss.xml b/pelican/tests/output/custom_locale/feeds/misc.rss.xml index 01c24157..d1493ae8 100644 --- a/pelican/tests/output/custom_locale/feeds/misc.rss.xml +++ b/pelican/tests/output/custom_locale/feeds/misc.rss.xml @@ -13,4 +13,4 @@ <h2>Testing another case</h2> <p>This will now have a line number in 'custom' since it's the default in pelican.conf, it will …</p></div>Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:/posts/2010/octobre/15/unbelievable/miscThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> -Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc \ No newline at end of file +Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:/tag/baz.htmlmisc diff --git a/pelican/tests/output/custom_locale/feeds/yeah.atom.xml b/pelican/tests/output/custom_locale/feeds/yeah.atom.xml index 6f2e5f82..f316ada5 100644 --- a/pelican/tests/output/custom_locale/feeds/yeah.atom.xml +++ b/pelican/tests/output/custom_locale/feeds/yeah.atom.xml @@ -13,4 +13,4 @@ as well as <strong>inline markup</strong>.</p> </pre> <p>→ And now try with some utf8 hell: ééé</p> </div> - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/feeds/yeah.rss.xml b/pelican/tests/output/custom_locale/feeds/yeah.rss.xml index b7fb81f8..a0d0ba28 100644 --- a/pelican/tests/output/custom_locale/feeds/yeah.rss.xml +++ b/pelican/tests/output/custom_locale/feeds/yeah.rss.xml @@ -1,4 +1,4 @@ Alexis' log - yeahhttp://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100This is a super article !http://blog.notmyidea.org/posts/2010/d%C3%A9cembre/02/this-is-a-super-article/<p class="first last">Multi-line metadata should be supported as well as <strong>inline markup</strong>.</p> -Alexis MétaireauThu, 02 Dec 2010 10:14:00 +0100tag:blog.notmyidea.org,2010-12-02:/posts/2010/décembre/02/this-is-a-super-article/yeahfoobarfoobar \ No newline at end of file +Alexis MétaireauThu, 02 Dec 2010 10:14:00 +0100tag:blog.notmyidea.org,2010-12-02:/posts/2010/décembre/02/this-is-a-super-article/yeahfoobarfoobar diff --git a/pelican/tests/output/custom_locale/index.html b/pelican/tests/output/custom_locale/index.html index 054011cc..37bfa0ac 100644 --- a/pelican/tests/output/custom_locale/index.html +++ b/pelican/tests/output/custom_locale/index.html @@ -171,4 +171,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/index2.html b/pelican/tests/output/custom_locale/index2.html index fa2c4d2d..871fd594 100644 --- a/pelican/tests/output/custom_locale/index2.html +++ b/pelican/tests/output/custom_locale/index2.html @@ -186,4 +186,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html index 71ccb4b1..e3bbdffd 100644 --- a/pelican/tests/output/custom_locale/index3.html +++ b/pelican/tests/output/custom_locale/index3.html @@ -136,4 +136,4 @@ pelican.conf, it will …

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/jinja2_template.html b/pelican/tests/output/custom_locale/jinja2_template.html index 01957524..c4bcf019 100644 --- a/pelican/tests/output/custom_locale/jinja2_template.html +++ b/pelican/tests/output/custom_locale/jinja2_template.html @@ -72,4 +72,4 @@ Some text }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/oh-yeah-fr.html b/pelican/tests/output/custom_locale/oh-yeah-fr.html index 01c7800f..02d30117 100644 --- a/pelican/tests/output/custom_locale/oh-yeah-fr.html +++ b/pelican/tests/output/custom_locale/oh-yeah-fr.html @@ -114,4 +114,4 @@ Translations: }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/override/index.html b/pelican/tests/output/custom_locale/override/index.html index a5fcbe04..e1212e43 100644 --- a/pelican/tests/output/custom_locale/override/index.html +++ b/pelican/tests/output/custom_locale/override/index.html @@ -28,7 +28,7 @@

    Override url/save_as

    - +

    Test page which overrides save_as and url so that this page will be generated at a custom location.

    @@ -76,4 +76,4 @@ at a custom location.

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html index 3e33e3fc..5ba42b3f 100644 --- a/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html @@ -28,7 +28,7 @@

    This is a test hidden page

    - +

    This is great for things like error(404) pages Anyone can see this page but it's not linked to anywhere!

    @@ -76,4 +76,4 @@ Anyone can see this page but it's not linked to anywhere!

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html b/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html index e4f1d60b..83aaddb0 100644 --- a/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html +++ b/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html @@ -28,7 +28,7 @@

    This is a test page

    - +

    Just an image.

    alternate text wrong path since 'images' folder does not exist @@ -77,4 +77,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html index 4b522248..33866221 100644 --- a/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html @@ -125,4 +125,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html index f27464f8..ef9994c6 100644 --- a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html @@ -142,4 +142,4 @@ pelican.conf, it will have nothing in default.

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html index b47732b7..07332310 100644 --- a/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html @@ -119,4 +119,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html index 80e57bc3..5410e9d1 100644 --- a/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html @@ -111,4 +111,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html index c427e2c7..d4afa8b2 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html @@ -110,4 +110,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html index 88c7adf2..2d849548 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html @@ -110,4 +110,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html index 257fb6dd..c6b03cc2 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html @@ -110,4 +110,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html index 3b3bd10a..a5a12bc9 100644 --- a/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html @@ -114,4 +114,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html index 2618f705..848a0431 100644 --- a/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html +++ b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html @@ -110,4 +110,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/second-article-fr.html b/pelican/tests/output/custom_locale/second-article-fr.html index 7edf7710..51ee3977 100644 --- a/pelican/tests/output/custom_locale/second-article-fr.html +++ b/pelican/tests/output/custom_locale/second-article-fr.html @@ -114,4 +114,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/tag/bar.html b/pelican/tests/output/custom_locale/tag/bar.html index e0e0487f..a4bda96f 100644 --- a/pelican/tests/output/custom_locale/tag/bar.html +++ b/pelican/tests/output/custom_locale/tag/bar.html @@ -152,4 +152,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/tag/baz.html b/pelican/tests/output/custom_locale/tag/baz.html index 7fe3b54c..a27bc92e 100644 --- a/pelican/tests/output/custom_locale/tag/baz.html +++ b/pelican/tests/output/custom_locale/tag/baz.html @@ -110,4 +110,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/tag/foo.html b/pelican/tests/output/custom_locale/tag/foo.html index a31e348b..736fa5b8 100644 --- a/pelican/tests/output/custom_locale/tag/foo.html +++ b/pelican/tests/output/custom_locale/tag/foo.html @@ -122,4 +122,4 @@ as well as inline markup.

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/tag/foobar.html b/pelican/tests/output/custom_locale/tag/foobar.html index a33f3362..93757930 100644 --- a/pelican/tests/output/custom_locale/tag/foobar.html +++ b/pelican/tests/output/custom_locale/tag/foobar.html @@ -101,4 +101,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/tag/oh.html b/pelican/tests/output/custom_locale/tag/oh.html index 5d021c7c..1e583e7e 100644 --- a/pelican/tests/output/custom_locale/tag/oh.html +++ b/pelican/tests/output/custom_locale/tag/oh.html @@ -28,7 +28,7 @@

    Oh Oh Oh

    - +

    This page overrides the listening of the articles under the oh tag.

    @@ -75,4 +75,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/tag/yeah.html b/pelican/tests/output/custom_locale/tag/yeah.html index 72b905f5..219ab7e7 100644 --- a/pelican/tests/output/custom_locale/tag/yeah.html +++ b/pelican/tests/output/custom_locale/tag/yeah.html @@ -93,4 +93,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/tags.html b/pelican/tests/output/custom_locale/tags.html index 88b02724..3e59c960 100644 --- a/pelican/tests/output/custom_locale/tags.html +++ b/pelican/tests/output/custom_locale/tags.html @@ -82,4 +82,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/theme/css/reset.css b/pelican/tests/output/custom_locale/theme/css/reset.css index c88e6196..f5123cf6 100644 --- a/pelican/tests/output/custom_locale/theme/css/reset.css +++ b/pelican/tests/output/custom_locale/theme/css/reset.css @@ -49,4 +49,4 @@ del {text-decoration: line-through;} table { border-collapse: collapse; border-spacing: 0; -} \ No newline at end of file +} diff --git a/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt b/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt index 309fd710..c70bcad3 100644 --- a/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt +++ b/pelican/tests/output/custom_locale/theme/fonts/Yanone_Kaffeesatz_LICENSE.txt @@ -18,7 +18,7 @@ with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, +fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The From 7f07c220deaa6a9e62aaab783651a3591330b524 Mon Sep 17 00:00:00 2001 From: boxydog Date: Thu, 30 May 2024 15:45:59 -0500 Subject: [PATCH 390/465] Add pre-commit djhtml, djcss, djjs to indent templates --- .pre-commit-config.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a6179814..bfdd6149 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,6 +13,7 @@ repos: - id: end-of-file-fixer - id: forbid-new-submodules - id: trailing-whitespace + - repo: https://github.com/astral-sh/ruff-pre-commit # ruff version should match the one in pyproject.toml rev: v0.4.6 @@ -20,3 +21,10 @@ repos: - id: ruff args: [--fix, --exit-non-zero-on-fix] - id: ruff-format + + - repo: https://github.com/rtts/djhtml + rev: '3.0.6' + hooks: + - id: djhtml + - id: djcss + - id: djjs From 4af40e80772a58eac8969360e5caeb99e3e26e78 Mon Sep 17 00:00:00 2001 From: boxydog Date: Fri, 31 May 2024 07:34:01 -0500 Subject: [PATCH 391/465] pre-commit filter auto-indents templates, css, js --- docs/_templates/page.html | 372 +++++++-------- .../content/article_with_inline_svg.html | 14 +- ...2017-04-21_-medium-post--d1bf01d62ba3.html | 140 +++--- .../basic/a-markdown-powered-article.html | 112 ++--- pelican/tests/output/basic/archives.html | 122 ++--- pelican/tests/output/basic/article-1.html | 108 ++--- pelican/tests/output/basic/article-2.html | 108 ++--- pelican/tests/output/basic/article-3.html | 108 ++--- .../output/basic/author/alexis-metaireau.html | 182 ++++---- pelican/tests/output/basic/authors.html | 84 ++-- pelican/tests/output/basic/categories.html | 90 ++-- pelican/tests/output/basic/category/bar.html | 114 ++--- pelican/tests/output/basic/category/cat1.html | 194 ++++---- pelican/tests/output/basic/category/misc.html | 216 ++++----- pelican/tests/output/basic/category/yeah.html | 124 ++--- .../drafts/a-draft-article-without-date.html | 110 ++--- .../output/basic/drafts/a-draft-article.html | 110 ++--- .../basic/filename_metadata-example.html | 108 ++--- pelican/tests/output/basic/index.html | 438 +++++++++--------- pelican/tests/output/basic/oh-yeah-fr.html | 114 ++--- pelican/tests/output/basic/oh-yeah.html | 130 +++--- .../tests/output/basic/override/index.html | 82 ++-- .../pages/this-is-a-test-hidden-page.html | 82 ++-- .../basic/pages/this-is-a-test-page.html | 84 ++-- .../tests/output/basic/second-article-fr.html | 114 ++--- .../tests/output/basic/second-article.html | 114 ++--- pelican/tests/output/basic/tag/bar.html | 204 ++++---- pelican/tests/output/basic/tag/baz.html | 108 ++--- pelican/tests/output/basic/tag/foo.html | 154 +++--- pelican/tests/output/basic/tag/foobar.html | 124 ++--- pelican/tests/output/basic/tag/oh.html | 80 ++-- pelican/tests/output/basic/tag/yeah.html | 114 ++--- pelican/tests/output/basic/tags.html | 94 ++-- .../tests/output/basic/theme/css/fonts.css | 16 +- pelican/tests/output/basic/theme/css/main.css | 372 +++++++-------- .../tests/output/basic/theme/css/pygment.css | 166 +++---- .../tests/output/basic/theme/css/reset.css | 24 +- .../tests/output/basic/theme/fonts/font.css | 16 +- .../output/basic/this-is-a-super-article.html | 138 +++--- pelican/tests/output/basic/unbelievable.html | 166 +++---- .../custom/a-markdown-powered-article.html | 204 ++++---- pelican/tests/output/custom/archives.html | 178 +++---- pelican/tests/output/custom/article-1.html | 200 ++++---- pelican/tests/output/custom/article-2.html | 200 ++++---- pelican/tests/output/custom/article-3.html | 200 ++++---- .../custom/author/alexis-metaireau.html | 294 ++++++------ .../custom/author/alexis-metaireau2.html | 320 ++++++------- .../custom/author/alexis-metaireau3.html | 238 +++++----- pelican/tests/output/custom/authors.html | 138 +++--- pelican/tests/output/custom/categories.html | 144 +++--- pelican/tests/output/custom/category/bar.html | 170 +++---- .../tests/output/custom/category/cat1.html | 274 +++++------ .../tests/output/custom/category/misc.html | 296 ++++++------ .../tests/output/custom/category/yeah.html | 180 +++---- .../drafts/a-draft-article-without-date.html | 172 +++---- .../output/custom/drafts/a-draft-article.html | 172 +++---- .../custom/filename_metadata-example.html | 200 ++++---- pelican/tests/output/custom/index.html | 294 ++++++------ pelican/tests/output/custom/index2.html | 320 ++++++------- pelican/tests/output/custom/index3.html | 238 +++++----- .../tests/output/custom/jinja2_template.html | 130 +++--- pelican/tests/output/custom/oh-yeah-fr.html | 206 ++++---- pelican/tests/output/custom/oh-yeah.html | 216 ++++----- .../tests/output/custom/override/index.html | 138 +++--- .../pages/this-is-a-test-hidden-page.html | 138 +++--- .../custom/pages/this-is-a-test-page.html | 140 +++--- .../output/custom/second-article-fr.html | 206 ++++---- .../tests/output/custom/second-article.html | 206 ++++---- pelican/tests/output/custom/tag/bar.html | 266 +++++------ pelican/tests/output/custom/tag/baz.html | 200 ++++---- pelican/tests/output/custom/tag/foo.html | 216 ++++----- pelican/tests/output/custom/tag/foobar.html | 180 +++---- pelican/tests/output/custom/tag/oh.html | 136 +++--- pelican/tests/output/custom/tag/yeah.html | 170 +++---- pelican/tests/output/custom/tags.html | 148 +++--- .../tests/output/custom/theme/css/fonts.css | 16 +- .../tests/output/custom/theme/css/main.css | 372 +++++++-------- .../tests/output/custom/theme/css/pygment.css | 166 +++---- .../tests/output/custom/theme/css/reset.css | 24 +- .../tests/output/custom/theme/fonts/font.css | 16 +- .../custom/this-is-a-super-article.html | 224 ++++----- pelican/tests/output/custom/unbelievable.html | 258 +++++------ .../tests/output/custom_locale/archives.html | 178 +++---- .../author/alexis-metaireau.html | 294 ++++++------ .../author/alexis-metaireau2.html | 320 ++++++------- .../author/alexis-metaireau3.html | 238 +++++----- .../tests/output/custom_locale/authors.html | 138 +++--- .../output/custom_locale/categories.html | 144 +++--- .../output/custom_locale/category/bar.html | 170 +++---- .../output/custom_locale/category/cat1.html | 274 +++++------ .../output/custom_locale/category/misc.html | 296 ++++++------ .../output/custom_locale/category/yeah.html | 180 +++---- .../drafts/a-draft-article-without-date.html | 172 +++---- .../custom_locale/drafts/a-draft-article.html | 172 +++---- pelican/tests/output/custom_locale/index.html | 294 ++++++------ .../tests/output/custom_locale/index2.html | 320 ++++++------- .../tests/output/custom_locale/index3.html | 238 +++++----- .../output/custom_locale/jinja2_template.html | 130 +++--- .../output/custom_locale/oh-yeah-fr.html | 206 ++++---- .../output/custom_locale/override/index.html | 138 +++--- .../pages/this-is-a-test-hidden-page.html | 138 +++--- .../pages/this-is-a-test-page.html | 140 +++--- .../02/this-is-a-super-article/index.html | 224 ++++----- .../2010/octobre/15/unbelievable/index.html | 258 +++++------ .../posts/2010/octobre/20/oh-yeah/index.html | 216 ++++----- .../20/a-markdown-powered-article/index.html | 204 ++++---- .../2011/février/17/article-1/index.html | 200 ++++---- .../2011/février/17/article-2/index.html | 200 ++++---- .../2011/février/17/article-3/index.html | 200 ++++---- .../2012/février/29/second-article/index.html | 206 ++++---- .../30/filename_metadata-example/index.html | 200 ++++---- .../custom_locale/second-article-fr.html | 206 ++++---- .../tests/output/custom_locale/tag/bar.html | 266 +++++------ .../tests/output/custom_locale/tag/baz.html | 200 ++++---- .../tests/output/custom_locale/tag/foo.html | 216 ++++----- .../output/custom_locale/tag/foobar.html | 180 +++---- .../tests/output/custom_locale/tag/oh.html | 136 +++--- .../tests/output/custom_locale/tag/yeah.html | 170 +++---- pelican/tests/output/custom_locale/tags.html | 148 +++--- .../output/custom_locale/theme/css/fonts.css | 16 +- .../output/custom_locale/theme/css/main.css | 372 +++++++-------- .../custom_locale/theme/css/pygment.css | 166 +++---- .../output/custom_locale/theme/css/reset.css | 24 +- .../output/custom_locale/theme/fonts/font.css | 16 +- pelican/themes/notmyidea/static/css/fonts.css | 16 +- pelican/themes/notmyidea/static/css/main.css | 372 +++++++-------- .../themes/notmyidea/static/css/pygment.css | 166 +++---- pelican/themes/notmyidea/static/css/reset.css | 24 +- .../themes/notmyidea/static/fonts/font.css | 16 +- .../themes/notmyidea/templates/analytics.html | 2 +- .../themes/notmyidea/templates/archives.html | 18 +- .../themes/notmyidea/templates/article.html | 72 +-- .../notmyidea/templates/article_infos.html | 26 +- .../themes/notmyidea/templates/authors.html | 16 +- pelican/themes/notmyidea/templates/base.html | 150 +++--- .../notmyidea/templates/categories.html | 16 +- .../notmyidea/templates/disqus_script.html | 18 +- .../themes/notmyidea/templates/github.html | 14 +- pelican/themes/notmyidea/templates/index.html | 110 ++--- pelican/themes/notmyidea/templates/page.html | 12 +- .../notmyidea/templates/period_archives.html | 18 +- pelican/themes/notmyidea/templates/tags.html | 16 +- .../notmyidea/templates/translations.html | 18 +- .../themes/notmyidea/templates/twitter.html | 2 +- pelican/themes/simple/templates/archives.html | 14 +- pelican/themes/simple/templates/article.html | 76 +-- pelican/themes/simple/templates/author.html | 2 +- pelican/themes/simple/templates/authors.html | 6 +- pelican/themes/simple/templates/base.html | 92 ++-- .../themes/simple/templates/categories.html | 6 +- pelican/themes/simple/templates/category.html | 2 +- pelican/themes/simple/templates/index.html | 30 +- pelican/themes/simple/templates/page.html | 12 +- .../themes/simple/templates/pagination.html | 22 +- .../simple/templates/period_archives.html | 14 +- pelican/themes/simple/templates/tag.html | 2 +- pelican/themes/simple/templates/tags.html | 6 +- .../themes/simple/templates/translations.html | 22 +- samples/content/pages/jinja2_template.html | 2 +- 159 files changed, 11584 insertions(+), 11584 deletions(-) diff --git a/docs/_templates/page.html b/docs/_templates/page.html index 233f43ad..0fbfdf7d 100644 --- a/docs/_templates/page.html +++ b/docs/_templates/page.html @@ -1,201 +1,201 @@ {% extends "base.html" %} {% block body -%} -{{ super() }} -{% include "partials/icons.html" %} + {{ super() }} + {% include "partials/icons.html" %} - - - - + + + + -{% if theme_announcement -%} -
    - -
    -{%- endif %} + {% if theme_announcement -%} +
    + +
    + {%- endif %} -
    -
    -
    - -
    - -
    -
    - +
    +
    +
    +
    - -
    -
    - -
    -
    -
    - - - - - {% trans %}Back to top{% endtrans %} - -
    - {% if theme_top_of_page_button == "edit" -%} - {%- include "components/edit-this-page.html" with context -%} - {%- elif theme_top_of_page_button != None -%} - {{ warning("Got an unsupported value for 'top_of_page_button'") }} - {%- endif -%} - {#- Theme toggle -#} -
    - -
    - +
    +
    +
    -
    - {% block content %}{{ body }}{% endblock %} -
    +
    -
    - {% block footer %} - -
    -
    - {%- if show_copyright %} - - {%- endif %} - {%- if last_updated -%} -
    - {% trans last_updated=last_updated|e -%} - Last updated on {{ last_updated }} - {%- endtrans -%} -
    - {%- endif %} + +
    -
    - +
    +
    +
    + + + + + {% trans %}Back to top{% endtrans %} + +
    + {% if theme_top_of_page_button == "edit" -%} + {%- include "components/edit-this-page.html" with context -%} + {%- elif theme_top_of_page_button != None -%} + {{ warning("Got an unsupported value for 'top_of_page_button'") }} + {%- endif -%} + {#- Theme toggle -#} +
    + +
    + +
    +
    + {% block content %}{{ body }}{% endblock %} +
    +
    + +
    + +
    -
    {%- endblock %} diff --git a/pelican/tests/content/article_with_inline_svg.html b/pelican/tests/content/article_with_inline_svg.html index 07f97a8a..06725704 100644 --- a/pelican/tests/content/article_with_inline_svg.html +++ b/pelican/tests/content/article_with_inline_svg.html @@ -5,13 +5,13 @@ Ensure that the title attribute in an inline svg is not handled as an HTML title. - - A different title inside the inline SVG - - - - - + + A different title inside the inline SVG + + + + + diff --git a/pelican/tests/content/medium_posts/2017-04-21_-medium-post--d1bf01d62ba3.html b/pelican/tests/content/medium_posts/2017-04-21_-medium-post--d1bf01d62ba3.html index 02d272dc..6d28f1a2 100644 --- a/pelican/tests/content/medium_posts/2017-04-21_-medium-post--d1bf01d62ba3.html +++ b/pelican/tests/content/medium_posts/2017-04-21_-medium-post--d1bf01d62ba3.html @@ -1,72 +1,72 @@ A title
    -
    -

    A name (like title)

    -
    -
    + * { + font-family: Georgia, Cambria, "Times New Roman", Times, serif; + } + html, body { + margin: 0; + padding: 0; + } + h1 { + font-size: 50px; + margin-bottom: 17px; + color: #333; + } + h2 { + font-size: 24px; + line-height: 1.6; + margin: 30px 0 0 0; + margin-bottom: 18px; + margin-top: 33px; + color: #333; + } + h3 { + font-size: 30px; + margin: 10px 0 20px 0; + color: #333; + } + header { + width: 640px; + margin: auto; + } + section { + width: 640px; + margin: auto; + } + section p { + margin-bottom: 27px; + font-size: 20px; + line-height: 1.6; + color: #333; + } + section img { + max-width: 640px; + } + footer { + padding: 0 20px; + margin: 50px 0; + text-align: center; + font-size: 12px; + } + .aspectRatioPlaceholder { + max-width: auto !important; + max-height: auto !important; + } + .aspectRatioPlaceholder-fill { + padding-bottom: 0 !important; + } + header, + section[data-field=subtitle], + section[data-field=description] { + display: none; + } + +
    +
    +

    Title header

    A paragraph of content.

    Paragraph number two.

    A list:

    1. One.
    2. Two.
    3. Three.

    A link: link text.

    Header 2

    A block quote:

    quote words strong words

    after blockquote

    A figure caption.

    A final note: Cross-Validated has sometimes been helpful.


    +
    diff --git a/pelican/tests/output/basic/a-markdown-powered-article.html b/pelican/tests/output/basic/a-markdown-powered-article.html index 3c2821f1..66136d87 100644 --- a/pelican/tests/output/basic/a-markdown-powered-article.html +++ b/pelican/tests/output/basic/a-markdown-powered-article.html @@ -1,68 +1,68 @@ - - - - - A markdown powered article - - - - + + + + + A markdown powered article + + + + - - -
    -
    -
    -

    - A markdown powered article

    -
    + + +
    + -
    -
    -
    +
    +
    + -
    + +
    +
    - - + diff --git a/pelican/tests/output/basic/archives.html b/pelican/tests/output/basic/archives.html index 3218fe1d..7aa6b263 100644 --- a/pelican/tests/output/basic/archives.html +++ b/pelican/tests/output/basic/archives.html @@ -1,70 +1,70 @@ - - - - - A Pelican Blog - - - + + + + + A Pelican Blog + + + - - -
    -

    Archives for A Pelican Blog

    + + +
    +

    Archives for A Pelican Blog

    -
    -
    Fri 30 November 2012
    -
    FILENAME_METADATA example
    -
    Wed 29 February 2012
    -
    Second article
    -
    Wed 20 April 2011
    -
    A markdown powered article
    -
    Thu 17 February 2011
    -
    Article 1
    -
    Thu 17 February 2011
    -
    Article 2
    -
    Thu 17 February 2011
    -
    Article 3
    -
    Thu 02 December 2010
    -
    This is a super article !
    -
    Wed 20 October 2010
    -
    Oh yeah !
    -
    Fri 15 October 2010
    -
    Unbelievable !
    -
    Sun 14 March 2010
    -
    The baz tag
    -
    -
    -
    -
    +
    + -
    + + +
    - - + diff --git a/pelican/tests/output/basic/article-1.html b/pelican/tests/output/basic/article-1.html index 91dcff19..4ec2a0e1 100644 --- a/pelican/tests/output/basic/article-1.html +++ b/pelican/tests/output/basic/article-1.html @@ -1,67 +1,67 @@ - - - - - Article 1 - - - - + + + + + Article 1 + + + + - - -
    - +
    +
    + -
    + + +
    - - + diff --git a/pelican/tests/output/basic/article-2.html b/pelican/tests/output/basic/article-2.html index e3ad5724..99819902 100644 --- a/pelican/tests/output/basic/article-2.html +++ b/pelican/tests/output/basic/article-2.html @@ -1,67 +1,67 @@ - - - - - Article 2 - - - - + + + + + Article 2 + + + + - - -
    - +
    +
    + -
    + + +
    - - + diff --git a/pelican/tests/output/basic/article-3.html b/pelican/tests/output/basic/article-3.html index 2ec3f1e7..596db91f 100644 --- a/pelican/tests/output/basic/article-3.html +++ b/pelican/tests/output/basic/article-3.html @@ -1,67 +1,67 @@ - - - - - Article 3 - - - - + + + + + Article 3 + + + + - - -
    - +
    +
    + -
    + + +
    - - + diff --git a/pelican/tests/output/basic/author/alexis-metaireau.html b/pelican/tests/output/basic/author/alexis-metaireau.html index d682ab29..e4bde41d 100644 --- a/pelican/tests/output/basic/author/alexis-metaireau.html +++ b/pelican/tests/output/basic/author/alexis-metaireau.html @@ -1,112 +1,112 @@ - - - - - A Pelican Blog - Alexis Métaireau - - - + + + + + A Pelican Blog - Alexis Métaireau + + + - - + + - +

    → And now try with some utf8 hell: ééé

    + + +
    -

    Other articles

    -
    -
      +

      Other articles

      +
      +
        -
      1. -
        -

        Oh yeah !

        -
        +
      2. +
        +

        Oh yeah !

        +
        -
        -
        - - Published: Wed 20 October 2010 - +
        +
        -

        Why not ?

        -

        After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! -YEAH !

        -alternate text -
        +
        +

        Why not ?

        +

        After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! + YEAH !

        + alternate text +
        - read more -
        -
      3. -
      + read more + + +
    -
    - +
    - - + diff --git a/pelican/tests/output/basic/authors.html b/pelican/tests/output/basic/authors.html index 86d2249a..27d10c2f 100644 --- a/pelican/tests/output/basic/authors.html +++ b/pelican/tests/output/basic/authors.html @@ -1,52 +1,52 @@ - - - - - A Pelican Blog - Authors - - - + + + + + A Pelican Blog - Authors + + + - - + + -
    -

    Authors on A Pelican Blog

    - -
    - -
    - -
    +
    - + + +
    - + + + diff --git a/pelican/tests/output/basic/categories.html b/pelican/tests/output/basic/categories.html index 86606fbe..5c85b20e 100644 --- a/pelican/tests/output/basic/categories.html +++ b/pelican/tests/output/basic/categories.html @@ -1,55 +1,55 @@ - - - - - A Pelican Blog - Categories - - - + + + + + A Pelican Blog - Categories + + + - - + + -
    -

    Categories for A Pelican Blog

    - -
    - -
    - -
    +
    - + + +
    - + + + diff --git a/pelican/tests/output/basic/category/bar.html b/pelican/tests/output/basic/category/bar.html index c4f57bdd..e89375bf 100644 --- a/pelican/tests/output/basic/category/bar.html +++ b/pelican/tests/output/basic/category/bar.html @@ -1,68 +1,68 @@ - - - - - A Pelican Blog - bar - - - + + + + + A Pelican Blog - bar + + + - - + + - +
    + -
    + + +
    - - + diff --git a/pelican/tests/output/basic/category/cat1.html b/pelican/tests/output/basic/category/cat1.html index a291aae4..6c0cd64c 100644 --- a/pelican/tests/output/basic/category/cat1.html +++ b/pelican/tests/output/basic/category/cat1.html @@ -1,125 +1,125 @@ - - - - - A Pelican Blog - cat1 - - - + + + + + A Pelican Blog - cat1 + + + - - + + -
    -

    Other articles

    -
    -
      +

      Other articles

      +
      +
        -
      1. -
      2. -
      3. -
        -

        Article 3

        -
        +
      4. +
        +

        Article 3

        +
        -
        -
        - - Published: Thu 17 February 2011 - +
        +
        + + Published: Thu 17 February 2011 + -

        In cat1.

        +

        In cat1.

        -

        Article 3

        +

        Article 3

        - read more -
        -
      5. -
      + read more + + +
    -
    - +
    - - + diff --git a/pelican/tests/output/basic/category/misc.html b/pelican/tests/output/basic/category/misc.html index fa786910..fa9eb563 100644 --- a/pelican/tests/output/basic/category/misc.html +++ b/pelican/tests/output/basic/category/misc.html @@ -1,136 +1,136 @@ - - - - - A Pelican Blog - misc - - - + + + + + A Pelican Blog - misc + + + - - + + -
    -

    Other articles

    -
    -
      +

      Other articles

      +
      +
        -
      1. -
      2. -
        -

        Unbelievable !

        -
        +
      3. +
        +

        Unbelievable !

        +
        -
        -