Merge branch 'master' into hidden-posts

This commit is contained in:
Seth Giovanetti 2021-04-20 17:32:54 -05:00
commit 05c9c1f617
82 changed files with 735 additions and 413 deletions

View file

@ -11,5 +11,5 @@ trim_trailing_whitespace = true
[*.py]
max_line_length = 79
[*.yml]
[*.{yml,yaml}]
indent_size = 2

View file

@ -15,14 +15,14 @@ jobs:
strategy:
matrix:
config:
- os: ubuntu
python: 3.5
- os: ubuntu
python: 3.6
- os: ubuntu
python: 3.7
- os: ubuntu
python: 3.8
- os: ubuntu
python: 3.9
- os: macos
python: 3.7
- os: windows
@ -31,7 +31,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,9 +84,9 @@ 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
python-version: 3.7
- name: Set pip cache (Linux)
uses: actions/cache@v1
if: startsWith(runner.os, 'Linux')
@ -108,9 +108,9 @@ 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
python-version: 3.7
- name: Set pip cache (Linux)
uses: actions/cache@v1
if: startsWith(runner.os, 'Linux')
@ -134,7 +134,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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,5 +1,5 @@
Pelican |build-status| |pypi-version| |repology|
=====================================
================================================
Pelican is a static site generator, written in Python_.
@ -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

1
THANKS
View file

@ -97,6 +97,7 @@ Kyle Fuller
Laureline Guerin
Leonard Huang
Leroy Jiang
Lucas Cimon
Marcel Hellkamp
Marco Milanesi
Marcus Fredriksson

View file

@ -1,6 +1,52 @@
Release history
###############
4.6.0 - 2021-03-23
==================
* Add new URL pattern to ``PAGINATION_PATTERNS`` for the last page in the list `(#1401) <https://github.com/getpelican/pelican/issues/1401>`_
* Speed up ``livereload`` Invoke task via caching `(#2847) <https://github.com/getpelican/pelican/pull/2847>`_
* Ignore ``None`` return value from ``get_generators`` signal `(#2850) <https://github.com/getpelican/pelican/pull/2850>`_
* Relax dependency minimum versions and remove upper bounds
4.5.4 - 2021-01-04
==================
Replace plugin definitions in settings with string representations after registering, so they can be cached correctly `(#2828) <https://github.com/getpelican/pelican/issues/2828>`_.
4.5.3 - 2020-12-01
==================
Fix a mistake made in PR #2821
4.5.2 - 2020-11-22
==================
Improve logging of generators and writer loaders
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
==================
* 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_SUFFIX`` 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 +58,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 +76,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
==================

View file

@ -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!
@ -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
@ -145,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

View file

@ -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?
=============================================
@ -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?
===================================
@ -228,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?
@ -242,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 steamline Pelican, `tag_cloud` generation has been
moved out of the pelican core and into a separate `plugin
<https://github.com/getpelican/pelican-plugins/tree/master/tag_cloud>`_. 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
<https://github.com/pelican-plugins/tag-cloud>`_. See the :ref:`plugins`
documentation for further information about the Pelican plugin system.
Since I upgraded Pelican my pages are no longer rendered
========================================================

View file

@ -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

View file

@ -1,19 +1,17 @@
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 <https://pip.pypa.io/en/stable/>`_::
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
(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
@ -26,7 +24,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 +33,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<publish>` section.
@ -46,17 +44,13 @@ Optional packages
If you plan on using `Markdown <https://pypi.org/project/Markdown/>`_ as a
markup format, you can install Pelican with Markdown support::
pip install pelican[Markdown]
Or you might need to install it a posteriori::
pip install Markdown
python -m pip install "pelican[markdown]"
Typographical enhancements can be enabled in your settings file, but first the
requisite `Typogrify <https://pypi.org/project/typogrify/>`_ library must be
installed::
pip install typogrify
python -m pip install typogrify
Dependencies
------------
@ -75,9 +69,8 @@ automatically installed without any action on your part:
broadcast signaling system
* `unidecode <https://pypi.org/project/Unidecode/>`_, for ASCII
transliterations of Unicode text
* `six <https://pypi.org/project/six/>`_, for Python 2 and 3 compatibility
utilities
* `MarkupSafe <https://pypi.org/project/MarkupSafe/>`_, for a markup safe
* `MarkupSafe <https://pypi.org/project/MarkupSafe/>`_, for a markup-safe
string implementation
* `python-dateutil <https://pypi.org/project/python-dateutil/>`_, to read
the date metadata
@ -85,10 +78,10 @@ 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``::
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.
@ -126,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/

View file

@ -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():

View file

@ -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
@ -70,8 +70,8 @@ How to create plugins
=====================
Plugins are based on the concept of signals. Pelican sends signals, and plugins
subscribe to those signals. The list of signals are defined in a subsequent
section.
subscribe to those signals. The list of available signals is documented in a
subsequent section.
The only rule to follow for plugins is to define a ``register`` callable, in
which you map the signals to your plugin logic. Let's take a simple example::
@ -94,6 +94,10 @@ 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.
Namespace plugin structure
--------------------------
@ -272,3 +276,4 @@ Adding a new generator is also really easy. You might want to have a look at
.. _Pip: https://pip.pypa.io/
.. _pelican-plugins bug #314: https://github.com/getpelican/pelican-plugins/issues/314
.. _Blinker: https://pythonhosted.org/blinker/

View file

@ -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

View file

@ -8,10 +8,10 @@ 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::
pip install pelican[Markdown]
python -m pip install "pelican[markdown]"
Create a project
----------------

View file

@ -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)
@ -270,8 +276,8 @@ Basic settings
If set to True, several typographical improvements will be incorporated into
the generated HTML via the `Typogrify
<https://pypi.python.org/pypi/typogrify>`_ library, which can be installed
via: ``pip install typogrify``
<https://pypi.org/project/typogrify/>`_ library, which can be installed
via: ``python -m pip install typogrify``
.. data:: TYPOGRIFY_IGNORE_TAGS = []
@ -297,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
@ -770,7 +776,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
@ -1061,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
============
@ -1123,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'

