From 780ccfe137a910b404d651a4973f68c85adf01f2 Mon Sep 17 00:00:00 2001
From: Justin Mayer
There are comments.
-- Page 1 / 1 -
There are comments.
- Page 1 / 1 -
There are comments.
-- Page 1 / 1 -
→ And now try with some utf8 hell: ééé
There are comments.
-- Page 1 / 1 -
There are comments.
- Page 1 / 1 -
There are comments.
- Page 1 / 1 -
→ And now try with some utf8 hell: ééé
There are comments.
-- Page 1 / 1 -
There are comments.
-- Page 1 / 1 -
There are comments.
-- Page 1 / 1 -
There are comments.
- Page 1 / 1 -
There are comments.
-- Page 1 / 1 -
→ And now try with some utf8 hell: ééé
There are comments.
-- Page 1 / 1 -
There are comments.
- Page 1 / 1 -
There are comments.
- Page 1 / 1 -
→ And now try with some utf8 hell: ééé
There are comments.
-- Page 1 / 1 -
There are comments.
-- Page 1 / 1 -
+ :::python
+ print("The triple-colon syntax will *not* show line numbers.")
+
+ To display line numbers, use a path-less shebang instead of colons:
+
+ #!python
+ print("The path-less shebang syntax *will* show line numbers.")
The specified identifier (e.g. ``python``, ``ruby``) should be one that
appears on the `list of available lexers `_.
@@ -521,4 +528,5 @@ metadata to include ``Status: published``.
.. _AsciiDoc: http://www.methods.co.nz/asciidoc/
.. _pelican-plugins: http://github.com/getpelican/pelican-plugins
.. _Markdown Extensions: http://pythonhosted.org/Markdown/extensions/
+.. _CodeHilite extension: http://pythonhosted.org/Markdown/extensions/code_hilite.html#syntax
.. _i18n_subsites plugin: http://github.com/getpelican/pelican-plugins/tree/master/i18n_subsites
From ec5c77b25145f7c20fee24c6b85b478295dbc956 Mon Sep 17 00:00:00 2001
From: derwinlu
Date: Thu, 18 Jun 2015 23:33:20 +0200
Subject: [PATCH 0007/1007] remove PAGES; use pages instead
* remove PAGES from context as pages is available
* add section to FAQ to provide guidance
---
docs/faq.rst | 13 +++++++++++++
docs/themes.rst | 2 ++
pelican/generators.py | 1 -
pelican/themes/notmyidea/templates/base.html | 2 +-
pelican/themes/simple/templates/base.html | 2 +-
5 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/docs/faq.rst b/docs/faq.rst
index ff473624..08df017d 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -250,3 +250,16 @@ moved out of the pelican core and into a separate `plugin
`_.
See the :ref:`plugins` documentation further information about the
Pelican plugin system.
+
+Since I upgraded Pelican my Pages are no longer rendered
+========================================================
+Pages were available to Themes as lowercase ``pages`` and uppercase
+``PAGES``. To bring this inline with the :ref:`templates-variables` section,
+``PAGES`` has been removed. This is quickly resolved by updating your theme
+to iterate over ``pages`` instead of ``PAGES``. Just replace::
+
+ {% for pg in PAGES %}
+
+with something like::
+
+ {% for pg in pages %}
diff --git a/docs/themes.rst b/docs/themes.rst
index 6ca753c6..3978e693 100644
--- a/docs/themes.rst
+++ b/docs/themes.rst
@@ -47,6 +47,8 @@ To make your own theme, you must follow the following structure::
if it helps you keep things organized while creating your theme.
+.. _templates-variables:
+
Templates and variables
=======================
diff --git a/pelican/generators.py b/pelican/generators.py
index 0a5298e4..da651252 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -644,7 +644,6 @@ class PagesGenerator(CachingGenerator):
process_translations(hidden_pages))
self._update_context(('pages', 'hidden_pages'))
- self.context['PAGES'] = self.pages
self.save_cache()
self.readers.save_cache()
diff --git a/pelican/themes/notmyidea/templates/base.html b/pelican/themes/notmyidea/templates/base.html
index 45ab4044..188715d4 100644
--- a/pelican/themes/notmyidea/templates/base.html
+++ b/pelican/themes/notmyidea/templates/base.html
@@ -25,7 +25,7 @@
{{ title }}
{% endfor %}
{% if DISPLAY_PAGES_ON_MENU -%}
- {% for pg in PAGES %}
+ {% for pg in pages %}
{{ pg.title }}
{% endfor %}
{% endif %}
diff --git a/pelican/themes/simple/templates/base.html b/pelican/themes/simple/templates/base.html
index bde7983b..760ca5da 100644
--- a/pelican/themes/simple/templates/base.html
+++ b/pelican/themes/simple/templates/base.html
@@ -40,7 +40,7 @@
{{ title }}
{% endfor %}
{% if DISPLAY_PAGES_ON_MENU %}
- {% for p in PAGES %}
+ {% for p in pages %}
{{ p.title }}
{% endfor %}
{% else %}
From 5389543a39f3772afdb5701c5bcbb71e44883f05 Mon Sep 17 00:00:00 2001
From: derwinlu
Date: Fri, 19 Jun 2015 11:19:21 +0200
Subject: [PATCH 0008/1007] improve URLWrapper comparison
* speed up via reduced slugify calls (only call when needed)
* fix __repr__ to not contain str, should call repr on name
* add test_urlwrappers and move URLWrappers tests there
* add new equality test
* cleanup header
additionally:
* Content is now decorated with python_2_unicode_compatible
instead of treating __str__ differently
* better formatting for test_article_metadata_key_lowercase
to actually output the conflict instead of a non descriptive
error
---
pelican/contents.py | 8 ++---
pelican/tests/test_contents.py | 27 ----------------
pelican/tests/test_readers.py | 13 ++++----
pelican/tests/test_urlwrappers.py | 51 +++++++++++++++++++++++++++++++
pelican/urlwrappers.py | 29 ++++++++++++------
5 files changed, 80 insertions(+), 48 deletions(-)
create mode 100644 pelican/tests/test_urlwrappers.py
diff --git a/pelican/contents.py b/pelican/contents.py
index 005d045c..0e91933b 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -25,6 +25,7 @@ from pelican.urlwrappers import (URLWrapper, Author, Category, Tag) # NOQA
logger = logging.getLogger(__name__)
+@python_2_unicode_compatible
class Content(object):
"""Represents a content.
@@ -148,12 +149,7 @@ class Content(object):
signals.content_object_init.send(self)
def __str__(self):
- if self.source_path is None:
- return repr(self)
- elif six.PY3:
- return self.source_path or repr(self)
- else:
- return str(self.source_path.encode('utf-8', 'replace'))
+ return self.source_path or repr(self)
def check_properties(self):
"""Test mandatory properties are set."""
diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py
index 5879bde2..01d2ed3b 100644
--- a/pelican/tests/test_contents.py
+++ b/pelican/tests/test_contents.py
@@ -578,30 +578,3 @@ class TestStatic(unittest.TestCase):
content = page.get_content('')
self.assertNotEqual(content, html)
-
-
-class TestURLWrapper(unittest.TestCase):
- def test_comparisons(self):
- # URLWrappers are sorted by name
- wrapper_a = URLWrapper(name='first', settings={})
- wrapper_b = URLWrapper(name='last', settings={})
- self.assertFalse(wrapper_a > wrapper_b)
- self.assertFalse(wrapper_a >= wrapper_b)
- self.assertFalse(wrapper_a == wrapper_b)
- self.assertTrue(wrapper_a != wrapper_b)
- self.assertTrue(wrapper_a <= wrapper_b)
- self.assertTrue(wrapper_a < wrapper_b)
- wrapper_b.name = 'first'
- self.assertFalse(wrapper_a > wrapper_b)
- self.assertTrue(wrapper_a >= wrapper_b)
- self.assertTrue(wrapper_a == wrapper_b)
- self.assertFalse(wrapper_a != wrapper_b)
- self.assertTrue(wrapper_a <= wrapper_b)
- self.assertFalse(wrapper_a < wrapper_b)
- wrapper_a.name = 'last'
- self.assertTrue(wrapper_a > wrapper_b)
- self.assertTrue(wrapper_a >= wrapper_b)
- self.assertFalse(wrapper_a == wrapper_b)
- self.assertTrue(wrapper_a != wrapper_b)
- self.assertFalse(wrapper_a <= wrapper_b)
- self.assertFalse(wrapper_a < wrapper_b)
diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py
index 18e5111e..dd7e5fc2 100644
--- a/pelican/tests/test_readers.py
+++ b/pelican/tests/test_readers.py
@@ -29,12 +29,10 @@ class ReaderTest(unittest.TestCase):
self.assertEqual(
value,
real_value,
- str('Expected %r to have value %r, but was %r')
- % (key, value, real_value))
+ 'Expected %s to have value %s, but was %s' % (key, value, real_value))
else:
self.fail(
- str('Expected %r to have value %r, but was not in Dict')
- % (key, value))
+ 'Expected %s to have value %s, but was not in Dict' % (key, value))
class TestAssertDictHasSubset(ReaderTest):
def setUp(self):
@@ -566,9 +564,12 @@ class HTMLReaderTest(ReaderTest):
def test_article_metadata_key_lowercase(self):
# Keys of metadata should be lowercase.
page = self.read_file(path='article_with_uppercase_metadata.html')
+
+ # Key should be lowercase
self.assertIn('category', page.metadata, 'Key should be lowercase.')
- self.assertEqual('Yeah', page.metadata.get('category'),
- 'Value keeps cases.')
+
+ # Value should keep cases
+ self.assertEqual('Yeah', page.metadata.get('category'))
def test_article_with_nonconformant_meta_tags(self):
page = self.read_file(path='article_with_nonconformant_meta_tags.html')
diff --git a/pelican/tests/test_urlwrappers.py b/pelican/tests/test_urlwrappers.py
new file mode 100644
index 00000000..20a87114
--- /dev/null
+++ b/pelican/tests/test_urlwrappers.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from pelican.urlwrappers import URLWrapper, Tag, Category
+from pelican.tests.support import unittest
+
+class TestURLWrapper(unittest.TestCase):
+ def test_ordering(self):
+ # URLWrappers are sorted by name
+ wrapper_a = URLWrapper(name='first', settings={})
+ wrapper_b = URLWrapper(name='last', settings={})
+ self.assertFalse(wrapper_a > wrapper_b)
+ self.assertFalse(wrapper_a >= wrapper_b)
+ self.assertFalse(wrapper_a == wrapper_b)
+ self.assertTrue(wrapper_a != wrapper_b)
+ self.assertTrue(wrapper_a <= wrapper_b)
+ self.assertTrue(wrapper_a < wrapper_b)
+ wrapper_b.name = 'first'
+ self.assertFalse(wrapper_a > wrapper_b)
+ self.assertTrue(wrapper_a >= wrapper_b)
+ self.assertTrue(wrapper_a == wrapper_b)
+ self.assertFalse(wrapper_a != wrapper_b)
+ self.assertTrue(wrapper_a <= wrapper_b)
+ self.assertFalse(wrapper_a < wrapper_b)
+ wrapper_a.name = 'last'
+ self.assertTrue(wrapper_a > wrapper_b)
+ self.assertTrue(wrapper_a >= wrapper_b)
+ self.assertFalse(wrapper_a == wrapper_b)
+ self.assertTrue(wrapper_a != wrapper_b)
+ self.assertFalse(wrapper_a <= wrapper_b)
+ self.assertFalse(wrapper_a < wrapper_b)
+
+ def test_equality(self):
+ tag = Tag('test', settings={})
+ cat = Category('test', settings={})
+
+ # same name, but different class
+ self.assertNotEqual(tag, cat)
+
+ # should be equal vs text representing the same name
+ self.assertEqual(tag, u'test')
+
+ # should not be equal vs binary
+ self.assertNotEqual(tag, b'test')
+
+ # Tags describing the same should be equal
+ tag_equal = Tag('Test', settings={})
+ self.assertEqual(tag, tag_equal)
+
+ cat_ascii = Category('指導書', settings={})
+ self.assertEqual(cat_ascii, u'zhi-dao-shu')
diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py
index 60bc6a3a..65dee23b 100644
--- a/pelican/urlwrappers.py
+++ b/pelican/urlwrappers.py
@@ -1,7 +1,9 @@
-import os
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
import functools
import logging
-
+import os
import six
from pelican.utils import (slugify, python_2_unicode_compatible)
@@ -52,27 +54,36 @@ class URLWrapper(object):
def __hash__(self):
return hash(self.slug)
- def _key(self):
- return self.slug
-
def _normalize_key(self, key):
subs = self.settings.get('SLUG_SUBSTITUTIONS', ())
return six.text_type(slugify(key, subs))
def __eq__(self, other):
- return self._key() == self._normalize_key(other)
+ if isinstance(other, self.__class__):
+ return self.slug == other.slug
+ if isinstance(other, six.text_type):
+ return self.slug == self._normalize_key(other)
+ return False
def __ne__(self, other):
- return self._key() != self._normalize_key(other)
+ if isinstance(other, self.__class__):
+ return self.slug != other.slug
+ if isinstance(other, six.text_type):
+ return self.slug != self._normalize_key(other)
+ return True
def __lt__(self, other):
- return self._key() < self._normalize_key(other)
+ if isinstance(other, self.__class__):
+ return self.slug < other.slug
+ if isinstance(other, six.text_type):
+ return self.slug < self._normalize_key(other)
+ return False
def __str__(self):
return self.name
def __repr__(self):
- return '<{} {}>'.format(type(self).__name__, str(self))
+ return '<{} {}>'.format(type(self).__name__, repr(self._name))
def _from_settings(self, key, get_page_name=False):
"""Returns URL information as defined in settings.
From ba9b4a1d9bab0dba7cc8563e14a0bbb80ae10b43 Mon Sep 17 00:00:00 2001
From: guanidene
Date: Tue, 30 Jun 2015 20:56:21 +0530
Subject: [PATCH 0009/1007] Watch for changes in mtime of symlinked folders and
files too
---
pelican/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/utils.py b/pelican/utils.py
index 6ad4de24..fb8ed9dc 100644
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -576,7 +576,7 @@ def folder_watcher(path, extensions, ignores=[]):
def file_times(path):
'''Return `mtime` for each file in path'''
- for root, dirs, files in os.walk(path):
+ for root, dirs, files in os.walk(path, followlinks=True):
dirs[:] = [x for x in dirs if not x.startswith(os.curdir)]
for f in files:
From aa318a1366b4a1f8ce4eb8e50d52421c8abca031 Mon Sep 17 00:00:00 2001
From: Jotham Apaloo
Date: Fri, 19 Jun 2015 14:50:12 -0400
Subject: [PATCH 0010/1007] Update tips.rst
add 404 page instructions for amazon s3.
---
docs/tips.rst | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/docs/tips.rst b/docs/tips.rst
index 15d68769..82018400 100644
--- a/docs/tips.rst
+++ b/docs/tips.rst
@@ -28,6 +28,11 @@ configuration file's ``location`` block::
For Apache::
ErrorDocument 404 /404.html
+
+For Amazon S3, first navigate to the ``Static Site Hosting`` menu in the
+bucket settings on your AWS cosole. From there::
+
+ Error Document: 404.html
Publishing to GitHub
====================
From bd5dfa0b3ec9560a1a453907f9f3767b9b89ea01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?=
Date: Fri, 10 Jul 2015 00:48:41 +0300
Subject: [PATCH 0011/1007] Avoid adding the tests in bdists (like wheel
bdist). Supersedes and closes #1618.
---
setup.py | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/setup.py b/setup.py
index 93d7e5f6..01d40ae4 100755
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+from os import walk
+from os.path import join, relpath, dirname
+
from setuptools import setup
requires = ['feedgenerator >= 1.6', 'jinja2 >= 2.7', 'pygments', 'docutils',
@@ -14,11 +17,9 @@ entry_points = {
]
}
-
README = open('README.rst').read()
CHANGELOG = open('docs/changelog.rst').read()
-
setup(
name="pelican",
version="3.6.1.dev",
@@ -29,7 +30,19 @@ setup(
"Markdown input files.",
long_description=README + '\n' + CHANGELOG,
packages=['pelican', 'pelican.tools'],
- include_package_data=True,
+ 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)
+ '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,
entry_points=entry_points,
classifiers=[
From 379f8666c1ada0f091edb0792a7e84a5e273576b Mon Sep 17 00:00:00 2001
From: Andrea Corbellini
Date: Thu, 30 Jul 2015 21:04:28 +0200
Subject: [PATCH 0012/1007] Rewrite pelican.utils.truncate_html_words() to use
an HTML parser instead of regular expressions.
---
pelican/tests/test_utils.py | 26 +++++++++
pelican/utils.py | 107 +++++++++++++++++++-----------------
2 files changed, 84 insertions(+), 49 deletions(-)
diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py
index f5a60584..0f8878af 100644
--- a/pelican/tests/test_utils.py
+++ b/pelican/tests/test_utils.py
@@ -144,6 +144,32 @@ class TestUtils(LoggedTestCase):
for value, expected in samples:
self.assertEqual(utils.get_relative_path(value), expected)
+ def test_truncate_html_words(self):
+ self.assertEqual(
+ utils.truncate_html_words('short string', 20),
+ 'short string')
+
+ self.assertEqual(
+ utils.truncate_html_words('word ' * 100, 20),
+ 'word ' * 20 + '...')
+
+ self.assertEqual(
+ utils.truncate_html_words('' + 'word ' * 100 + '
', 20),
+ '' + 'word ' * 20 + '...
')
+
+ self.assertEqual(
+ utils.truncate_html_words(
+ '' + 'word ' * 100 + '', 20),
+ '' + 'word ' * 20 + '...')
+
+ self.assertEqual(
+ utils.truncate_html_words('
' + 'word ' * 100, 20),
+ '
' + 'word ' * 20 + '...')
+
+ self.assertEqual(
+ utils.truncate_html_words('' + 'word ' * 100, 20),
+ '' + 'word ' * 20 + '...')
+
def test_process_translations(self):
# create a bunch of articles
# 1: no translation metadata
diff --git a/pelican/utils.py b/pelican/utils.py
index fb8ed9dc..43dca212 100644
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -24,6 +24,7 @@ from itertools import groupby
from jinja2 import Markup
from operator import attrgetter
from posixpath import join as posix_join
+from six.moves.html_parser import HTMLParser
logger = logging.getLogger(__name__)
@@ -402,6 +403,58 @@ def posixize_path(rel_path):
return rel_path.replace(os.sep, '/')
+class _HTMLWordTruncator(HTMLParser):
+
+ _word_regex = re.compile(r'\w[\w-]*', re.U)
+ _singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area',
+ 'hr', 'input')
+
+ def __init__(self, max_words):
+ # In Python 2, HTMLParser is not a new-style class,
+ # hence super() cannot be used.
+ HTMLParser.__init__(self)
+
+ self.max_words = max_words
+ self.words_found = 0
+ self.open_tags = []
+ self.truncate_at = None
+
+ def handle_starttag(self, tag, attrs):
+ if self.truncate_at is not None:
+ return
+ if tag not in self._singlets:
+ self.open_tags.insert(0, tag)
+
+ def handle_endtag(self, tag):
+ if self.truncate_at is not None:
+ return
+ try:
+ i = self.open_tags.index(tag)
+ except ValueError:
+ pass
+ else:
+ # SGML: An end tag closes, back to the matching start tag,
+ # all unclosed intervening start tags with omitted end tags
+ del self.open_tags[:i + 1]
+
+ def handle_data(self, data):
+ word_end = 0
+
+ while self.words_found < self.max_words:
+ match = self._word_regex.search(data, word_end)
+ if not match:
+ break
+ word_end = match.end(0)
+ self.words_found += 1
+
+ if self.words_found == self.max_words:
+ line_start = 0
+ lineno, line_offset = self.getpos()
+ for i in range(lineno - 1):
+ line_start = self.rawdata.index('\n', line_start) + 1
+ self.truncate_at = line_start + line_offset + word_end
+
+
def truncate_html_words(s, num, end_text='...'):
"""Truncates HTML to a certain number of words.
@@ -414,59 +467,15 @@ def truncate_html_words(s, num, end_text='...'):
length = int(num)
if length <= 0:
return ''
- html4_singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area',
- 'hr', 'input')
-
- # Set up regular expressions
- re_words = re.compile(r'&.*?;|<.*?>|(\w[\w-]*)', re.U)
- re_tag = re.compile(r'<(/)?([^ ]+?)(?: (/)| .*?)?>')
- # Count non-HTML words and keep note of open tags
- pos = 0
- end_text_pos = 0
- words = 0
- open_tags = []
- while words <= length:
- m = re_words.search(s, pos)
- if not m:
- # Checked through whole string
- break
- pos = m.end(0)
- if m.group(1):
- # It's an actual non-HTML word
- words += 1
- if words == length:
- end_text_pos = pos
- continue
- # Check for tag
- tag = re_tag.match(m.group(0))
- if not tag or end_text_pos:
- # Don't worry about non tags or tags after our truncate point
- continue
- closing_tag, tagname, self_closing = tag.groups()
- tagname = tagname.lower() # Element names are always case-insensitive
- if self_closing or tagname in html4_singlets:
- pass
- elif closing_tag:
- # Check for match in open tags list
- try:
- i = open_tags.index(tagname)
- except ValueError:
- pass
- else:
- # SGML: An end tag closes, back to the matching start tag,
- # all unclosed intervening start tags with omitted end tags
- open_tags = open_tags[i + 1:]
- else:
- # Add it to the start of the open tags list
- open_tags.insert(0, tagname)
- if words <= length:
- # Don't try to close tags if we don't need to truncate
+ truncator = _HTMLWordTruncator(length)
+ truncator.feed(s)
+ if truncator.truncate_at is None:
return s
- out = s[:end_text_pos]
+ out = s[:truncator.truncate_at]
if end_text:
out += ' ' + end_text
# Close any tags still open
- for tag in open_tags:
+ for tag in truncator.open_tags:
out += '%s>' % tag
# Return string
return out
From 5e36a87916dd2cb431b7c916f53348605a687542 Mon Sep 17 00:00:00 2001
From: Justin Mayer
Date: Sat, 1 Aug 2015 13:20:04 -0700
Subject: [PATCH 0013/1007] Update changelog
---
docs/changelog.rst | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/docs/changelog.rst b/docs/changelog.rst
index f08f67ec..72f71fa5 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,7 +4,10 @@ Release history
Next release
============
-- Nothing yet
+* Fix installation errors related to Unicode in tests
+* Don't show pagination in ``notmyidea`` theme if there's only one page
+* Make hidden pages available in context
+* Improve URLWrapper comparison
3.6.0 (2015-06-15)
==================
From 96b23e03bd7a2b8a904fafb70d87484f0c81c86e Mon Sep 17 00:00:00 2001
From: Justin Mayer
Date: Sat, 1 Aug 2015 13:39:10 -0700
Subject: [PATCH 0014/1007] Bump version 3.6.2
---
docs/changelog.rst | 4 ++--
docs/conf.py | 2 +-
pelican/__init__.py | 2 +-
setup.py | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 72f71fa5..664e1310 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,8 +1,8 @@
Release history
###############
-Next release
-============
+3.6.2 (2015-08-01)
+==================
* Fix installation errors related to Unicode in tests
* Don't show pagination in ``notmyidea`` theme if there's only one page
diff --git a/docs/conf.py b/docs/conf.py
index 43af8da4..15ae49b9 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,7 +18,7 @@ copyright = '2015, Alexis Metaireau and contributors'
exclude_patterns = ['_build']
release = __version__
version = '.'.join(release.split('.')[:1])
-last_stable = '3.6.0'
+last_stable = '3.6.2'
rst_prolog = '''
.. |last_stable| replace:: :pelican-doc:`{0}`
'''.format(last_stable)
diff --git a/pelican/__init__.py b/pelican/__init__.py
index 932974db..0b5c62d4 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -26,7 +26,7 @@ from pelican.utils import (clean_output_dir, folder_watcher,
file_watcher, maybe_pluralize)
from pelican.writers import Writer
-__version__ = "3.6.1.dev"
+__version__ = "3.6.2"
DEFAULT_CONFIG_NAME = 'pelicanconf.py'
diff --git a/setup.py b/setup.py
index 01d40ae4..b6b5c3d8 100755
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@ CHANGELOG = open('docs/changelog.rst').read()
setup(
name="pelican",
- version="3.6.1.dev",
+ version="3.6.2",
url='http://getpelican.com/',
author='Alexis Metaireau',
author_email='authors@getpelican.com',
From 7181cc36d59b7e50d1d2da0f4f03e0a77fe02be5 Mon Sep 17 00:00:00 2001
From: Justin Mayer
Date: Sat, 1 Aug 2015 13:39:24 -0700
Subject: [PATCH 0015/1007] Prepare version 3.6.3.dev for next development
cycle
---
docs/changelog.rst | 5 +++++
pelican/__init__.py | 2 +-
setup.py | 2 +-
3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 664e1310..b90fd997 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,6 +1,11 @@
Release history
###############
+Next release
+============
+
+- Nothing yet
+
3.6.2 (2015-08-01)
==================
diff --git a/pelican/__init__.py b/pelican/__init__.py
index 0b5c62d4..2762ae71 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -26,7 +26,7 @@ from pelican.utils import (clean_output_dir, folder_watcher,
file_watcher, maybe_pluralize)
from pelican.writers import Writer
-__version__ = "3.6.2"
+__version__ = "3.6.3.dev"
DEFAULT_CONFIG_NAME = 'pelicanconf.py'
diff --git a/setup.py b/setup.py
index b6b5c3d8..806464f2 100755
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@ CHANGELOG = open('docs/changelog.rst').read()
setup(
name="pelican",
- version="3.6.2",
+ version="3.6.3.dev",
url='http://getpelican.com/',
author='Alexis Metaireau',
author_email='authors@getpelican.com',
From 2ca7aa8c80fa225021ff57da150ce25faaf315db Mon Sep 17 00:00:00 2001
From: Kubilay Kocak
Date: Fri, 7 Aug 2015 14:46:12 +1000
Subject: [PATCH 0016/1007] Add missing *.markdown files to PyPI sdist
The following file is missing from the PyPI source distribution (sdist) due to *.markdown not being referenced in MANIFEST.in:
pelican/tests/content/article_with_markdown_extension.markdown
This missing file appears to cause a number of errors running the test suite. An alternative to this change would be to rename the file to .md so that the file is covered by the existing *.md entry.
---
MANIFEST.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index dcf9ea45..64ed4b3c 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,3 @@
include *.rst
-recursive-include pelican *.html *.css *png *.in *.rst *.md *.mkd *.xml *.py
+recursive-include pelican *.html *.css *png *.in *.rst *.markdown *.md *.mkd *.xml *.py
include LICENSE THANKS docs/changelog.rst
From 657ffdd75fc49f5364d7198e0f6ce80f1f473aa8 Mon Sep 17 00:00:00 2001
From: winlu
Date: Sat, 8 Aug 2015 14:38:25 +0200
Subject: [PATCH 0017/1007] add warning for unknown replacement indicators
If an unknown replacement indicator {bla} was used, it was ignored
silently. This commit adds a warning when an unmatched indicator occurs
to help identify the issue.
closes #1794
---
pelican/contents.py | 5 +++++
pelican/tests/test_contents.py | 24 +++++++++++++++++++++---
2 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/pelican/contents.py b/pelican/contents.py
index 0e91933b..a6b8cc5f 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -248,6 +248,11 @@ class Content(object):
origin = '/'.join((siteurl, Category(path, self.settings).url))
elif what == 'tag':
origin = '/'.join((siteurl, Tag(path, self.settings).url))
+ else:
+ logger.warning(
+ "Replacement Indicator '%s' not recognized, "
+ "skipping replacement",
+ what)
# keep all other parts, such as query, fragment, etc.
parts = list(value)
diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py
index 01d2ed3b..145a53b6 100644
--- a/pelican/tests/test_contents.py
+++ b/pelican/tests/test_contents.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals, absolute_import
+import logging
import locale
import os.path
import six
@@ -11,7 +12,7 @@ from pelican.contents import (Page, Article, Static, URLWrapper,
Author, Category)
from pelican.settings import DEFAULT_CONFIG
from pelican.signals import content_object_init
-from pelican.tests.support import unittest, get_settings
+from pelican.tests.support import LoggedTestCase, mute, unittest, get_settings
from pelican.utils import (path_to_url, truncate_html_words, SafeDatetime,
posix_join)
@@ -413,10 +414,10 @@ class TestArticle(TestPage):
self.assertEqual(article.save_as, 'obrien/csharp-stuff/fnord/index.html')
-class TestStatic(unittest.TestCase):
+class TestStatic(LoggedTestCase):
def setUp(self):
-
+ super(TestStatic, self).setUp()
self.settings = get_settings(
STATIC_SAVE_AS='{path}',
STATIC_URL='{path}',
@@ -578,3 +579,20 @@ class TestStatic(unittest.TestCase):
content = page.get_content('')
self.assertNotEqual(content, html)
+
+ def test_unknown_link_syntax(self):
+ "{unknown} link syntax should trigger warning."
+
+ html = 'link'
+ page = Page(content=html,
+ metadata={'title': 'fakepage'}, settings=self.settings,
+ source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
+ context=self.context)
+ content = page.get_content('')
+
+ self.assertEqual(content, html)
+ self.assertLogCountEqual(
+ count=1,
+ msg="Replacement Indicator 'unknown' not recognized, "
+ "skipping replacement",
+ level=logging.WARNING)
From 45ff578f69df5c0f8eb3728569e9e583469d7f1c Mon Sep 17 00:00:00 2001
From: Your Name
Date: Mon, 10 Aug 2015 15:44:13 -0400
Subject: [PATCH 0018/1007] Added optional override_output to write_feed.
The write_feed function now takes an override_output parameter that
does the same thing as override_output does to write_file. Implemented
for custom generators.
---
pelican/writers.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/pelican/writers.py b/pelican/writers.py
index e90a0004..36d2f038 100644
--- a/pelican/writers.py
+++ b/pelican/writers.py
@@ -79,7 +79,8 @@ class Writer(object):
self._written_files.add(filename)
return open(filename, 'w', encoding=encoding)
- def write_feed(self, elements, context, path=None, feed_type='atom'):
+ def write_feed(self, elements, context, path=None, feed_type='atom',
+ override_output=False):
"""Generate a feed with the list of articles provided
Return the feed. If no path or output_path is specified, just
@@ -89,6 +90,9 @@ class Writer(object):
:param context: the context to get the feed metadata.
:param path: the path to output.
:param feed_type: the feed type to use (atom or rss)
+ :param override_output: boolean telling if we can override previous
+ output with the same name (and if next files written with the same
+ name should be skipped to keep that one)
"""
if not is_selected_for_writing(self.settings, path):
return
@@ -115,7 +119,7 @@ class Writer(object):
pass
encoding = 'utf-8' if six.PY3 else None
- with self._open_w(complete_path, encoding) as fp:
+ with self._open_w(complete_path, encoding, override_output) as fp:
feed.write(fp, 'utf-8')
logger.info('Writing %s', complete_path)
From ed34ee1808699fa34d2170bf4fa8a26152f27514 Mon Sep 17 00:00:00 2001
From: jah
Date: Tue, 11 Aug 2015 20:03:43 +0100
Subject: [PATCH 0019/1007] Correct render of article description meta
in the simple theme: the template incorrectly assumed that the source
metadata is made available as a list of strings. Fixes #1792.
---
pelican/themes/simple/templates/article.html | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/pelican/themes/simple/templates/article.html b/pelican/themes/simple/templates/article.html
index d558183d..8ddda4d0 100644
--- a/pelican/themes/simple/templates/article.html
+++ b/pelican/themes/simple/templates/article.html
@@ -5,9 +5,9 @@
{% endfor %}
- {% for description in article.description %}
-
- {% endfor %}
+ {% if article.description %}
+
+ {% endif %}
{% for tag in article.tags %}
From ed83ad75a9aeaea97c6ab530b0679d1555c7691c Mon Sep 17 00:00:00 2001
From: Justin Mayer
Date: Fri, 14 Aug 2015 16:34:25 -0700
Subject: [PATCH 0020/1007] Bump version 3.6.3
---
docs/changelog.rst | 5 +++++
docs/conf.py | 2 +-
pelican/__init__.py | 2 +-
setup.py | 2 +-
4 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 664e1310..70a96b30 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,6 +1,11 @@
Release history
###############
+3.6.3 (2015-08-14)
+==================
+
+* Fix permissions issue in release tarball
+
3.6.2 (2015-08-01)
==================
diff --git a/docs/conf.py b/docs/conf.py
index 15ae49b9..d3f58905 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,7 +18,7 @@ copyright = '2015, Alexis Metaireau and contributors'
exclude_patterns = ['_build']
release = __version__
version = '.'.join(release.split('.')[:1])
-last_stable = '3.6.2'
+last_stable = '3.6.3'
rst_prolog = '''
.. |last_stable| replace:: :pelican-doc:`{0}`
'''.format(last_stable)
diff --git a/pelican/__init__.py b/pelican/__init__.py
index 0b5c62d4..a738506a 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -26,7 +26,7 @@ from pelican.utils import (clean_output_dir, folder_watcher,
file_watcher, maybe_pluralize)
from pelican.writers import Writer
-__version__ = "3.6.2"
+__version__ = "3.6.3"
DEFAULT_CONFIG_NAME = 'pelicanconf.py'
diff --git a/setup.py b/setup.py
index b6b5c3d8..bdcdea37 100755
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@ CHANGELOG = open('docs/changelog.rst').read()
setup(
name="pelican",
- version="3.6.2",
+ version="3.6.3",
url='http://getpelican.com/',
author='Alexis Metaireau',
author_email='authors@getpelican.com',
From e06d0046b1e0ac988167810c0e8906d52d16960a Mon Sep 17 00:00:00 2001
From: Justin Mayer
Date: Fri, 14 Aug 2015 17:24:29 -0700
Subject: [PATCH 0021/1007] Prepare version 3.6.4.dev0 for next development
cycle
---
docs/changelog.rst | 5 +++++
pelican/__init__.py | 2 +-
setup.py | 2 +-
3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 70a96b30..f52d6449 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,6 +1,11 @@
Release history
###############
+Next release
+============
+
+- Nothing yet
+
3.6.3 (2015-08-14)
==================
diff --git a/pelican/__init__.py b/pelican/__init__.py
index a738506a..1af14897 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -26,7 +26,7 @@ from pelican.utils import (clean_output_dir, folder_watcher,
file_watcher, maybe_pluralize)
from pelican.writers import Writer
-__version__ = "3.6.3"
+__version__ = "3.6.4.dev0"
DEFAULT_CONFIG_NAME = 'pelicanconf.py'
diff --git a/setup.py b/setup.py
index bdcdea37..86028424 100755
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@ CHANGELOG = open('docs/changelog.rst').read()
setup(
name="pelican",
- version="3.6.3",
+ version="3.6.4.dev0",
url='http://getpelican.com/',
author='Alexis Metaireau',
author_email='authors@getpelican.com',
From 5dc6d2914e702c2b599a592fb8d6faeb179e3b0e Mon Sep 17 00:00:00 2001
From: Justin Mayer
Date: Sun, 16 Aug 2015 09:57:55 -0700
Subject: [PATCH 0022/1007] Minor tweaks to FAQ docs
---
docs/faq.rst | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/docs/faq.rst b/docs/faq.rst
index 08df017d..195a4e33 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -12,10 +12,10 @@ How can I help?
================
There are several ways to help out. First, you can report any Pelican
-suggestions or problems you might have via IRC or the `issue tracker
-`_. If submitting an issue
-report, please first check the existing issue list (both open and closed) in
-order to avoid submitting a duplicate issue.
+suggestions or problems you might have via IRC (preferred) or the
+`issue tracker `_. If submitting
+an issue report, please first check the existing issue list (both open and
+closed) in order to avoid submitting a duplicate issue.
If you want to contribute, please fork `the git repository
`_, create a new feature branch, make
@@ -25,20 +25,20 @@ section for more details.
You can also contribute by creating themes and improving the documentation.
-Is it mandatory to have a configuration file?
+Is the Pelican settings file mandatory?
=============================================
Configuration files are optional and are just an easy way to configure Pelican.
For basic operations, it's possible to specify options while invoking Pelican
via the command line. See ``pelican --help`` for more information.
-Changes to the setting file take no effect
+Changes to the settings file take no effect
==========================================
When experimenting with different settings (especially the metadata
ones) caching may interfere and the changes may not be visible. In
-such cases disable caching with ``LOAD_CONTENT_CACHE = False`` or
-use the ``--ignore-cache`` command-line switch.
+such cases, ensure that caching is disabled via ``LOAD_CONTENT_CACHE = False``
+or use the ``--ignore-cache`` command-line switch.
I'm creating my own theme. How do I use Pygments for syntax highlighting?
=========================================================================
@@ -251,15 +251,15 @@ moved out of the pelican core and into a separate `plugin
See the :ref:`plugins` documentation further information about the
Pelican plugin system.
-Since I upgraded Pelican my Pages are no longer rendered
+Since I upgraded Pelican my pages are no longer rendered
========================================================
-Pages were available to Themes as lowercase ``pages`` and uppercase
+Pages were available to themes as lowercase ``pages`` and uppercase
``PAGES``. To bring this inline with the :ref:`templates-variables` section,
``PAGES`` has been removed. This is quickly resolved by updating your theme
to iterate over ``pages`` instead of ``PAGES``. Just replace::
{% for pg in PAGES %}
-with something like::
+...with something like::
{% for pg in pages %}
From bd3bec493eb395699e6564383a50e1b89a11d25f Mon Sep 17 00:00:00 2001
From: Justin Mayer
Date: Sun, 16 Aug 2015 10:14:49 -0700
Subject: [PATCH 0023/1007] Fix reST error in docs
---
docs/faq.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/faq.rst b/docs/faq.rst
index 195a4e33..cd7f598a 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -260,6 +260,6 @@ to iterate over ``pages`` instead of ``PAGES``. Just replace::
{% for pg in PAGES %}
-...with something like::
+with something like::
{% for pg in pages %}
From de6bd537b51ccba24f0666ee5d732e3d8453b08e Mon Sep 17 00:00:00 2001
From: Justin Mayer
Date: Sun, 16 Aug 2015 10:37:43 -0700
Subject: [PATCH 0024/1007] Fix FAQ header underlines
---
docs/faq.rst | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/docs/faq.rst b/docs/faq.rst
index cd7f598a..bb4cd9e6 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -9,7 +9,7 @@ What's the best way to communicate a problem, question, or suggestion?
Please read our :doc:`feedback guidelines `.
How can I help?
-================
+===============
There are several ways to help out. First, you can report any Pelican
suggestions or problems you might have via IRC (preferred) or the
@@ -26,14 +26,14 @@ section for more details.
You can also contribute by creating themes and improving the documentation.
Is the Pelican settings file mandatory?
-=============================================
+=======================================
Configuration files are optional and are just an easy way to configure Pelican.
For basic operations, it's possible to specify options while invoking Pelican
via the command line. See ``pelican --help`` for more information.
Changes to the settings file take no effect
-==========================================
+===========================================
When experimenting with different settings (especially the metadata
ones) caching may interfere and the changes may not be visible. In
@@ -60,12 +60,12 @@ CSS file to your new theme::
Don't forget to import your ``pygment.css`` file from your main CSS file.
How do I create my own theme?
-==============================
+=============================
Please refer to :ref:`theming-pelican`.
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``.
@@ -77,7 +77,7 @@ permissions require it::
pip install markdown
Can I use arbitrary metadata in my templates?
-==============================================
+=============================================
Yes. For example, to include a modified date in a Markdown post, one could
include the following at the top of the article::
From 44f9cfaaf1c4cb7553314be8c00357825520aaa9 Mon Sep 17 00:00:00 2001
From: derwinlu
Date: Mon, 8 Jun 2015 12:50:35 +0200
Subject: [PATCH 0025/1007] add flake8 testing environment
---
.travis.yml | 1 +
tox.ini | 14 +++++++++++++-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 5d7d4a5f..1be196f2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ python:
- "2.7"
env:
- TOX_ENV=docs
+ - TOX_ENV=flake8
- TOX_ENV=py27
- TOX_ENV=py33
- TOX_ENV=py34
diff --git a/tox.ini b/tox.ini
index ff16929e..34335b82 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py{27,33,34},docs
+envlist = py{27,33,34},docs,flake8
[testenv]
basepython =
@@ -27,3 +27,15 @@ deps =
changedir = docs
commands =
sphinx-build -W -b html -d {envtmpdir}/doctrees . _build/html
+
+[flake8]
+application-import-names = pelican
+import-order-style = cryptography
+
+[testenv:flake8]
+basepython = python2.7
+deps =
+ flake8 <= 2.4.1
+ git+https://github.com/public/flake8-import-order@2ac7052a4e02b4a8a0125a106d87465a3b9fd688
+commands =
+ flake8 pelican
From 8993c55e6edc4993790e2aef182b924ff60b5239 Mon Sep 17 00:00:00 2001
From: derwinlu
Date: Tue, 16 Jun 2015 09:25:09 +0200
Subject: [PATCH 0026/1007] fulfil pep8 standard
---
pelican/__init__.py | 68 +++---
pelican/cache.py | 11 +-
pelican/contents.py | 66 +++--
pelican/generators.py | 100 ++++----
pelican/log.py | 30 +--
pelican/paginator.py | 13 +-
pelican/readers.py | 100 ++++----
pelican/rstdirectives.py | 14 +-
pelican/server.py | 16 +-
pelican/settings.py | 88 ++++---
pelican/signals.py | 3 +-
pelican/tests/default_conf.py | 7 +-
pelican/tests/support.py | 32 +--
pelican/tests/test_cache.py | 26 +-
pelican/tests/test_contents.py | 138 ++++++-----
pelican/tests/test_generators.py | 158 +++++++-----
pelican/tests/test_importer.py | 161 ++++++++-----
pelican/tests/test_paginator.py | 18 +-
pelican/tests/test_pelican.py | 70 +++---
pelican/tests/test_readers.py | 86 ++++---
pelican/tests/test_rstdirectives.py | 7 +-
pelican/tests/test_settings.py | 65 ++---
pelican/tests/test_urlwrappers.py | 3 +-
pelican/tests/test_utils.py | 111 +++++----
pelican/tools/pelican_import.py | 361 +++++++++++++++++-----------
pelican/tools/pelican_quickstart.py | 160 ++++++++----
pelican/tools/pelican_themes.py | 122 ++++++----
pelican/urlwrappers.py | 3 +-
pelican/utils.py | 85 ++++---
pelican/writers.py | 46 ++--
tox.ini | 1 +
31 files changed, 1280 insertions(+), 889 deletions(-)
diff --git a/pelican/__init__.py b/pelican/__init__.py
index 1af14897..7fb8dfe4 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -1,45 +1,41 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
-import six
+from __future__ import print_function, unicode_literals
+import argparse
+import collections
+import locale
+import logging
import os
import re
import sys
import time
-import logging
-import argparse
-import locale
-import collections
+
+import six
# pelican.log has to be the first pelican module to be loaded
# because logging.setLoggerClass has to be called before logging.getLogger
-from pelican.log import init
-
+from pelican.log import init # noqa
from pelican import signals
-
from pelican.generators import (ArticlesGenerator, PagesGenerator,
- StaticGenerator, SourceFileGenerator,
+ SourceFileGenerator, StaticGenerator,
TemplatePagesGenerator)
from pelican.readers import Readers
from pelican.settings import read_settings
-from pelican.utils import (clean_output_dir, folder_watcher,
- file_watcher, maybe_pluralize)
+from pelican.utils import (clean_output_dir, file_watcher,
+ folder_watcher, maybe_pluralize)
from pelican.writers import Writer
__version__ = "3.6.4.dev0"
-
DEFAULT_CONFIG_NAME = 'pelicanconf.py'
-
-
logger = logging.getLogger(__name__)
class Pelican(object):
def __init__(self, settings):
- """
- Pelican initialisation, performs some checks on the environment before
- doing anything else.
+ """Pelican initialisation
+
+ Performs some checks on the environment before doing anything else.
"""
# define the default settings
@@ -152,7 +148,7 @@ class Pelican(object):
context = self.settings.copy()
# Share these among all the generators and content objects:
context['filenames'] = {} # maps source path to Content object or None
- context['localsiteurl'] = self.settings['SITEURL']
+ context['localsiteurl'] = self.settings['SITEURL']
generators = [
cls(
@@ -190,23 +186,23 @@ class Pelican(object):
if isinstance(g, PagesGenerator))
pluralized_articles = maybe_pluralize(
- len(articles_generator.articles) +
- len(articles_generator.translations),
+ (len(articles_generator.articles) +
+ len(articles_generator.translations)),
'article',
'articles')
pluralized_drafts = maybe_pluralize(
- len(articles_generator.drafts) +
- len(articles_generator.drafts_translations),
+ (len(articles_generator.drafts) +
+ len(articles_generator.drafts_translations)),
'draft',
'drafts')
pluralized_pages = maybe_pluralize(
- len(pages_generator.pages) +
- len(pages_generator.translations),
+ (len(pages_generator.pages) +
+ len(pages_generator.translations)),
'page',
'pages')
pluralized_hidden_pages = maybe_pluralize(
- len(pages_generator.hidden_pages) +
- len(pages_generator.hidden_translations),
+ (len(pages_generator.hidden_pages) +
+ len(pages_generator.hidden_translations)),
'hidden page',
'hidden pages')
@@ -243,8 +239,8 @@ class Pelican(object):
return generators
def get_writer(self):
- writers = [ w for (_, w) in signals.get_writer.send(self)
- if isinstance(w, type) ]
+ writers = [w for (_, w) in signals.get_writer.send(self)
+ if isinstance(w, type)]
writers_found = len(writers)
if writers_found == 0:
return Writer(self.output_path, settings=self.settings)
@@ -254,15 +250,15 @@ class Pelican(object):
logger.debug('Found writer: %s', writer)
else:
logger.warning(
- '%s writers found, using only first one: %s',
+ '%s writers found, using only first one: %s',
writers_found, writer)
return writer(self.output_path, settings=self.settings)
def parse_arguments():
parser = argparse.ArgumentParser(
- description="""A tool to generate a static blog,
- with restructured text input files.""",
+ description='A tool to generate a static blog, '
+ ' with restructured text input files.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
@@ -354,7 +350,7 @@ def get_config(args):
# argparse returns bytes in Py2. There is no definite answer as to which
# encoding argparse (or sys.argv) uses.
# "Best" option seems to be locale.getpreferredencoding()
- # ref: http://mail.python.org/pipermail/python-list/2006-October/405766.html
+ # http://mail.python.org/pipermail/python-list/2006-October/405766.html
if not six.PY3:
enc = locale.getpreferredencoding()
for key in config:
@@ -424,7 +420,8 @@ def main():
# Added static paths
# Add new watchers and set them as modified
- for static_path in set(new_static).difference(old_static):
+ new_watchers = set(new_static).difference(old_static)
+ for static_path in new_watchers:
static_key = '[static]%s' % static_path
watchers[static_key] = folder_watcher(
os.path.join(pelican.path, static_path),
@@ -434,7 +431,8 @@ def main():
# Removed static paths
# Remove watchers and modified values
- for static_path in set(old_static).difference(new_static):
+ old_watchers = set(old_static).difference(new_static)
+ for static_path in old_watchers:
static_key = '[static]%s' % static_path
watchers.pop(static_key)
modified.pop(static_key)
diff --git a/pelican/cache.py b/pelican/cache.py
index d955ae08..e6c10cb9 100644
--- a/pelican/cache.py
+++ b/pelican/cache.py
@@ -1,16 +1,14 @@
+# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import hashlib
import logging
import os
-try:
- import cPickle as pickle
-except:
- import pickle
+
+from six.moves import cPickle as pickle
from pelican.utils import mkdir_p
-
logger = logging.getLogger(__name__)
@@ -83,6 +81,7 @@ class FileStampDataCacher(FileDataCacher):
"""This sublcass additionally sets filestamp function
and base path for filestamping operations
"""
+
super(FileStampDataCacher, self).__init__(settings, cache_name,
caching_policy,
load_policy)
@@ -118,6 +117,7 @@ class FileStampDataCacher(FileDataCacher):
a hash for a function name in the hashlib module
or an empty bytes string otherwise
"""
+
try:
return self._filestamp_func(filename)
except (IOError, OSError, TypeError) as err:
@@ -133,6 +133,7 @@ class FileStampDataCacher(FileDataCacher):
Modification is checked by comparing the cached
and current file stamp.
"""
+
stamp, data = super(FileStampDataCacher, self).get_cached_data(
filename, (None, default))
if stamp != self._get_file_stamp(filename):
diff --git a/pelican/contents.py b/pelican/contents.py
index a6b8cc5f..16d1f074 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -1,23 +1,24 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
-import six
-from six.moves.urllib.parse import urlparse, urlunparse
+from __future__ import print_function, unicode_literals
import copy
import locale
import logging
-import functools
import os
import re
import sys
import pytz
+import six
+from six.moves.urllib.parse import urlparse, urlunparse
+
from pelican import signals
from pelican.settings import DEFAULT_CONFIG
-from pelican.utils import (slugify, truncate_html_words, memoized, strftime,
- python_2_unicode_compatible, deprecated_attribute,
- path_to_url, posixize_path, set_date_tzinfo, SafeDatetime)
+from pelican.utils import (SafeDatetime, deprecated_attribute, memoized,
+ path_to_url, posixize_path,
+ python_2_unicode_compatible, set_date_tzinfo,
+ slugify, strftime, truncate_html_words)
# Import these so that they're avalaible when you import from pelican.contents.
from pelican.urlwrappers import (URLWrapper, Author, Category, Tag) # NOQA
@@ -66,7 +67,7 @@ class Content(object):
# also keep track of the metadata attributes available
self.metadata = local_metadata
- #default template if it's not defined in page
+ # default template if it's not defined in page
self.template = self._get_template()
# First, read the authors from "authors", if not, fallback to "author"
@@ -94,13 +95,16 @@ class Content(object):
# create the slug if not existing, generate slug according to
# setting of SLUG_ATTRIBUTE
if not hasattr(self, 'slug'):
- if settings['SLUGIFY_SOURCE'] == 'title' and hasattr(self, 'title'):
+ if (settings['SLUGIFY_SOURCE'] == 'title' and
+ hasattr(self, 'title')):
self.slug = slugify(self.title,
- settings.get('SLUG_SUBSTITUTIONS', ()))
- elif settings['SLUGIFY_SOURCE'] == 'basename' and source_path != None:
- basename = os.path.basename(os.path.splitext(source_path)[0])
- self.slug = slugify(basename,
- settings.get('SLUG_SUBSTITUTIONS', ()))
+ settings.get('SLUG_SUBSTITUTIONS', ()))
+ elif (settings['SLUGIFY_SOURCE'] == 'basename' and
+ source_path is not None):
+ basename = os.path.basename(
+ os.path.splitext(source_path)[0])
+ self.slug = slugify(
+ basename, settings.get('SLUG_SUBSTITUTIONS', ()))
self.source_path = source_path
@@ -233,7 +237,8 @@ class Content(object):
if isinstance(linked_content, Static):
linked_content.attach_to(self)
else:
- logger.warning("%s used {attach} link syntax on a "
+ logger.warning(
+ "%s used {attach} link syntax on a "
"non-static file. Use {filename} instead.",
self.get_relative_source_path())
origin = '/'.join((siteurl, linked_content.url))
@@ -241,7 +246,7 @@ class Content(object):
else:
logger.warning(
"Unable to find `%s`, skipping url replacement.",
- value.geturl(), extra = {
+ value.geturl(), extra={
'limit_msg': ("Other resources were not found "
"and their urls not replaced")})
elif what == 'category':
@@ -250,9 +255,9 @@ class Content(object):
origin = '/'.join((siteurl, Tag(path, self.settings).url))
else:
logger.warning(
- "Replacement Indicator '%s' not recognized, "
- "skipping replacement",
- what)
+ "Replacement Indicator '%s' not recognized, "
+ "skipping replacement",
+ what)
# keep all other parts, such as query, fragment, etc.
parts = list(value)
@@ -337,7 +342,9 @@ class Content(object):
return posixize_path(
os.path.relpath(
- os.path.abspath(os.path.join(self.settings['PATH'], source_path)),
+ os.path.abspath(os.path.join(
+ self.settings['PATH'],
+ source_path)),
os.path.abspath(self.settings['PATH'])
))
@@ -402,9 +409,12 @@ class Static(Page):
def attach_to(self, content):
"""Override our output directory with that of the given content object.
"""
- # Determine our file's new output path relative to the linking document.
- # If it currently lives beneath the linking document's source directory,
- # preserve that relationship on output. Otherwise, make it a sibling.
+
+ # Determine our file's new output path relative to the linking
+ # document. If it currently lives beneath the linking
+ # document's source directory, preserve that relationship on output.
+ # Otherwise, make it a sibling.
+
linking_source_dir = os.path.dirname(content.source_path)
tail_path = os.path.relpath(self.source_path, linking_source_dir)
if tail_path.startswith(os.pardir + os.sep):
@@ -420,11 +430,14 @@ class Static(Page):
# 'some/content' with a file named 'index.html'.) Rather than trying
# to figure it out by comparing the linking document's url and save_as
# path, we simply build our new url from our new save_as path.
+
new_url = path_to_url(new_save_as)
def _log_reason(reason):
- logger.warning("The {attach} link in %s cannot relocate %s "
- "because %s. Falling back to {filename} link behavior instead.",
+ logger.warning(
+ "The {attach} link in %s cannot relocate "
+ "%s because %s. Falling back to "
+ "{filename} link behavior instead.",
content.get_relative_source_path(),
self.get_relative_source_path(), reason,
extra={'limit_msg': "More {attach} warnings silenced."})
@@ -452,5 +465,6 @@ def is_valid_content(content, f):
content.check_properties()
return True
except NameError as e:
- logger.error("Skipping %s: could not find information about '%s'", f, e)
+ logger.error(
+ "Skipping %s: could not find information about '%s'", f, e)
return False
diff --git a/pelican/generators.py b/pelican/generators.py
index da651252..ff9a9d7c 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -1,28 +1,28 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
+from __future__ import print_function, unicode_literals
-import os
-import six
-import logging
-import shutil
-import fnmatch
import calendar
-
+import fnmatch
+import logging
+import os
+import shutil
from codecs import open
from collections import defaultdict
from functools import partial
from itertools import chain, groupby
from operator import attrgetter
-from jinja2 import (Environment, FileSystemLoader, PrefixLoader, ChoiceLoader,
- BaseLoader, TemplateNotFound)
+from jinja2 import (BaseLoader, ChoiceLoader, Environment, FileSystemLoader,
+ PrefixLoader, TemplateNotFound)
+import six
+
+from pelican import signals
from pelican.cache import FileStampDataCacher
from pelican.contents import Article, Draft, Page, Static, is_valid_content
from pelican.readers import Readers
-from pelican.utils import (copy, process_translations, mkdir_p, DateFormatter,
- python_2_unicode_compatible, posixize_path)
-from pelican import signals
+from pelican.utils import (DateFormatter, copy, mkdir_p, posixize_path,
+ process_translations, python_2_unicode_compatible)
logger = logging.getLogger(__name__)
@@ -31,6 +31,7 @@ logger = logging.getLogger(__name__)
class PelicanTemplateNotFound(Exception):
pass
+
@python_2_unicode_compatible
class Generator(object):
"""Baseclass generator"""
@@ -90,8 +91,9 @@ class Generator(object):
try:
self._templates[name] = self.env.get_template(name + '.html')
except TemplateNotFound:
- raise PelicanTemplateNotFound('[templates] unable to load %s.html from %s'
- % (name, self._templates_path))
+ raise PelicanTemplateNotFound(
+ '[templates] unable to load {}.html from {}'.format(
+ name, self._templates_path))
return self._templates[name]
def _include_path(self, path, extensions=None):
@@ -105,7 +107,7 @@ class Generator(object):
extensions = tuple(self.readers.extensions)
basename = os.path.basename(path)
- #check IGNORE_FILES
+ # check IGNORE_FILES
ignores = self.settings['IGNORE_FILES']
if any(fnmatch.fnmatch(basename, ignore) for ignore in ignores):
return False
@@ -122,8 +124,9 @@ class Generator(object):
:param extensions: the list of allowed extensions (if False, all
extensions are allowed)
"""
+ # backward compatibility for older generators
if isinstance(paths, six.string_types):
- paths = [paths] # backward compatibility for older generators
+ paths = [paths]
# group the exclude dir names by parent path, for use with os.walk()
exclusions_by_dirpath = {}
@@ -138,7 +141,8 @@ class Generator(object):
root = os.path.join(self.path, path) if path else self.path
if os.path.isdir(root):
- for dirpath, dirs, temp_files in os.walk(root, followlinks=True):
+ for dirpath, dirs, temp_files in os.walk(
+ root, followlinks=True):
drop = []
excl = exclusions_by_dirpath.get(dirpath, ())
for d in dirs:
@@ -178,7 +182,8 @@ class Generator(object):
before this method is called, even if they failed to process.)
The path argument is expected to be relative to self.path.
"""
- return posixize_path(os.path.normpath(path)) in self.context['filenames']
+ return (posixize_path(os.path.normpath(path))
+ in self.context['filenames'])
def _update_context(self, items):
"""Update the context with the given items from the currrent
@@ -211,7 +216,8 @@ class CachingGenerator(Generator, FileStampDataCacher):
readers_cache_name=(cls_name + '-Readers'),
**kwargs)
- cache_this_level = self.settings['CONTENT_CACHING_LAYER'] == 'generator'
+ cache_this_level = \
+ self.settings['CONTENT_CACHING_LAYER'] == 'generator'
caching_policy = cache_this_level and self.settings['CACHE_CONTENT']
load_policy = cache_this_level and self.settings['LOAD_CONTENT_CACHE']
FileStampDataCacher.__init__(self, self.settings, cls_name,
@@ -259,14 +265,14 @@ class ArticlesGenerator(CachingGenerator):
def __init__(self, *args, **kwargs):
"""initialize properties"""
- self.articles = [] # only articles in default language
+ self.articles = [] # only articles in default language
self.translations = []
self.dates = {}
self.tags = defaultdict(list)
self.categories = defaultdict(list)
self.related_posts = []
self.authors = defaultdict(list)
- self.drafts = [] # only drafts in default language
+ self.drafts = [] # only drafts in default language
self.drafts_translations = []
super(ArticlesGenerator, self).__init__(*args, **kwargs)
signals.article_generator_init.send(self)
@@ -282,8 +288,8 @@ class ArticlesGenerator(CachingGenerator):
writer.write_feed(self.articles, self.context,
self.settings['FEED_RSS'], feed_type='rss')
- if (self.settings.get('FEED_ALL_ATOM')
- or self.settings.get('FEED_ALL_RSS')):
+ if (self.settings.get('FEED_ALL_ATOM') or
+ self.settings.get('FEED_ALL_RSS')):
all_articles = list(self.articles)
for article in self.articles:
all_articles.extend(article.translations)
@@ -322,8 +328,8 @@ class ArticlesGenerator(CachingGenerator):
self.settings['AUTHOR_FEED_RSS']
% auth.slug, feed_type='rss')
- if (self.settings.get('TAG_FEED_ATOM')
- or self.settings.get('TAG_FEED_RSS')):
+ if (self.settings.get('TAG_FEED_ATOM') or
+ self.settings.get('TAG_FEED_RSS')):
for tag, arts in self.tags.items():
arts.sort(key=attrgetter('date'), reverse=True)
if self.settings.get('TAG_FEED_ATOM'):
@@ -336,8 +342,8 @@ class ArticlesGenerator(CachingGenerator):
self.settings['TAG_FEED_RSS'] % tag.slug,
feed_type='rss')
- if (self.settings.get('TRANSLATION_FEED_ATOM')
- or self.settings.get('TRANSLATION_FEED_RSS')):
+ if (self.settings.get('TRANSLATION_FEED_ATOM') or
+ self.settings.get('TRANSLATION_FEED_RSS')):
translations_feeds = defaultdict(list)
for article in chain(self.articles, self.translations):
translations_feeds[article.lang].append(article)
@@ -472,9 +478,9 @@ class ArticlesGenerator(CachingGenerator):
"""Generate drafts pages."""
for draft in chain(self.drafts_translations, self.drafts):
write(draft.save_as, self.get_template(draft.template),
- self.context, article=draft, category=draft.category,
- override_output=hasattr(draft, 'override_save_as'),
- blog=True, all_articles=self.articles)
+ self.context, article=draft, category=draft.category,
+ override_output=hasattr(draft, 'override_save_as'),
+ blog=True, all_articles=self.articles)
def generate_pages(self, writer):
"""Generate the pages on the disk"""
@@ -503,7 +509,8 @@ class ArticlesGenerator(CachingGenerator):
exclude=self.settings['ARTICLE_EXCLUDES']):
article_or_draft = self.get_cached_data(f, None)
if article_or_draft is None:
- #TODO needs overhaul, maybe nomad for read_file solution, unified behaviour
+ # TODO needs overhaul, maybe nomad for read_file
+ # solution, unified behaviour
try:
article_or_draft = self.readers.read_file(
base_path=self.path, path=f, content_class=Article,
@@ -513,7 +520,8 @@ class ArticlesGenerator(CachingGenerator):
context_signal=signals.article_generator_context,
context_sender=self)
except Exception as e:
- logger.error('Could not process %s\n%s', f, e,
+ logger.error(
+ 'Could not process %s\n%s', f, e,
exc_info=self.settings.get('DEBUG', False))
self._add_failed_source_path(f)
continue
@@ -535,8 +543,9 @@ class ArticlesGenerator(CachingGenerator):
self.add_source_path(article_or_draft)
all_drafts.append(article_or_draft)
else:
- logger.error("Unknown status '%s' for file %s, skipping it.",
- article_or_draft.status, f)
+ logger.error(
+ "Unknown status '%s' for file %s, skipping it.",
+ article_or_draft.status, f)
self._add_failed_source_path(f)
continue
@@ -544,9 +553,9 @@ class ArticlesGenerator(CachingGenerator):
self.add_source_path(article_or_draft)
-
- self.articles, self.translations = process_translations(all_articles,
- order_by=self.settings['ARTICLE_ORDER_BY'])
+ self.articles, self.translations = process_translations(
+ all_articles,
+ order_by=self.settings['ARTICLE_ORDER_BY'])
self.drafts, self.drafts_translations = \
process_translations(all_drafts)
@@ -615,7 +624,8 @@ class PagesGenerator(CachingGenerator):
context_signal=signals.page_generator_context,
context_sender=self)
except Exception as e:
- logger.error('Could not process %s\n%s', f, e,
+ logger.error(
+ 'Could not process %s\n%s', f, e,
exc_info=self.settings.get('DEBUG', False))
self._add_failed_source_path(f)
continue
@@ -629,8 +639,9 @@ class PagesGenerator(CachingGenerator):
elif page.status.lower() == "hidden":
hidden_pages.append(page)
else:
- logger.error("Unknown status '%s' for file %s, skipping it.",
- page.status, f)
+ logger.error(
+ "Unknown status '%s' for file %s, skipping it.",
+ page.status, f)
self._add_failed_source_path(f)
continue
@@ -638,10 +649,11 @@ class PagesGenerator(CachingGenerator):
self.add_source_path(page)
- self.pages, self.translations = process_translations(all_pages,
- order_by=self.settings['PAGE_ORDER_BY'])
- self.hidden_pages, self.hidden_translations = (
- process_translations(hidden_pages))
+ self.pages, self.translations = process_translations(
+ all_pages,
+ order_by=self.settings['PAGE_ORDER_BY'])
+ self.hidden_pages, self.hidden_translations = \
+ process_translations(hidden_pages)
self._update_context(('pages', 'hidden_pages'))
diff --git a/pelican/log.py b/pelican/log.py
index c83c5810..0f4b795b 100644
--- a/pelican/log.py
+++ b/pelican/log.py
@@ -1,18 +1,18 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
+from __future__ import print_function, unicode_literals
+
+import locale
+import logging
+import os
+import sys
+from collections import Mapping, defaultdict
+
+import six
__all__ = [
'init'
]
-import os
-import sys
-import logging
-import locale
-
-from collections import defaultdict, Mapping
-
-import six
class BaseFormatter(logging.Formatter):
def __init__(self, fmt=None, datefmt=None):
@@ -20,7 +20,8 @@ class BaseFormatter(logging.Formatter):
super(BaseFormatter, self).__init__(fmt=FORMAT, datefmt=datefmt)
def format(self, record):
- record.__dict__['customlevelname'] = self._get_levelname(record.levelname)
+ customlevel = self._get_levelname(record.levelname)
+ record.__dict__['customlevelname'] = customlevel
# format multiline messages 'nicely' to make it clear they are together
record.msg = record.msg.replace('\n', '\n | ')
return super(BaseFormatter, self).format(record)
@@ -132,13 +133,13 @@ class SafeLogger(logging.Logger):
def _log(self, level, msg, args, exc_info=None, extra=None):
# if the only argument is a Mapping, Logger uses that for formatting
# format values for that case
- if args and len(args)==1 and isinstance(args[0], Mapping):
+ if args and len(args) == 1 and isinstance(args[0], Mapping):
args = ({k: self._decode_arg(v) for k, v in args[0].items()},)
# otherwise, format each arg
else:
args = tuple(self._decode_arg(arg) for arg in args)
- super(SafeLogger, self)._log(level, msg, args,
- exc_info=exc_info, extra=extra)
+ super(SafeLogger, self)._log(
+ level, msg, args, exc_info=exc_info, extra=extra)
def _decode_arg(self, arg):
'''
@@ -175,8 +176,7 @@ def init(level=None, handler=logging.StreamHandler()):
logger = logging.getLogger()
- if (os.isatty(sys.stdout.fileno())
- and not sys.platform.startswith('win')):
+ if os.isatty(sys.stdout.fileno()) and not sys.platform.startswith('win'):
fmt = ANSIFormatter()
else:
fmt = TextFormatter()
diff --git a/pelican/paginator.py b/pelican/paginator.py
index 0189ec91..9aca550b 100644
--- a/pelican/paginator.py
+++ b/pelican/paginator.py
@@ -1,18 +1,15 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
-import six
+from __future__ import print_function, unicode_literals
-# From django.core.paginator
-from collections import namedtuple
import functools
import logging
import os
-
+from collections import namedtuple
from math import ceil
+import six
+
logger = logging.getLogger(__name__)
-
-
PaginationRule = namedtuple(
'PaginationRule',
'min_page URL SAVE_AS',
@@ -143,7 +140,7 @@ class Page(object):
'settings': self.settings,
'base_name': os.path.dirname(self.name),
'number_sep': '/',
- 'extension': self.extension,
+ 'extension': self.extension,
}
if self.number == 1:
diff --git a/pelican/readers.py b/pelican/readers.py
index c1c8dbfa..bc4515e7 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
+from __future__ import print_function, unicode_literals
import logging
import os
@@ -9,24 +9,50 @@ import docutils
import docutils.core
import docutils.io
from docutils.writers.html4css1 import HTMLTranslator
-import six
-# import the directives to have pygments support
+import six
+from six.moves.html_parser import HTMLParser
+
from pelican import rstdirectives # NOQA
+from pelican import signals
+from pelican.cache import FileStampDataCacher
+from pelican.contents import Author, Category, Page, Tag
+from pelican.utils import SafeDatetime, get_date, pelican_open, posixize_path
+
try:
from markdown import Markdown
except ImportError:
Markdown = False # NOQA
+
try:
from html import escape
except ImportError:
from cgi import escape
-from six.moves.html_parser import HTMLParser
-from pelican import signals
-from pelican.cache import FileStampDataCacher
-from pelican.contents import Page, Category, Tag, Author
-from pelican.utils import get_date, pelican_open, SafeDatetime, posixize_path
+# Metadata processors have no way to discard an unwanted value, so we have
+# them return this value instead to signal that it should be discarded later.
+# This means that _filter_discardable_metadata() must be called on processed
+# metadata dicts before use, to remove the items with the special value.
+_DISCARD = object()
+METADATA_PROCESSORS = {
+ 'tags': lambda x, y: ([
+ Tag(tag, y)
+ for tag in ensure_metadata_list(x)
+ ] or _DISCARD),
+ 'date': lambda x, y: get_date(x.replace('_', ' ')),
+ 'modified': lambda x, y: get_date(x),
+ 'status': lambda x, y: x.strip() or _DISCARD,
+ 'category': lambda x, y: _process_if_nonempty(Category, x, y),
+ 'author': lambda x, y: _process_if_nonempty(Author, x, y),
+ 'authors': lambda x, y: ([
+ Author(author, y)
+ for author in ensure_metadata_list(x)
+ ] or _DISCARD),
+ 'slug': lambda x, y: x.strip() or _DISCARD,
+}
+
+logger = logging.getLogger(__name__)
+
def ensure_metadata_list(text):
"""Canonicalize the format of a list of authors or tags. This works
@@ -49,13 +75,6 @@ def ensure_metadata_list(text):
return [v for v in (w.strip() for w in text) if v]
-# Metadata processors have no way to discard an unwanted value, so we have
-# them return this value instead to signal that it should be discarded later.
-# This means that _filter_discardable_metadata() must be called on processed
-# metadata dicts before use, to remove the items with the special value.
-_DISCARD = object()
-
-
def _process_if_nonempty(processor, name, settings):
"""Removes extra whitespace from name and applies a metadata processor.
If name is empty or all whitespace, returns _DISCARD instead.
@@ -64,28 +83,11 @@ def _process_if_nonempty(processor, name, settings):
return processor(name, settings) if name else _DISCARD
-METADATA_PROCESSORS = {
- 'tags': lambda x, y: ([Tag(tag, y) for tag in ensure_metadata_list(x)]
- or _DISCARD),
- 'date': lambda x, y: get_date(x.replace('_', ' ')),
- 'modified': lambda x, y: get_date(x),
- 'status': lambda x, y: x.strip() or _DISCARD,
- 'category': lambda x, y: _process_if_nonempty(Category, x, y),
- 'author': lambda x, y: _process_if_nonempty(Author, x, y),
- 'authors': lambda x, y: ([Author(author, y)
- for author in ensure_metadata_list(x)]
- or _DISCARD),
- 'slug': lambda x, y: x.strip() or _DISCARD,
-}
-
-
def _filter_discardable_metadata(metadata):
"""Return a copy of a dict, minus any items marked as discardable."""
return {name: val for name, val in metadata.items() if val is not _DISCARD}
-logger = logging.getLogger(__name__)
-
class BaseReader(object):
"""Base class to read files.
@@ -267,8 +269,10 @@ class MarkdownReader(BaseReader):
output[name] = self.process_metadata(name, summary)
elif name in METADATA_PROCESSORS:
if len(value) > 1:
- logger.warning('Duplicate definition of `%s` '
- 'for %s. Using first one.', name, self._source_path)
+ logger.warning(
+ 'Duplicate definition of `%s` '
+ 'for %s. Using first one.',
+ name, self._source_path)
output[name] = self.process_metadata(name, value[0])
elif len(value) > 1:
# handle list metadata as list of string
@@ -380,7 +384,8 @@ class HTMLReader(BaseReader):
def _handle_meta_tag(self, attrs):
name = self._attr_value(attrs, 'name')
if name is None:
- attr_serialized = ', '.join(['{}="{}"'.format(k, v) for k, v in attrs])
+ attr_list = ['{}="{}"'.format(k, v) for k, v in attrs]
+ attr_serialized = ', '.join(attr_list)
logger.warning("Meta tag in file %s does not have a 'name' "
"attribute, skipping. Attributes: %s",
self._filename, attr_serialized)
@@ -394,9 +399,9 @@ class HTMLReader(BaseReader):
"Meta tag attribute 'contents' used in file %s, should"
" be changed to 'content'",
self._filename,
- extra={'limit_msg': ("Other files have meta tag "
- "attribute 'contents' that should "
- "be changed to 'content'")})
+ extra={'limit_msg': "Other files have meta tag "
+ "attribute 'contents' that should "
+ "be changed to 'content'"})
if name == 'keywords':
name = 'tags'
@@ -474,7 +479,8 @@ class Readers(FileStampDataCacher):
path = os.path.abspath(os.path.join(base_path, path))
source_path = posixize_path(os.path.relpath(path, base_path))
- logger.debug('Read file %s -> %s',
+ logger.debug(
+ 'Read file %s -> %s',
source_path, content_class.__name__)
if not fmt:
@@ -486,7 +492,8 @@ class Readers(FileStampDataCacher):
'Pelican does not know how to parse %s', path)
if preread_signal:
- logger.debug('Signal %s.send(%s)',
+ logger.debug(
+ 'Signal %s.send(%s)',
preread_signal.name, preread_sender)
preread_signal.send(preread_sender)
@@ -527,7 +534,9 @@ class Readers(FileStampDataCacher):
def typogrify_wrapper(text):
"""Ensures ignore_tags feature is backward compatible"""
try:
- return typogrify(text, self.settings['TYPOGRIFY_IGNORE_TAGS'])
+ return typogrify(
+ text,
+ self.settings['TYPOGRIFY_IGNORE_TAGS'])
except TypeError:
return typogrify(text)
@@ -539,8 +548,10 @@ class Readers(FileStampDataCacher):
metadata['summary'] = typogrify_wrapper(metadata['summary'])
if context_signal:
- logger.debug('Signal %s.send(%s, )',
- context_signal.name, context_sender)
+ logger.debug(
+ 'Signal %s.send(%s, )',
+ context_signal.name,
+ context_sender)
context_signal.send(context_sender, metadata=metadata)
return content_class(content=content, metadata=metadata,
@@ -591,7 +602,8 @@ def default_metadata(settings=None, process=None):
if process:
value = process('category', value)
metadata['category'] = value
- if settings.get('DEFAULT_DATE', None) and settings['DEFAULT_DATE'] != 'fs':
+ if settings.get('DEFAULT_DATE', None) and \
+ settings['DEFAULT_DATE'] != 'fs':
metadata['date'] = SafeDatetime(*settings['DEFAULT_DATE'])
return metadata
diff --git a/pelican/rstdirectives.py b/pelican/rstdirectives.py
index 1c25cc42..b52785dd 100644
--- a/pelican/rstdirectives.py
+++ b/pelican/rstdirectives.py
@@ -1,13 +1,17 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
+from __future__ import print_function, unicode_literals
+
+import re
from docutils import nodes, utils
-from docutils.parsers.rst import directives, roles, Directive
-from pygments.formatters import HtmlFormatter
+from docutils.parsers.rst import Directive, directives, roles
+
from pygments import highlight
-from pygments.lexers import get_lexer_by_name, TextLexer
-import re
+from pygments.formatters import HtmlFormatter
+from pygments.lexers import TextLexer, get_lexer_by_name
+
import six
+
import pelican.settings as pys
diff --git a/pelican/server.py b/pelican/server.py
index f58ac085..9074135b 100644
--- a/pelican/server.py
+++ b/pelican/server.py
@@ -1,16 +1,18 @@
-from __future__ import print_function
+# -*- coding: utf-8 -*-
+from __future__ import print_function, unicode_literals
+
+import logging
import os
import sys
-import logging
-
-from six.moves import SimpleHTTPServer as srvmod
-from six.moves import socketserver
try:
from magic import from_file as magic_from_file
except ImportError:
magic_from_file = None
+from six.moves import SimpleHTTPServer as srvmod
+from six.moves import socketserver
+
class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler):
SUFFIXES = ['', '.html', '/index.html']
@@ -54,12 +56,12 @@ if __name__ == '__main__':
socketserver.TCPServer.allow_reuse_address = True
try:
- httpd = socketserver.TCPServer((SERVER, PORT), ComplexHTTPRequestHandler)
+ httpd = socketserver.TCPServer(
+ (SERVER, PORT), ComplexHTTPRequestHandler)
except OSError as e:
logging.error("Could not listen on port %s, server %s.", PORT, SERVER)
sys.exit(getattr(e, 'exitcode', 1))
-
logging.info("Serving at port %s, server %s.", PORT, SERVER)
try:
httpd.serve_forever()
diff --git a/pelican/settings.py b/pelican/settings.py
index c1a902cd..4d75333a 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -1,31 +1,32 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
-import six
+from __future__ import print_function, unicode_literals
import copy
import inspect
-import os
import locale
import logging
+import os
+from os.path import isabs
+from posixpath import join as posix_join
+
+import six
+
+from pelican.log import LimitFilter
try:
# SourceFileLoader is the recommended way in 3.3+
from importlib.machinery import SourceFileLoader
- load_source = lambda name, path: SourceFileLoader(name, path).load_module()
+
+ def load_source(name, path):
+ return SourceFileLoader(name, path).load_module()
except ImportError:
# but it does not exist in 3.2-, so fall back to imp
import imp
load_source = imp.load_source
-from os.path import isabs
-from pelican.utils import posix_join
-
-from pelican.log import LimitFilter
-
logger = logging.getLogger(__name__)
-
DEFAULT_THEME = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'themes', 'notmyidea')
DEFAULT_CONFIG = {
@@ -131,7 +132,7 @@ DEFAULT_CONFIG = {
'LOAD_CONTENT_CACHE': False,
'WRITE_SELECTED': [],
'FORMATTED_FIELDS': ['summary'],
- }
+}
PYGMENTS_RST_OPTIONS = None
@@ -158,8 +159,20 @@ def read_settings(path=None, override=None):
"has been deprecated (should be a list)")
local_settings['PLUGIN_PATHS'] = [local_settings['PLUGIN_PATHS']]
elif local_settings['PLUGIN_PATHS'] is not None:
- local_settings['PLUGIN_PATHS'] = [os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(path), pluginpath)))
- if not isabs(pluginpath) else pluginpath for pluginpath in local_settings['PLUGIN_PATHS']]
+ def getabs(path, pluginpath):
+ if isabs(pluginpath):
+ return pluginpath
+ else:
+ path_dirname = os.path.dirname(path)
+ path_joined = os.path.join(path_dirname, pluginpath)
+ path_normed = os.path.normpath(path_joined)
+ path_absolute = os.path.abspath(path_normed)
+ return path_absolute
+
+ pluginpath_list = [getabs(path, pluginpath)
+ for pluginpath
+ in local_settings['PLUGIN_PATHS']]
+ local_settings['PLUGIN_PATHS'] = pluginpath_list
else:
local_settings = copy.deepcopy(DEFAULT_CONFIG)
@@ -199,13 +212,13 @@ def configure_settings(settings):
settings.
Also, specify the log messages to be ignored.
"""
- if not 'PATH' in settings or not os.path.isdir(settings['PATH']):
+ if 'PATH' not in settings or not os.path.isdir(settings['PATH']):
raise Exception('You need to specify a path containing the content'
' (see pelican --help for more information)')
# specify the log messages to be ignored
- LimitFilter._ignore.update(set(settings.get('LOG_FILTER',
- DEFAULT_CONFIG['LOG_FILTER'])))
+ log_filter = settings.get('LOG_FILTER', DEFAULT_CONFIG['LOG_FILTER'])
+ LimitFilter._ignore.update(set(log_filter))
# lookup the theme in "pelican/themes" if the given one doesn't exist
if not os.path.isdir(settings['THEME']):
@@ -223,19 +236,15 @@ def configure_settings(settings):
settings['WRITE_SELECTED'] = [
os.path.abspath(path) for path in
settings.get('WRITE_SELECTED', DEFAULT_CONFIG['WRITE_SELECTED'])
- ]
+ ]
# standardize strings to lowercase strings
- for key in [
- 'DEFAULT_LANG',
- ]:
+ for key in ['DEFAULT_LANG']:
if key in settings:
settings[key] = settings[key].lower()
# standardize strings to lists
- for key in [
- 'LOCALE',
- ]:
+ for key in ['LOCALE']:
if key in settings and isinstance(settings[key], six.string_types):
settings[key] = [settings[key]]
@@ -243,12 +252,13 @@ def configure_settings(settings):
for key, types in [
('OUTPUT_SOURCES_EXTENSION', six.string_types),
('FILENAME_METADATA', six.string_types),
- ]:
+ ]:
if key in settings and not isinstance(settings[key], types):
value = settings.pop(key)
- logger.warn('Detected misconfigured %s (%s), '
- 'falling back to the default (%s)',
- key, value, DEFAULT_CONFIG[key])
+ logger.warn(
+ 'Detected misconfigured %s (%s), '
+ 'falling back to the default (%s)',
+ key, value, DEFAULT_CONFIG[key])
# try to set the different locales, fallback on the default.
locales = settings.get('LOCALE', DEFAULT_CONFIG['LOCALE'])
@@ -270,16 +280,16 @@ def configure_settings(settings):
logger.warning("Removed extraneous trailing slash from SITEURL.")
# If SITEURL is defined but FEED_DOMAIN isn't,
# set FEED_DOMAIN to SITEURL
- if not 'FEED_DOMAIN' in settings:
+ if 'FEED_DOMAIN' not in settings:
settings['FEED_DOMAIN'] = settings['SITEURL']
# check content caching layer and warn of incompatibilities
- if (settings.get('CACHE_CONTENT', False) and
- settings.get('CONTENT_CACHING_LAYER', '') == 'generator' and
- settings.get('WITH_FUTURE_DATES', DEFAULT_CONFIG['WITH_FUTURE_DATES'])):
- logger.warning('WITH_FUTURE_DATES conflicts with '
- "CONTENT_CACHING_LAYER set to 'generator', "
- "use 'reader' layer instead")
+ if settings.get('CACHE_CONTENT', False) and \
+ settings.get('CONTENT_CACHING_LAYER', '') == 'generator' and \
+ settings.get('WITH_FUTURE_DATES', False):
+ logger.warning(
+ "WITH_FUTURE_DATES conflicts with CONTENT_CACHING_LAYER "
+ "set to 'generator', use 'reader' layer instead")
# Warn if feeds are generated with both SITEURL & FEED_DOMAIN undefined
feed_keys = [
@@ -296,7 +306,7 @@ def configure_settings(settings):
logger.warning('Feeds generated without SITEURL set properly may'
' not be valid')
- if not 'TIMEZONE' in settings:
+ if 'TIMEZONE' not in settings:
logger.warning(
'No timezone information specified in the settings. Assuming'
' your timezone is UTC for feed generation. Check '
@@ -321,7 +331,8 @@ def configure_settings(settings):
old_key = key + '_DIR'
new_key = key + '_PATHS'
if old_key in settings:
- logger.warning('Deprecated setting %s, moving it to %s list',
+ logger.warning(
+ 'Deprecated setting %s, moving it to %s list',
old_key, new_key)
settings[new_key] = [settings[old_key]] # also make a list
del settings[old_key]
@@ -365,8 +376,9 @@ def configure_settings(settings):
for old, new, doc in [
('LESS_GENERATOR', 'the Webassets plugin', None),
('FILES_TO_COPY', 'STATIC_PATHS and EXTRA_PATH_METADATA',
- 'https://github.com/getpelican/pelican/blob/master/docs/settings.rst#path-metadata'),
- ]:
+ 'https://github.com/getpelican/pelican/'
+ 'blob/master/docs/settings.rst#path-metadata'),
+ ]:
if old in settings:
message = 'The {} setting has been removed in favor of {}'.format(
old, new)
diff --git a/pelican/signals.py b/pelican/signals.py
index 65c98df7..aeeea9f6 100644
--- a/pelican/signals.py
+++ b/pelican/signals.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
+from __future__ import print_function, unicode_literals
+
from blinker import signal
# Run-level signals:
diff --git a/pelican/tests/default_conf.py b/pelican/tests/default_conf.py
index f38ef804..77c2b947 100644
--- a/pelican/tests/default_conf.py
+++ b/pelican/tests/default_conf.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
+from __future__ import print_function, unicode_literals
AUTHOR = 'Alexis Métaireau'
SITENAME = "Alexis' log"
SITEURL = 'http://blog.notmyidea.org'
@@ -31,17 +31,16 @@ DEFAULT_METADATA = {'yeah': 'it is'}
# path-specific metadata
EXTRA_PATH_METADATA = {
'extra/robots.txt': {'path': 'robots.txt'},
- }
+}
# static paths will be copied without parsing their contents
STATIC_PATHS = [
'pictures',
'extra/robots.txt',
- ]
+]
FORMATTED_FIELDS = ['summary', 'custom_formatted_field']
# foobar will not be used, because it's not in caps. All configuration keys
# have to be in caps
foobar = "barbaz"
-
diff --git a/pelican/tests/support.py b/pelican/tests/support.py
index 151fa3b6..3c2a203f 100644
--- a/pelican/tests/support.py
+++ b/pelican/tests/support.py
@@ -1,25 +1,26 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
-__all__ = ['get_article', 'unittest', ]
+from __future__ import print_function, unicode_literals
+import locale
+import logging
import os
import re
import subprocess
import sys
-from six import StringIO
-import logging
-from logging.handlers import BufferingHandler
import unittest
-import locale
-
-from functools import wraps
from contextlib import contextmanager
-from tempfile import mkdtemp
+from functools import wraps
+from logging.handlers import BufferingHandler
from shutil import rmtree
+from tempfile import mkdtemp
+
+from six import StringIO
from pelican.contents import Article
from pelican.settings import DEFAULT_CONFIG
+__all__ = ['get_article', 'unittest', ]
+
@contextmanager
def temporary_folder():
@@ -167,7 +168,7 @@ def get_settings(**kwargs):
Set keyword arguments to override specific settings.
"""
settings = DEFAULT_CONFIG.copy()
- for key,value in kwargs.items():
+ for key, value in kwargs.items():
settings[key] = value
return settings
@@ -179,10 +180,13 @@ class LogCountHandler(BufferingHandler):
logging.handlers.BufferingHandler.__init__(self, capacity)
def count_logs(self, msg=None, level=None):
- return len([l for l in self.buffer
- if (msg is None or re.match(msg, l.getMessage()))
- and (level is None or l.levelno == level)
- ])
+ return len([
+ l
+ for l
+ in self.buffer
+ if (msg is None or re.match(msg, l.getMessage())) and
+ (level is None or l.levelno == level)
+ ])
class LoggedTestCase(unittest.TestCase):
diff --git a/pelican/tests/test_cache.py b/pelican/tests/test_cache.py
index 8a20c36b..006e421b 100644
--- a/pelican/tests/test_cache.py
+++ b/pelican/tests/test_cache.py
@@ -1,7 +1,14 @@
+# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
-from codecs import open
+
+from shutil import rmtree
+from tempfile import mkdtemp
+
+from pelican.generators import ArticlesGenerator, PagesGenerator
+from pelican.tests.support import get_settings, unittest
+
try:
from unittest.mock import MagicMock
except ImportError:
@@ -10,12 +17,6 @@ except ImportError:
except ImportError:
MagicMock = False
-from shutil import rmtree
-from tempfile import mkdtemp
-
-from pelican.generators import ArticlesGenerator, PagesGenerator
-from pelican.tests.support import unittest, get_settings
-
CUR_DIR = os.path.dirname(__file__)
CONTENT_DIR = os.path.join(CUR_DIR, 'content')
@@ -35,7 +36,6 @@ class TestCache(unittest.TestCase):
settings['CACHE_PATH'] = self.temp_cache
return settings
-
@unittest.skipUnless(MagicMock, 'Needs Mock module')
def test_article_object_caching(self):
"""Test Article objects caching at the generator level"""
@@ -44,7 +44,6 @@ class TestCache(unittest.TestCase):
settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['READERS'] = {'asc': None}
-
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
@@ -108,7 +107,9 @@ class TestCache(unittest.TestCase):
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock()
generator.generate_context()
- self.assertEqual(generator.readers.read_file.call_count, orig_call_count)
+ self.assertEqual(
+ generator.readers.read_file.call_count,
+ orig_call_count)
@unittest.skipUnless(MagicMock, 'Needs Mock module')
def test_page_object_caching(self):
@@ -181,5 +182,6 @@ class TestCache(unittest.TestCase):
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock()
generator.generate_context()
- self.assertEqual(generator.readers.read_file.call_count, orig_call_count)
-
+ self.assertEqual(
+ generator.readers.read_file.call_count,
+ orig_call_count)
diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py
index 145a53b6..a3664383 100644
--- a/pelican/tests/test_contents.py
+++ b/pelican/tests/test_contents.py
@@ -1,20 +1,21 @@
-from __future__ import unicode_literals, absolute_import
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, unicode_literals
-import logging
import locale
+import logging
import os.path
-import six
-
-from jinja2.utils import generate_lorem_ipsum
+from posixpath import join as posix_join
from sys import platform
-from pelican.contents import (Page, Article, Static, URLWrapper,
- Author, Category)
+from jinja2.utils import generate_lorem_ipsum
+
+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, mute, unittest, get_settings
-from pelican.utils import (path_to_url, truncate_html_words, SafeDatetime,
- posix_join)
+from pelican.tests.support import LoggedTestCase, get_settings, unittest
+from pelican.utils import SafeDatetime, path_to_url, truncate_html_words
# generate one paragraph, enclosed with
@@ -49,7 +50,7 @@ class TestPage(unittest.TestCase):
# them to initialise object's attributes.
metadata = {'foo': 'bar', 'foobar': 'baz', 'title': 'foobar', }
page = Page(TEST_CONTENT, metadata=metadata,
- context={'localsiteurl': ''})
+ context={'localsiteurl': ''})
for key, value in metadata.items():
self.assertTrue(hasattr(page, key))
self.assertEqual(value, getattr(page, key))
@@ -139,14 +140,9 @@ class TestPage(unittest.TestCase):
page = Page(**page_kwargs)
# page.locale_date is a unicode string in both python2 and python3
- dt_date = dt.strftime(DEFAULT_CONFIG['DEFAULT_DATE_FORMAT'])
- # dt_date is a byte string in python2, and a unicode string in python3
- # Let's make sure it is a unicode string (relies on python 3.3 supporting the u prefix)
- if type(dt_date) != type(u''):
- # python2:
- dt_date = unicode(dt_date, 'utf8')
+ dt_date = dt.strftime(DEFAULT_CONFIG['DEFAULT_DATE_FORMAT'])
- self.assertEqual(page.locale_date, dt_date )
+ self.assertEqual(page.locale_date, dt_date)
page_kwargs['settings'] = get_settings()
# I doubt this can work on all platforms ...
@@ -307,10 +303,14 @@ class TestPage(unittest.TestCase):
args['settings'] = get_settings()
args['source_path'] = 'content'
args['context']['filenames'] = {
- 'images/poster.jpg': type(cls_name, (object,), {'url': 'images/poster.jpg'}),
- 'assets/video.mp4': type(cls_name, (object,), {'url': 'assets/video.mp4'}),
- 'images/graph.svg': type(cls_name, (object,), {'url': 'images/graph.svg'}),
- 'reference.rst': type(cls_name, (object,), {'url': 'reference.html'}),
+ 'images/poster.jpg': type(
+ cls_name, (object,), {'url': 'images/poster.jpg'}),
+ 'assets/video.mp4': type(
+ cls_name, (object,), {'url': 'assets/video.mp4'}),
+ 'images/graph.svg': type(
+ cls_name, (object,), {'url': 'images/graph.svg'}),
+ 'reference.rst': type(
+ cls_name, (object,), {'url': 'reference.html'}),
}
# video.poster
@@ -325,20 +325,25 @@ class TestPage(unittest.TestCase):
content,
'There is a video with poster '
''
)
# object.data
args['content'] = (
'There is a svg object '
- ''
+ ''
)
content = Page(**args).get_content('http://notmyidea.org')
self.assertEqual(
content,
'There is a svg object '
- ''
+ ''
)
# blockquote.cite
@@ -350,7 +355,9 @@ class TestPage(unittest.TestCase):
self.assertEqual(
content,
'There is a blockquote with cite attribute '
- '
blah blah
'
+ ''
+ 'blah blah'
+ '
'
)
def test_intrasite_link_markdown_spaces(self):
@@ -401,17 +408,19 @@ class TestArticle(TestPage):
def test_slugify_category_author(self):
settings = get_settings()
- settings['SLUG_SUBSTITUTIONS'] = [ ('C#', 'csharp') ]
+ settings['SLUG_SUBSTITUTIONS'] = [('C#', 'csharp')]
settings['ARTICLE_URL'] = '{author}/{category}/{slug}/'
settings['ARTICLE_SAVE_AS'] = '{author}/{category}/{slug}/index.html'
article_kwargs = self._copy_page_kwargs()
article_kwargs['metadata']['author'] = Author("O'Brien", settings)
- article_kwargs['metadata']['category'] = Category('C# & stuff', settings)
+ article_kwargs['metadata']['category'] = Category(
+ 'C# & stuff', settings)
article_kwargs['metadata']['title'] = 'fnord'
article_kwargs['settings'] = settings
article = Article(**article_kwargs)
self.assertEqual(article.url, 'obrien/csharp-stuff/fnord/')
- self.assertEqual(article.save_as, 'obrien/csharp-stuff/fnord/index.html')
+ self.assertEqual(
+ article.save_as, 'obrien/csharp-stuff/fnord/index.html')
class TestStatic(LoggedTestCase):
@@ -426,7 +435,8 @@ class TestStatic(LoggedTestCase):
self.context = self.settings.copy()
self.static = Static(content=None, metadata={}, settings=self.settings,
- source_path=posix_join('dir', 'foo.jpg'), context=self.context)
+ source_path=posix_join('dir', 'foo.jpg'),
+ context=self.context)
self.context['filenames'] = {self.static.source_path: self.static}
@@ -436,8 +446,10 @@ class TestStatic(LoggedTestCase):
def test_attach_to_same_dir(self):
"""attach_to() overrides a static file's save_as and url.
"""
- page = Page(content="fake page",
- metadata={'title': 'fakepage'}, settings=self.settings,
+ page = Page(
+ content="fake page",
+ metadata={'title': 'fakepage'},
+ settings=self.settings,
source_path=os.path.join('dir', 'fakepage.md'))
self.static.attach_to(page)
@@ -449,7 +461,7 @@ class TestStatic(LoggedTestCase):
"""attach_to() preserves dirs inside the linking document dir.
"""
page = Page(content="fake page", metadata={'title': 'fakepage'},
- settings=self.settings, source_path='fakepage.md')
+ settings=self.settings, source_path='fakepage.md')
self.static.attach_to(page)
expected_save_as = os.path.join('outpages', 'dir', 'foo.jpg')
@@ -460,8 +472,8 @@ class TestStatic(LoggedTestCase):
"""attach_to() ignores dirs outside the linking document dir.
"""
page = Page(content="fake page",
- metadata={'title': 'fakepage'}, settings=self.settings,
- source_path=os.path.join('dir', 'otherdir', 'fakepage.md'))
+ metadata={'title': 'fakepage'}, settings=self.settings,
+ source_path=os.path.join('dir', 'otherdir', 'fakepage.md'))
self.static.attach_to(page)
expected_save_as = os.path.join('outpages', 'foo.jpg')
@@ -472,8 +484,8 @@ class TestStatic(LoggedTestCase):
"""attach_to() does nothing when called a second time.
"""
page = Page(content="fake page",
- metadata={'title': 'fakepage'}, settings=self.settings,
- source_path=os.path.join('dir', 'fakepage.md'))
+ metadata={'title': 'fakepage'}, settings=self.settings,
+ source_path=os.path.join('dir', 'fakepage.md'))
self.static.attach_to(page)
@@ -481,8 +493,10 @@ class TestStatic(LoggedTestCase):
otherdir_settings.update(dict(
PAGE_SAVE_AS=os.path.join('otherpages', '{slug}.html'),
PAGE_URL='otherpages/{slug}.html'))
- otherdir_page = Page(content="other page",
- metadata={'title': 'otherpage'}, settings=otherdir_settings,
+ otherdir_page = Page(
+ content="other page",
+ metadata={'title': 'otherpage'},
+ settings=otherdir_settings,
source_path=os.path.join('dir', 'otherpage.md'))
self.static.attach_to(otherdir_page)
@@ -497,8 +511,10 @@ class TestStatic(LoggedTestCase):
"""
original_save_as = self.static.save_as
- page = Page(content="fake page",
- metadata={'title': 'fakepage'}, settings=self.settings,
+ page = Page(
+ content="fake page",
+ metadata={'title': 'fakepage'},
+ settings=self.settings,
source_path=os.path.join('dir', 'fakepage.md'))
self.static.attach_to(page)
@@ -511,8 +527,10 @@ class TestStatic(LoggedTestCase):
"""
original_url = self.static.url
- page = Page(content="fake page",
- metadata={'title': 'fakepage'}, settings=self.settings,
+ page = Page(
+ content="fake page",
+ metadata={'title': 'fakepage'},
+ settings=self.settings,
source_path=os.path.join('dir', 'fakepage.md'))
self.static.attach_to(page)
@@ -523,13 +541,15 @@ class TestStatic(LoggedTestCase):
"""attach_to() does not override paths that were overridden elsewhere.
(For example, by the user with EXTRA_PATH_METADATA)
"""
- customstatic = Static(content=None,
+ customstatic = Static(
+ content=None,
metadata=dict(save_as='customfoo.jpg', url='customfoo.jpg'),
settings=self.settings,
source_path=os.path.join('dir', 'foo.jpg'),
context=self.settings.copy())
- page = Page(content="fake page",
+ page = Page(
+ content="fake page",
metadata={'title': 'fakepage'}, settings=self.settings,
source_path=os.path.join('dir', 'fakepage.md'))
@@ -542,13 +562,16 @@ class TestStatic(LoggedTestCase):
"""{attach} link syntax triggers output path override & url replacement.
"""
html = 'link'
- page = Page(content=html,
- metadata={'title': 'fakepage'}, settings=self.settings,
+ page = Page(
+ content=html,
+ metadata={'title': 'fakepage'},
+ settings=self.settings,
source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
context=self.context)
content = page.get_content('')
- self.assertNotEqual(content, html,
+ self.assertNotEqual(
+ content, html,
"{attach} link syntax did not trigger URL replacement.")
expected_save_as = os.path.join('outpages', 'foo.jpg')
@@ -561,7 +584,8 @@ class TestStatic(LoggedTestCase):
html = 'link'
page = Page(
content=html,
- metadata={'title': 'fakepage'}, settings=self.settings,
+ metadata={'title': 'fakepage'},
+ settings=self.settings,
source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
context=self.context)
content = page.get_content('')
@@ -572,8 +596,10 @@ class TestStatic(LoggedTestCase):
"{category} link syntax triggers url replacement."
html = 'link'
- page = Page(content=html,
- metadata={'title': 'fakepage'}, settings=self.settings,
+ page = Page(
+ content=html,
+ metadata={'title': 'fakepage'},
+ settings=self.settings,
source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
context=self.context)
content = page.get_content('')
@@ -588,11 +614,11 @@ class TestStatic(LoggedTestCase):
metadata={'title': 'fakepage'}, settings=self.settings,
source_path=os.path.join('dir', 'otherdir', 'fakepage.md'),
context=self.context)
- content = page.get_content('')
+ content = page.get_content('')
self.assertEqual(content, html)
self.assertLogCountEqual(
- count=1,
- msg="Replacement Indicator 'unknown' not recognized, "
- "skipping replacement",
- level=logging.WARNING)
+ count=1,
+ msg="Replacement Indicator 'unknown' not recognized, "
+ "skipping replacement",
+ level=logging.WARNING)
diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py
index c424b60f..2cfca04f 100644
--- a/pelican/tests/test_generators.py
+++ b/pelican/tests/test_generators.py
@@ -1,8 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
+import locale
import os
+
from codecs import open
+from shutil import rmtree
+from tempfile import mkdtemp
+
+from pelican.generators import (ArticlesGenerator, Generator, PagesGenerator,
+ StaticGenerator, TemplatePagesGenerator)
+from pelican.tests.support import get_settings, unittest
+from pelican.writers import Writer
+
try:
from unittest.mock import MagicMock
except ImportError:
@@ -10,14 +20,7 @@ except ImportError:
from mock import MagicMock
except ImportError:
MagicMock = False
-from shutil import rmtree
-from tempfile import mkdtemp
-from pelican.generators import (Generator, ArticlesGenerator, PagesGenerator,
- StaticGenerator, TemplatePagesGenerator)
-from pelican.writers import Writer
-from pelican.tests.support import unittest, get_settings
-import locale
CUR_DIR = os.path.dirname(__file__)
CONTENT_DIR = os.path.join(CUR_DIR, 'content')
@@ -35,7 +38,6 @@ class TestGenerator(unittest.TestCase):
def tearDown(self):
locale.setlocale(locale.LC_ALL, self.old_locale)
-
def test_include_path(self):
self.settings['IGNORE_FILES'] = {'ignored1.rst', 'ignored2.rst'}
@@ -52,7 +54,8 @@ class TestGenerator(unittest.TestCase):
"""Test that Generator.get_files() properly excludes directories.
"""
# We use our own Generator so we can give it our own content path
- generator = Generator(context=self.settings.copy(),
+ generator = Generator(
+ context=self.settings.copy(),
settings=self.settings,
path=os.path.join(CUR_DIR, 'nested_content'),
theme=self.settings['THEME'], output_path=None)
@@ -60,34 +63,42 @@ class TestGenerator(unittest.TestCase):
filepaths = generator.get_files(paths=['maindir'])
found_files = {os.path.basename(f) for f in filepaths}
expected_files = {'maindir.md', 'subdir.md'}
- self.assertFalse(expected_files - found_files,
+ self.assertFalse(
+ expected_files - found_files,
"get_files() failed to find one or more files")
# Test string as `paths` argument rather than list
filepaths = generator.get_files(paths='maindir')
found_files = {os.path.basename(f) for f in filepaths}
expected_files = {'maindir.md', 'subdir.md'}
- self.assertFalse(expected_files - found_files,
+ self.assertFalse(
+ expected_files - found_files,
"get_files() failed to find one or more files")
filepaths = generator.get_files(paths=[''], exclude=['maindir'])
found_files = {os.path.basename(f) for f in filepaths}
- self.assertNotIn('maindir.md', found_files,
+ self.assertNotIn(
+ 'maindir.md', found_files,
"get_files() failed to exclude a top-level directory")
- self.assertNotIn('subdir.md', found_files,
+ self.assertNotIn(
+ 'subdir.md', found_files,
"get_files() failed to exclude a subdir of an excluded directory")
- filepaths = generator.get_files(paths=[''],
+ filepaths = generator.get_files(
+ paths=[''],
exclude=[os.path.join('maindir', 'subdir')])
found_files = {os.path.basename(f) for f in filepaths}
- self.assertNotIn('subdir.md', found_files,
+ self.assertNotIn(
+ 'subdir.md', found_files,
"get_files() failed to exclude a subdirectory")
filepaths = generator.get_files(paths=[''], exclude=['subdir'])
found_files = {os.path.basename(f) for f in filepaths}
- self.assertIn('subdir.md', found_files,
+ self.assertIn(
+ 'subdir.md', found_files,
"get_files() excluded a subdirectory by name, ignoring its path")
+
class TestArticlesGenerator(unittest.TestCase):
@classmethod
@@ -96,7 +107,7 @@ class TestArticlesGenerator(unittest.TestCase):
settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['READERS'] = {'asc': None}
- settings['CACHE_CONTENT'] = False # cache not needed for this logic tests
+ settings['CACHE_CONTENT'] = False
cls.generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
@@ -152,25 +163,30 @@ class TestArticlesGenerator(unittest.TestCase):
['Test mkd File', 'published', 'test', 'article'],
['This is a super article !', 'published', 'Yeah', 'article'],
['This is a super article !', 'published', 'Yeah', 'article'],
- ['Article with Nonconformant HTML meta tags', 'published', 'Default', 'article'],
+ ['Article with Nonconformant HTML meta tags', 'published',
+ 'Default', 'article'],
['This is a super article !', 'published', 'yeah', 'article'],
['This is a super article !', 'published', 'yeah', 'article'],
['This is a super article !', 'published', 'yeah', 'article'],
['This is a super article !', 'published', 'Default', 'article'],
['This is an article with category !', 'published', 'yeah',
'article'],
- ['This is an article with multiple authors!', 'published', 'Default', 'article'],
- ['This is an article with multiple authors!', 'published', 'Default', 'article'],
- ['This is an article with multiple authors in list format!', 'published', 'Default', 'article'],
- ['This is an article with multiple authors in lastname, firstname format!', 'published', 'Default', 'article'],
+ ['This is an article with multiple authors!', 'published',
+ 'Default', 'article'],
+ ['This is an article with multiple authors!', 'published',
+ 'Default', 'article'],
+ ['This is an article with multiple authors in list format!',
+ 'published', 'Default', 'article'],
+ ['This is an article with multiple authors in lastname, '
+ 'firstname format!', 'published', 'Default', 'article'],
['This is an article without category !', 'published', 'Default',
- 'article'],
+ 'article'],
['This is an article without category !', 'published',
'TestCategory', 'article'],
['An Article With Code Block To Test Typogrify Ignore',
- 'published', 'Default', 'article'],
- ['マックOS X 10.8でパイソンとVirtualenvをインストールと設定', 'published',
- '指導書', 'article'],
+ 'published', 'Default', 'article'],
+ ['マックOS X 10.8でパイソンとVirtualenvをインストールと設定',
+ 'published', '指導書', 'article'],
]
self.assertEqual(sorted(articles_expected), sorted(self.articles))
@@ -292,7 +308,7 @@ class TestArticlesGenerator(unittest.TestCase):
generator.generate_period_archives(write)
dates = [d for d in generator.dates if d.date.year == 1970]
self.assertEqual(len(dates), 1)
- #among other things it must have at least been called with this
+ # among other things it must have at least been called with this
settings["period"] = (1970,)
write.assert_called_with("posts/1970/index.html",
generator.get_template("period_archives"),
@@ -300,37 +316,42 @@ class TestArticlesGenerator(unittest.TestCase):
blog=True, dates=dates)
del settings["period"]
- settings['MONTH_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/{date:%b}/index.html'
+ settings['MONTH_ARCHIVE_SAVE_AS'] = \
+ 'posts/{date:%Y}/{date:%b}/index.html'
generator = ArticlesGenerator(
context=settings, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
write = MagicMock()
generator.generate_period_archives(write)
- dates = [d for d in generator.dates if d.date.year == 1970
- and d.date.month == 1]
+ dates = [d for d in generator.dates
+ if d.date.year == 1970 and d.date.month == 1]
self.assertEqual(len(dates), 1)
settings["period"] = (1970, "January")
- #among other things it must have at least been called with this
+ # among other things it must have at least been called with this
write.assert_called_with("posts/1970/Jan/index.html",
generator.get_template("period_archives"),
settings,
blog=True, dates=dates)
del settings["period"]
- settings['DAY_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html'
+ settings['DAY_ARCHIVE_SAVE_AS'] = \
+ 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html'
generator = ArticlesGenerator(
context=settings, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
write = MagicMock()
generator.generate_period_archives(write)
- dates = [d for d in generator.dates if d.date.year == 1970
- and d.date.month == 1
- and d.date.day == 1]
+ dates = [
+ d for d in generator.dates if
+ d.date.year == 1970 and
+ d.date.month == 1 and
+ d.date.day == 1
+ ]
self.assertEqual(len(dates), 1)
settings["period"] = (1970, "January", 1)
- #among other things it must have at least been called with this
+ # among other things it must have at least been called with this
write.assert_called_with("posts/1970/Jan/01/index.html",
generator.get_template("period_archives"),
settings,
@@ -347,11 +368,14 @@ class TestArticlesGenerator(unittest.TestCase):
def test_generate_authors(self):
"""Check authors generation."""
authors = [author.name for author, _ in self.generator.authors]
- authors_expected = sorted(['Alexis Métaireau', 'Author, First', 'Author, Second', 'First Author', 'Second Author'])
+ authors_expected = sorted(
+ ['Alexis Métaireau', 'Author, First', 'Author, Second',
+ 'First Author', 'Second Author'])
self.assertEqual(sorted(authors), authors_expected)
# test for slug
authors = [author.slug for author, _ in self.generator.authors]
- authors_expected = ['alexis-metaireau', 'author-first', 'author-second', 'first-author', 'second-author']
+ authors_expected = ['alexis-metaireau', 'author-first',
+ 'author-second', 'first-author', 'second-author']
self.assertEqual(sorted(authors), sorted(authors_expected))
def test_standard_metadata_in_default_metadata(self):
@@ -391,7 +415,6 @@ class TestArticlesGenerator(unittest.TestCase):
settings = get_settings(filenames={})
settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1)
- settings['CACHE_CONTENT'] = False # cache not needed for this logic tests
settings['ARTICLE_ORDER_BY'] = 'title'
generator = ArticlesGenerator(
@@ -420,7 +443,8 @@ class TestArticlesGenerator(unittest.TestCase):
'This is a super article !',
'This is a super article !',
'This is an article with category !',
- 'This is an article with multiple authors in lastname, firstname format!',
+ ('This is an article with multiple authors in lastname, '
+ 'firstname format!'),
'This is an article with multiple authors in list format!',
'This is an article with multiple authors!',
'This is an article with multiple authors!',
@@ -435,7 +459,6 @@ class TestArticlesGenerator(unittest.TestCase):
settings = get_settings(filenames={})
settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1)
- settings['CACHE_CONTENT'] = False # cache not needed for this logic tests
settings['ARTICLE_ORDER_BY'] = 'reversed-title'
generator = ArticlesGenerator(
@@ -561,7 +584,7 @@ class TestPageGenerator(unittest.TestCase):
are generated correctly on pages
"""
settings = get_settings(filenames={})
- settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
+ settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
settings['CACHE_PATH'] = self.temp_cache
settings['DEFAULT_DATE'] = (1970, 1, 1)
@@ -586,7 +609,6 @@ class TestTemplatePagesGenerator(unittest.TestCase):
self.old_locale = locale.setlocale(locale.LC_ALL)
locale.setlocale(locale.LC_ALL, str('C'))
-
def tearDown(self):
rmtree(self.temp_content)
rmtree(self.temp_output)
@@ -632,59 +654,67 @@ class TestStaticGenerator(unittest.TestCase):
def test_static_excludes(self):
"""Test that StaticGenerator respects STATIC_EXCLUDES.
"""
- settings = get_settings(STATIC_EXCLUDES=['subdir'],
- PATH=self.content_path, STATIC_PATHS=[''])
+ settings = get_settings(
+ STATIC_EXCLUDES=['subdir'],
+ PATH=self.content_path,
+ STATIC_PATHS=[''],
+ filenames={})
context = settings.copy()
- context['filenames'] = {}
- StaticGenerator(context=context, settings=settings,
+ StaticGenerator(
+ context=context, settings=settings,
path=settings['PATH'], output_path=None,
theme=settings['THEME']).generate_context()
staticnames = [os.path.basename(c.source_path)
- for c in context['staticfiles']]
+ for c in context['staticfiles']]
- self.assertNotIn('subdir_fake_image.jpg', staticnames,
+ self.assertNotIn(
+ 'subdir_fake_image.jpg', staticnames,
"StaticGenerator processed a file in a STATIC_EXCLUDES directory")
- self.assertIn('fake_image.jpg', staticnames,
+ self.assertIn(
+ 'fake_image.jpg', staticnames,
"StaticGenerator skipped a file that it should have included")
def test_static_exclude_sources(self):
"""Test that StaticGenerator respects STATIC_EXCLUDE_SOURCES.
"""
- # Test STATIC_EXCLUDE_SOURCES=True
- settings = get_settings(STATIC_EXCLUDE_SOURCES=True,
- PATH=self.content_path, PAGE_PATHS=[''], STATIC_PATHS=[''],
- CACHE_CONTENT=False)
+ settings = get_settings(
+ STATIC_EXCLUDE_SOURCES=True,
+ PATH=self.content_path,
+ PAGE_PATHS=[''],
+ STATIC_PATHS=[''],
+ CACHE_CONTENT=False,
+ filenames={})
context = settings.copy()
- context['filenames'] = {}
for generator_class in (PagesGenerator, StaticGenerator):
- generator_class(context=context, settings=settings,
+ generator_class(
+ context=context, settings=settings,
path=settings['PATH'], output_path=None,
theme=settings['THEME']).generate_context()
staticnames = [os.path.basename(c.source_path)
- for c in context['staticfiles']]
+ for c in context['staticfiles']]
- self.assertFalse(any(name.endswith(".md") for name in staticnames),
+ self.assertFalse(
+ any(name.endswith(".md") for name in staticnames),
"STATIC_EXCLUDE_SOURCES=True failed to exclude a markdown file")
- # Test STATIC_EXCLUDE_SOURCES=False
-
settings.update(STATIC_EXCLUDE_SOURCES=False)
context = settings.copy()
context['filenames'] = {}
for generator_class in (PagesGenerator, StaticGenerator):
- generator_class(context=context, settings=settings,
+ generator_class(
+ context=context, settings=settings,
path=settings['PATH'], output_path=None,
theme=settings['THEME']).generate_context()
staticnames = [os.path.basename(c.source_path)
- for c in context['staticfiles']]
+ for c in context['staticfiles']]
- self.assertTrue(any(name.endswith(".md") for name in staticnames),
+ self.assertTrue(
+ any(name.endswith(".md") for name in staticnames),
"STATIC_EXCLUDE_SOURCES=False failed to include a markdown file")
-
diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py
index 4ace5ccc..6af59212 100644
--- a/pelican/tests/test_importer.py
+++ b/pelican/tests/test_importer.py
@@ -1,16 +1,19 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
+# -*- coding: utf-8 -*-
+from __future__ import print_function, unicode_literals
+import locale
import os
import re
-import locale
from codecs import open
-from pelican.tools.pelican_import import wp2fields, fields2pelican, decode_wp_content, build_header, build_markdown_header, get_attachments, download_attachments
-from pelican.tests.support import (unittest, temporary_folder, mute,
- skipIfNoExecutable)
-from pelican.utils import slugify, path_to_file_url
+from pelican.tests.support import (mute, skipIfNoExecutable, temporary_folder,
+ unittest)
+from pelican.tools.pelican_import import (build_header, build_markdown_header,
+ decode_wp_content,
+ download_attachments, fields2pelican,
+ get_attachments, wp2fields)
+from pelican.utils import path_to_file_url, slugify
CUR_DIR = os.path.abspath(os.path.dirname(__file__))
WORDPRESS_XML_SAMPLE = os.path.join(CUR_DIR, 'content', 'wordpressexport.xml')
@@ -32,7 +35,6 @@ except ImportError:
LXML = False
-
@skipIfNoExecutable(['pandoc', '--version'])
@unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module')
class TestWordpressXmlImporter(unittest.TestCase):
@@ -48,17 +50,19 @@ class TestWordpressXmlImporter(unittest.TestCase):
def test_ignore_empty_posts(self):
self.assertTrue(self.posts)
- for title, content, fname, date, author, categ, tags, status, kind, format in self.posts:
- self.assertTrue(title.strip())
+ for (title, content, fname, date, author,
+ categ, tags, status, kind, format) in self.posts:
+ self.assertTrue(title.strip())
def test_recognise_page_kind(self):
""" Check that we recognise pages in wordpress, as opposed to posts """
self.assertTrue(self.posts)
# Collect (title, filename, kind) of non-empty posts recognised as page
pages_data = []
- for title, content, fname, date, author, categ, tags, status, kind, format in self.posts:
- if kind == 'page':
- pages_data.append((title, fname))
+ for (title, content, fname, date, author,
+ categ, tags, status, kind, format) in self.posts:
+ if kind == 'page':
+ pages_data.append((title, fname))
self.assertEqual(2, len(pages_data))
self.assertEqual(('Page', 'contact'), pages_data[0])
self.assertEqual(('Empty Page', 'empty'), pages_data[1])
@@ -67,7 +71,8 @@ class TestWordpressXmlImporter(unittest.TestCase):
silent_f2p = mute(True)(fields2pelican)
test_post = filter(lambda p: p[0].startswith("Empty Page"), self.posts)
with temporary_folder() as temp:
- fname = list(silent_f2p(test_post, 'markdown', temp, dirpage=True))[0]
+ fname = list(silent_f2p(test_post, 'markdown',
+ temp, dirpage=True))[0]
self.assertTrue(fname.endswith('pages%sempty.md' % os.path.sep))
def test_dircat(self):
@@ -75,10 +80,11 @@ class TestWordpressXmlImporter(unittest.TestCase):
test_posts = []
for post in self.posts:
# check post kind
- if len(post[5]) > 0: # Has a category
+ if len(post[5]) > 0: # Has a category
test_posts.append(post)
with temporary_folder() as temp:
- fnames = list(silent_f2p(test_posts, 'markdown', temp, dircat=True))
+ fnames = list(silent_f2p(test_posts, 'markdown',
+ temp, dircat=True))
index = 0
for post in test_posts:
name = post[2]
@@ -92,25 +98,33 @@ class TestWordpressXmlImporter(unittest.TestCase):
def test_unless_custom_post_all_items_should_be_pages_or_posts(self):
self.assertTrue(self.posts)
pages_data = []
- for title, content, fname, date, author, categ, tags, status, kind, format in self.posts:
- if kind == 'page' or kind == 'article':
- pass
- else:
- pages_data.append((title, fname))
+ for (title, content, fname, date, author, categ,
+ tags, status, kind, format) in self.posts:
+ if kind == 'page' or kind == 'article':
+ pass
+ else:
+ pages_data.append((title, fname))
self.assertEqual(0, len(pages_data))
def test_recognise_custom_post_type(self):
self.assertTrue(self.custposts)
cust_data = []
- for title, content, fname, date, author, categ, tags, status, kind, format in self.custposts:
- if kind == 'article' or kind == 'page':
- pass
- else:
- cust_data.append((title, kind))
+ for (title, content, fname, date, author, categ,
+ tags, status, kind, format) in self.custposts:
+ if kind == 'article' or kind == 'page':
+ pass
+ else:
+ cust_data.append((title, kind))
self.assertEqual(3, len(cust_data))
- self.assertEqual(('A custom post in category 4', 'custom1'), cust_data[0])
- self.assertEqual(('A custom post in category 5', 'custom1'), cust_data[1])
- self.assertEqual(('A 2nd custom post type also in category 5', 'custom2'), cust_data[2])
+ self.assertEqual(
+ ('A custom post in category 4', 'custom1'),
+ cust_data[0])
+ self.assertEqual(
+ ('A custom post in category 5', 'custom1'),
+ cust_data[1])
+ self.assertEqual(
+ ('A 2nd custom post type also in category 5', 'custom2'),
+ cust_data[2])
def test_custom_posts_put_in_own_dir(self):
silent_f2p = mute(True)(fields2pelican)
@@ -122,7 +136,8 @@ class TestWordpressXmlImporter(unittest.TestCase):
else:
test_posts.append(post)
with temporary_folder() as temp:
- fnames = list(silent_f2p(test_posts, 'markdown', temp, wp_custpost = True))
+ fnames = list(silent_f2p(test_posts, 'markdown',
+ temp, wp_custpost=True))
index = 0
for post in test_posts:
name = post[2]
@@ -144,7 +159,7 @@ class TestWordpressXmlImporter(unittest.TestCase):
test_posts.append(post)
with temporary_folder() as temp:
fnames = list(silent_f2p(test_posts, 'markdown', temp,
- wp_custpost=True, dircat=True))
+ wp_custpost=True, dircat=True))
index = 0
for post in test_posts:
name = post[2]
@@ -157,7 +172,7 @@ class TestWordpressXmlImporter(unittest.TestCase):
index += 1
def test_wp_custpost_true_dirpage_false(self):
- #pages should only be put in their own directory when dirpage = True
+ # pages should only be put in their own directory when dirpage = True
silent_f2p = mute(True)(fields2pelican)
test_posts = []
for post in self.custposts:
@@ -166,7 +181,7 @@ class TestWordpressXmlImporter(unittest.TestCase):
test_posts.append(post)
with temporary_folder() as temp:
fnames = list(silent_f2p(test_posts, 'markdown', temp,
- wp_custpost=True, dirpage=False))
+ wp_custpost=True, dirpage=False))
index = 0
for post in test_posts:
name = post[2]
@@ -175,7 +190,6 @@ class TestWordpressXmlImporter(unittest.TestCase):
out_name = fnames[index]
self.assertFalse(out_name.endswith(filename))
-
def test_can_toggle_raw_html_code_parsing(self):
def r(f):
with open(f, encoding='utf-8') as infile:
@@ -184,10 +198,12 @@ class TestWordpressXmlImporter(unittest.TestCase):
with temporary_folder() as temp:
- rst_files = (r(f) for f in silent_f2p(self.posts, 'markdown', temp))
+ rst_files = (r(f) for f
+ in silent_f2p(self.posts, 'markdown', temp))
self.assertTrue(any(' This will now have a line number in 'custom' since it's the default in -pelican.conf, it ...
This will now have a line number in 'custom' since it's the default in -pelican.conf, it ...
There are comments.
diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html index fa71085d..b705a552 100644 --- a/pelican/tests/output/custom/category/misc.html +++ b/pelican/tests/output/custom/category/misc.html @@ -103,7 +103,7 @@This will now have a line number in 'custom' since it's the default in -pelican.conf, it ...
There are comments.
diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index 1dab4e7d..b968b7e8 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -59,7 +59,7 @@This will now have a line number in 'custom' since it's the default in -pelican.conf, it ...
There are comments.
diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html index 66575c71..2fea24c3 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html @@ -59,7 +59,7 @@This will now have a line number in 'custom' since it's the default in -pelican.conf, it ...
There are comments.
@@ -135,4 +135,4 @@ pelican.conf, it ... }()); -