Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Helio Chissini de Castro 2019-10-12 17:24:06 +02:00
commit f4b64b8700
17 changed files with 215 additions and 113 deletions

View file

@ -2,11 +2,18 @@ language: python
python:
- "3.6"
env:
- TOX_ENV=docs
- TOX_ENV=flake8
- TOX_ENV=py27
- TOX_ENV=py35
- TOX_ENV=py36
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=py27
- TOX_ENV=py35
- TOX_ENV=py36
matrix:
include:
- python: 3.7
@ -23,8 +30,25 @@ before_install:
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:
- "irc.freenode.org#pelican"
- secure: "JP57f61QovrhmLoAF6oPOzIK2aXGfSO06FHg7yiuKBOWMiaxQejZUGJX919muCLhWJXDugsviIqCMoAWwNV3o1WQbqIr+G5TR+N9MrtCs4Zi6vpGj09bR8giKUKx+PPKEoe1Ew56E4y2LxzGO4Lj9hZx8M2YVdwPNWrWZgp6WXE="
on_success: change

View file

@ -97,6 +97,14 @@ Using Git and GitHub
For example, if you're hacking 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.
* 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::
Release type: minor
Reload browser window upon changes to content, settings, or theme
* Check for unnecessary whitespace via ``git diff --check`` before committing.
* First line of your commit message should start with present-tense verb, be 50
characters or less, and include the relevant issue number(s) if applicable.

View file

@ -1,6 +1,30 @@
Release history
###############
4.1.3 - 2019-10-09
==================
* 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
* Place all deprecated settings handling together
* Add related project URLs for display on PyPI
* Skip some tests on Windows that can't pass due to filesystem differences
4.1.2 - 2019-09-23
==================
Fix pelican.settings.load_source to avoid caching issues - PR #2621
4.1.1 - 2019-08-23
==================
* 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`
4.1.0 - 2019-07-14
==================

View file

@ -9,6 +9,10 @@ You can install Pelican via several different methods. The simplest is via
pip install pelican
Or, if you plan on using 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.)
@ -40,7 +44,11 @@ Optional packages
-----------------
If you plan on using `Markdown <http://pypi.python.org/pypi/Markdown>`_ as a
markup format, you'll need to install the Markdown library::
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

View file