View file

@ -20,10 +20,10 @@ 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 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
@ -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"""
@ -93,7 +97,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 +118,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'):
@ -168,46 +172,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 v in value:
if isinstance(v, type):
logger.debug('Found generator: %s', v)
generators.append(v)
for receiver, values in signals.get_generators.send(self):
if not isinstance(values, Iterable):
values = (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
# 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):
@ -236,6 +251,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, '
@ -329,6 +356,16 @@ def parse_arguments(argv=None):
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. 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
)
args = parser.parse_args(argv)
if args.port is not None and not args.listen:
@ -364,6 +401,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
@ -441,7 +479,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:

View file

@ -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)
@ -392,7 +410,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):

View file

@ -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 ''

View file

@ -1,6 +1,7 @@
import importlib
import importlib.machinery
import importlib.util
import inspect
import logging
import pkgutil
import sys
@ -40,6 +41,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:
@ -48,6 +54,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
@ -99,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__

View file

@ -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)

View file

@ -1,6 +1,7 @@
import copy
import importlib.util
import inspect
import json
import locale
import logging
import os
@ -135,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,
@ -658,3 +659,25 @@ def configure_settings(settings):
continue # setting not specified, nothing to do
return settings
def coerce_overrides(overrides):
if overrides is None:
return {}
coerced = {}
types_to_cast = {int, str, bool}
for k, v in overrides.items():
if k not in DEFAULT_CONFIG:
logger.warning('Override for unknown setting %s, ignoring', k)
continue
setting_type = type(DEFAULT_CONFIG[k])
if setting_type not in types_to_cast:
coerced[k] = json.loads(v)
else:
try:
coerced[k] = setting_type(v)
except ValueError:
logger.debug('ValueError for %s override with %s, try to '
'load as json', k, v)
coerced[k] = json.loads(v)
return coerced

4
pelican/signals.py Normal file
View file

@ -0,0 +1,4 @@
raise ImportError(
'Importing from `pelican.signals` is deprecated. '
'Use `from pelican import signals` or `import pelican.plugins.signals` instead.'
)

View file

@ -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"...

View file

@ -1,7 +1,5 @@
from .submodule import noop # noqa: F401
NAME = 'normal plugin'
def register():
pass

View file

@ -0,0 +1,2 @@
def register():
pass

View file

@ -0,0 +1,2 @@
def register():
pass

View file

@ -81,7 +81,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">

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -220,7 +220,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">

File diff suppressed because one or more lines are too long

View file

@ -168,4 +168,4 @@
}());
</script>
</body>
</html>
</html>

View file

@ -183,4 +183,4 @@ YEAH !</p>
}());
</script>
</body>
</html>
</html>

View file

@ -50,7 +50,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">
@ -133,4 +133,4 @@ pelican.conf, it will …</p></div>
}());
</script>
</body>
</html>
</html>

View file

@ -94,7 +94,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">

File diff suppressed because one or more lines are too long

View file

@ -19,7 +19,7 @@ YEAH !&lt;/p&gt;
&lt;a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html"&gt;a file-relative link to markdown-article&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="testing-sourcecode-directive"&gt;
&lt;h2&gt;Testing sourcecode directive&lt;/h2&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;div class="section" id="testing-another-case"&gt;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -21,7 +21,7 @@ YEAH !&lt;/p&gt;
&lt;a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html"&gt;a file-relative link to markdown-article&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="testing-sourcecode-directive"&gt;
&lt;h2&gt;Testing sourcecode directive&lt;/h2&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;div class="section" id="testing-another-case"&gt;

File diff suppressed because one or more lines are too long

View file

@ -6,7 +6,7 @@
&lt;a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html"&gt;a file-relative link to markdown-article&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="testing-sourcecode-directive"&gt;
&lt;h2&gt;Testing sourcecode directive&lt;/h2&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;div class="section" id="testing-another-case"&gt;

View file

@ -168,4 +168,4 @@
}());
</script>
</body>
</html>
</html>

View file

@ -183,4 +183,4 @@ YEAH !</p>
}());
</script>
</body>
</html>
</html>

View file

@ -50,7 +50,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">
@ -133,4 +133,4 @@ pelican.conf, it will …</p></div>
}());
</script>
</body>
</html>
</html>

File diff suppressed because one or more lines are too long

View file

@ -168,4 +168,4 @@
}());
</script>
</body>
</html>
</html>

View file

@ -183,4 +183,4 @@ YEAH !</p>
}());
</script>
</body>
</html>
</html>

View file

@ -50,7 +50,7 @@
<a class="reference external" href="../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">
@ -133,4 +133,4 @@ pelican.conf, it will …</p></div>
}());
</script>
</body>
</html>
</html>

View file

@ -94,7 +94,7 @@
<a class="reference external" href="../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">

File diff suppressed because one or more lines are too long

View file

@ -19,7 +19,7 @@ YEAH !&lt;/p&gt;
&lt;a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/"&gt;a file-relative link to markdown-article&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="testing-sourcecode-directive"&gt;
&lt;h2&gt;Testing sourcecode directive&lt;/h2&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;div class="section" id="testing-another-case"&gt;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -21,7 +21,7 @@ YEAH !&lt;/p&gt;
&lt;a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/"&gt;a file-relative link to markdown-article&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="testing-sourcecode-directive"&gt;
&lt;h2&gt;Testing sourcecode directive&lt;/h2&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;div class="section" id="testing-another-case"&gt;

File diff suppressed because one or more lines are too long

View file

@ -6,7 +6,7 @@
&lt;a class="reference external" href="http://blog.notmyidea.org/posts/2011/avril/20/a-markdown-powered-article/"&gt;a file-relative link to markdown-article&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="testing-sourcecode-directive"&gt;
&lt;h2&gt;Testing sourcecode directive&lt;/h2&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;VARIANTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;div class="section" id="testing-another-case"&gt;

View file

@ -168,4 +168,4 @@
}());
</script>
</body>
</html>
</html>

View file

@ -183,4 +183,4 @@ YEAH !</p>
}());
</script>
</body>
</html>
</html>

View file

@ -50,7 +50,7 @@
<a class="reference external" href="./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">
@ -133,4 +133,4 @@ pelican.conf, it will …</p></div>
}());
</script>
</body>
</html>
</html>

File diff suppressed because one or more lines are too long

View file

@ -30,6 +30,9 @@ class TestBase(LoggedTestCase):
'content': TEST_CONTENT,
'context': {
'localsiteurl': '',
'generated_content': {},
'static_content': {},
'static_links': set()
},
'metadata': {
'summary': TEST_SUMMARY,
@ -110,14 +113,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,
@ -519,6 +522,60 @@ class TestPage(TestBase):
'<img src="http://static.cool.site/images/poster.jpg"/>'
)
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 '
'<a href="http://notmyidea.org/article-spaces.html#anchor">link</a> '
'<a href="http://notmyidea.org/name@example.com#anchor">file</a>'
)
# not escaped
args['content'] = (
'A simple test with a '
'<a href="{filename}article spaces.rst#anchor">link</a> '
'<a href="{static}name@example.com#anchor">file</a>'
)
content = Page(**args).get_content('http://notmyidea.org')
self.assertEqual(content, expected_output)
# html escaped
args['content'] = (
'A simple test with a '
'<a href="{filename}article spaces.rst#anchor">link</a> '
'<a href="{static}name&#64;example.com#anchor">file</a>'
)
content = Page(**args).get_content('http://notmyidea.org')
self.assertEqual(content, expected_output)
# url escaped
args['content'] = (
'A simple test with a '
'<a href="{filename}article%20spaces.rst#anchor">link</a> '
'<a href="{static}name%40example.com#anchor">file</a>'
)
content = Page(**args).get_content('http://notmyidea.org')
self.assertEqual(content, expected_output)
# html and url escaped
args['content'] = (
'A simple test with a '
'<a href="{filename}article%20spaces.rst#anchor">link</a> '
'<a href="{static}name&#64;example.com#anchor">file</a>'
)
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'})

View file

@ -262,6 +262,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',
@ -590,6 +591,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!'),

View file

@ -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/')

View file

@ -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

View file

@ -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,9 +113,35 @@ 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
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.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 = 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']
@ -123,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
@ -133,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
@ -144,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.<locals>.NoopPlugin')
self.assertEqual(
get_plugin_name(NoopPlugin()),
'PluginTest.test_get_plugin_name.<locals>.NoopPlugin')

View file

@ -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': '<p class="first last">Multi-line metadata should be'
' supported\nas well as <strong>inline'
' markup</strong> and stuff to &quot;typogrify'
'&quot;...</p>\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',

View file

@ -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)

View file

@ -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 %}

View file

@ -41,7 +41,7 @@ _jinja_env = Environment(
_GITHUB_PAGES_BRANCHES = {
'personal': 'master',
'personal': 'main',
'project': 'gh-pages'
}

View file

@ -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 %}
@ -101,32 +104,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)
@ -139,7 +126,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 %}

View file

@ -1,15 +1,19 @@
# -*- coding: utf-8 -*-
import os
import shlex
import shutil
import sys
import datetime
from invoke import task
from invoke.main import program
from invoke.util import cd
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)
@ -54,17 +58,17 @@ def clean(c):
@task
def build(c):
"""Build local version of site"""
c.run('pelican -s {settings_base}'.format(**CONFIG))
pelican_run('-s {settings_base}'.format(**CONFIG))
@task
def rebuild(c):
"""`build` with the delete switch"""
c.run('pelican -d -s {settings_base}'.format(**CONFIG))
pelican_run('-d -s {settings_base}'.format(**CONFIG))
@task
def regenerate(c):
"""Automatically regenerate site upon file modification"""
c.run('pelican -r -s {settings_base}'.format(**CONFIG))
pelican_run('-r -s {settings_base}'.format(**CONFIG))
@task
def serve(c):
@ -78,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()
@ -90,29 +99,43 @@ def reserve(c):
@task
def preview(c):
"""Build production version of site"""
c.run('pelican -s {settings_publish}'.format(**CONFIG))
pelican_run('-s {settings_publish}'.format(**CONFIG))
@task
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)
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 %}
@ -130,7 +153,7 @@ def cf_upload(c):
@task
def publish(c):
"""Publish to production via rsync"""
c.run('pelican -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}" '
@ -147,3 +170,7 @@ def gh_pages(c):
'-m {commit_message} '
'{deploy_path} -p'.format(**CONFIG))
{% endif %}
def pelican_run(cmd):
cmd += ' ' + program.core.remainder # allows to pass-through args to pelican
pelican_main(shlex.split(cmd))

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "pelican"
version = "4.2.0"
version = "4.6.0"
description = "Static site generator supporting Markdown and reStructuredText"
authors = ["Justin Mayer <entrop@gmail.com>"]
license = "AGPLv3"
@ -15,48 +15,53 @@ 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.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"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",
"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.6"
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}
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.7"
BeautifulSoup4 = "^4.9"
jinja2 = "~2.11"
lxml = "^4.3"
markdown = "~3.1.1"
markdown = "~3.3.4"
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"
psutil = {version = "^5.7", optional = true}
pygments = "~2.8"
pytest = "^6.0"
pytest-cov = "^2.8"
pytest-pythonpath = "^0.7.3"
pytest-sugar = "^0.9.2"
pytest-xdist = "^1.31"
tox = "^3.13"
flake8 = "^3.7"
pytest-sugar = "^0.9.4"
pytest-xdist = "^2.0"
tox = {version = "^3.13", optional = true}
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]
@ -64,6 +69,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"

View file

@ -1,11 +1,11 @@
# Tests
Pygments==2.6.1
pytest==5.3.5
Pygments==2.8.1
pytest
pytest-cov
pytest-xdist
pytest-xdist[psutil]
# Optional Packages
Markdown >= 3.1
Markdown==3.3.4
BeautifulSoup4
lxml
typogrify

View file

@ -3,12 +3,12 @@
from os import walk
from os.path import join, relpath
from setuptools import setup
from setuptools import find_packages, setup
version = "4.2.0"
version = "4.6.0"
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']
@ -44,19 +44,15 @@ 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)
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],
'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={
@ -70,9 +66,9 @@ 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',
'Programming Language :: Python :: Implementation :: CPython',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Software Development :: Libraries :: Python Modules',

View file

@ -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)
@ -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")
@ -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
@ -64,19 +65,17 @@ 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} ."
)
@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)

10
tox.ini
View file

@ -1,12 +1,12 @@
[tox]
envlist = py{3.5,3.6,3.7,3.8},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
py3.9: python3.9
passenv = *
usedevelop=True
deps =
@ -17,7 +17,7 @@ commands =
pytest -s --cov=pelican pelican
[testenv:docs]
basepython = python3.6
basepython = python3.7
deps =
-rrequirements/docs.pip
changedir = docs
@ -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
@ -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 =