Merge branch 'getpelican:master' into Chinese-translation

This commit is contained in:
GeorgeHu 2023-10-08 11:24:08 +08:00 committed by GitHub
commit 2cf90ccc0c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 266 additions and 182 deletions

4
.gitattributes vendored
View file

@ -4,4 +4,8 @@
# Improve accuracy of GitHub's Linguist-powered language statistics
pelican/tests/content/* linguist-vendored
pelican/tests/output/* linguist-vendored
pelican/tests/theme_overrides/* linguist-vendored
pelican/themes/notmyidea/templates/*.html linguist-language=Jinja
pelican/themes/simple/templates/*.html linguist-language=Jinja
samples/* linguist-vendored
*.html linguist-vendored

View file

@ -25,6 +25,8 @@ jobs:
python: "3.10"
- os: ubuntu
python: "3.11"
- os: ubuntu
python: "3.12"
- os: macos
python: "3.7"
- os: windows
@ -32,12 +34,12 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Setup Python ${{ matrix.config.python }}
- name: Set up Python ${{ matrix.config.python }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.config.python }}
cache: 'pip'
cache-dependency-path: '**/requirements/*'
cache: "pip"
cache-dependency-path: "**/requirements/*"
- name: Install locale (Linux)
if: startsWith(runner.os, 'Linux')
run: sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8
@ -56,73 +58,78 @@ jobs:
- name: Run tests
run: tox -e py${{ matrix.config.python }}
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Python
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
cache: 'pip'
cache-dependency-path: '**/requirements/*'
cache: "pip"
cache-dependency-path: "**/requirements/*"
- name: Install tox
run: python -m pip install -U pip tox
- name: Check
run: tox -e flake8
docs:
name: Build docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Python
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
cache: 'pip'
cache-dependency-path: '**/requirements/*'
cache: "pip"
cache-dependency-path: "**/requirements/*"
- name: Install tox
run: python -m pip install -U pip tox
- name: Check
run: tox -e docs
deploy:
name: Deploy
environment: Deployment
needs: [test, lint, docs]
runs-on: ubuntu-latest
if: ${{ github.ref=='refs/heads/master' && github.event_name!='pull_request' }}
if: github.ref=='refs/heads/master' && github.event_name!='pull_request'
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v3
- name: Setup Python
with:
token: ${{ secrets.GH_TOKEN }}
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Check release
id: check_release
run: |
python -m pip install pip --upgrade
pip install poetry
pip install githubrelease
pip install --pre autopub
python -m pip install --upgrade pip
python -m pip install autopub[github]
autopub check
continue-on-error: true
- name: Publish
if: steps.check_release.outcome=='success'
if: ${{ steps.check_release.outputs.autopub_release=='true' }}
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
git remote set-url origin https://$GITHUB_TOKEN@github.com/${{ github.repository }}
autopub prepare
poetry build
autopub commit
autopub build
autopub githubrelease
poetry publish -u __token__ -p $PYPI_PASSWORD
- name: Upload package to PyPI
if: ${{ steps.check_release.outputs.autopub_release=='true' }}
uses: pypa/gh-action-pypi-publish@release/v1

28
.readthedocs.yaml Normal file
View file

@ -0,0 +1,28 @@
---
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the OS, Python version, and any other needed tools
build:
os: ubuntu-22.04
tools:
python: "3.10"
# Build HTML & PDF formats
formats:
- htmlzip
- pdf
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# Version of Python and requirements required to build the docs
python:
install:
- requirements: requirements/developer.pip
- method: pip
path: .

View file

@ -20,7 +20,7 @@ Before you ask for help, please make sure you do the following:
you read the docs for the Pelican version you are using.
2. Use a search engine (e.g., DuckDuckGo, Google) to search for a solution to
your problem. Someone may have already found a solution, perhaps in the
form of a plugin_ or a specific combination of settings.
form of a ':pelican-doc:`plugins` or a specific combination of settings.
3. Try reproducing the issue in a clean environment, ensuring you are using:
@ -77,7 +77,7 @@ Contributing code
Before you submit a contribution, please ask whether it is desired so that you
don't spend a lot of time working on something that would be rejected for a
known reason. Consider also whether your new feature might be better suited as
a plugin_ — you can `ask for help`_ to make that determination.
a ':pelican-doc:`plugins` — you can `ask for help`_ to make that determination.
Using Git and GitHub
--------------------
@ -132,8 +132,8 @@ Contribution quality standards
* Ensure your code is compatible with the `officially-supported Python releases`_.
* Add docs and tests for your changes. Undocumented and untested features will
not be accepted.
* `Run all the tests`_ **on all versions of Python supported by Pelican** to
ensure nothing was accidentally broken.
* :pelican-doc:`Run all the tests <contribute>` **on all versions of Python
supported by Pelican** to ensure nothing was accidentally broken.
Check out our `Git Tips`_ page or `ask for help`_ if you
need assistance or have any questions about these guidelines.
@ -141,7 +141,6 @@ need assistance or have any questions about these guidelines.
.. _`plugin`: https://docs.getpelican.com/en/latest/plugins.html
.. _`Create a new branch`: https://github.com/getpelican/pelican/wiki/Git-Tips#making-your-changes
.. _`Squash your commits`: https://github.com/getpelican/pelican/wiki/Git-Tips#squashing-commits
.. _`Run all the tests`: https://docs.getpelican.com/en/latest/contribute.html#running-the-test-suite
.. _`Git Tips`: https://github.com/getpelican/pelican/wiki/Git-Tips
.. _`PEP8 coding standards`: https://www.python.org/dev/peps/pep-0008/
.. _`ask for help`: `How to get help`_

View file

@ -1,10 +1,10 @@
Pelican |build-status| |pypi-version| |repology|
================================================
Pelican |build-status| |pypi-version| |downloads| |repology|
============================================================
Pelican is a static site generator, written in Python_, that allows you to create
web sites by composing text files in formats such as Markdown, reStructuredText, and HTML.
With Pelican, you can create web sites without worrying about databases or server-side programming.
With Pelican, you can create web sites without worrying about databases or server-side programming.
Pelican generates static sites that can be served via any web server or hosting service.
You can perform the following functions with Pelican:
@ -70,6 +70,9 @@ Why the name “Pelican”?
.. |pypi-version| image:: https://img.shields.io/pypi/v/pelican.svg
:target: https://pypi.org/project/pelican/
:alt: PyPI: the Python Package Index
.. |downloads| image:: https://img.shields.io/pypi/dm/pelican.svg
:target: https://pypi.org/project/pelican/
:alt: Monthly Downloads from PyPI
.. |repology| image:: https://repology.org/badge/tiny-repos/pelican.svg
:target: https://repology.org/project/pelican/versions
:alt: Repology: the packaging hub

View file

@ -25,7 +25,7 @@ rst_prolog = '''
'''.format(last_stable)
extlinks = {
'pelican-doc': ('https://docs.getpelican.com/%s/', '%s')
'pelican-doc': ('https://docs.getpelican.com/en/latest/%s.html', '%s')
}
# -- Options for HTML output --------------------------------------------------

View file

@ -95,8 +95,9 @@ contains a list of reserved metadata keywords:
``url`` URL to use for this article/page
=============== ===============================================================
Readers for additional formats (such as AsciiDoc_) are available via plugins.
Refer to `pelican-plugins`_ repository for those.
Readers for additional formats (such as AsciiDoc_) are available via plugins,
which you can find via the `Pelican Plugins`_ collection as well as the legacy
`pelican-plugins`_ repository.
Pelican can also process HTML files ending in ``.html`` and ``.htm``. Pelican
interprets the HTML in a very straightforward manner, reading metadata from
@ -183,7 +184,7 @@ files in it will be used to generate static pages, such as **About** or
You can use the ``DISPLAY_PAGES_ON_MENU`` setting to control whether all those
pages are displayed in the primary navigation menu. (Default is ``True``.)
If you want to exclude any pages from being linked to or listed in the menu
If you want to exclude any pages from being linked to or listed in the menu,
then add a ``status: hidden`` attribute to its metadata. This is useful for
things like making error pages that fit the generated theme of your site.
@ -234,7 +235,7 @@ that may be sitting alongside that post (instead of having to determine where
the other content will be placed after site generation).
To link to internal content (files in the ``content`` directory), use the
following syntax for the link target: ``{filename}path/to/file``
following syntax for the link target: ``{filename}path/to/file``.
Note: forward slashes, ``/``,
are the required path separator in the ``{filename}`` directive
on all operating systems, including Windows.
@ -306,7 +307,7 @@ Attaching static files
----------------------
Starting with Pelican 3.5, static files can be "attached" to a page or article
using this syntax for the link target: ``{attach}path/to/file`` This works
using this syntax for the link target: ``{attach}path/to/file``. This works
like the ``{static}`` syntax, but also relocates the static file into the
linking document's output directory. If the static file originates from a
subdirectory beneath the linking document's source, that relationship will be
@ -538,12 +539,12 @@ The specified identifier (e.g. ``python``, ``ruby``) should be one that
appears on the `list of available lexers <https://pygments.org/docs/lexers/>`_.
When using reStructuredText the following options are available in the
code-block directive:
`code-block` directive:
============= ============ =========================================
Option Valid values Description
============= ============ =========================================
anchorlinenos N/A If present wrap line numbers in <a> tags.
anchorlinenos N/A If present, wrap line numbers in ``<a>`` tags.
classprefix string String to prepend to token class names
hl_lines numbers List of lines to be highlighted, where
line numbers to highlight are separated
@ -554,22 +555,22 @@ hl_lines numbers List of lines to be highlighted, where
line numbers.
lineanchors string Wrap each line in an anchor using this
string and -linenumber.
linenos string If present or set to "table" output line
numbers in a table, if set to
"inline" output them inline. "none" means
linenos string If present or set to "table", output line
numbers in a table; if set to
"inline", output them inline. "none" means
do not output the line numbers for this
table.
linenospecial number If set every nth line will be given the
'special' css class.
linenospecial number If set, every nth line will be given the
'special' CSS class.
linenostart number Line number for the first line.
linenostep number Print every nth line number.
lineseparator string String to print between lines of code,
'\n' by default.
linespans string Wrap each line in a span using this and
-linenumber.
nobackground N/A If set do not output background color for
nobackground N/A If set, do not output background color for
the wrapping element
nowrap N/A If set do not wrap the tokens at all.
nowrap N/A If set, do not wrap the tokens at all.
tagsfile string ctags file to use for name definitions.
tagurlformat string format for the ctag links.
============= ============ =========================================
@ -595,7 +596,7 @@ Pelican settings file to include options that will be automatically applied to
every code block.
For example, if you want to have line numbers displayed for every code block
and a CSS prefix you would set this variable to::
and a CSS prefix, you would set this variable to::
PYGMENTS_RST_OPTIONS = {'classprefix': 'pgcss', 'linenos': 'table'}
@ -611,7 +612,7 @@ its metadata. That article will then be output to the ``drafts`` folder and not
listed on the index page nor on any category or tag page.
If your articles should be automatically published as a draft (to not
accidentally publish an article before it is finished) include the status in
accidentally publish an article before it is finished), include the status in
the ``DEFAULT_METADATA``::
DEFAULT_METADATA = {
@ -626,11 +627,12 @@ Hidden Posts
Like pages, posts can also be marked as ``hidden`` with the ``Status: hidden``
attribute. Hidden posts will be output to ``ARTICLE_SAVE_AS`` as expected, but
are not included by default in tag, category, and author indexes, nor in the
are not included by default in tag, category, and author indexes, nor in the
main article feed. This has the effect of creating an "unlisted" post.
.. _W3C ISO 8601: https://www.w3.org/TR/NOTE-datetime
.. _AsciiDoc: https://www.methods.co.nz/asciidoc/
.. _Pelican Plugins: https://github.com/pelican-plugins
.. _pelican-plugins: https://github.com/getpelican/pelican-plugins
.. _Python-Markdown: https://github.com/Python-Markdown/markdown
.. _Markdown Extensions: https://python-markdown.github.io/extensions/

View file

@ -17,7 +17,7 @@ suggestions or problems you might have via `Pelican Discussions
existing list of discussions and issues (both open and closed) in order to
avoid submitting topics that have already been covered before.
If you want to contribute, please fork `the git repository
If you want to contribute, please fork `the Git repository
<https://github.com/getpelican/pelican/>`_, create a new feature branch, make
your changes, and issue a pull request. Someone will review your changes as
soon as possible. Please refer to the :doc:`How to Contribute <contribute>`
@ -68,7 +68,7 @@ I want to use Markdown, but I got an error.
===========================================
If you try to generate Markdown content without first installing the Markdown
library, may see a message that says ``No valid files found in content``.
library, you may see a message that says ``No valid files found in content``.
Markdown is not a hard dependency for Pelican, so if you have content in
Markdown format, you will need to explicitly install the Markdown library. You
can do so by typing the following command, prepending ``sudo`` if permissions
@ -128,7 +128,7 @@ to override the generated URL. Here is an example page in reST format::
:save_as: override/url/index.html
With this metadata, the page will be written to ``override/url/index.html``
and Pelican will use url ``override/url/`` to link to this page.
and Pelican will use the URL ``override/url/`` to link to this page.
How can I use a static page as my home page?
============================================
@ -229,7 +229,7 @@ This can be achieved by explicitly specifying only the filenames of those
articles in ``ARTICLE_PATHS``. A list of such filenames could be found using a
command similar to ``cd content; find -name '*.md' | head -n 10``.
My tag-cloud is missing/broken since I upgraded Pelican
My tag cloud is missing/broken since I upgraded Pelican
=======================================================
In an ongoing effort to streamline Pelican, tag cloud generation has been

View file

@ -64,7 +64,6 @@ automatically installed without any action on your part:
* `pygments <https://pypi.org/project/Pygments/>`_, for syntax highlighting
* `docutils <https://pypi.org/project/docutils/>`_, for supporting
reStructuredText as an input format
* `pytz <https://pypi.org/project/pytz/>`_, for timezone definitions
* `blinker <https://pypi.org/project/blinker/>`_, an object-to-object and
broadcast signaling system
* `unidecode <https://pypi.org/project/Unidecode/>`_, for ASCII

View file

@ -21,7 +21,7 @@ Optional arguments:
"""""""""""""""""""
-h, --help Show the help an exit
-h, --help Show the help and exit
-l, --list Show the themes already installed
@ -29,7 +29,7 @@ Optional arguments:
-r theme_name, --remove theme_name One or more themes to remove
-s theme_path, --symlink theme_path Same as "--install", but create a symbolic link instead of copying the theme.
-s theme_path, --symlink theme_path Same as ``--install``, but create a symbolic link instead of copying the theme.
Useful for theme development
-v, --verbose Verbose output
@ -62,7 +62,7 @@ or ``--list`` option:
In this example, we can see there are three themes available: ``notmyidea``,
``simple``, and ``two-column``.
``two-column`` is prefixed with an ``@`` because this theme is not copied to
``two-column`` is followed by an ``@`` because this theme is not copied to
the Pelican theme path, but is instead just linked to it (see `Creating
symbolic links`_ for details about creating symbolic links).
@ -82,7 +82,7 @@ Installing themes
You can install one or more themes using the ``-i`` or ``--install`` option.
This option takes as argument the path(s) of the theme(s) you want to install,
and can be combined with the verbose option:
and can be combined with the ``--verbose`` option:
.. code-block:: console
@ -154,7 +154,7 @@ This is useful for theme development:
Doing several things at once
""""""""""""""""""""""""""""
The ``--install``, ``--remove`` and ``--symlink`` option are not mutually
The ``--install``, ``--remove`` and ``--symlink`` options are not mutually
exclusive, so you can combine them in the same command line to do more than one
operation at time, like this:

View file

@ -67,7 +67,7 @@ Deployment
After you have generated your site, previewed it in your local development
environment, and are ready to deploy it to production, you might first
re-generate your site with any production-specific settings (e.g., analytics
re-generate your site with any production-specific settings (e.g., analytics,
feeds, etc.) that you may have defined::
pelican content -s publishconf.py

View file

@ -15,6 +15,9 @@ setting file. Note that values must follow JSON notation::
pelican content -e SITENAME='"A site"' READERS='{"html": null}' CACHE_CONTENT=true
Environment variables can also be used here but must be escaped appropriately::
pelican content -e API_KEY=''\"$API_KEY\"''
.. note::
@ -1242,18 +1245,19 @@ Feel free to use them in your themes as well.
Your GitHub URL (if you have one). It will then use this information to
create a GitHub ribbon.
.. data:: GOOGLE_ANALYTICS
.. data:: ANALYTICS
Set to ``UA-XXXXX-Y`` Property's tracking ID to activate Google Analytics.
Put any desired analytics scripts in this setting in ``publishconf.py``.
Example:
.. data:: GA_COOKIE_DOMAIN
.. parsed-literal::
Set cookie domain field of Google Analytics tracking code. Defaults to
``auto``.
.. data:: GOSQUARED_SITENAME
Set to 'XXX-YYYYYY-X' to activate GoSquared.
ANALYTICS = """
<script src="/theme/js/primary-analytics.js"></script>
<script>
[ … in-line Javascript code for secondary analytics … ]
</script>
"""
.. data:: MENUITEMS

View file

@ -34,11 +34,10 @@ Structure
To make your own theme, you must follow the following structure::
├── static
   ├── css
   └── images
├── css
└── images
└── templates
├── archives.html // to display archives
├── period_archives.html // to display time-period archives
├── article.html // processed for each article
├── author.html // processed for each author
├── authors.html // must list all the authors
@ -46,6 +45,7 @@ To make your own theme, you must follow the following structure::
├── category.html // processed for each category
├── index.html // the index (list all the articles)
├── page.html // processed for each page
├── period_archives.html // to display time-period archives
├── tag.html // processed for each tag
└── tags.html // must list all the tags. Can be a tag cloud.
@ -465,14 +465,14 @@ The feed variables changed in 3.0. Each variable now explicitly lists ATOM or
RSS in the name. ATOM is still the default. Old themes will need to be updated.
Here is a complete list of the feed variables::
FEED_ATOM
FEED_RSS
FEED_ALL_ATOM
FEED_ALL_RSS
CATEGORY_FEED_ATOM
CATEGORY_FEED_RSS
AUTHOR_FEED_ATOM
AUTHOR_FEED_RSS
CATEGORY_FEED_ATOM
CATEGORY_FEED_RSS
FEED_ALL_ATOM
FEED_ALL_RSS
FEED_ATOM
FEED_RSS
TAG_FEED_ATOM
TAG_FEED_RSS
TRANSLATION_FEED_ATOM

View file

@ -543,13 +543,15 @@ def main(argv=None):
target=listen,
args=(settings.get('BIND'), settings.get('PORT'),
settings.get("OUTPUT_PATH"), excqueue))
p1.start()
p2.start()
exc = excqueue.get()
p1.terminate()
p2.terminate()
if exc is not None:
logger.critical(exc)
try:
p1.start()
p2.start()
exc = excqueue.get()
if exc is not None:
logger.critical(exc)
finally:
p1.terminate()
p2.terminate()
elif args.autoreload:
autoreload(args)
elif args.listen:

View file

@ -4,10 +4,15 @@ import locale
import logging
import os
import re
from datetime import timezone
from html import unescape
from urllib.parse import unquote, urljoin, urlparse, urlunparse
import pytz
try:
from zoneinfo import ZoneInfo
except ModuleNotFoundError:
from backports.zoneinfo import ZoneInfo
from pelican.plugins import signals
from pelican.settings import DEFAULT_CONFIG
@ -120,9 +125,9 @@ class Content:
self.date_format = self.date_format[1]
# manage timezone
default_timezone = settings.get('TIMEZONE', 'UTC')
timezone = getattr(self, 'timezone', default_timezone)
self.timezone = pytz.timezone(timezone)
default_timezone = settings.get("TIMEZONE", "UTC")
timezone = getattr(self, "timezone", default_timezone)
self.timezone = ZoneInfo(timezone)
if hasattr(self, 'date'):
self.date = set_date_tzinfo(self.date, timezone)
@ -525,7 +530,7 @@ class Article(Content):
if self.date.tzinfo is None:
now = datetime.datetime.now()
else:
now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
now = datetime.datetime.utcnow().replace(tzinfo=timezone.utc)
if self.date > now:
self.status = 'draft'

View file

@ -206,8 +206,9 @@ class Generator:
self.context['static_links'] |= content.get_static_links()
def _update_context(self, items):
"""Update the context with the given items from the current
processor.
"""Update the context with the given items from the current processor.
Note that dictionary arguments will be converted to a list of tuples.
"""
for item in items:
value = getattr(self, item)

View file

@ -317,7 +317,7 @@ class TestWordpressXmlImporter(unittest.TestCase):
self.posts)
with temporary_folder() as temp:
md = [r(f) for f in silent_f2p(test_post, 'markdown', temp)][0]
sample_line = re.search(r'- This is a code sample', md).group(0)
sample_line = re.search(r'- This is a code sample', md).group(0)
code_line = re.search(r'\s+a = \[1, 2, 3\]', md).group(0)
self.assertTrue(sample_line.rindex('This') < code_line.rindex('a'))

View file

@ -3,10 +3,14 @@ import logging
import os
import shutil
import time
from datetime import timezone
from sys import platform
from tempfile import mkdtemp
import pytz
try:
from zoneinfo import ZoneInfo
except ModuleNotFoundError:
from backports.zoneinfo import ZoneInfo
from pelican import utils
from pelican.generators import TemplatePagesGenerator
@ -50,21 +54,21 @@ class TestUtils(LoggedTestCase):
year=2012, month=11, day=22, hour=22, minute=11)
date_hour_z = utils.SafeDatetime(
year=2012, month=11, day=22, hour=22, minute=11,
tzinfo=pytz.timezone('UTC'))
tzinfo=timezone.utc)
date_hour_est = utils.SafeDatetime(
year=2012, month=11, day=22, hour=22, minute=11,
tzinfo=pytz.timezone('EST'))
tzinfo=ZoneInfo("EST"))
date_hour_sec = utils.SafeDatetime(
year=2012, month=11, day=22, hour=22, minute=11, second=10)
date_hour_sec_z = utils.SafeDatetime(
year=2012, month=11, day=22, hour=22, minute=11, second=10,
tzinfo=pytz.timezone('UTC'))
tzinfo=timezone.utc)
date_hour_sec_est = utils.SafeDatetime(
year=2012, month=11, day=22, hour=22, minute=11, second=10,
tzinfo=pytz.timezone('EST'))
tzinfo=ZoneInfo("EST"))
date_hour_sec_frac_z = utils.SafeDatetime(
year=2012, month=11, day=22, hour=22, minute=11, second=10,
microsecond=123000, tzinfo=pytz.timezone('UTC'))
microsecond=123000, tzinfo=timezone.utc)
dates = {
'2012-11-22': date,
'2012/11/22': date,
@ -86,13 +90,13 @@ class TestUtils(LoggedTestCase):
iso_8601_date = utils.SafeDatetime(year=1997, month=7, day=16)
iso_8601_date_hour_tz = utils.SafeDatetime(
year=1997, month=7, day=16, hour=19, minute=20,
tzinfo=pytz.timezone('CET'))
tzinfo=ZoneInfo("Europe/London"))
iso_8601_date_hour_sec_tz = utils.SafeDatetime(
year=1997, month=7, day=16, hour=19, minute=20, second=30,
tzinfo=pytz.timezone('CET'))
tzinfo=ZoneInfo("Europe/London"))
iso_8601_date_hour_sec_ms_tz = utils.SafeDatetime(
year=1997, month=7, day=16, hour=19, minute=20, second=30,
microsecond=450000, tzinfo=pytz.timezone('CET'))
microsecond=450000, tzinfo=ZoneInfo("Europe/London"))
iso_8601 = {
'1997-07-16': iso_8601_date,
'1997-07-16T19:20+01:00': iso_8601_date_hour_tz,
@ -259,6 +263,26 @@ class TestUtils(LoggedTestCase):
utils.truncate_html_words('<!-- comment -->' + 'word ' * 100, 20),
'<!-- comment -->' + 'word ' * 20 + '')
# Words enclosed or intervaled by HTML tags with a custom end
# marker containing HTML tags.
self.assertEqual(
utils.truncate_html_words('<p>' + 'word ' * 100 + '</p>', 20,
'<span>marker</span>'),
'<p>' + 'word ' * 20 + '<span>marker</span></p>')
self.assertEqual(
utils.truncate_html_words(
'<span\nstyle="\n\n">' + 'word ' * 100 + '</span>', 20,
'<span>marker</span>'),
'<span\nstyle="\n\n">' + 'word ' * 20 + '<span>marker</span></span>')
self.assertEqual(
utils.truncate_html_words('<br>' + 'word ' * 100, 20,
'<span>marker</span>'),
'<br>' + 'word ' * 20 + '<span>marker</span>')
self.assertEqual(
utils.truncate_html_words('<!-- comment -->' + 'word ' * 100, 20,
'<span>marker</span>'),
'<!-- comment -->' + 'word ' * 20 + '<span>marker</span>')
# Words with hypens and apostrophes.
self.assertEqual(
utils.truncate_html_words("a-b " * 100, 20),
@ -860,3 +884,34 @@ class TestSanitisedJoin(unittest.TestCase):
utils.posixize_path(
os.path.abspath(os.path.join("/foo/bar", "test")))
)
class TestMemoized(unittest.TestCase):
def test_memoized(self):
class Container:
def _get(self, key):
pass
@utils.memoized
def get(self, key):
return self._get(key)
container = Container()
with unittest.mock.patch.object(
container, "_get", side_effect=lambda x: x
) as get_mock:
self.assertEqual("foo", container.get("foo"))
get_mock.assert_called_once_with("foo")
get_mock.reset_mock()
self.assertEqual("foo", container.get("foo"))
get_mock.assert_not_called()
self.assertEqual("bar", container.get("bar"))
get_mock.assert_called_once_with("bar")
get_mock.reset_mock()
container.get.cache.clear()
self.assertEqual("bar", container.get("bar"))
get_mock.assert_called_once_with("bar")

View file

@ -1,26 +1,3 @@
{% if GOOGLE_ANALYTICS %}
<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', '{{GOOGLE_ANALYTICS}}', '{{GA_COOKIE_DOMAIN if GA_COOKIE_DOMAIN else 'auto'}}');
ga('send', 'pageview');
</script>
{% endif %}
{% if GAUGES %}
<script type="text/javascript">
var _gauges = _gauges || [];
(function() {
var t = document.createElement('script');
t.type = 'text/javascript';
t.async = true;
t.id = 'gauges-tracker';
t.setAttribute('data-site-id', '{{GAUGES}}');
t.src = '//secure.gaug.es/track.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(t, s);
})();
</script>
{% if ANALYTICS %}
{{ ANALYTICS }}
{% endif %}

View file

@ -1,14 +0,0 @@
{% if GOSQUARED_SITENAME %}
<script type="text/javascript">
var GoSquared={};
GoSquared.acct = "{{ GOSQUARED_SITENAME }}";
(function(w){
function gs(){
w._gstc_lt=+(new Date); var d=document;
var g = d.createElement("script"); g.type = "text/javascript"; g.async = true; g.src = "https://d1l6p2sc9645hc.cloudfront.net/tracker.js";
var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(g, s);
}
w.addEventListener?w.addEventListener("load",gs,false):w.attachEvent("onload",gs);
})(window);
</script>
{% endif %}

View file

@ -15,7 +15,7 @@ from urllib.request import urlretrieve
# because logging.setLoggerClass has to be called before logging.getLogger
from pelican.log import init
from pelican.settings import read_settings
from pelican.settings import DEFAULT_CONFIG
from pelican.utils import SafeDatetime, slugify
@ -285,8 +285,7 @@ def dc2fields(file):
print("%i posts read." % len(posts))
settings = read_settings()
subs = settings['SLUG_REGEX_SUBSTITUTIONS']
subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS']
for post in posts:
fields = post.split('","')
@ -404,8 +403,7 @@ def posterous2fields(api_token, email, password):
page = 1
posts = get_posterous_posts(api_token, email, password, page)
settings = read_settings()
subs = settings['SLUG_REGEX_SUBSTITUTIONS']
subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS']
while len(posts) > 0:
posts = get_posterous_posts(api_token, email, password, page)
page += 1
@ -446,8 +444,7 @@ def tumblr2fields(api_key, blogname):
offset = 0
posts = get_tumblr_posts(api_key, blogname, offset)
settings = read_settings()
subs = settings['SLUG_REGEX_SUBSTITUTIONS']
subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS']
while len(posts) > 0:
for post in posts:
title = \
@ -531,8 +528,7 @@ def feed2fields(file):
"""Read a feed and yield pelican fields"""
import feedparser
d = feedparser.parse(file)
settings = read_settings()
subs = settings['SLUG_REGEX_SUBSTITUTIONS']
subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS']
for entry in d.entries:
date = (time.strftime('%Y-%m-%d %H:%M', entry.updated_parsed)
if hasattr(entry, 'updated_parsed') else None)
@ -778,8 +774,7 @@ def fields2pelican(
pandoc_version = get_pandoc_version()
posts_require_pandoc = []
settings = read_settings()
slug_subs = settings['SLUG_REGEX_SUBSTITUTIONS']
slug_subs = DEFAULT_CONFIG['SLUG_REGEX_SUBSTITUTIONS']
for (title, content, filename, date, author, categories, tags, status,
kind, in_markup) in fields:
@ -839,12 +834,15 @@ def fields2pelican(
if pandoc_version >= (1, 16) else '--no-wrap'
cmd = ('pandoc --normalize {0} --from=html'
' --to={1} {2} -o "{3}" "{4}"')
cmd = cmd.format(parse_raw, out_markup, wrap_none,
cmd = cmd.format(parse_raw,
out_markup if out_markup != 'markdown' else "gfm",
wrap_none,
out_filename, html_filename)
else:
from_arg = '-f html+raw_html' if not strip_raw else '-f html'
cmd = ('pandoc {0} --to={1}-smart --wrap=none -o "{2}" "{3}"')
cmd = cmd.format(from_arg, out_markup,
cmd = cmd.format(from_arg,
out_markup if out_markup != 'markdown' else "gfm",
out_filename, html_filename)
try:

View file

@ -7,7 +7,10 @@ from typing import Mapping
from jinja2 import Environment, FileSystemLoader
import pytz
try:
import zoneinfo
except ModuleNotFoundError:
from backports import zoneinfo
try:
import readline # NOQA
@ -16,9 +19,12 @@ except ImportError:
try:
import tzlocal
_DEFAULT_TIMEZONE = tzlocal.get_localzone().zone
except ImportError:
_DEFAULT_TIMEZONE = 'Europe/Rome'
if hasattr(tzlocal.get_localzone(), "zone"):
_DEFAULT_TIMEZONE = tzlocal.get_localzone().zone
else:
_DEFAULT_TIMEZONE = tzlocal.get_localzone_name()
except ModuleNotFoundError:
_DEFAULT_TIMEZONE = "Europe/Rome"
from pelican import __version__
@ -158,16 +164,15 @@ def ask(question, answer=str, default=None, length=None):
def ask_timezone(question, default, tzurl):
"""Prompt for time zone and validate input"""
lower_tz = [tz.lower() for tz in pytz.all_timezones]
tz_dict = {tz.lower(): tz for tz in zoneinfo.available_timezones()}
while True:
r = ask(question, str, default)
r = r.strip().replace(' ', '_').lower()
if r in lower_tz:
r = pytz.all_timezones[lower_tz.index(r)]
r = r.strip().replace(" ", "_").lower()
if r in tz_dict.keys():
r = tz_dict[r]
break
else:
print('Please enter a valid time zone:\n'
' (check [{}])'.format(tzurl))
print("Please enter a valid time zone:\n" " (check [{}])".format(tzurl))
return r

View file

@ -114,7 +114,7 @@ devserver:
"$(PELICAN)" -lr "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS)
devserver-global:
$(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -b 0.0.0.0
"$(PELICAN)" -lr "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(CONFFILE)" $(PELICANOPTS) -b 0.0.0.0
publish:
"$(PELICAN)" "$(INPUTDIR)" -o "$(OUTPUTDIR)" -s "$(PUBLISHCONF)" $(PELICANOPTS)
@ -125,6 +125,7 @@ publish:
ssh_upload: publish
scp -P $(SSH_PORT) -r "$(OUTPUTDIR)"/* "$(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)"
{% set upload = upload + ["sftp_upload"] %}
sftp_upload: publish
printf 'put -r $(OUTPUTDIR)/*' | sftp $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
@ -165,4 +166,4 @@ github: publish
{% endif %}
.PHONY: html help clean regenerate serve serve-global devserver publish {{ upload|join(" ") }}
.PHONY: html help clean regenerate serve serve-global devserver devserver-global publish {{ upload|join(" ") }}

View file

@ -18,10 +18,12 @@ from operator import attrgetter
import dateutil.parser
try:
from zoneinfo import ZoneInfo
except ModuleNotFoundError:
from backports.zoneinfo import ZoneInfo
from markupsafe import Markup
import pytz
logger = logging.getLogger(__name__)
@ -155,7 +157,9 @@ class memoized:
def __get__(self, obj, objtype):
'''Support instance methods.'''
return partial(self.__call__, obj)
fn = partial(self.__call__, obj)
fn.cache = self.cache
return fn
def deprecated_attribute(old, new, since=None, remove=None, doc=None):
@ -917,10 +921,11 @@ class FileSystemWatcher:
def set_date_tzinfo(d, tz_name=None):
"""Set the timezone for dates that don't have tzinfo"""
if tz_name and not d.tzinfo:
tz = pytz.timezone(tz_name)
d = tz.localize(d)
return SafeDatetime(d.year, d.month, d.day, d.hour, d.minute, d.second,
d.microsecond, d.tzinfo)
timezone = ZoneInfo(tz_name)
d = d.replace(tzinfo=timezone)
return SafeDatetime(
d.year, d.month, d.day, d.hour, d.minute, d.second, d.microsecond, d.tzinfo
)
return d

View file

@ -37,10 +37,10 @@ feedgenerator = ">=1.9"
jinja2 = ">=2.7"
pygments = ">=2.6"
python-dateutil = ">=2.8"
pytz = ">=2020.1"
rich = ">=10.1"
unidecode = ">=1.1"
markdown = {version = ">=3.1", optional = true}
backports-zoneinfo = {version = "^0.2.1", python = "<3.9"}
[tool.poetry.dev-dependencies]
BeautifulSoup4 = "^4.9"
@ -49,12 +49,12 @@ lxml = "^4.3"
markdown = "~3.4.3"
typogrify = "^2.0"
sphinx = "^5.1"
furo = "2022.12.07"
furo = "2023.03.27"
livereload = "^2.6"
psutil = {version = "^5.7", optional = true}
pygments = "~2.14"
pygments = "~2.15"
pytest = "^7.1"
pytest-cov = "^3.0"
pytest-cov = "^4.0"
pytest-sugar = "^0.9.5"
pytest-xdist = "^2.0"
tox = {version = "^3.13", optional = true}
@ -77,7 +77,7 @@ pelican-themes = "pelican.tools.pelican_themes:main"
[tool.autopub]
project-name = "Pelican"
git-username = "botpub"
git-email = "botpub@autopub.rocks"
git-email = "52496925+botpub@users.noreply.github.com"
changelog-file = "docs/changelog.rst"
changelog-header = "###############"
version-header = "="

View file

@ -9,8 +9,8 @@ from setuptools import find_packages, setup
version = "4.8.0"
requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments',
'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode',
'python-dateutil', 'rich']
'docutils>=0.15', 'blinker', 'unidecode', 'python-dateutil',
'rich', 'backports-zoneinfo[tzdata] >= 0.2; python_version<"3.9"']
entry_points = {
'console_scripts': [
@ -77,6 +77,8 @@ setup(
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: Implementation :: CPython',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Software Development :: Libraries :: Python Modules',

View file

@ -1,5 +1,5 @@
[tox]
envlist = py{3.7,3.8,3.9,3.10,3.11},docs,flake8
envlist = py{3.7,3.8,3.9,3.10,3.11.3.12},docs,flake8
[testenv]
basepython =
@ -8,6 +8,7 @@ basepython =
py3.9: python3.9
py3.10: python3.10
py3.11: python3.11
py3.12: python3.12
passenv = *
usedevelop=True
deps =