@ -54,20 +54,12 @@ HTML files directly::
firefox output/index.html
Because the above method may have trouble locating your CSS and other linked
assets, running a simple web server using Python will often provide a more
reliable previewing experience.
assets, running Pelican's simple built-in web server will often provide a more
reliable previewing experience::
For Python 2, run::
pelican --listen
cd output
python -m SimpleHTTPServer
For Python 3, run::
cd output
python -m http.server
Once the basic server has been started, you can preview your site at
Once the web server has been started, you can preview your site at:
http://localhost:8000/
Deployment

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.3+ by running the following command in your preferred
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
----------------
@ -50,18 +50,18 @@ Given that this example article is in Markdown format, save it as
Generate your site
------------------
From your site directory, run the ``pelican`` command to generate your site::
From your project root directory, run the ``pelican`` command to generate your site::
pelican content
Your site has now been generated inside the ``output`` directory. (You may see
Your site has now been generated inside the ``output/`` directory. (You may see
a warning related to feeds, but that is normal when developing locally and can
be ignored for now.)
Preview your site
-----------------
Open a new terminal session, navigate to your generated output directory and
Open a new terminal session, navigate to your project root directory, and
run the following command to launch Pelican's web server::
pelican --listen

View file

@ -991,7 +991,7 @@ By default, pages subsequent to ``.../foo.html`` are created as
``.../foo2.html``, etc. The ``PAGINATION_PATTERNS`` setting can be used to
change this. It takes a sequence of triples, where each triple consists of::
(minimum_page, page_url, page_save_as,)
(minimum_page, page_url, page_save_as,)
For ``page_url`` and ``page_save_as``, you may use a number of variables.
``{url}`` and ``{save_as}`` correspond respectively to the ``*_URL`` and
@ -1005,7 +1005,7 @@ subsequent pages at ``.../page/2/`` etc, you could set ``PAGINATION_PATTERNS``
as follows::
PAGINATION_PATTERNS = (
(1, '{url}', '{save_as}`,
(1, '{url}', '{save_as}',
(2, '{base_name}/page/{number}/', '{base_name}/page/{number}/index.html'),
)

View file

@ -75,7 +75,7 @@ output_file The name of the file currently being generated. For
articles The list of articles, ordered descending by date.
All the elements are `Article` objects, so you can
access their attributes (e.g. title, summary, author
etc.). Sometimes this is shadowed (for instance in
etc.). Sometimes this is shadowed (for instance, in
the tags page). You will then find info about it
in the `all_articles` variable.
dates The same list of articles, but ordered by date,

View file

@ -11,7 +11,6 @@ import logging
import multiprocessing
import os
import pprint
import re
import sys
import time
import traceback
@ -52,7 +51,6 @@ class Pelican(object):
# define the default settings
self.settings = settings
self._handle_deprecation()
self.path = settings['PATH']
self.theme = settings['THEME']
@ -94,65 +92,6 @@ class Pelican(object):
logger.debug('Restoring system path')
sys.path = _sys_path
def _handle_deprecation(self):
if self.settings.get('CLEAN_URLS', False):
logger.warning('Found deprecated `CLEAN_URLS` in settings.'
' Modifying the following settings for the'
' same behaviour.')
self.settings['ARTICLE_URL'] = '{slug}/'
self.settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/'
self.settings['PAGE_URL'] = 'pages/{slug}/'
self.settings['PAGE_LANG_URL'] = 'pages/{slug}-{lang}/'
for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL',
'PAGE_LANG_URL'):
logger.warning("%s = '%s'", setting, self.settings[setting])
if self.settings.get('AUTORELOAD_IGNORE_CACHE'):
logger.warning('Found deprecated `AUTORELOAD_IGNORE_CACHE` in '
'settings. Use --ignore-cache instead.')
self.settings.pop('AUTORELOAD_IGNORE_CACHE')
if self.settings.get('ARTICLE_PERMALINK_STRUCTURE', False):
logger.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in'
' settings. Modifying the following settings for'
' the same behaviour.')
structure = self.settings['ARTICLE_PERMALINK_STRUCTURE']
# Convert %(variable) into {variable}.
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)
# Strip a / prefix
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'):
self.settings[setting] = os.path.join(structure,
self.settings[setting])
logger.warning("%s = '%s'", setting, self.settings[setting])
for new, old in [('FEED', 'FEED_ATOM'), ('TAG_FEED', 'TAG_FEED_ATOM'),
('CATEGORY_FEED', 'CATEGORY_FEED_ATOM'),
('TRANSLATION_FEED', 'TRANSLATION_FEED_ATOM')]:
if self.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}
)
self.settings[old] = self.settings[new]
def run(self):
"""Run the generators and return"""
start_time = time.time()
@ -307,7 +246,7 @@ class PrintSettings(argparse.Action):
parser.exit()
def parse_arguments():
def parse_arguments(argv=None):
parser = argparse.ArgumentParser(
description='A tool to generate a static blog, '
' with restructured text input files.',
@ -400,7 +339,7 @@ def parse_arguments():
help='IP to bind to when serving files via HTTP '
'(default: 127.0.0.1)')
args = parser.parse_args()
args = parser.parse_args(argv)
if args.port is not None and not args.listen:
logger.warning('--port without --listen has no effect')
@ -560,8 +499,8 @@ def listen(server, port, output, excqueue=None):
return
def main():
args = parse_arguments()
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,
logs_dedup_min_level=logs_dedup_min_level)

View file

@ -141,7 +141,8 @@ class Content(object):
# manage status
if not hasattr(self, 'status'):
self.status = getattr(self, 'default_status', None)
# Previous default of None broke comment plugins and perhaps others
self.status = getattr(self, 'default_status', '')
# store the summary metadata if it is set
if 'summary' in metadata:

View file

@ -14,12 +14,16 @@ import six
from pelican.log import LimitFilter
try:
# SourceFileLoader is the recommended way in Python 3.3+
from importlib.machinery import SourceFileLoader
# spec_from_file_location is the recommended way in Python 3.5+
import importlib.util
def load_source(name, path):
return SourceFileLoader(name, path).load_module()
spec = importlib.util.spec_from_file_location(name, path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
except ImportError:
# but it does not exist in Python 2.7, so fall back to imp
import imp
@ -167,7 +171,7 @@ DEFAULT_CONFIG = {
'WRITE_SELECTED': [],
'FORMATTED_FIELDS': ['summary'],
'PORT': 8000,
'BIND': '',
'BIND': '127.0.0.1',
}
PYGMENTS_RST_OPTIONS = None
@ -439,6 +443,67 @@ def handle_deprecated_settings(settings):
'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.')
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'):
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')
# 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.')
structure = settings['ARTICLE_PERMALINK_STRUCTURE']
# Convert %(variable) into {variable}.
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)
# Strip a / prefix
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])
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')]:
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}
)
settings[old] = settings[new]
return settings

View file

@ -14,9 +14,10 @@ import six
from pelican.contents import Article, Author, Category, Page, Static
from pelican.settings import DEFAULT_CONFIG
from pelican.signals import content_object_init
from pelican.tests.support import LoggedTestCase, get_context, get_settings,\
unittest
from pelican.utils import SafeDatetime, path_to_url, truncate_html_words
from pelican.tests.support import (LoggedTestCase, get_context, get_settings,
unittest)
from pelican.utils import (SafeDatetime, path_to_url, posixize_path,
truncate_html_words)
# generate one paragraph, enclosed with <p>
@ -943,7 +944,7 @@ class TestStatic(LoggedTestCase):
source_path=os.path.join('dir', 'foo.jpg'),
context=self.settings.copy())
expected_save_as = os.path.join('dir', 'foo.jpg')
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))

View file

@ -1089,7 +1089,12 @@ class TestStaticGenerator(unittest.TestCase):
os.mkdir(os.path.join(self.temp_output, "static"))
self.generator.fallback_to_symlinks = True
self.generator.generate_context()
self.generator.generate_output(None)
try:
self.generator.generate_output(None)
except OSError as e:
# On Windows, possibly others, due to not holding symbolic link
# privilege
self.skipTest(e)
self.assertTrue(os.path.islink(self.endfile))
def test_existing_symlink_is_considered_up_to_date(self):
@ -1097,7 +1102,11 @@ class TestStaticGenerator(unittest.TestCase):
with open(self.startfile, "w") as f:
f.write("staticcontent")
os.mkdir(os.path.join(self.temp_output, "static"))
os.symlink(self.startfile, self.endfile)
try:
os.symlink(self.startfile, self.endfile)
except OSError as e:
# On Windows, possibly others
self.skipTest(e)
staticfile = MagicMock()
staticfile.source_path = self.startfile
staticfile.save_as = self.endfile
@ -1109,7 +1118,11 @@ class TestStaticGenerator(unittest.TestCase):
with open(self.startfile, "w") as f:
f.write("staticcontent")
os.mkdir(os.path.join(self.temp_output, "static"))
os.symlink("invalid", self.endfile)
try:
os.symlink("invalid", self.endfile)
except OSError as e:
# On Windows, possibly others
self.skipTest(e)
staticfile = MagicMock()
staticfile.source_path = self.startfile
staticfile.save_as = self.endfile

View file

@ -717,6 +717,8 @@ class TestDateFormatter(unittest.TestCase):
class TestSanitisedJoin(unittest.TestCase):
@unittest.skipIf(platform == 'win32',
"Different filesystem root on Windows")
def test_detect_parent_breakout(self):
with six.assertRaisesRegex(
self,
@ -727,6 +729,8 @@ class TestSanitisedJoin(unittest.TestCase):
"../test"
)
@unittest.skipIf(platform == 'win32',
"Different filesystem root on Windows")
def test_detect_root_breakout(self):
with six.assertRaisesRegex(
self,
@ -737,6 +741,8 @@ class TestSanitisedJoin(unittest.TestCase):
"/test"
)
@unittest.skipIf(platform == 'win32',
"Different filesystem root on Windows")
def test_pass_deep_subpaths(self):
self.assertEqual(
utils.sanitised_join(

View file

@ -23,8 +23,10 @@ CONFIG = {
'deploy_path': SETTINGS['OUTPUT_PATH'],
{% if ssh %}
# Remote server configuration
'production': '{{ssh_user}}@{{ssh_host}}:{{ssh_port}}',
'dest_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
@ -130,7 +132,8 @@ def publish(c):
c.run('pelican -s {settings_publish}'.format(**CONFIG))
c.run(
'rsync --delete --exclude ".DS_Store" -pthrvz -c '
'{} {production}:{dest_path}'.format(
'-e "ssh -p {ssh_port}" '
'{} {ssh_user}@{ssh_host}:{ssh_path}'.format(
CONFIG['deploy_path'].rstrip('/') + '/',
**CONFIG))

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "pelican"
version = "4.1.0"
version = "4.1.3"
description = "Static site generator supporting Markdown and reStructuredText"
authors = ["Justin Mayer <entrop@gmail.com>"]
license = "AGPLv3"
@ -59,6 +59,15 @@ markdown = ["markdown"]
[tool.poetry.scripts]
pelican = "pelican.__main__:main"
[tool.autopub]
project-name = "Pelican"
git-username = "botpub"
git-email = "botpub@autopub.rocks"
changelog-file = "docs/changelog.rst"
changelog-header = "###############"
version-header = "="
version-strings = ["setup.py"]
build-system = "setuptools"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
requires = ["setuptools >= 40.6.0", "wheel"]

View file

@ -7,7 +7,7 @@ from os.path import join, relpath
from setuptools import setup
version = "4.1.0"
version = "4.1.3"
requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', 'docutils',
'pytz >= 0a', 'blinker', 'unidecode', 'six >= 1.4',
@ -33,11 +33,17 @@ setup(
name='pelican',
version=version,
url='https://getpelican.com/',
author='Alexis Metaireau',
maintainer='Justin Mayer',
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,
packages=['pelican', 'pelican.tools'],
@ -55,6 +61,9 @@ setup(
for name in names],
},
install_requires=requires,
extras_require={
'Markdown': ['markdown~=3.1.1']
},
entry_points=entry_points,
classifiers=[
'Development Status :: 5 - Production/Stable',