From 6c9d158609f10963e83c9bc18e355f9ce2673869 Mon Sep 17 00:00:00 2001 From: Dominique Plante Date: Sat, 18 May 2013 06:33:49 -0700 Subject: [PATCH 0001/1427] makes Pelican-quickstart debuggable in PyCharm --- pelican/tools/pelican_quickstart.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index a6045256..8d697fa3 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -283,3 +283,6 @@ needed by Pelican. print('Error: {0}'.format(e)) print('Done. Your new project is available at %s' % CONF['basedir']) + +if __name__ == "__main__": + main() \ No newline at end of file From 7a3bc410d0757ee4bec22ffaf6920e2f27878fbd Mon Sep 17 00:00:00 2001 From: Dominique Plante Date: Thu, 23 May 2013 21:53:46 -0700 Subject: [PATCH 0002/1427] Add test for the case where we try to read a file with an unhandled extension --- .../content/article_with_metadata.unknownextension | 12 ++++++++++++ pelican/tests/test_readers.py | 11 +++++++++++ 2 files changed, 23 insertions(+) create mode 100644 pelican/tests/content/article_with_metadata.unknownextension diff --git a/pelican/tests/content/article_with_metadata.unknownextension b/pelican/tests/content/article_with_metadata.unknownextension new file mode 100644 index 00000000..d4bac1c0 --- /dev/null +++ b/pelican/tests/content/article_with_metadata.unknownextension @@ -0,0 +1,12 @@ + +This is a super article ! +######################### + +:tags: foo, bar, foobar +:date: 2010-12-02 10:14 +:category: yeah +:author: Alexis Métaireau +:summary: + Multi-line metadata should be supported + as well as **inline markup**. +:custom_field: http://notmyidea.org diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index 47cb70ee..c3c7eaef 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -14,6 +14,17 @@ CONTENT_PATH = os.path.join(CUR_DIR, 'content') def _path(*args): return os.path.join(CONTENT_PATH, *args) +class ReaderTests(unittest.TestCase): + def test_readfile_unknown_extension(self): + f = _path('article_with_metadata.unknownextension') + with self.assertRaises(TypeError) as cm: + readers.read_file(f) + ex = cm.exception + self.assertEqual('Pelican does not know how to parse ' + f, ex.message) + #, setattr, root.c1.c2, 'text', "test") + # self.assertTrue(1 == 0) + # except TypeError: + # self.assertTrue(1 == 1) class RstReaderTest(unittest.TestCase): From 39dd4a025581cfa3b4d6256a3b8f327b26372e1d Mon Sep 17 00:00:00 2001 From: Kyle Machulis Date: Fri, 14 Jun 2013 12:12:19 -0700 Subject: [PATCH 0003/1427] Changed meta tag "contents" attribute to "content", to conform to HTML spec. Fixes #918 --- docs/getting_started.rst | 10 +++++----- pelican/readers.py | 15 ++++++++++++--- pelican/tests/content/article_with_keywords.html | 2 +- pelican/tests/content/article_with_metadata.html | 12 ++++++------ .../article_with_metadata_and_contents.html | 15 +++++++++++++++ .../content/article_with_uppercase_metadata.html | 2 +- pelican/tests/test_readers.py | 15 +++++++++++++++ 7 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 pelican/tests/content/article_with_metadata_and_contents.html diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 383acdc4..1e31f26d 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -265,11 +265,11 @@ interprets the HTML in a very straightforward manner, reading metadata from My super title - - - - - + + + + + This is the content of my super blog post. diff --git a/pelican/readers.py b/pelican/readers.py index bd9f5914..fb2ccfc4 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -5,6 +5,7 @@ import datetime import logging import os import re +import logging try: import docutils import docutils.core @@ -47,6 +48,8 @@ METADATA_PROCESSORS = { 'author': Author, } +logger = logging.getLogger(__name__) + class Reader(object): enabled = True @@ -199,7 +202,7 @@ class HTMLReader(Reader): enabled = True class _HTMLParser(HTMLParser): - def __init__(self, settings): + def __init__(self, settings, filename): HTMLParser.__init__(self) self.body = '' self.metadata = {} @@ -207,6 +210,8 @@ class HTMLReader(Reader): self._data_buffer = '' + self._filename = filename + self._in_top_level = True self._in_head = False self._in_title = False @@ -275,7 +280,11 @@ class HTMLReader(Reader): def _handle_meta_tag(self, attrs): name = self._attr_value(attrs, 'name').lower() - contents = self._attr_value(attrs, 'contents', '') + contents = self._attr_value(attrs, 'content', '') + if not contents: + contents = self._attr_value(attrs, 'contents', '') + if contents: + logger.warning("Meta tag attribute 'contents' used in file %s, should be changed to 'content'", self._filename) if name == 'keywords': name = 'tags' @@ -288,7 +297,7 @@ class HTMLReader(Reader): def read(self, filename): """Parse content and metadata of HTML files""" with pelican_open(filename) as content: - parser = self._HTMLParser(self.settings) + parser = self._HTMLParser(self.settings, filename) parser.feed(content) parser.close() diff --git a/pelican/tests/content/article_with_keywords.html b/pelican/tests/content/article_with_keywords.html index c869f514..0744c754 100644 --- a/pelican/tests/content/article_with_keywords.html +++ b/pelican/tests/content/article_with_keywords.html @@ -1,6 +1,6 @@ This is a super article ! - + diff --git a/pelican/tests/content/article_with_metadata.html b/pelican/tests/content/article_with_metadata.html index b108ac8a..b501ea29 100644 --- a/pelican/tests/content/article_with_metadata.html +++ b/pelican/tests/content/article_with_metadata.html @@ -1,12 +1,12 @@ This is a super article ! - - - - - - + + + + + + Multi-line metadata should be supported diff --git a/pelican/tests/content/article_with_metadata_and_contents.html b/pelican/tests/content/article_with_metadata_and_contents.html new file mode 100644 index 00000000..b108ac8a --- /dev/null +++ b/pelican/tests/content/article_with_metadata_and_contents.html @@ -0,0 +1,15 @@ + + + This is a super article ! + + + + + + + + + Multi-line metadata should be supported + as well as inline markup. + + diff --git a/pelican/tests/content/article_with_uppercase_metadata.html b/pelican/tests/content/article_with_uppercase_metadata.html index 4fe5a9ee..b4cedf39 100644 --- a/pelican/tests/content/article_with_uppercase_metadata.html +++ b/pelican/tests/content/article_with_uppercase_metadata.html @@ -1,6 +1,6 @@ This is a super article ! - + diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index 14d42325..c67b8a1f 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -350,6 +350,21 @@ class HTMLReaderTest(ReaderTest): for key, value in expected.items(): self.assertEqual(value, page.metadata[key], key) + def test_article_with_metadata_and_contents_attrib(self): + page = self.read_file(path='article_with_metadata_and_contents.html') + expected = { + 'category': 'yeah', + 'author': 'Alexis Métaireau', + 'title': 'This is a super article !', + 'summary': 'Summary and stuff', + 'date': datetime.datetime(2010, 12, 2, 10, 14), + 'tags': ['foo', 'bar', 'foobar'], + 'custom_field': 'http://notmyidea.org', + } + for key, value in expected.items(): + self.assertEqual(value, page.metadata[key], key) + + def test_article_with_null_attributes(self): page = self.read_file(path='article_with_null_attributes.html') From 12fd53c27e133ed4e79c2f5a85ea8fb038c23b12 Mon Sep 17 00:00:00 2001 From: bas smit Date: Wed, 26 Jun 2013 13:25:20 +0200 Subject: [PATCH 0004/1427] Add debug target to the template makefile If the DEBUG variable is set (e.g. DEBUG=1 make target) debugging will be enabled by using pelicans -D flag. --- pelican/tools/templates/Makefile.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in index 221568aa..fd553550 100644 --- a/pelican/tools/templates/Makefile.in +++ b/pelican/tools/templates/Makefile.in @@ -20,6 +20,11 @@ S3_BUCKET=$s3_bucket DROPBOX_DIR=$dropbox_dir +DEBUG ?= 0 +ifeq ($(DEBUG), 1) + PELICANOPTS += -D +endif + help: @echo 'Makefile for a pelican Web site ' @echo ' ' From 0d63b4520a302d5fb8e249c1d98c0043c566315b Mon Sep 17 00:00:00 2001 From: bas smit Date: Wed, 26 Jun 2013 13:58:35 +0200 Subject: [PATCH 0005/1427] Add info about debugging to the help output of the makefile --- pelican/tools/templates/Makefile.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in index fd553550..80cf4737 100644 --- a/pelican/tools/templates/Makefile.in +++ b/pelican/tools/templates/Makefile.in @@ -43,7 +43,8 @@ help: @echo ' s3_upload upload the web site via S3 ' @echo ' github upload the web site via gh-pages ' @echo ' ' - + @echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html' + @echo ' ' html: clean $$(OUTPUTDIR)/index.html From 298151237e9f418aabcf4d70333dc3550a519914 Mon Sep 17 00:00:00 2001 From: Andrew Ma Date: Wed, 3 Jul 2013 22:36:52 -0700 Subject: [PATCH 0006/1427] Adding stackoverflow to social icons --- pelican/themes/notmyidea/static/css/main.css | 1 + .../static/images/icons/stackoverflow.png | Bin 0 -> 916 bytes 2 files changed, 1 insertion(+) create mode 100644 pelican/themes/notmyidea/static/images/icons/stackoverflow.png diff --git a/pelican/themes/notmyidea/static/css/main.css b/pelican/themes/notmyidea/static/css/main.css index 7f4ca363..fa0bcf1c 100644 --- a/pelican/themes/notmyidea/static/css/main.css +++ b/pelican/themes/notmyidea/static/css/main.css @@ -326,6 +326,7 @@ img.left, figure.left {float: left; margin: 0 2em 2em 0;} .social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');} .social a[href*='slideshare.net'] {background-image: url('../images/icons/slideshare.png');} .social a[href*='speakerdeck.com'] {background-image: url('../images/icons/speakerdeck.png');} + .social a[href*='stackoverflow.com'] {background-image: url('../images/icons/stackoverflow.png');} .social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');} .social a[href*='vimeo.com'] {background-image: url('../images/icons/vimeo.png');} .social a[href*='youtube.com'] {background-image: url('../images/icons/youtube.png');} diff --git a/pelican/themes/notmyidea/static/images/icons/stackoverflow.png b/pelican/themes/notmyidea/static/images/icons/stackoverflow.png new file mode 100644 index 0000000000000000000000000000000000000000..f5b65e9990dbf423ff652b297f1d0172c8c1cf27 GIT binary patch literal 916 zcmeAS@N?(olHy`uVBq!ia0vp^0w65F1|7sn8f<3~fRJ+vJ~_8MEyP6_>_pwTJhc%)&)Mu9Ed(YG|jmTlZ9 z@KIPeZ^FK^-?9GB|W>{Q7m)EC+@8;a3mYxk}1?tCE&x6kxKjd-nU)D+_Cb1MZtP zf6ioHdF7Ur;v^T%WC`(qYnHY?KFysk()nSxK=;)vJ-w30K5t&s)3ykMV9;LODnQ81ohPJtCw>uw6%3rniLY9y-{G%>&K7poH)Ybf9hcZ zPx7%K&0{68BG<3+JpQ`j393DPNr7J)G`t`kgi;Fv5%&IGQ?s!oY8v6HF>egkKm6gM^6?fU09+hB7{`$2g z!Dr>|-0l5+On2^2_f4p`m-+THD#}N=VM2Fz(!8K!0$xisQhfv_dG-h{GMTvHCdbLN z^XFvvX21P9HE8+fMNXk>uPs~lGBfn^9g#*-%#3?x$4 ze30#20|W~;m8*zxxBvb3Dl3rX#Piv+IUi Date: Fri, 28 Jun 2013 15:09:36 -0700 Subject: [PATCH 0007/1427] Updating unit tests --- pelican/tests/output/basic/theme/css/main.css | 1 + .../basic/theme/images/icons/stackoverflow.png | Bin 0 -> 916 bytes pelican/tests/output/custom/theme/css/main.css | 1 + .../custom/theme/images/icons/stackoverflow.png | Bin 0 -> 916 bytes 4 files changed, 2 insertions(+) create mode 100644 pelican/tests/output/basic/theme/images/icons/stackoverflow.png create mode 100644 pelican/tests/output/custom/theme/images/icons/stackoverflow.png diff --git a/pelican/tests/output/basic/theme/css/main.css b/pelican/tests/output/basic/theme/css/main.css index 7f4ca363..fa0bcf1c 100644 --- a/pelican/tests/output/basic/theme/css/main.css +++ b/pelican/tests/output/basic/theme/css/main.css @@ -326,6 +326,7 @@ img.left, figure.left {float: left; margin: 0 2em 2em 0;} .social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');} .social a[href*='slideshare.net'] {background-image: url('../images/icons/slideshare.png');} .social a[href*='speakerdeck.com'] {background-image: url('../images/icons/speakerdeck.png');} + .social a[href*='stackoverflow.com'] {background-image: url('../images/icons/stackoverflow.png');} .social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');} .social a[href*='vimeo.com'] {background-image: url('../images/icons/vimeo.png');} .social a[href*='youtube.com'] {background-image: url('../images/icons/youtube.png');} diff --git a/pelican/tests/output/basic/theme/images/icons/stackoverflow.png b/pelican/tests/output/basic/theme/images/icons/stackoverflow.png new file mode 100644 index 0000000000000000000000000000000000000000..f5b65e9990dbf423ff652b297f1d0172c8c1cf27 GIT binary patch literal 916 zcmeAS@N?(olHy`uVBq!ia0vp^0w65F1|7sn8f<3~fRJ+vJ~_8MEyP6_>_pwTJhc%)&)Mu9Ed(YG|jmTlZ9 z@KIPeZ^FK^-?9GB|W>{Q7m)EC+@8;a3mYxk}1?tCE&x6kxKjd-nU)D+_Cb1MZtP zf6ioHdF7Ur;v^T%WC`(qYnHY?KFysk()nSxK=;)vJ-w30K5t&s)3ykMV9;LODnQ81ohPJtCw>uw6%3rniLY9y-{G%>&K7poH)Ybf9hcZ zPx7%K&0{68BG<3+JpQ`j393DPNr7J)G`t`kgi;Fv5%&IGQ?s!oY8v6HF>egkKm6gM^6?fU09+hB7{`$2g z!Dr>|-0l5+On2^2_f4p`m-+THD#}N=VM2Fz(!8K!0$xisQhfv_dG-h{GMTvHCdbLN z^XFvvX21P9HE8+fMNXk>uPs~lGBfn^9g#*-%#3?x$4 ze30#20|W~;m8*zxxBvb3Dl3rX#Piv+IUi7sn8f<3~fRJ+vJ~_8MEyP6_>_pwTJhc%)&)Mu9Ed(YG|jmTlZ9 z@KIPeZ^FK^-?9GB|W>{Q7m)EC+@8;a3mYxk}1?tCE&x6kxKjd-nU)D+_Cb1MZtP zf6ioHdF7Ur;v^T%WC`(qYnHY?KFysk()nSxK=;)vJ-w30K5t&s)3ykMV9;LODnQ81ohPJtCw>uw6%3rniLY9y-{G%>&K7poH)Ybf9hcZ zPx7%K&0{68BG<3+JpQ`j393DPNr7J)G`t`kgi;Fv5%&IGQ?s!oY8v6HF>egkKm6gM^6?fU09+hB7{`$2g z!Dr>|-0l5+On2^2_f4p`m-+THD#}N=VM2Fz(!8K!0$xisQhfv_dG-h{GMTvHCdbLN z^XFvvX21P9HE8+fMNXk>uPs~lGBfn^9g#*-%#3?x$4 ze30#20|W~;m8*zxxBvb3Dl3rX#Piv+IUi Date: Fri, 14 Jun 2013 15:54:06 +0100 Subject: [PATCH 0008/1427] Allow text substitutions when generating slugs The `slugify()` function used by Pelican is in general very good at coming up with something both readable and URL-safe. However, there are a few specific cases where it causes conflicts. One that I've run into is using the strings `C++` and `C` as tags, both of which transform to the slug `c`. This commit adds an optional `SLUG_SUBSTITUTIONS` setting which is a list of 2-tuples of substitutions to be carried out case-insensitively just prior to stripping out non-alphanumeric characters. This allows cases like `C++` to be transformed to `CPP` or similar. This can also improve the readability of slugs. --- docs/settings.rst | 4 ++++ pelican/contents.py | 3 ++- pelican/settings.py | 1 + pelican/tests/test_utils.py | 11 +++++++++++ pelican/urlwrappers.py | 11 ++++++----- pelican/utils.py | 8 +++++--- 6 files changed, 29 insertions(+), 9 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 78a0ddf7..61ccc2b2 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -258,6 +258,10 @@ Setting name (default value) What does it do? posts. `DAY_ARCHIVE_SAVE_AS` (False) The location to save per-day archives of your posts. +`SLUG_SUBSTITUTIONS` (``()``) Substitutions to make prior to stripping out + non-alphanumerics when generating slugs. Specified + as a list of 2-tuples of ``(from, to)`` which are + applied in order. ==================================================== ===================================================== .. note:: diff --git a/pelican/contents.py b/pelican/contents.py index 1b604f19..d56335dd 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -86,7 +86,8 @@ class Content(object): # create the slug if not existing, from the title if not hasattr(self, 'slug') and hasattr(self, 'title'): - self.slug = slugify(self.title) + self.slug = slugify(self.title, + settings.get('SLUG_SUBSTITUTIONS', ())) self.source_path = source_path diff --git a/pelican/settings.py b/pelican/settings.py index 1c9b48c3..01203504 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -105,6 +105,7 @@ DEFAULT_CONFIG = { 'PLUGINS': [], 'TEMPLATE_PAGES': {}, 'IGNORE_FILES': ['.#*'], + 'SLUG_SUBSTITUTIONS': (), } def read_settings(path=None, override=None): diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index ab35d991..0e65003a 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -94,6 +94,17 @@ class TestUtils(LoggedTestCase): for value, expected in samples: self.assertEqual(utils.slugify(value), expected) + def test_slugify_substitute(self): + + samples = (('C++ is based on C', 'cpp-is-based-on-c'), + ('C+++ test C+ test', 'cpp-test-c-test'), + ('c++, c#, C#, C++', 'cpp-c-sharp-c-sharp-cpp'), + ('c++-streams', 'cpp-streams'),) + + subs = (('C++', 'CPP'), ('C#', 'C-SHARP')) + for value, expected in samples: + self.assertEqual(utils.slugify(value, subs), expected) + def test_get_relative_path(self): samples = ((os.path.join('test', 'test.html'), os.pardir), diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index b0df61ad..acb8e07d 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -15,10 +15,10 @@ class URLWrapper(object): def __init__(self, name, settings): # next 2 lines are redundant with the setter of the name property # but are here for clarity - self._name = name - self.slug = slugify(name) - self.name = name self.settings = settings + self._name = name + self.slug = slugify(name, self.settings.get('SLUG_SUBSTITUTIONS', ())) + self.name = name @property def name(self): @@ -27,7 +27,7 @@ class URLWrapper(object): @name.setter def name(self, name): self._name = name - self.slug = slugify(name) + self.slug = slugify(name, self.settings.get('SLUG_SUBSTITUTIONS', ())) def as_dict(self): d = self.__dict__ @@ -41,7 +41,8 @@ class URLWrapper(object): return self.slug def _normalize_key(self, key): - return six.text_type(slugify(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) diff --git a/pelican/utils.py b/pelican/utils.py index 2c70ae8c..b1524036 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -231,7 +231,7 @@ class pelican_open(object): pass -def slugify(value): +def slugify(value, substitutions=()): """ Normalizes string, converts to lowercase, removes non-alpha characters, and converts spaces to hyphens. @@ -249,8 +249,10 @@ def slugify(value): if isinstance(value, six.binary_type): value = value.decode('ascii') # still unicode - value = unicodedata.normalize('NFKD', value) - value = re.sub('[^\w\s-]', '', value).strip().lower() + value = unicodedata.normalize('NFKD', value).lower() + for src, dst in substitutions: + value = value.replace(src.lower(), dst.lower()) + value = re.sub('[^\w\s-]', '', value).strip() value = re.sub('[-\s]+', '-', value) # we want only ASCII chars value = value.encode('ascii', 'ignore') From 3da4c2e13e4146e1d0a58fd0e7267bb4ad957881 Mon Sep 17 00:00:00 2001 From: Stefan hr Berder Date: Sun, 7 Jul 2013 12:44:21 +0200 Subject: [PATCH 0009/1427] add port option to pelican.server --- pelican/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/server.py b/pelican/server.py index fd99b209..24f3ae04 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -10,7 +10,7 @@ try: except ImportError: import socketserver # NOQA -PORT = 8000 +PORT = len(sys.argv) == 2 and int(sys.argv[1]) or 8000 Handler = srvmod.SimpleHTTPRequestHandler @@ -26,4 +26,4 @@ try: httpd.serve_forever() except KeyboardInterrupt as e: print("shutting down server") - httpd.socket.close() \ No newline at end of file + httpd.socket.close() From 00a1cbb6b84d9faad2d7dde01b068ff113b0184e Mon Sep 17 00:00:00 2001 From: Lingzhu Xiang Date: Sat, 4 May 2013 06:13:11 +0800 Subject: [PATCH 0010/1427] Support importing Tumblr Try to integrate Tumblr's various post types without using additonal templates. --- docs/importer.rst | 14 ++++-- pelican/tools/pelican_import.py | 88 ++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/docs/importer.rst b/docs/importer.rst index 9a0c513e..057fecd8 100644 --- a/docs/importer.rst +++ b/docs/importer.rst @@ -14,6 +14,7 @@ software to reStructuredText or Markdown. The supported import formats are: - WordPress XML export - Dotclear export - Posterous API +- Tumblr API - RSS/Atom feed The conversion from HTML to reStructuredText or Markdown relies on `Pandoc`_. @@ -41,16 +42,17 @@ Usage :: - pelican-import [-h] [--wpfile] [--dotclear] [--posterous] [--feed] [-o OUTPUT] + pelican-import [-h] [--wpfile] [--dotclear] [--posterous] [--tumblr] [--feed] [-o OUTPUT] [-m MARKUP] [--dir-cat] [--dir-page] [--strip-raw] [--disable-slugs] - [-e EMAIL] [-p PASSWORD] - input|api_token + [-e EMAIL] [-p PASSWORD] [-b BLOGNAME] + input|api_token|api_key Positional arguments -------------------- input The input file to read api_token [Posterous only] api_token can be obtained from http://posterous.com/api/ + api_key [Tumblr only] api_key can be obtained from http://www.tumblr.com/oauth/apps Optional arguments ------------------ @@ -59,6 +61,7 @@ Optional arguments --wpfile WordPress XML export (default: False) --dotclear Dotclear export (default: False) --posterous Posterous API (default: False) + --tumblr Tumblr API (default: False) --feed Feed to parse (default: False) -o OUTPUT, --output OUTPUT Output path (default: output) @@ -80,6 +83,8 @@ Optional arguments Email used to authenticate Posterous API -p PASSWORD, --password=PASSWORD Password used to authenticate Posterous API + -b BLOGNAME, --blogname=BLOGNAME + Blog name used in Tumblr API Examples @@ -97,6 +102,9 @@ for Posterous:: $ pelican-import --posterous -o ~/output --email= --password= +For Tumblr:: + + $ pelican-import --tumblr -o ~/output --blogname= Tests ===== diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 630142e7..5f637c73 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -326,6 +326,84 @@ def posterous2fields(api_token, email, password): yield (post.get('title'), post.get('body_cleaned'), slug, date, post.get('user').get('display_name'), [], tags, kind, "html") + +def tumblr2fields(api_key, blogname): + """ Imports Tumblr posts (API v2)""" + from time import strftime, localtime + try: + # py3k import + import json + except ImportError: + # py2 import + import simplejson as json + + try: + # py3k import + import urllib.request as urllib_request + except ImportError: + # py2 import + import urllib2 as urllib_request + + def get_tumblr_posts(api_key, blogname, offset=0): + url = "http://api.tumblr.com/v2/blog/%s.tumblr.com/posts?api_key=%s&offset=%d&filter=raw" % (blogname, api_key, offset) + request = urllib_request.Request(url) + handle = urllib_request.urlopen(request) + posts = json.loads(handle.read().decode('utf-8')) + return posts.get('response').get('posts') + + offset = 0 + posts = get_tumblr_posts(api_key, blogname, offset) + while len(posts) > 0: + for post in posts: + title = post.get('title') or post.get('source_title') + slug = post.get('slug') or slugify(title) + tags = post.get('tags') + timestamp = post.get('timestamp') + date = strftime("%Y-%m-%d %H:%M:%S", localtime(int(timestamp))) + slug = strftime("%Y-%m-%d-", localtime(int(timestamp))) + slug + format = post.get('format') + content = post.get('body') + type = post.get('type') + if type == 'photo': + if format == 'markdown': + fmtstr = '![%s](%s)' + else: + fmtstr = '%s' + content = '\n'.join(fmtstr % (photo.get('caption'), photo.get('original_size').get('url')) for photo in post.get('photos')) + elif type == 'quote': + if format == 'markdown': + fmtstr = '\n\n— %s' + else: + fmtstr = '

— %s

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

via

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

via

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

via

\n' + content = fmtstr % post.get('source_url') + post.get('caption') + '\n'.join(player.get('embed_code') for player in post.get('player')) + elif type == 'answer': + title = post.get('question') + content = '

%s: %s

\n%s' % (post.get('asking_name'), post.get('asking_url'), post.get('question'), post.get('answer')) + + yield (title, content, slug, date, post.get('blog_name'), [type], tags, format) + + offset += len(posts) + posts = get_tumblr_posts(api_key, blogname, offset) + + def feed2fields(file): """Read a feed and yield pelican fields""" import feedparser @@ -476,6 +554,8 @@ def main(): help='Dotclear export') parser.add_argument('--posterous', action='store_true', dest='posterous', help='Posterous export') + parser.add_argument('--tumblr', action='store_true', dest='tumblr', + help='Tumblr export') parser.add_argument('--feed', action='store_true', dest='feed', help='Feed to parse') parser.add_argument('-o', '--output', dest='output', default='output', @@ -499,6 +579,8 @@ def main(): help="Email address (posterous import only)") parser.add_argument('-p', '--password', dest='password', help="Password (posterous import only)") + parser.add_argument('-b', '--blogname', dest='blogname', + help="Blog name (Tumblr import only)") args = parser.parse_args() @@ -509,10 +591,12 @@ def main(): input_type = 'dotclear' elif args.posterous: input_type = 'posterous' + elif args.tumblr: + input_type = 'tumblr' elif args.feed: input_type = 'feed' else: - error = "You must provide either --wpfile, --dotclear, --posterous or --feed options" + error = "You must provide either --wpfile, --dotclear, --posterous, --tumblr or --feed options" exit(error) if not os.path.exists(args.output): @@ -528,6 +612,8 @@ def main(): fields = dc2fields(args.input) elif input_type == 'posterous': fields = posterous2fields(args.input, args.email, args.password) + elif input_type == 'tumblr': + fields = tumblr2fields(args.input, args.blogname) elif input_type == 'feed': fields = feed2fields(args.input) From 75263fa852fae5ed6e591c18fe1afad8fe5a7b0f Mon Sep 17 00:00:00 2001 From: Lingzhu Xiang Date: Sun, 5 May 2013 02:09:24 +0800 Subject: [PATCH 0011/1427] Fix importing Tumblr photo caption Besides each photo's caption, the general caption is also needed. While we're at it, also add a linefeed at the end of file. --- pelican/tools/pelican_import.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 5f637c73..3c40dd56 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -370,6 +370,7 @@ def tumblr2fields(api_key, blogname): else: fmtstr = '%s' content = '\n'.join(fmtstr % (photo.get('caption'), photo.get('original_size').get('url')) for photo in post.get('photos')) + content += '\n\n' + post.get('caption') elif type == 'quote': if format == 'markdown': fmtstr = '\n\n— %s' @@ -398,6 +399,8 @@ def tumblr2fields(api_key, blogname): title = post.get('question') content = '

%s: %s

\n%s' % (post.get('asking_name'), post.get('asking_url'), post.get('question'), post.get('answer')) + content = content.rstrip() + '\n' + yield (title, content, slug, date, post.get('blog_name'), [type], tags, format) offset += len(posts) From 241ac2400a33d6ad804012ae11b36a38e223476f Mon Sep 17 00:00:00 2001 From: Lingzhu Xiang Date: Sun, 5 May 2013 22:53:21 +0800 Subject: [PATCH 0012/1427] Use better titles than None for Tumblr posts without title --- pelican/tools/pelican_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 3c40dd56..3b5c55f2 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -355,7 +355,7 @@ def tumblr2fields(api_key, blogname): posts = get_tumblr_posts(api_key, blogname, offset) while len(posts) > 0: for post in posts: - title = post.get('title') or post.get('source_title') + title = post.get('title') or post.get('source_title') or post.get('type').capitalize() slug = post.get('slug') or slugify(title) tags = post.get('tags') timestamp = post.get('timestamp') From 3a5db543bb8fa385ac10347d81d98778959619bc Mon Sep 17 00:00:00 2001 From: Stefan hr Berder Date: Sun, 7 Jul 2013 13:27:50 +0200 Subject: [PATCH 0013/1427] add port parameter to bash script --- pelican/tools/templates/develop_server.sh.in | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pelican/tools/templates/develop_server.sh.in b/pelican/tools/templates/develop_server.sh.in index 6dd11e6d..16b61518 100755 --- a/pelican/tools/templates/develop_server.sh.in +++ b/pelican/tools/templates/develop_server.sh.in @@ -18,7 +18,7 @@ SRV_PID=$$BASEDIR/srv.pid PELICAN_PID=$$BASEDIR/pelican.pid function usage(){ - echo "usage: $$0 (stop) (start) (restart)" + echo "usage: $$0 (stop) (start) (restart) [port]" echo "This starts pelican in debug and reload mode and then launches" echo "A pelican.server to help site development. It doesn't read" echo "your pelican options so you edit any paths in your Makefile" @@ -59,13 +59,14 @@ function shut_down(){ } function start_up(){ + local port=$$1 echo "Starting up Pelican and pelican.server" shift $$PELICAN --debug --autoreload -r $$INPUTDIR -o $$OUTPUTDIR -s $$CONFFILE $$PELICANOPTS & pelican_pid=$$! echo $$pelican_pid > $$PELICAN_PID cd $$OUTPUTDIR - $PY -m pelican.server & + $PY -m pelican.server $$port & srv_pid=$$! echo $$srv_pid > $$SRV_PID cd $$BASEDIR @@ -83,15 +84,18 @@ function start_up(){ ### # MAIN ### -[[ $$# -ne 1 ]] && usage +[[ ($$# -eq 0) || ($$# -gt 2) ]] && usage +port='' +[[ $$# -eq 2 ]] && port=$$2 + if [[ $$1 == "stop" ]]; then shut_down elif [[ $$1 == "restart" ]]; then shut_down - start_up + start_up $$port elif [[ $$1 == "start" ]]; then - if ! start_up; then - shut_down + if ! start_up $$port; then + shut_down fi else usage From 689632835eb2119c2f822456dee6c11def11ced5 Mon Sep 17 00:00:00 2001 From: Stefan hr Berder Date: Sun, 7 Jul 2013 14:28:15 +0200 Subject: [PATCH 0014/1427] add port option to Makefile target serve/devserver --- pelican/tools/templates/Makefile.in | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in index 4bc764ca..f2e0ccc9 100644 --- a/pelican/tools/templates/Makefile.in +++ b/pelican/tools/templates/Makefile.in @@ -28,8 +28,8 @@ help: @echo ' make clean remove the generated files ' @echo ' make regenerate regenerate files upon modification ' @echo ' make publish generate using production settings ' - @echo ' make serve serve site at http://localhost:8000' - @echo ' make devserver start/restart develop_server.sh ' + @echo ' make serve [PORT=8000] serve site at http://localhost:8000' + @echo ' make devserver [PORT=8000] start/restart develop_server.sh ' @echo ' make stopserver stop local server ' @echo ' ssh_upload upload the web site via SSH ' @echo ' rsync_upload upload the web site via rsync+ssh ' @@ -50,10 +50,18 @@ regenerate: $$(PELICAN) -r $$(INPUTDIR) -o $$(OUTPUTDIR) -s $$(CONFFILE) $$(PELICANOPTS) serve: +ifdef PORT + cd $$(OUTPUTDIR) && $(PY) -m pelican.server $$(PORT) +else cd $$(OUTPUTDIR) && $(PY) -m pelican.server +endif devserver: +ifdef PORT + $$(BASEDIR)/develop_server.sh restart $$(PORT) +else $$(BASEDIR)/develop_server.sh restart +endif stopserver: kill -9 `cat pelican.pid` From cb650c1c992a92c25040387d77ecc774e6f5a4ac Mon Sep 17 00:00:00 2001 From: Benjamin Port Date: Thu, 9 May 2013 05:06:12 +0200 Subject: [PATCH 0015/1427] Add filter-author option to importer Allow to import post from only one author when importing data --- docs/importer.rst | 1 + pelican/tools/pelican_import.py | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/importer.rst b/docs/importer.rst index 9a0c513e..228471da 100644 --- a/docs/importer.rst +++ b/docs/importer.rst @@ -69,6 +69,7 @@ Optional arguments (default: False) --dir-page Put files recognised as pages in "pages/" sub- directory (wordpress import only) (default: False) + --filter-author Import only post from the specified author. --strip-raw Strip raw HTML code that can't be converted to markup such as flash embeds or iframes (wordpress import only) (default: False) diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 630142e7..59b767f9 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -376,9 +376,11 @@ def build_markdown_header(title, date, author, categories, tags, slug): def fields2pelican(fields, out_markup, output_path, dircat=False, strip_raw=False, disable_slugs=False, - dirpage=False, filename_template=None): + dirpage=False, filename_template=None, filter_author=None): for (title, content, filename, date, author, categories, tags, kind, in_markup) in fields: + if filter_author and filter_author != author: + continue slug = not disable_slugs and filename or None if (in_markup == "markdown") or (out_markup == "markdown") : ext = '.md' @@ -487,6 +489,8 @@ def main(): parser.add_argument('--dir-page', action='store_true', dest='dirpage', help=('Put files recognised as pages in "pages/" sub-directory' ' (wordpress import only)')) + parser.add_argument('--filter-author', dest='author', + help='Import only post from the specified author') parser.add_argument('--strip-raw', action='store_true', dest='strip_raw', help="Strip raw HTML code that can't be converted to " "markup such as flash embeds or iframes (wordpress import only)") @@ -537,4 +541,5 @@ def main(): dircat=args.dircat or False, dirpage=args.dirpage or False, strip_raw=args.strip_raw or False, - disable_slugs=args.disable_slugs or False) + disable_slugs=args.disable_slugs or False, + filter_author=args.author) From 6c5444eb6833cda2ea9129321c3a4ba43505b772 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Sun, 14 Jul 2013 23:01:16 +1000 Subject: [PATCH 0016/1427] do slug_substitutions on category and author ... --- pelican/contents.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index d56335dd..ed213c31 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -141,14 +141,21 @@ class Content(object): """Returns the URL, formatted with the proper values""" metadata = copy.copy(self.metadata) path = self.metadata.get('path', self.get_relative_source_path()) + default_category = self.settings['DEFAULT_CATEGORY'] + slug_substitutions = self.settings.get('SLUG_SUBSTITUTIONS', ()) metadata.update({ 'path': path_to_url(path), 'slug': getattr(self, 'slug', ''), 'lang': getattr(self, 'lang', 'en'), 'date': getattr(self, 'date', datetime.now()), - 'author': getattr(self, 'author', ''), - 'category': getattr(self, 'category', - self.settings['DEFAULT_CATEGORY']), + 'author': slugify( + getattr(self, 'author', ''), + slug_substitutions + ), + 'category': slugify( + getattr(self, 'category', default_category), + slug_substitutions + ) }) return metadata From 9b7ae20aa9182e7d3c10bea111716bb4e12edbd8 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Mon, 15 Jul 2013 00:22:05 +1000 Subject: [PATCH 0017/1427] test for author & category slugification --- pelican/tests/test_contents.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index c081639d..af97db3f 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -191,6 +191,20 @@ class TestArticle(TestPage): custom_article = Article(**article_kwargs) self.assertEqual('custom', custom_article.template) + def test_slugify_category_author(self): + settings = get_settings() + 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'] = "O'Brien" + article_kwargs['metadata']['category'] = 'C# & stuff' + 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') + class TestURLWrapper(unittest.TestCase): def test_comparisons(self): From 4ca5d908ff767fc07a95a8b31487dcbb9416e2e5 Mon Sep 17 00:00:00 2001 From: Chris Howie Date: Mon, 15 Jul 2013 16:48:08 -0400 Subject: [PATCH 0018/1427] Update tag cloud documentation for SLUG_SUBSTITUTIONS --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index 61ccc2b2..2c16ca83 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -497,7 +497,7 @@ The default theme does not support tag clouds, but it is pretty easy to add:: From f43742c3f0b6b3a260ff5c338d7c4309865b0b54 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 15 Jul 2013 14:25:39 -0700 Subject: [PATCH 0019/1427] Add missing SITEURL variable to tag cloud docs --- docs/settings.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 2c16ca83..eb0d028f 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -493,11 +493,11 @@ Setting name (default value) What does it do? `TAG_CLOUD_MAX_ITEMS` (``100``) Maximum number of tags in the cloud. ================================================ ===================================================== -The default theme does not support tag clouds, but it is pretty easy to add:: +The default theme does not include a tag cloud, but it is pretty easy to add:: From c5008f61e0a480afb954a5258748eec576ee9a72 Mon Sep 17 00:00:00 2001 From: Jude N Date: Tue, 16 Jul 2013 23:44:53 -0400 Subject: [PATCH 0020/1427] Adding a FEED_ALL_ATOM check in the "social" div. The head section has a tests for FEED_ALL_ATOM when building the atom link. This diff add a similar test in the "social" div. --- pelican/themes/notmyidea/templates/base.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pelican/themes/notmyidea/templates/base.html b/pelican/themes/notmyidea/templates/base.html index 9bf4b12b..e44e20fd 100644 --- a/pelican/themes/notmyidea/templates/base.html +++ b/pelican/themes/notmyidea/templates/base.html @@ -53,7 +53,9 @@ - - -
-
+
+ -
+
diff --git a/pelican/tests/output/basic/categories.html b/pelican/tests/output/basic/categories.html index 7a85b52a..f1541892 100644 --- a/pelican/tests/output/basic/categories.html +++ b/pelican/tests/output/basic/categories.html @@ -4,8 +4,8 @@ A Pelican Blog - - + + @@ -15,29 +15,29 @@ - - +
diff --git a/pelican/tests/output/basic/category/bar.html b/pelican/tests/output/basic/category/bar.html index 78894c94..f272dd2b 100644 --- a/pelican/tests/output/basic/category/bar.html +++ b/pelican/tests/output/basic/category/bar.html @@ -4,8 +4,8 @@ A Pelican Blog - bar - - + + @@ -15,29 +15,27 @@ - - - + + +
+ -
+
diff --git a/pelican/tests/output/basic/category/cat1.html b/pelican/tests/output/basic/category/cat1.html index 43226146..4e2f0f5a 100644 --- a/pelican/tests/output/basic/category/cat1.html +++ b/pelican/tests/output/basic/category/cat1.html @@ -4,8 +4,8 @@ A Pelican Blog - cat1 - - + + @@ -15,112 +15,104 @@ - - - +

Other articles


    - - -
-
-
-
+
+ -
+
diff --git a/pelican/tests/output/basic/category/misc.html b/pelican/tests/output/basic/category/misc.html index 4aebcdf1..fce85481 100644 --- a/pelican/tests/output/basic/category/misc.html +++ b/pelican/tests/output/basic/category/misc.html @@ -4,8 +4,8 @@ A Pelican Blog - misc - - + + @@ -15,93 +15,87 @@ - - - +

Other articles


    - - -
-
-
-
+
+ -
+
diff --git a/pelican/tests/output/basic/category/yeah.html b/pelican/tests/output/basic/category/yeah.html index b6f59771..b7c383fa 100644 --- a/pelican/tests/output/basic/category/yeah.html +++ b/pelican/tests/output/basic/category/yeah.html @@ -4,8 +4,8 @@ A Pelican Blog - yeah - - + + @@ -15,29 +15,27 @@ - - - + + +
+ -
+
diff --git a/pelican/tests/output/basic/filename_metadata-example.html b/pelican/tests/output/basic/filename_metadata-example.html index 4ceef81f..a9f548a5 100644 --- a/pelican/tests/output/basic/filename_metadata-example.html +++ b/pelican/tests/output/basic/filename_metadata-example.html @@ -4,8 +4,8 @@ FILENAME_METADATA example - - + + @@ -15,45 +15,45 @@ -
+
-
+
diff --git a/pelican/tests/output/basic/index.html b/pelican/tests/output/basic/index.html index 49c1883c..b10b479a 100644 --- a/pelican/tests/output/basic/index.html +++ b/pelican/tests/output/basic/index.html @@ -4,8 +4,8 @@ A Pelican Blog - - + + @@ -15,192 +15,176 @@ - - - +

Other articles


    - - -
  1. +
  2. - - -
  3. +
  4. - - -
  5. +
  6. - - -
  7. +
  8. - - -
  9. +
  10. - - -
  11. +
  12. - - -
-
-
-
+
+ -
+
diff --git a/pelican/tests/output/basic/oh-yeah.html b/pelican/tests/output/basic/oh-yeah.html index 498e9cac..cb29ea80 100644 --- a/pelican/tests/output/basic/oh-yeah.html +++ b/pelican/tests/output/basic/oh-yeah.html @@ -4,8 +4,8 @@ Oh yeah ! - - + + @@ -15,32 +15,32 @@ -
+
-
+
diff --git a/pelican/tests/output/basic/override/index.html b/pelican/tests/output/basic/override/index.html index 2cc639d2..ff6775cf 100644 --- a/pelican/tests/output/basic/override/index.html +++ b/pelican/tests/output/basic/override/index.html @@ -4,8 +4,8 @@ Override url/save_as - - + + @@ -15,31 +15,30 @@ - -
+

Override url/save_as

- -

Test page which overrides save_as and url so that this page will be generated + +

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

-
+
diff --git a/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html index cd582b96..e4a18249 100644 --- a/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html @@ -4,8 +4,8 @@ This is a test hidden page - - + + @@ -15,31 +15,30 @@ - -
+

This is a test hidden page

- -

This is great for things like error(404) pages + +

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

-
+
diff --git a/pelican/tests/output/basic/pages/this-is-a-test-page.html b/pelican/tests/output/basic/pages/this-is-a-test-page.html index 8ba85e1d..d0e11cfc 100644 --- a/pelican/tests/output/basic/pages/this-is-a-test-page.html +++ b/pelican/tests/output/basic/pages/this-is-a-test-page.html @@ -4,8 +4,8 @@ This is a test page - - + + @@ -15,31 +15,30 @@ - -
+

This is a test page

- -

Just an image.

+ +

Just an image.

alternate text
-
+
diff --git a/pelican/tests/output/basic/second-article-fr.html b/pelican/tests/output/basic/second-article-fr.html index 44965a76..ca4abcfc 100644 --- a/pelican/tests/output/basic/second-article-fr.html +++ b/pelican/tests/output/basic/second-article-fr.html @@ -4,8 +4,8 @@ Deuxième article - - + + @@ -15,47 +15,47 @@ -
+
-
+
diff --git a/pelican/tests/output/basic/second-article.html b/pelican/tests/output/basic/second-article.html index 780d9b20..7008b847 100644 --- a/pelican/tests/output/basic/second-article.html +++ b/pelican/tests/output/basic/second-article.html @@ -4,8 +4,8 @@ Second article - - + + @@ -15,47 +15,47 @@ -
+
-
+
diff --git a/pelican/tests/output/basic/tag/bar.html b/pelican/tests/output/basic/tag/bar.html index e23071e8..80d2d212 100644 --- a/pelican/tests/output/basic/tag/bar.html +++ b/pelican/tests/output/basic/tag/bar.html @@ -4,8 +4,8 @@ A Pelican Blog - bar - - + + @@ -15,81 +15,75 @@ - - - +

Other articles


    - - -
-
-
-
+
+ -
+
diff --git a/pelican/tests/output/basic/tag/baz.html b/pelican/tests/output/basic/tag/baz.html index e429b6d8..6c25194d 100644 --- a/pelican/tests/output/basic/tag/baz.html +++ b/pelican/tests/output/basic/tag/baz.html @@ -4,8 +4,8 @@ A Pelican Blog - baz - - + + @@ -15,43 +15,41 @@ - - - + + +
+ -
+
diff --git a/pelican/tests/output/basic/tag/foo.html b/pelican/tests/output/basic/tag/foo.html index 17fac77a..87372eb6 100644 --- a/pelican/tests/output/basic/tag/foo.html +++ b/pelican/tests/output/basic/tag/foo.html @@ -4,8 +4,8 @@ A Pelican Blog - foo - - + + @@ -15,73 +15,69 @@ - - - +

Other articles


    - - -
-
-
-
+
+ -
+
diff --git a/pelican/tests/output/basic/tag/foobar.html b/pelican/tests/output/basic/tag/foobar.html index cb8dfec2..4812467f 100644 --- a/pelican/tests/output/basic/tag/foobar.html +++ b/pelican/tests/output/basic/tag/foobar.html @@ -4,8 +4,8 @@ A Pelican Blog - foobar - - + + @@ -15,29 +15,27 @@ - - - + + +
+ -
+
diff --git a/pelican/tests/output/basic/tag/oh.html b/pelican/tests/output/basic/tag/oh.html index f79e6e0e..47ef8946 100644 --- a/pelican/tests/output/basic/tag/oh.html +++ b/pelican/tests/output/basic/tag/oh.html @@ -4,8 +4,8 @@ A Pelican Blog - oh - - + + @@ -15,29 +15,27 @@ - - - + + +
+ -
+
diff --git a/pelican/tests/output/basic/tag/yeah.html b/pelican/tests/output/basic/tag/yeah.html index dc3c1212..47f8076a 100644 --- a/pelican/tests/output/basic/tag/yeah.html +++ b/pelican/tests/output/basic/tag/yeah.html @@ -4,8 +4,8 @@ A Pelican Blog - yeah - - + + @@ -15,29 +15,27 @@ - - - + + +
+ -
+
diff --git a/pelican/tests/output/basic/this-is-a-super-article.html b/pelican/tests/output/basic/this-is-a-super-article.html index 85ad0ad8..e472a8e2 100644 --- a/pelican/tests/output/basic/this-is-a-super-article.html +++ b/pelican/tests/output/basic/this-is-a-super-article.html @@ -4,8 +4,8 @@ This is a super article ! - - + + @@ -15,32 +15,32 @@ -
+
-
+
diff --git a/pelican/tests/output/basic/unbelievable.html b/pelican/tests/output/basic/unbelievable.html index 3cc9a557..849b4d78 100644 --- a/pelican/tests/output/basic/unbelievable.html +++ b/pelican/tests/output/basic/unbelievable.html @@ -4,8 +4,8 @@ Unbelievable ! - - + + @@ -15,47 +15,47 @@ -
+
-
+
diff --git a/pelican/tests/output/custom/a-markdown-powered-article.html b/pelican/tests/output/custom/a-markdown-powered-article.html index 6a2f1fc5..c5fca991 100644 --- a/pelican/tests/output/custom/a-markdown-powered-article.html +++ b/pelican/tests/output/custom/a-markdown-powered-article.html @@ -4,9 +4,9 @@ A markdown powered article - - - + + + @@ -19,38 +19,38 @@ -
+
-
+
diff --git a/pelican/tests/output/custom/archives.html b/pelican/tests/output/custom/archives.html index b1704bdb..077c1268 100644 --- a/pelican/tests/output/custom/archives.html +++ b/pelican/tests/output/custom/archives.html @@ -4,9 +4,9 @@ Alexis' log - - - + + + @@ -19,15 +19,15 @@ -
+

Archives for Alexis' log

@@ -52,29 +52,29 @@
-
+
diff --git a/pelican/tests/output/custom/article-1.html b/pelican/tests/output/custom/article-1.html index c77bd4cf..80f1ceaf 100644 --- a/pelican/tests/output/custom/article-1.html +++ b/pelican/tests/output/custom/article-1.html @@ -4,9 +4,9 @@ Article 1 - - - + + + @@ -19,37 +19,37 @@ -
+
-
+
diff --git a/pelican/tests/output/custom/article-2.html b/pelican/tests/output/custom/article-2.html index f93b8a85..8511afc8 100644 --- a/pelican/tests/output/custom/article-2.html +++ b/pelican/tests/output/custom/article-2.html @@ -4,9 +4,9 @@ Article 2 - - - + + + @@ -19,37 +19,37 @@ -
+
-
+

This article overrides the listening of the articles under the baz tag.

+ + +
+

Comments !

+
+ +
+ + +

blogroll

diff --git a/pelican/tests/output/custom/tag/foo.html b/pelican/tests/output/custom/tag/foo.html index b6c741bd..3bc93546 100644 --- a/pelican/tests/output/custom/tag/foo.html +++ b/pelican/tests/output/custom/tag/foo.html @@ -19,6 +19,7 @@

blogroll

diff --git a/pelican/tests/output/custom/tag/yeah.html b/pelican/tests/output/custom/tag/yeah.html index ba6089f0..ba2da881 100644 --- a/pelican/tests/output/custom/tag/yeah.html +++ b/pelican/tests/output/custom/tag/yeah.html @@ -19,6 +19,7 @@

Or completely awesome. Depends the needs.

a root-relative link to markdown-article a file-relative link to markdown-article

+
+

Testing sourcecode directive

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

Lovely.

+
read more diff --git a/pelican/tests/output/basic/feeds/all-en.atom.xml b/pelican/tests/output/basic/feeds/all-en.atom.xml index 9b819f84..6d0c563f 100644 --- a/pelican/tests/output/basic/feeds/all-en.atom.xml +++ b/pelican/tests/output/basic/feeds/all-en.atom.xml @@ -27,5 +27,11 @@ YEAH !</p> Unbelievable !2010-10-15T20:30:00Ztag:,2010-10-15:unbelievable.html<p>Or completely awesome. Depends the needs.</p> <p><a class="reference external" href="/a-markdown-powered-article.html">a root-relative link to markdown-article</a> <a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00Ztag:,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/basic/feeds/all.atom.xml b/pelican/tests/output/basic/feeds/all.atom.xml index 63628281..1abf742a 100644 --- a/pelican/tests/output/basic/feeds/all.atom.xml +++ b/pelican/tests/output/basic/feeds/all.atom.xml @@ -28,5 +28,11 @@ YEAH !</p> Unbelievable !2010-10-15T20:30:00Ztag:,2010-10-15:unbelievable.html<p>Or completely awesome. Depends the needs.</p> <p><a class="reference external" href="/a-markdown-powered-article.html">a root-relative link to markdown-article</a> <a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00Ztag:,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/basic/feeds/misc.atom.xml b/pelican/tests/output/basic/feeds/misc.atom.xml index f2885b1d..b5588ce7 100644 --- a/pelican/tests/output/basic/feeds/misc.atom.xml +++ b/pelican/tests/output/basic/feeds/misc.atom.xml @@ -4,5 +4,11 @@ Unbelievable !2010-10-15T20:30:00Ztag:,2010-10-15:unbelievable.html<p>Or completely awesome. Depends the needs.</p> <p><a class="reference external" href="/a-markdown-powered-article.html">a root-relative link to markdown-article</a> <a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00Ztag:,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/basic/index.html b/pelican/tests/output/basic/index.html index 6a4fb8f3..6989f25a 100644 --- a/pelican/tests/output/basic/index.html +++ b/pelican/tests/output/basic/index.html @@ -215,6 +215,12 @@ YEAH !

Or completely awesome. Depends the needs.

a root-relative link to markdown-article a file-relative link to markdown-article

+
+

Testing sourcecode directive

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

Lovely.

+
read more diff --git a/pelican/tests/output/basic/unbelievable.html b/pelican/tests/output/basic/unbelievable.html index 97df4b07..3f2d2f80 100644 --- a/pelican/tests/output/basic/unbelievable.html +++ b/pelican/tests/output/basic/unbelievable.html @@ -43,6 +43,12 @@

Or completely awesome. Depends the needs.

a root-relative link to markdown-article a file-relative link to markdown-article

+
+

Testing sourcecode directive

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

Lovely.

+
diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index 15e8ac9c..e3e07e10 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -51,6 +51,12 @@

Or completely awesome. Depends the needs.

a root-relative link to markdown-article a file-relative link to markdown-article

+
+

Testing sourcecode directive

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

Lovely.

+
read more

There are comments.

diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html index e950d863..63c659da 100644 --- a/pelican/tests/output/custom/category/misc.html +++ b/pelican/tests/output/custom/category/misc.html @@ -95,6 +95,12 @@

Or completely awesome. Depends the needs.

a root-relative link to markdown-article a file-relative link to markdown-article

+
+

Testing sourcecode directive

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

Lovely.

+
read more

There are comments.

diff --git a/pelican/tests/output/custom/feeds/all-en.atom.xml b/pelican/tests/output/custom/feeds/all-en.atom.xml index 49b0de79..d4631d7c 100644 --- a/pelican/tests/output/custom/feeds/all-en.atom.xml +++ b/pelican/tests/output/custom/feeds/all-en.atom.xml @@ -27,5 +27,11 @@ YEAH !</p> Unbelievable !2010-10-15T20:30:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-15:unbelievable.html<p>Or completely awesome. Depends the needs.</p> <p><a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a root-relative link to markdown-article</a> <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/all.atom.xml b/pelican/tests/output/custom/feeds/all.atom.xml index a9e67acd..2a673ada 100644 --- a/pelican/tests/output/custom/feeds/all.atom.xml +++ b/pelican/tests/output/custom/feeds/all.atom.xml @@ -29,5 +29,11 @@ YEAH !</p> Unbelievable !2010-10-15T20:30:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-15:unbelievable.html<p>Or completely awesome. Depends the needs.</p> <p><a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a root-relative link to markdown-article</a> <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/all.rss.xml b/pelican/tests/output/custom/feeds/all.rss.xml index 7d7890a9..e8cd9b80 100644 --- a/pelican/tests/output/custom/feeds/all.rss.xml +++ b/pelican/tests/output/custom/feeds/all.rss.xml @@ -29,5 +29,11 @@ YEAH !</p> Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0200tag:blog.notmyidea.org,2010-10-20:oh-yeah.htmlohbaryeahUnbelievable !http://blog.notmyidea.org/unbelievable.html<p>Or completely awesome. Depends the needs.</p> <p><a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a root-relative link to markdown-article</a> <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:unbelievable.htmlThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:tag/baz.html \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/misc.atom.xml b/pelican/tests/output/custom/feeds/misc.atom.xml index 029184b0..250b1b65 100644 --- a/pelican/tests/output/custom/feeds/misc.atom.xml +++ b/pelican/tests/output/custom/feeds/misc.atom.xml @@ -4,5 +4,11 @@ Unbelievable !2010-10-15T20:30:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-15:unbelievable.html<p>Or completely awesome. Depends the needs.</p> <p><a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a root-relative link to markdown-article</a> <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/misc.rss.xml b/pelican/tests/output/custom/feeds/misc.rss.xml index ab2639cf..195812cc 100644 --- a/pelican/tests/output/custom/feeds/misc.rss.xml +++ b/pelican/tests/output/custom/feeds/misc.rss.xml @@ -4,5 +4,11 @@ Alexis MétaireauWed, 29 Feb 2012 00:00:00 +0100tag:blog.notmyidea.org,2012-02-29:second-article.htmlfoobarbazUnbelievable !http://blog.notmyidea.org/unbelievable.html<p>Or completely awesome. Depends the needs.</p> <p><a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a root-relative link to markdown-article</a> <a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:unbelievable.htmlThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:tag/baz.html \ No newline at end of file diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index 67988a51..52657306 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -51,6 +51,12 @@

Or completely awesome. Depends the needs.

a root-relative link to markdown-article a file-relative link to markdown-article

+
+

Testing sourcecode directive

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

Lovely.

+
read more

There are comments.

diff --git a/pelican/tests/output/custom/unbelievable.html b/pelican/tests/output/custom/unbelievable.html index fcf4adaf..26b60f38 100644 --- a/pelican/tests/output/custom/unbelievable.html +++ b/pelican/tests/output/custom/unbelievable.html @@ -50,6 +50,12 @@

Or completely awesome. Depends the needs.

a root-relative link to markdown-article a file-relative link to markdown-article

+
+

Testing sourcecode directive

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

Lovely.

+
diff --git a/samples/content/unbelievable.rst b/samples/content/unbelievable.rst index 20cb9dc7..745ff217 100644 --- a/samples/content/unbelievable.rst +++ b/samples/content/unbelievable.rst @@ -7,3 +7,14 @@ Or completely awesome. Depends the needs. `a root-relative link to markdown-article <|filename|/cat1/markdown-article.md>`_ `a file-relative link to markdown-article <|filename|cat1/markdown-article.md>`_ + +Testing sourcecode directive +---------------------------- + +.. sourcecode:: python + :linenos: + + formatter = self.options and VARIANTS[self.options.keys()[0]] + + +Lovely. From 42f9726ffafac947b91425df58a70cc49aceb100 Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Thu, 25 Jul 2013 23:25:05 +0200 Subject: [PATCH 0061/1427] Support TAGS_* and AUTHORS_* by default. --- docs/settings.rst | 2 +- pelican/settings.py | 2 +- pelican/tests/output/basic/authors.html | 51 +++++++++++ pelican/tests/output/basic/tags.html | 56 +++++++++++++ pelican/tests/output/custom/authors.html | 79 +++++++++++++++++ pelican/tests/output/custom/tags.html | 84 +++++++++++++++++++ .../themes/notmyidea/templates/authors.html | 15 ++++ pelican/themes/notmyidea/templates/tags.html | 15 ++++ pelican/themes/simple/templates/authors.html | 10 +++ pelican/themes/simple/templates/tags.html | 10 +++ 10 files changed, 322 insertions(+), 2 deletions(-) create mode 100644 pelican/tests/output/basic/authors.html create mode 100644 pelican/tests/output/custom/authors.html create mode 100644 pelican/themes/notmyidea/templates/tags.html create mode 100644 pelican/themes/simple/templates/authors.html diff --git a/docs/settings.rst b/docs/settings.rst index 8ecac7c9..0d8f924f 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -244,7 +244,7 @@ Setting name (default value) What does it do? `CATEGORY_SAVE_AS` (``'category/{slug}.html'``) The location to save a category. `TAG_URL` (``'tag/{slug}.html'``) The URL to use for a tag. `TAG_SAVE_AS` (``'tag/{slug}.html'``) The location to save the tag page. -`TAGS_URL` (``'tag/{slug}.html'``) The URL to use for the tag list. +`TAGS_URL` (``'tags.html'``) The URL to use for the tag list. `TAGS_SAVE_AS` (``'tags.html'``) The location to save the tag list. `AUTHOR_URL` (``'author/{slug}.html'``) The URL to use for an author. `AUTHOR_SAVE_AS` (``'author/{slug}.html'``) The location to save an author. diff --git a/pelican/settings.py b/pelican/settings.py index 0f37c98d..d4ce0c5c 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -84,7 +84,7 @@ DEFAULT_CONFIG = { 'DEFAULT_LANG': 'en', 'TAG_CLOUD_STEPS': 4, 'TAG_CLOUD_MAX_ITEMS': 100, - 'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'archives'), + 'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'authors', 'archives'), 'EXTRA_TEMPLATES_PATHS': [], 'PAGINATED_DIRECT_TEMPLATES': ('index', ), 'PELICAN_CLASS': 'pelican.Pelican', diff --git a/pelican/tests/output/basic/authors.html b/pelican/tests/output/basic/authors.html new file mode 100644 index 00000000..766bf566 --- /dev/null +++ b/pelican/tests/output/basic/authors.html @@ -0,0 +1,51 @@ + + + + + A Pelican Blog - Authors + + + + + + + + + +
+

Authors on A Pelican Blog

  • Alexis Métaireau (2)
  • +
    + +
    + +
    + + + + + \ No newline at end of file diff --git a/pelican/tests/output/basic/tags.html b/pelican/tests/output/basic/tags.html index e69de29b..b9fa57f9 100644 --- a/pelican/tests/output/basic/tags.html +++ b/pelican/tests/output/basic/tags.html @@ -0,0 +1,56 @@ + + + + + A Pelican Blog - Tags + + + + + + + + + +
    +

    Tags for A Pelican Blog

  • bar (3)
  • +
  • baz (1)
  • +
  • foo (2)
  • +
  • foobar (1)
  • +
  • oh (1)
  • +
  • yeah (1)
  • +
    + +
    + +
    + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom/authors.html b/pelican/tests/output/custom/authors.html new file mode 100644 index 00000000..264c1698 --- /dev/null +++ b/pelican/tests/output/custom/authors.html @@ -0,0 +1,79 @@ + + + + + Alexis' log - Authors + + + + + + + + + +Fork me on GitHub + + + +
    +

    Authors on Alexis' log

  • Alexis Métaireau (10)
  • +
    + +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom/tags.html b/pelican/tests/output/custom/tags.html index e69de29b..7130b3ae 100644 --- a/pelican/tests/output/custom/tags.html +++ b/pelican/tests/output/custom/tags.html @@ -0,0 +1,84 @@ + + + + + Alexis' log - Tags + + + + + + + + + +Fork me on GitHub + + + +
    +

    Tags for Alexis' log

  • bar (3)
  • +
  • baz (1)
  • +
  • foo (2)
  • +
  • foobar (1)
  • +
  • oh (1)
  • +
  • yeah (1)
  • +
    + +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/themes/notmyidea/templates/authors.html b/pelican/themes/notmyidea/templates/authors.html index e69de29b..a203422b 100644 --- a/pelican/themes/notmyidea/templates/authors.html +++ b/pelican/themes/notmyidea/templates/authors.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block title %}{{ SITENAME }} - Authors{% endblock %} + +{% block content %} + +
    +

    Authors on {{ SITENAME }}

    + + {%- for author, articles in authors|sort %} +
  • {{ author }} ({{ articles|count }})
  • + {% endfor %} +
    + +{% endblock %} diff --git a/pelican/themes/notmyidea/templates/tags.html b/pelican/themes/notmyidea/templates/tags.html new file mode 100644 index 00000000..76955f27 --- /dev/null +++ b/pelican/themes/notmyidea/templates/tags.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block title %}{{ SITENAME }} - Tags{% endblock %} + +{% block content %} + +
    +

    Tags for {{ SITENAME }}

    + + {%- for tag, articles in tags|sort %} +
  • {{ tag }} ({{ articles|count }})
  • + {% endfor %} +
    + +{% endblock %} diff --git a/pelican/themes/simple/templates/authors.html b/pelican/themes/simple/templates/authors.html new file mode 100644 index 00000000..fa9b5170 --- /dev/null +++ b/pelican/themes/simple/templates/authors.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} + +{% block title %}{{ SITENAME }} - Authors{% endblock %} + +{% block content %} +

    Authors on {{ SITENAME }}

    + {%- for author, articles in authors|sort %} +
  • {{ author }} ({{ articles|count }})
  • + {% endfor %} +{% endblock %} diff --git a/pelican/themes/simple/templates/tags.html b/pelican/themes/simple/templates/tags.html index e69de29b..b5d1482d 100644 --- a/pelican/themes/simple/templates/tags.html +++ b/pelican/themes/simple/templates/tags.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} + +{% block title %}{{ SITENAME }} - Tags{% endblock %} + +{% block content %} +

    Tags for {{ SITENAME }}

    + {%- for tag, articles in tags|sort %} +
  • {{ tag }} ({{ articles|count }})
  • + {% endfor %} +{% endblock %} From bbea6d1747c3f95ab4b30aec47691e81bd4ef453 Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Mon, 5 Aug 2013 19:18:43 +0200 Subject: [PATCH 0062/1427] Explicitly set locale when generating test output --- docs/contribute.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/contribute.rst b/docs/contribute.rst index 80d07644..304d1de8 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -88,9 +88,10 @@ Pelican, and the changes to that output are expected and deemed correct given the nature of your changes, then you should update the output used by the functional tests. To do so, you can use the following two commands:: - $ pelican -o pelican/tests/output/custom/ -s samples/pelican.conf.py \ + $ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/custom/ \ + -s samples/pelican.conf.py samples/content/ + $ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/basic/ \ samples/content/ - $ pelican -o pelican/tests/output/basic/ samples/content/ Testing on Python 2 and 3 ------------------------- From 6b68d940797a6889466cc95af4e3c1585d0d8b07 Mon Sep 17 00:00:00 2001 From: Nicholas Kuechler Date: Tue, 11 Jun 2013 21:43:23 -0500 Subject: [PATCH 0063/1427] Adds Rackspace Cloud Files support to quickstart and fabfile --- pelican/tools/pelican_quickstart.py | 7 +++++++ pelican/tools/templates/Makefile.in | 10 +++++++++- pelican/tools/templates/fabfile.py.in | 15 ++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 3f02355a..ba11d968 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -27,6 +27,9 @@ CONF = { 'ssh_user': 'root', 'ssh_target_dir': '/var/www', 's3_bucket': 'my_s3_bucket', + 'cloudfiles_username': 'my_rackspace_username', + 'cloudfiles_api_key': 'my_rackspace_api_key', + 'cloudfiles_container': 'my_cloudfiles_container', 'dropbox_dir': '~/Dropbox/Public/', 'default_pagination': 10, 'siteurl': '', @@ -210,6 +213,10 @@ needed by Pelican. CONF['dropbox_dir'] = ask('Where is your Dropbox directory?', str_compat, CONF['dropbox_dir']) if ask('Do you want to upload your website using S3?', answer=bool, default=False): CONF['s3_bucket'] = ask('What is the name of your S3 bucket?', str_compat, CONF['s3_bucket']) + if ask('Do you want to upload your website using Rackspace Cloud Files?', answer=bool, default=False): + CONF['cloudfiles_username'] = ask('What is your Rackspace Cloud username?', str_compat, CONF['cloudfiles_username']) + CONF['cloudfiles_api_key'] = ask('What is your Rackspace Cloud API key?', str_compat, CONF['cloudfiles_api_key']) + CONF['cloudfiles_container'] = ask('What is the name of your Cloud Files container?', str_compat, CONF['cloudfiles_container']) try: os.makedirs(os.path.join(CONF['basedir'], 'content')) diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in index f68694e3..4ca0af6a 100644 --- a/pelican/tools/templates/Makefile.in +++ b/pelican/tools/templates/Makefile.in @@ -18,6 +18,10 @@ SSH_TARGET_DIR=$ssh_target_dir S3_BUCKET=$s3_bucket +CLOUDFILES_USERNAME=$cloudfiles_username +CLOUDFILES_API_KEY=$cloudfiles_api_key +CLOUDFILES_CONTAINER=$cloudfiles_container + DROPBOX_DIR=$dropbox_dir DEBUG ?= 0 @@ -41,6 +45,7 @@ help: @echo ' dropbox_upload upload the web site via Dropbox ' @echo ' ftp_upload upload the web site via FTP ' @echo ' s3_upload upload the web site via S3 ' + @echo ' cf_upload upload the web site via Cloud Files' @echo ' github upload the web site via gh-pages ' @echo ' ' @echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html' @@ -92,8 +97,11 @@ ftp_upload: publish s3_upload: publish s3cmd sync $(OUTPUTDIR)/ s3://$(S3_BUCKET) --acl-public --delete-removed +cf_upload: publish + cd $(OUTPUTDIR) && swift -v -A https://auth.api.rackspacecloud.com/v1.0 -U $(CLOUDFILES_USERNAME) -K $(CLOUDFILES_API_KEY) upload -c $(CLOUDFILES_CONTAINER) . + github: publish ghp-import $$(OUTPUTDIR) git push origin gh-pages -.PHONY: html help clean regenerate serve devserver publish ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload github +.PHONY: html help clean regenerate serve devserver publish ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload cf_upload github diff --git a/pelican/tools/templates/fabfile.py.in b/pelican/tools/templates/fabfile.py.in index e991b763..73c64ac8 100644 --- a/pelican/tools/templates/fabfile.py.in +++ b/pelican/tools/templates/fabfile.py.in @@ -4,12 +4,17 @@ import os # Local path configuration (can be absolute or relative to fabfile) env.deploy_path = 'output' +DEPLOY_PATH = env.deploy_path # Remote server configuration production = '$ssh_user@$ssh_host:$ssh_port' dest_path = '$ssh_target_dir' -DEPLOY_PATH = env.deploy_path +# Rackspace Cloud Files configuration settings +env.cloudfiles_username = '$cloudfiles_username' +env.cloudfiles_api_key = '$cloudfiles_api_key' +env.cloudfiles_container = '$cloudfiles_container' + def clean(): if os.path.isdir(DEPLOY_PATH): @@ -36,6 +41,14 @@ def reserve(): def preview(): local('pelican -s publishconf.py') +def cf_upload(): + rebuild() + local('cd {deploy_path} && ' + 'swift -v -A https://auth.api.rackspacecloud.com/v1.0 ' + '-U {cloudfiles_username} ' + '-K {cloudfiles_api_key} ' + 'upload -c {cloudfiles_container} .'.format(**env)) + @hosts(production) def publish(): local('pelican -s publishconf.py') From ece437f8fd69975c9a78aa1bb4c04910d97e7a0e Mon Sep 17 00:00:00 2001 From: SDGSDG Date: Mon, 5 Aug 2013 19:53:32 -0700 Subject: [PATCH 0064/1427] Added more pygments options for code blocks --- pelican/rstdirectives.py | 39 ++++++++++---- pelican/tests/output/basic/category/misc.html | 4 +- .../tests/output/basic/feeds/all-en.atom.xml | 10 ++++ pelican/tests/output/basic/feeds/all.atom.xml | 10 ++++ .../tests/output/basic/feeds/misc.atom.xml | 10 ++++ pelican/tests/output/basic/index.html | 4 +- pelican/tests/output/basic/unbelievable.html | 10 ++++ .../custom/author/alexis-metaireau3.html | 4 +- .../tests/output/custom/category/misc.html | 4 +- .../tests/output/custom/feeds/all-en.atom.xml | 10 ++++ .../tests/output/custom/feeds/all.atom.xml | 10 ++++ pelican/tests/output/custom/feeds/all.rss.xml | 10 ++++ .../tests/output/custom/feeds/misc.atom.xml | 10 ++++ .../tests/output/custom/feeds/misc.rss.xml | 10 ++++ pelican/tests/output/custom/index3.html | 4 +- pelican/tests/output/custom/unbelievable.html | 10 ++++ samples/content/unbelievable.rst | 53 +++++++++++++++++++ 17 files changed, 196 insertions(+), 16 deletions(-) diff --git a/pelican/rstdirectives.py b/pelican/rstdirectives.py index 1fdd09d6..ba3a6b89 100644 --- a/pelican/rstdirectives.py +++ b/pelican/rstdirectives.py @@ -8,20 +8,29 @@ from pygments import highlight from pygments.lexers import get_lexer_by_name, TextLexer import re -INLINESTYLES = False -DEFAULT = HtmlFormatter(noclasses=INLINESTYLES) -VARIANTS = { - 'linenos': HtmlFormatter(noclasses=INLINESTYLES, linenos=True), -} - class Pygments(Directive): - """ Source code syntax hightlighting. + """ Source code syntax highlighting. """ required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = dict([(key, directives.flag) for key in VARIANTS]) + option_spec = { + 'anchorlinenos': directives.flag, + 'classprefix': directives.unchanged, + 'hl_lines': directives.unchanged, + 'lineanchors': directives.unchanged, + 'linenos': directives.unchanged, + 'linenospecial': directives.nonnegative_int, + 'linenostart': directives.nonnegative_int, + 'linenostep': directives.nonnegative_int, + 'lineseparator': directives.unchanged, + 'linespans': directives.unchanged, + 'nobackground': directives.flag, + 'nowrap': directives.flag, + 'tagsfile': directives.unchanged, + 'tagurlformat': directives.unchanged, + } has_content = True def run(self): @@ -31,9 +40,17 @@ class Pygments(Directive): except ValueError: # no lexer found - use the text one instead of an exception lexer = TextLexer() - # take an arbitrary option if more than one is given - formatter = self.options and VARIANTS[list(self.options.keys())[0]] \ - or DEFAULT + + if ('linenos' in self.options and + self.options['linenos'] not in ('table', 'inline')): + self.options['linenos'] = 'table' + + for flag in ('nowrap', 'nobackground', 'anchorlinenos'): + if flag in self.options: + self.options[flag] = True + + # noclasses should already default to False, but just in case... + formatter = HtmlFormatter(noclasses=False, **self.options) parsed = highlight('\n'.join(self.content), lexer, formatter) return [nodes.raw('', parsed, format='html')] diff --git a/pelican/tests/output/basic/category/misc.html b/pelican/tests/output/basic/category/misc.html index f3b9e451..1c4af16a 100644 --- a/pelican/tests/output/basic/category/misc.html +++ b/pelican/tests/output/basic/category/misc.html @@ -88,7 +88,9 @@

    Lovely.

    - +
    +

    Testing more sourcecode directives

    +
     8 def run(self):
    self.assert_has_content()
    10 try:
    lexer = get_lexer_by_name(self.arguments[0])
    12 except ValueError ...
    read more diff --git a/pelican/tests/output/basic/feeds/all-en.atom.xml b/pelican/tests/output/basic/feeds/all-en.atom.xml index 6d0c563f..5b8eb591 100644 --- a/pelican/tests/output/basic/feeds/all-en.atom.xml +++ b/pelican/tests/output/basic/feeds/all-en.atom.xml @@ -33,5 +33,15 @@ YEAH !</p> </pre></div> </td></tr></table><p>Lovely.</p> </div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00Ztag:,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/basic/feeds/all.atom.xml b/pelican/tests/output/basic/feeds/all.atom.xml index 1abf742a..d19b0c30 100644 --- a/pelican/tests/output/basic/feeds/all.atom.xml +++ b/pelican/tests/output/basic/feeds/all.atom.xml @@ -34,5 +34,15 @@ YEAH !</p> </pre></div> </td></tr></table><p>Lovely.</p> </div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00Ztag:,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/basic/feeds/misc.atom.xml b/pelican/tests/output/basic/feeds/misc.atom.xml index b5588ce7..34b6b4fb 100644 --- a/pelican/tests/output/basic/feeds/misc.atom.xml +++ b/pelican/tests/output/basic/feeds/misc.atom.xml @@ -10,5 +10,15 @@ </pre></div> </td></tr></table><p>Lovely.</p> </div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00Ztag:,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/basic/index.html b/pelican/tests/output/basic/index.html index 6989f25a..5ecaa393 100644 --- a/pelican/tests/output/basic/index.html +++ b/pelican/tests/output/basic/index.html @@ -221,7 +221,9 @@ YEAH !

    Lovely.

    - +
    +

    Testing more sourcecode directives

    +
     8 def run(self):
    self.assert_has_content()
    10 try:
    lexer = get_lexer_by_name(self.arguments[0])
    12 except ValueError ...
    read more diff --git a/pelican/tests/output/basic/unbelievable.html b/pelican/tests/output/basic/unbelievable.html index 3f2d2f80..1fcb5553 100644 --- a/pelican/tests/output/basic/unbelievable.html +++ b/pelican/tests/output/basic/unbelievable.html @@ -48,6 +48,16 @@
    1
    formatter = self.options and VARIANTS[self.options.keys()[0]]
     

    Lovely.

    + +
    +

    Testing more sourcecode directives

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

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

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

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

    Lovely.

    +
    +
    +

    Testing even more sourcecode directives

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

    Lovely.

    diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index e3e07e10..659c0e37 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -57,7 +57,9 @@

    Lovely.

    - +
    +

    Testing more sourcecode directives

    +
     8 def run(self):
    self.assert_has_content()
    10 try:
    lexer = get_lexer_by_name(self.arguments[0])
    12 except ValueError ...
    read more

    There are comments.

    diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html index 63c659da..db11dd9e 100644 --- a/pelican/tests/output/custom/category/misc.html +++ b/pelican/tests/output/custom/category/misc.html @@ -101,7 +101,9 @@

    Lovely.

    - +
    +

    Testing more sourcecode directives

    +
     8 def run(self):
    self.assert_has_content()
    10 try:
    lexer = get_lexer_by_name(self.arguments[0])
    12 except ValueError ...
    read more

    There are comments.

    diff --git a/pelican/tests/output/custom/feeds/all-en.atom.xml b/pelican/tests/output/custom/feeds/all-en.atom.xml index d4631d7c..ce25290d 100644 --- a/pelican/tests/output/custom/feeds/all-en.atom.xml +++ b/pelican/tests/output/custom/feeds/all-en.atom.xml @@ -33,5 +33,15 @@ YEAH !</p> </pre></div> </td></tr></table><p>Lovely.</p> </div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/all.atom.xml b/pelican/tests/output/custom/feeds/all.atom.xml index 2a673ada..68986d40 100644 --- a/pelican/tests/output/custom/feeds/all.atom.xml +++ b/pelican/tests/output/custom/feeds/all.atom.xml @@ -35,5 +35,15 @@ YEAH !</p> </pre></div> </td></tr></table><p>Lovely.</p> </div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/all.rss.xml b/pelican/tests/output/custom/feeds/all.rss.xml index e8cd9b80..7fee491a 100644 --- a/pelican/tests/output/custom/feeds/all.rss.xml +++ b/pelican/tests/output/custom/feeds/all.rss.xml @@ -35,5 +35,15 @@ YEAH !</p> </pre></div> </td></tr></table><p>Lovely.</p> </div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:unbelievable.htmlThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:tag/baz.html \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/misc.atom.xml b/pelican/tests/output/custom/feeds/misc.atom.xml index 250b1b65..9328e05a 100644 --- a/pelican/tests/output/custom/feeds/misc.atom.xml +++ b/pelican/tests/output/custom/feeds/misc.atom.xml @@ -10,5 +10,15 @@ </pre></div> </td></tr></table><p>Lovely.</p> </div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/misc.rss.xml b/pelican/tests/output/custom/feeds/misc.rss.xml index 195812cc..b708c70d 100644 --- a/pelican/tests/output/custom/feeds/misc.rss.xml +++ b/pelican/tests/output/custom/feeds/misc.rss.xml @@ -10,5 +10,15 @@ </pre></div> </td></tr></table><p>Lovely.</p> </div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:unbelievable.htmlThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:tag/baz.html \ No newline at end of file diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index 52657306..821182fd 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -57,7 +57,9 @@

    Lovely.

    - +
    +

    Testing more sourcecode directives

    +
     8 def run(self):
    self.assert_has_content()
    10 try:
    lexer = get_lexer_by_name(self.arguments[0])
    12 except ValueError ...
    read more

    There are comments.

    diff --git a/pelican/tests/output/custom/unbelievable.html b/pelican/tests/output/custom/unbelievable.html index 26b60f38..99512327 100644 --- a/pelican/tests/output/custom/unbelievable.html +++ b/pelican/tests/output/custom/unbelievable.html @@ -55,6 +55,16 @@
    1
    formatter = self.options and VARIANTS[self.options.keys()[0]]
     

    Lovely.

    + +
    +

    Testing more sourcecode directives

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

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

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

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

    Lovely.

    +
    +
    +

    Testing even more sourcecode directives

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

    Lovely.

    diff --git a/samples/content/unbelievable.rst b/samples/content/unbelievable.rst index 745ff217..b990d20c 100644 --- a/samples/content/unbelievable.rst +++ b/samples/content/unbelievable.rst @@ -18,3 +18,56 @@ Testing sourcecode directive Lovely. + +Testing more sourcecode directives +---------------------------------- + +.. sourcecode:: python + :anchorlinenos: + :classprefix: testing + :hl_lines: 10,11,12 + :lineanchors: foo + :linenos: inline + :linenospecial: 2 + :linenostart: 8 + :linenostep: 2 + :lineseparator:
    + :linespans: foo + :nobackground: + + def run(self): + self.assert_has_content() + try: + lexer = get_lexer_by_name(self.arguments[0]) + except ValueError: + # no lexer found - use the text one instead of an exception + lexer = TextLexer() + + if ('linenos' in self.options and + self.options['linenos'] not in ('table', 'inline')): + self.options['linenos'] = 'table' + + for flag in ('nowrap', 'nobackground', 'anchorlinenos'): + if flag in self.options: + self.options[flag] = True + + # noclasses should already default to False, but just in case... + formatter = HtmlFormatter(noclasses=False, **self.options) + parsed = highlight('\n'.join(self.content), lexer, formatter) + return [nodes.raw('', parsed, format='html')] + + +Lovely. + +Testing even more sourcecode directives +--------------------------------------- + +.. sourcecode:: python + :linenos: table + :nowrap: + + + formatter = self.options and VARIANTS[self.options.keys()[0]] + + +Lovely. From 4bc4b1500c91b63a37891e60a1e262e4f3fada1c Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Sun, 4 Aug 2013 17:02:58 +0200 Subject: [PATCH 0065/1427] Refactor readers and remove MARKUP Add a `Readers` class which contains a dict of file extensions / `Reader` instances. This dict can be overwritten with a `READERS` settings, for instance to avoid processing *.html files: READERS = {'html': None} Or to add a custom reader for the `foo` extension: READERS = {'foo': FooReader} This dict is no storing the Reader classes as it was done before with `EXTENSIONS`. It stores the instances of the Reader classes to avoid instancing for each file reading. --- pelican/__init__.py | 22 ++-- pelican/generators.py | 37 +++--- pelican/readers.py | 209 +++++++++++++++++-------------- pelican/settings.py | 56 +++++---- pelican/tests/test_generators.py | 63 +++++----- pelican/tests/test_readers.py | 6 +- pelican/tests/test_utils.py | 11 +- 7 files changed, 201 insertions(+), 203 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 9bce4926..8ba79e0a 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -17,6 +17,7 @@ from pelican.generators import (ArticlesGenerator, PagesGenerator, StaticGenerator, SourceFileGenerator, TemplatePagesGenerator) from pelican.log import init +from pelican.readers import Readers from pelican.settings import read_settings from pelican.utils import clean_output_dir, folder_watcher, file_watcher from pelican.writers import Writer @@ -46,7 +47,6 @@ class Pelican(object): self.path = settings['PATH'] self.theme = settings['THEME'] self.output_path = settings['OUTPUT_PATH'] - self.markup = settings['MARKUP'] self.ignore_files = settings['IGNORE_FILES'] self.delete_outputdir = settings['DELETE_OUTPUT_DIRECTORY'] self.output_retention = settings['OUTPUT_RETENTION'] @@ -164,7 +164,6 @@ class Pelican(object): path=self.path, theme=self.theme, output_path=self.output_path, - markup=self.markup, ) for cls in self.get_generator_classes() ] @@ -236,10 +235,6 @@ def parse_arguments(): help='Where to output the generated files. If not specified, a ' 'directory will be created, named "output" in the current path.') - parser.add_argument('-m', '--markup', dest='markup', - help='The list of markup language to use (rst or md). Please indicate ' - 'them separated by commas.') - parser.add_argument('-s', '--settings', dest='settings', help='The settings of the application, this is automatically set to ' '{0} if a file exists with this name.'.format(DEFAULT_CONFIG_NAME)) @@ -279,8 +274,6 @@ def get_config(args): if args.output: config['OUTPUT_PATH'] = \ os.path.abspath(os.path.expanduser(args.output)) - if args.markup: - config['MARKUP'] = [a.strip().lower() for a in args.markup.split(',')] if args.theme: abstheme = os.path.abspath(os.path.expanduser(args.theme)) config['THEME'] = abstheme if os.path.exists(abstheme) else args.theme @@ -296,8 +289,6 @@ def get_config(args): for key in config: if key in ('PATH', 'OUTPUT_PATH', 'THEME'): config[key] = config[key].decode(enc) - if key == "MARKUP": - config[key] = [a.decode(enc) for a in config[key]] return config @@ -315,16 +306,17 @@ def get_instance(args): module = __import__(module) cls = getattr(module, cls_name) - return cls(settings) + return cls(settings), settings def main(): args = parse_arguments() init(args.verbosity) - pelican = get_instance(args) + pelican, settings = get_instance(args) + readers = Readers(settings) watchers = {'content': folder_watcher(pelican.path, - pelican.markup, + readers.extensions, pelican.ignore_files), 'theme': folder_watcher(pelican.theme, [''], @@ -333,8 +325,8 @@ def main(): try: if args.autoreload: - print(' --- AutoReload Mode: Monitoring `content`, `theme` and `settings`' - ' for changes. ---') + print(' --- AutoReload Mode: Monitoring `content`, `theme` and' + ' `settings` for changes. ---') while True: try: diff --git a/pelican/generators.py b/pelican/generators.py index 1444c95c..72c76b32 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -13,16 +13,13 @@ from functools import partial from itertools import chain, groupby from operator import attrgetter, itemgetter -from jinja2 import ( - Environment, FileSystemLoader, PrefixLoader, ChoiceLoader, BaseLoader, - TemplateNotFound -) +from jinja2 import (Environment, FileSystemLoader, PrefixLoader, ChoiceLoader, + BaseLoader, TemplateNotFound) from pelican.contents import Article, Page, Static, is_valid_content -from pelican.readers import read_file +from pelican.readers import Readers from pelican.utils import copy, process_translations, mkdir_p, DateFormatter from pelican import signals -import pelican.utils logger = logging.getLogger(__name__) @@ -31,23 +28,23 @@ logger = logging.getLogger(__name__) class Generator(object): """Baseclass generator""" - def __init__(self, context, settings, path, theme, output_path, markup, - **kwargs): + def __init__(self, context, settings, path, theme, output_path, **kwargs): self.context = context self.settings = settings self.path = path self.theme = theme self.output_path = output_path - self.markup = markup for arg, value in kwargs.items(): setattr(self, arg, value) + self.readers = Readers(self.settings) + # templates cache self._templates = {} self._templates_path = [] self._templates_path.append(os.path.expanduser( - os.path.join(self.theme, 'templates'))) + os.path.join(self.theme, 'templates'))) self._templates_path += self.settings['EXTRA_TEMPLATES_PATHS'] theme_path = os.path.dirname(os.path.abspath(__file__)) @@ -85,9 +82,8 @@ class Generator(object): try: self._templates[name] = self.env.get_template(name + '.html') except TemplateNotFound: - raise Exception( - ('[templates] unable to load %s.html from %s' - % (name, self._templates_path))) + raise Exception('[templates] unable to load %s.html from %s' + % (name, self._templates_path)) return self._templates[name] def _include_path(self, path, extensions=None): @@ -98,7 +94,7 @@ class Generator(object): extensions are allowed) """ if extensions is None: - extensions = tuple(self.markup) + extensions = tuple(self.readers.extensions) basename = os.path.basename(path) if extensions is False or basename.endswith(extensions): return True @@ -388,9 +384,9 @@ class ArticlesGenerator(Generator): self.settings['ARTICLE_DIR'], exclude=self.settings['ARTICLE_EXCLUDES']): try: - article = read_file( + article = self.readers.read_file( base_path=self.path, path=f, content_class=Article, - settings=self.settings, context=self.context, + context=self.context, preread_signal=signals.article_generator_preread, preread_sender=self, context_signal=signals.article_generator_context, @@ -496,9 +492,9 @@ class PagesGenerator(Generator): self.settings['PAGE_DIR'], exclude=self.settings['PAGE_EXCLUDES']): try: - page = read_file( + page = self.readers.read_file( base_path=self.path, path=f, content_class=Page, - settings=self.settings, context=self.context, + context=self.context, preread_signal=signals.page_generator_preread, preread_sender=self, context_signal=signals.page_generator_context, @@ -557,10 +553,9 @@ class StaticGenerator(Generator): for static_path in self.settings['STATIC_PATHS']: for f in self.get_files( static_path, extensions=False): - static = read_file( + static = self.readers.read_file( base_path=self.path, path=f, content_class=Static, - fmt='static', - settings=self.settings, context=self.context, + fmt='static', context=self.context, preread_signal=signals.static_generator_preread, preread_sender=self, context_signal=signals.static_generator_context, diff --git a/pelican/readers.py b/pelican/readers.py index 3923245e..9cf78042 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -37,7 +37,6 @@ except ImportError: from pelican.contents import Page, Category, Tag, Author from pelican.utils import get_date, pelican_open -logger = logging.getLogger(__name__) METADATA_PROCESSORS = { 'tags': lambda x, y: [Tag(tag, y) for tag in x.split(',')], @@ -50,7 +49,7 @@ METADATA_PROCESSORS = { logger = logging.getLogger(__name__) -class Reader(object): +class BaseReader(object): enabled = True file_extensions = ['static'] extensions = None @@ -110,7 +109,7 @@ class PelicanHTMLTranslator(HTMLTranslator): return HTMLTranslator.visit_image(self, node) -class RstReader(Reader): +class RstReader(BaseReader): enabled = bool(docutils) file_extensions = ['rst'] @@ -166,7 +165,7 @@ class RstReader(Reader): return content, metadata -class MarkdownReader(Reader): +class MarkdownReader(BaseReader): enabled = bool(Markdown) file_extensions = ['md', 'markdown', 'mkd', 'mdown'] @@ -174,7 +173,6 @@ class MarkdownReader(Reader): super(MarkdownReader, self).__init__(*args, **kwargs) self.extensions = self.settings['MD_EXTENSIONS'] self.extensions.append('meta') - self._md = Markdown(extensions=self.extensions) def _parse_metadata(self, meta): """Return the dict containing document metadata""" @@ -194,6 +192,7 @@ class MarkdownReader(Reader): def read(self, source_path): """Parse content and metadata of markdown files""" + self._md = Markdown(extensions=self.extensions) with pelican_open(source_path) as text: content = self._md.convert(text) @@ -201,7 +200,7 @@ class MarkdownReader(Reader): return content, metadata -class HTMLReader(Reader): +class HTMLReader(BaseReader): """Parses HTML files as input, looking for meta, title, and body tags""" file_extensions = ['htm', 'html'] enabled = True @@ -312,7 +311,7 @@ class HTMLReader(Reader): return parser.body, metadata -class AsciiDocReader(Reader): +class AsciiDocReader(BaseReader): enabled = bool(asciidoc) file_extensions = ['asc'] default_options = ["--no-header-footer", "-a newline=\\n"] @@ -344,109 +343,125 @@ class AsciiDocReader(Reader): return content, metadata -EXTENSIONS = {} +class Readers(object): -for cls in [Reader] + Reader.__subclasses__(): - for ext in cls.file_extensions: - EXTENSIONS[ext] = cls + def __init__(self, settings=None): + self.settings = settings or {} + self.readers = {} + extensions = {} + for cls in [BaseReader] + BaseReader.__subclasses__(): + for ext in cls.file_extensions: + extensions[ext] = cls -def read_file(base_path, path, content_class=Page, fmt=None, - settings=None, context=None, - preread_signal=None, preread_sender=None, - context_signal=None, context_sender=None): - """Return a content object parsed with the given format.""" - path = os.path.abspath(os.path.join(base_path, path)) - source_path = os.path.relpath(path, base_path) - base, ext = os.path.splitext(os.path.basename(path)) - logger.debug('read file {} -> {}'.format( + if self.settings['READERS']: + extensions.update(self.settings['READERS']) + + for fmt, reader_class in extensions.items(): + if not reader_class: + continue + + if not reader_class.enabled: + logger.warning('Missing dependencies for {}'.format(fmt)) + continue + + self.readers[fmt] = reader_class(self.settings) + + settings_key = '%s_EXTENSIONS' % fmt.upper() + + if settings_key in self.settings: + self.readers[fmt].extensions = self.settings[settings_key] + + @property + def extensions(self): + return self.readers.keys() + + def read_file(self, base_path, path, content_class=Page, fmt=None, + context=None, preread_signal=None, preread_sender=None, + context_signal=None, context_sender=None): + """Return a content object parsed with the given format.""" + + path = os.path.abspath(os.path.join(base_path, path)) + source_path = os.path.relpath(path, base_path) + logger.debug('read file {} -> {}'.format( source_path, content_class.__name__)) - if not fmt: - fmt = ext[1:] - if fmt not in EXTENSIONS: - raise TypeError('Pelican does not know how to parse {}'.format(path)) + if not fmt: + _, ext = os.path.splitext(os.path.basename(path)) + fmt = ext[1:] - if preread_signal: - logger.debug('signal {}.send({})'.format( + if fmt not in self.readers: + raise TypeError( + 'Pelican does not know how to parse {}'.format(path)) + + if preread_signal: + logger.debug('signal {}.send({})'.format( preread_signal, preread_sender)) - preread_signal.send(preread_sender) + preread_signal.send(preread_sender) - if settings is None: - settings = {} + reader = self.readers[fmt] - reader_class = EXTENSIONS[fmt] - if not reader_class.enabled: - raise ValueError('Missing dependencies for {}'.format(fmt)) - - reader = reader_class(settings) - - settings_key = '%s_EXTENSIONS' % fmt.upper() - - if settings and settings_key in settings: - reader.extensions = settings[settings_key] - - metadata = default_metadata( - settings=settings, process=reader.process_metadata) - metadata.update(path_metadata( - full_path=path, source_path=source_path, settings=settings)) - metadata.update(parse_path_metadata( - source_path=source_path, settings=settings, + metadata = default_metadata( + settings=self.settings, process=reader.process_metadata) + metadata.update(path_metadata( + full_path=path, source_path=source_path, + settings=self.settings)) + metadata.update(parse_path_metadata( + source_path=source_path, settings=self.settings, process=reader.process_metadata)) - content, reader_metadata = reader.read(path) - metadata.update(reader_metadata) - # create warnings for all images with empty alt (up to a certain number) - # as they are really likely to be accessibility flaws - if content: - # find images with empty alt - imgs = re.compile(r""" - (?: - # src before alt - ]* - src=(['"])(.*)\1 - [^\>]* - alt=(['"])\3 - )|(?: - # alt before src - ]* - alt=(['"])\4 - [^\>]* - src=(['"])(.*)\5 - ) - """, re.X) - matches = re.findall(imgs, content) - # find a correct threshold - nb_warnings = 10 - if len(matches) == nb_warnings + 1: - nb_warnings += 1 # avoid bad looking case - # print one warning per image with empty alt until threshold - for match in matches[:nb_warnings]: - logger.warning('Empty alt attribute for image {} in {}'.format( - os.path.basename(match[1] + match[5]), path)) - # print one warning for the other images with empty alt - if len(matches) > nb_warnings: - logger.warning('{} other images with empty alt attributes'.format( - len(matches) - nb_warnings)) + content, reader_metadata = reader.read(path) + metadata.update(reader_metadata) - # eventually filter the content with typogrify if asked so - if content and settings and settings['TYPOGRIFY']: - from typogrify.filters import typogrify - content = typogrify(content) - metadata['title'] = typogrify(metadata['title']) + # create warnings for all images with empty alt (up to a certain + # number) # as they are really likely to be accessibility flaws + if content: + # find images with empty alt + imgs = re.compile(r""" + (?: + # src before alt + ]* + src=(['"])(.*)\1 + [^\>]* + alt=(['"])\3 + )|(?: + # alt before src + ]* + alt=(['"])\4 + [^\>]* + src=(['"])(.*)\5 + ) + """, re.X) + matches = re.findall(imgs, content) + # find a correct threshold + nb_warnings = 10 + if len(matches) == nb_warnings + 1: + nb_warnings += 1 # avoid bad looking case + # print one warning per image with empty alt until threshold + for match in matches[:nb_warnings]: + logger.warning('Empty alt attribute for image {} in {}'.format( + os.path.basename(match[1] + match[5]), path)) + # print one warning for the other images with empty alt + if len(matches) > nb_warnings: + logger.warning('{} other images with empty alt attributes' + .format(len(matches) - nb_warnings)) - if context_signal: - logger.debug('signal {}.send({}, )'.format( + # eventually filter the content with typogrify if asked so + if content and self.settings['TYPOGRIFY']: + from typogrify.filters import typogrify + content = typogrify(content) + metadata['title'] = typogrify(metadata['title']) + + if context_signal: + logger.debug('signal {}.send({}, )'.format( context_signal, context_sender)) - context_signal.send(context_sender, metadata=metadata) - return content_class( - content=content, - metadata=metadata, - settings=settings, - source_path=path, - context=context) + context_signal.send(context_sender, metadata=metadata) + + return content_class(content=content, metadata=metadata, + settings=self.settings, source_path=path, + context=context) def default_metadata(settings=None, process=None): @@ -482,7 +497,7 @@ def parse_path_metadata(source_path, settings=None, process=None): ... 'PATH_METADATA': ... '(?P[^/]*)/(?P\d{4}-\d{2}-\d{2})/.*', ... } - >>> reader = Reader(settings=settings) + >>> reader = BaseReader(settings=settings) >>> metadata = parse_path_metadata( ... source_path='my-cat/2013-01-01/my-slug.html', ... settings=settings, diff --git a/pelican/settings.py b/pelican/settings.py index 0f37c98d..e71796a2 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -33,7 +33,7 @@ DEFAULT_CONFIG = { 'PAGE_EXCLUDES': (), 'THEME': DEFAULT_THEME, 'OUTPUT_PATH': 'output', - 'MARKUP': ('rst', 'md'), + 'READERS': {}, 'STATIC_PATHS': ['images', ], 'THEME_STATIC_DIR': 'theme', 'THEME_STATIC_PATHS': ['static', ], @@ -112,6 +112,7 @@ DEFAULT_CONFIG = { 'SLUG_SUBSTITUTIONS': (), } + def read_settings(path=None, override=None): if path: local_settings = get_settings_from_file(path) @@ -120,7 +121,7 @@ def read_settings(path=None, override=None): if p in local_settings and local_settings[p] is not None \ and not isabs(local_settings[p]): absp = os.path.abspath(os.path.normpath(os.path.join( - os.path.dirname(path), local_settings[p]))) + os.path.dirname(path), local_settings[p]))) if p not in ('THEME', 'PLUGIN_PATH') or os.path.exists(absp): local_settings[p] = absp else: @@ -138,7 +139,7 @@ def get_settings_from_module(module=None, default_settings=DEFAULT_CONFIG): context = copy.deepcopy(default_settings) if module is not None: context.update( - (k, v) for k, v in inspect.getmembers(module) if k.isupper()) + (k, v) for k, v in inspect.getmembers(module) if k.isupper()) return context @@ -221,17 +222,18 @@ def configure_settings(settings): settings['FEED_DOMAIN'] = settings['SITEURL'] # Warn if feeds are generated with both SITEURL & FEED_DOMAIN undefined - feed_keys = ['FEED_ATOM', 'FEED_RSS', - 'FEED_ALL_ATOM', 'FEED_ALL_RSS', - 'CATEGORY_FEED_ATOM', 'CATEGORY_FEED_RSS', - 'TAG_FEED_ATOM', 'TAG_FEED_RSS', - 'TRANSLATION_FEED_ATOM', 'TRANSLATION_FEED_RSS', - ] + feed_keys = [ + 'FEED_ATOM', 'FEED_RSS', + 'FEED_ALL_ATOM', 'FEED_ALL_RSS', + 'CATEGORY_FEED_ATOM', 'CATEGORY_FEED_RSS', + 'TAG_FEED_ATOM', 'TAG_FEED_RSS', + 'TRANSLATION_FEED_ATOM', 'TRANSLATION_FEED_RSS', + ] if any(settings.get(k) for k in feed_keys): if not settings.get('SITEURL'): - logger.warning('Feeds generated without SITEURL set properly may not' - ' be valid') + logger.warning('Feeds generated without SITEURL set properly may' + ' not be valid') if not 'TIMEZONE' in settings: logger.warning( @@ -255,26 +257,26 @@ def configure_settings(settings): # Save people from accidentally setting a string rather than a list path_keys = ( - 'ARTICLE_EXCLUDES', - 'DEFAULT_METADATA', - 'DIRECT_TEMPLATES', - 'EXTRA_TEMPLATES_PATHS', - 'FILES_TO_COPY', - 'IGNORE_FILES', - 'JINJA_EXTENSIONS', - 'MARKUP', - 'PAGINATED_DIRECT_TEMPLATES', - 'PLUGINS', - 'STATIC_PATHS', - 'THEME_STATIC_PATHS',) + 'ARTICLE_EXCLUDES', + 'DEFAULT_METADATA', + 'DIRECT_TEMPLATES', + 'EXTRA_TEMPLATES_PATHS', + 'FILES_TO_COPY', + 'IGNORE_FILES', + 'JINJA_EXTENSIONS', + 'PAGINATED_DIRECT_TEMPLATES', + 'PLUGINS', + 'STATIC_PATHS', + 'THEME_STATIC_PATHS', + ) for PATH_KEY in filter(lambda k: k in settings, path_keys): if isinstance(settings[PATH_KEY], six.string_types): - logger.warning("Detected misconfiguration with %s setting (must " - "be a list), falling back to the default" - % PATH_KEY) + logger.warning("Detected misconfiguration with %s setting " + "(must be a list), falling back to the default" + % PATH_KEY) settings[PATH_KEY] = DEFAULT_CONFIG[PATH_KEY] - for old,new,doc in [ + 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'), diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index d8a4336d..bd4e6021 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -20,8 +20,7 @@ class TestGenerator(unittest.TestCase): def setUp(self): self.settings = get_settings() self.generator = Generator(self.settings.copy(), self.settings, - CUR_DIR, self.settings['THEME'], None, - self.settings['MARKUP']) + CUR_DIR, self.settings['THEME'], None) def test_include_path(self): filename = os.path.join(CUR_DIR, 'content', 'article.rst') @@ -30,10 +29,6 @@ class TestGenerator(unittest.TestCase): self.assertTrue(include_path(filename, extensions=('rst',))) self.assertFalse(include_path(filename, extensions=('md',))) - # markup must be a tuple, test that this works also with a list - self.generator.markup = ['rst', 'md'] - self.assertTrue(include_path(filename)) - class TestArticlesGenerator(unittest.TestCase): @@ -45,8 +40,7 @@ class TestArticlesGenerator(unittest.TestCase): cls.generator = ArticlesGenerator( context=settings.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], - output_path=None, markup=settings['MARKUP']) + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) cls.generator.generate_context() cls.articles = [[page.title, page.status, page.category.name, page.template] for page in cls.generator.articles] @@ -55,8 +49,7 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings() generator = ArticlesGenerator( context=settings, settings=settings, - path=None, theme=settings['THEME'], - output_path=None, markup=settings['MARKUP']) + path=None, theme=settings['THEME'], output_path=None) writer = MagicMock() generator.generate_feeds(writer) writer.write_feed.assert_called_with([], settings, @@ -64,8 +57,7 @@ class TestArticlesGenerator(unittest.TestCase): generator = ArticlesGenerator( context=settings, settings=get_settings(FEED_ALL_ATOM=None), - path=None, theme=settings['THEME'], - output_path=None, markup=None) + path=None, theme=settings['THEME'], output_path=None) writer = MagicMock() generator.generate_feeds(writer) self.assertFalse(writer.write_feed.called) @@ -74,26 +66,33 @@ class TestArticlesGenerator(unittest.TestCase): articles_expected = [ ['Article title', 'published', 'Default', 'article'], - ['Article with markdown and summary metadata single', 'published', - 'Default', 'article'], ['Article with markdown and summary metadata multi', 'published', 'Default', 'article'], + ['Article with markdown and summary metadata single', 'published', + 'Default', 'article'], + ['Article with markdown containing footnotes', 'published', + 'Default', 'article'], ['Article with template', 'published', 'Default', 'custom'], - ['Test md File', 'published', 'test', 'article'], ['Rst with filename metadata', 'published', 'yeah', 'article'], ['Test Markdown extensions', 'published', 'Default', 'article'], + ['Test markdown File', 'published', 'test', 'article'], + ['Test md File', 'published', 'test', 'article'], + ['Test mdown File', 'published', 'test', 'article'], + ['Test mkd File', 'published', 'test', 'article'], ['This is a super article !', 'published', 'Yeah', 'article'], + ['This is a super article !', 'published', 'Yeah', 'article'], + ['This is a super article !', 'published', 'yeah', 'article'], + ['This is a super article !', 'published', 'yeah', 'article'], + ['This is a super article !', 'published', 'yeah', 'article'], + ['This is a super article !', 'published', 'Default', 'article'], ['This is an article with category !', 'published', 'yeah', - 'article'], + 'article'], ['This is an article without category !', 'published', 'Default', 'article'], ['This is an article without category !', 'published', 'TestCategory', 'article'], - ['This is a super article !', 'published', 'yeah', 'article'], - ['マックOS X 10.8でパイソンとVirtualenvをインストールと設定', - 'published', '指導書', 'article'], - ['Article with markdown containing footnotes', 'published', - 'Default', 'article'] + ['マックOS X 10.8でパイソンとVirtualenvをインストールと設定', 'published', + '指導書', 'article'], ] self.assertEqual(sorted(articles_expected), sorted(self.articles)) @@ -124,8 +123,7 @@ class TestArticlesGenerator(unittest.TestCase): settings['filenames'] = {} generator = ArticlesGenerator( context=settings.copy(), settings=settings, - path=CONTENT_DIR, theme=settings['THEME'], - output_path=None, markup=settings['MARKUP']) + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) generator.generate_context() # test for name # categories are grouped by slug; if two categories have the same slug @@ -147,8 +145,7 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings(filenames={}) generator = ArticlesGenerator( context=settings, settings=settings, - path=None, theme=settings['THEME'], - output_path=None, markup=settings['MARKUP']) + path=None, theme=settings['THEME'], output_path=None) write = MagicMock() generator.generate_direct_templates(write) write.assert_called_with("archives.html", @@ -162,8 +159,7 @@ class TestArticlesGenerator(unittest.TestCase): settings['ARCHIVES_SAVE_AS'] = 'archives/index.html' generator = ArticlesGenerator( context=settings, settings=settings, - path=None, theme=settings['THEME'], - output_path=None, markup=settings['MARKUP']) + path=None, theme=settings['THEME'], output_path=None) write = MagicMock() generator.generate_direct_templates(write) write.assert_called_with("archives/index.html", @@ -178,8 +174,7 @@ class TestArticlesGenerator(unittest.TestCase): settings['ARCHIVES_SAVE_AS'] = 'archives/index.html' generator = ArticlesGenerator( context=settings, settings=settings, - path=None, theme=settings['THEME'], - output_path=None, markup=settings['MARKUP']) + path=None, theme=settings['THEME'], output_path=None) write = MagicMock() generator.generate_direct_templates(write) write.assert_called_count == 0 @@ -212,8 +207,7 @@ class TestPageGenerator(unittest.TestCase): generator = PagesGenerator( context=settings.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], - output_path=None, markup=settings['MARKUP']) + path=CUR_DIR, theme=settings['THEME'], output_path=None) generator.generate_context() pages = self.distill_pages(generator.pages) hidden_pages = self.distill_pages(generator.hidden_pages) @@ -252,13 +246,12 @@ class TestTemplatePagesGenerator(unittest.TestCase): settings = get_settings() settings['STATIC_PATHS'] = ['static'] settings['TEMPLATE_PAGES'] = { - 'template/source.html': 'generated/file.html' - } + 'template/source.html': 'generated/file.html' + } generator = TemplatePagesGenerator( context={'foo': 'bar'}, settings=settings, - path=self.temp_content, theme='', - output_path=self.temp_output, markup=None) + path=self.temp_content, theme='', output_path=self.temp_output) # create a dummy template file template_dir = os.path.join(self.temp_content, 'template') diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index c67b8a1f..43cf5ecf 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -19,8 +19,8 @@ class ReaderTest(unittest.TestCase): def read_file(self, path, **kwargs): # Isolate from future API changes to readers.read_file - return readers.read_file( - base_path=CONTENT_PATH, path=path, settings=get_settings(**kwargs)) + r = readers.Readers(settings=get_settings(**kwargs)) + return r.read_file(base_path=CONTENT_PATH, path=path) class RstReaderTest(ReaderTest): @@ -160,7 +160,7 @@ class MdReaderTest(ReaderTest): ' with some footnotes' '2

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

      Numbered footnote ' diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 0e65003a..3a1cceca 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -353,12 +353,13 @@ class TestDateFormatter(unittest.TestCase): 'French locale needed') def test_french_locale(self): settings = read_settings( - override = {'LOCALE': locale.normalize('fr_FR.UTF-8'), - 'TEMPLATE_PAGES': {'template/source.html': - 'generated/file.html'}}) + override={'LOCALE': locale.normalize('fr_FR.UTF-8'), + 'TEMPLATE_PAGES': {'template/source.html': + 'generated/file.html'}}) - generator = TemplatePagesGenerator({'date': self.date}, settings, - self.temp_content, '', self.temp_output, None) + generator = TemplatePagesGenerator( + {'date': self.date}, settings, + self.temp_content, '', self.temp_output) generator.env.filters.update({'strftime': utils.DateFormatter()}) writer = Writer(self.temp_output, settings=settings) From cfe72c2736755173c774fb8a612ba3a84dd81131 Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Sun, 4 Aug 2013 21:17:15 +0200 Subject: [PATCH 0066/1427] Disable asciidoc files for tests --- pelican/tests/test_generators.py | 3 +++ pelican/tests/test_utils.py | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index bd4e6021..f47ce7d3 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -19,6 +19,7 @@ CONTENT_DIR = os.path.join(CUR_DIR, 'content') class TestGenerator(unittest.TestCase): def setUp(self): self.settings = get_settings() + self.settings['READERS'] = {'asc': None} self.generator = Generator(self.settings.copy(), self.settings, CUR_DIR, self.settings['THEME'], None) @@ -37,6 +38,7 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings(filenames={}) settings['DEFAULT_CATEGORY'] = 'Default' settings['DEFAULT_DATE'] = (1970, 1, 1) + settings['READERS'] = {'asc': None} cls.generator = ArticlesGenerator( context=settings.copy(), settings=settings, @@ -120,6 +122,7 @@ class TestArticlesGenerator(unittest.TestCase): settings['DEFAULT_CATEGORY'] = 'Default' settings['DEFAULT_DATE'] = (1970, 1, 1) settings['USE_FOLDER_AS_CATEGORY'] = False + settings['READERS'] = {'asc': None} settings['filenames'] = {} generator = ArticlesGenerator( context=settings.copy(), settings=settings, diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 3a1cceca..0642926e 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -386,8 +386,9 @@ class TestDateFormatter(unittest.TestCase): 'TEMPLATE_PAGES': {'template/source.html': 'generated/file.html'}}) - generator = TemplatePagesGenerator({'date': self.date}, settings, - self.temp_content, '', self.temp_output, None) + generator = TemplatePagesGenerator( + {'date': self.date}, settings, + self.temp_content, '', self.temp_output) generator.env.filters.update({'strftime': utils.DateFormatter()}) writer = Writer(self.temp_output, settings=settings) From 85ea737a98d5e269a0140039f12bffbd43fe0bbb Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Wed, 7 Aug 2013 00:01:12 +0200 Subject: [PATCH 0067/1427] Add a signal to give access to the dict of Reader classes. --- pelican/readers.py | 13 ++++++++----- pelican/signals.py | 4 ++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pelican/readers.py b/pelican/readers.py index 9cf78042..e5283de7 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -34,6 +34,7 @@ try: except ImportError: from HTMLParser import HTMLParser +from pelican import signals from pelican.contents import Page, Category, Tag, Author from pelican.utils import get_date, pelican_open @@ -348,16 +349,18 @@ class Readers(object): def __init__(self, settings=None): self.settings = settings or {} self.readers = {} + self.reader_classes = {} - extensions = {} for cls in [BaseReader] + BaseReader.__subclasses__(): for ext in cls.file_extensions: - extensions[ext] = cls + self.reader_classes[ext] = cls if self.settings['READERS']: - extensions.update(self.settings['READERS']) + self.reader_classes.update(self.settings['READERS']) - for fmt, reader_class in extensions.items(): + signals.readers_init.send(self) + + for fmt, reader_class in self.reader_classes.items(): if not reader_class: continue @@ -484,7 +487,7 @@ def path_metadata(full_path, source_path, settings=None): metadata['date'] = datetime.datetime.fromtimestamp( os.stat(full_path).st_ctime) metadata.update(settings.get('EXTRA_PATH_METADATA', {}).get( - source_path, {})) + source_path, {})) return metadata diff --git a/pelican/signals.py b/pelican/signals.py index cb010d37..77802e88 100644 --- a/pelican/signals.py +++ b/pelican/signals.py @@ -8,6 +8,10 @@ initialized = signal('pelican_initialized') get_generators = signal('get_generators') finalized = signal('pelican_finalized') +# Reader-level signals + +readers_init = signal('readers_init') + # Generator-level signals generator_init = signal('generator_init') From bab8d0b26a80ab3c9b38311d26fccad938ec2220 Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Wed, 7 Aug 2013 00:10:26 +0200 Subject: [PATCH 0068/1427] Move the "find image with an empty alt" block in a function. --- pelican/readers.py | 79 +++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/pelican/readers.py b/pelican/readers.py index e5283de7..3b3bfd12 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -416,40 +416,9 @@ class Readers(object): content, reader_metadata = reader.read(path) metadata.update(reader_metadata) - # create warnings for all images with empty alt (up to a certain - # number) # as they are really likely to be accessibility flaws if content: # find images with empty alt - imgs = re.compile(r""" - (?: - # src before alt - ]* - src=(['"])(.*)\1 - [^\>]* - alt=(['"])\3 - )|(?: - # alt before src - ]* - alt=(['"])\4 - [^\>]* - src=(['"])(.*)\5 - ) - """, re.X) - matches = re.findall(imgs, content) - # find a correct threshold - nb_warnings = 10 - if len(matches) == nb_warnings + 1: - nb_warnings += 1 # avoid bad looking case - # print one warning per image with empty alt until threshold - for match in matches[:nb_warnings]: - logger.warning('Empty alt attribute for image {} in {}'.format( - os.path.basename(match[1] + match[5]), path)) - # print one warning for the other images with empty alt - if len(matches) > nb_warnings: - logger.warning('{} other images with empty alt attributes' - .format(len(matches) - nb_warnings)) + find_empty_alt(content, path) # eventually filter the content with typogrify if asked so if content and self.settings['TYPOGRIFY']: @@ -467,6 +436,45 @@ class Readers(object): context=context) +def find_empty_alt(content, path): + """Find images with empty alt + + Create warnings for all images with empty alt (up to a certain number), + as they are really likely to be accessibility flaws. + + """ + imgs = re.compile(r""" + (?: + # src before alt + ]* + src=(['"])(.*)\1 + [^\>]* + alt=(['"])\3 + )|(?: + # alt before src + ]* + alt=(['"])\4 + [^\>]* + src=(['"])(.*)\5 + ) + """, re.X) + matches = re.findall(imgs, content) + # find a correct threshold + nb_warnings = 10 + if len(matches) == nb_warnings + 1: + nb_warnings += 1 # avoid bad looking case + # print one warning per image with empty alt until threshold + for match in matches[:nb_warnings]: + logger.warning('Empty alt attribute for image {} in {}'.format( + os.path.basename(match[1] + match[5]), path)) + # print one warning for the other images with empty alt + if len(matches) > nb_warnings: + logger.warning('{} other images with empty alt attributes' + .format(len(matches) - nb_warnings)) + + def default_metadata(settings=None, process=None): metadata = {} if settings: @@ -516,13 +524,12 @@ def parse_path_metadata(source_path, settings=None, process=None): subdir = os.path.basename(dirname) if settings: checks = [] - for key,data in [('FILENAME_METADATA', base), - ('PATH_METADATA', source_path), - ]: + for key, data in [('FILENAME_METADATA', base), + ('PATH_METADATA', source_path)]: checks.append((settings.get(key, None), data)) if settings.get('USE_FOLDER_AS_CATEGORY', None): checks.insert(0, ('(?P.*)', subdir)) - for regexp,data in checks: + for regexp, data in checks: if regexp and data: match = re.match(regexp, data) if match: From f47f054d0be29d95ecb22e025f1a50932c00fd2b Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Sun, 4 Aug 2013 22:03:37 +0200 Subject: [PATCH 0069/1427] Add documentation for readers. --- docs/internals.rst | 4 ++-- docs/plugins.rst | 15 ++++++++------- docs/settings.rst | 7 ++++--- pelican/readers.py | 26 ++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/docs/internals.rst b/docs/internals.rst index 704122ba..f69a9bb8 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -24,7 +24,7 @@ The logic is separated into different classes and concepts: then passed to the generators. * **Readers** are used to read from various formats (AsciiDoc, HTML, Markdown and - reStructuredText for now, but the system is extensible). Given a file, they + reStructuredText for now, but the system is extensible). Given a file, they return metadata (author, tags, category, etc.) and content (HTML-formatted). * **Generators** generate the different outputs. For instance, Pelican comes with @@ -44,7 +44,7 @@ method that returns HTML content and some metadata. Take a look at the Markdown reader:: - class MarkdownReader(Reader): + class MarkdownReader(BaseReader): enabled = bool(Markdown) def read(self, source_path): diff --git a/docs/plugins.rst b/docs/plugins.rst index 93307afb..582f40a7 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -71,6 +71,7 @@ finalized pelican object invoked after al - minifying js/css assets. - notify/ping search engines with an updated sitemap. generator_init generator invoked in the Generator.__init__ +readers_init readers invoked in the Readers.__init__ article_generate_context article_generator, metadata article_generate_preread article_generator invoked before a article is read in ArticlesGenerator.generate_context; use if code needs to do something before every article is parsed @@ -144,13 +145,13 @@ write and don't slow down pelican itself when they're not active. No more talking, here is the example:: from pelican import signals - from pelican.readers import EXTENSIONS, Reader + from pelican.readers import BaseReader - # Create a new reader class, inheriting from the pelican.reader.Reader - class NewReader(Reader): + # Create a new reader class, inheriting from the pelican.reader.BaseReader + class NewReader(BaseReader): enabled = True # Yeah, you probably want that :-) - # The list of extensions you want this reader to match with. + # The list of file extensions you want this reader to match with. # In the case multiple readers use the same extensions, the latest will # win (so the one you're defining here, most probably). file_extensions = ['yeah'] @@ -168,12 +169,12 @@ No more talking, here is the example:: return "Some content", parsed - def add_reader(arg): - EXTENSIONS['yeah'] = NewReader + def add_reader(readers): + readers.reader_classes['yeah'] = NewReader # this is how pelican works. def register(): - signals.initialized.connect(add_reader) + signals.readers_init.connect(add_reader) Adding a new generator diff --git a/docs/settings.rst b/docs/settings.rst index 8ecac7c9..04574127 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -84,9 +84,10 @@ Setting name (default value) What doe here or a single string representing one locale. When providing a list, all the locales will be tried until one works. -`MARKUP` (``('rst', 'md')``) A list of available markup languages you want - to use. For the moment, the only available values - are `rst`, `md`, `markdown`, `mkd`, `mdown`, `html`, and `htm`. +`READERS` (``{}``) A dict of file extensions / Reader classes to overwrite or + add file readers. for instance, to avoid processing .html files: + ``READERS = {'html': None}``. Or to add a custom reader for the + `foo` extension: ``READERS = {'foo': FooReader}`` `IGNORE_FILES` (``['.#*']``) A list of file globbing patterns to match against the source files to be ignored by the processor. For example, the default ``['.#*']`` will ignore emacs lock files. diff --git a/pelican/readers.py b/pelican/readers.py index 3b3bfd12..97a184d0 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -51,6 +51,18 @@ logger = logging.getLogger(__name__) class BaseReader(object): + """Base class to read files. + + This class is used to process static files, and it can be inherited for + other types of file. A Reader class must have the following attributes: + + - enabled: (boolean) tell if the Reader class is enabled. It + generally depends on the import of some dependency. + - file_extensions: a list of file extensions that the Reader will process. + - extensions: a list of extensions to use in the reader (typical use is + Markdown). + + """ enabled = True file_extensions = ['static'] extensions = None @@ -111,6 +123,8 @@ class PelicanHTMLTranslator(HTMLTranslator): class RstReader(BaseReader): + """Reader for reStructuredText files""" + enabled = bool(docutils) file_extensions = ['rst'] @@ -167,6 +181,8 @@ class RstReader(BaseReader): class MarkdownReader(BaseReader): + """Reader for Markdown files""" + enabled = bool(Markdown) file_extensions = ['md', 'markdown', 'mkd', 'mdown'] @@ -203,6 +219,7 @@ class MarkdownReader(BaseReader): class HTMLReader(BaseReader): """Parses HTML files as input, looking for meta, title, and body tags""" + file_extensions = ['htm', 'html'] enabled = True @@ -313,6 +330,8 @@ class HTMLReader(BaseReader): class AsciiDocReader(BaseReader): + """Reader for AsciiDoc files""" + enabled = bool(asciidoc) file_extensions = ['asc'] default_options = ["--no-header-footer", "-a newline=\\n"] @@ -345,7 +364,14 @@ class AsciiDocReader(BaseReader): class Readers(object): + """Interface for all readers. + This class contains a mapping of file extensions / Reader classes, to know + which Reader class must be used to read a file (based on its extension). + This is customizable both with the 'READERS' setting, and with the + 'readers_init' signall for plugins. + + """ def __init__(self, settings=None): self.settings = settings or {} self.readers = {} From 24e2ad5b3f0143851f74fd2f23b41b6fb85ed99a Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 7 Aug 2013 12:45:26 -0700 Subject: [PATCH 0070/1427] Improve AsciiDoc parts of Getting Started docs --- docs/getting_started.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/getting_started.rst b/docs/getting_started.rst index eb503295..6d9a8c41 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -48,7 +48,7 @@ Markdown library as well:: $ pip install Markdown -If you want to use AsciiDoc you need to install it from `source +If you want to use AsciiDoc_ you need to install it from `source `_ or use your operating system's package manager. @@ -323,6 +323,9 @@ pattern:: This is the content of my super blog post. +Conventions for AsciiDoc_ posts, which should have an ``.asc`` extension, can +be found on the AsciiDoc_ site. + Pelican can also process HTML files ending in ``.html`` and ``.htm``. Pelican interprets the HTML in a very straightforward manner, reading metadata from ``meta`` tags, the title from the ``title`` tag, and the body out from the @@ -551,3 +554,4 @@ listed on the index page nor on any category page. .. _virtualenv: http://www.virtualenv.org/ .. _W3C ISO 8601: http://www.w3.org/TR/NOTE-datetime .. _Fabric: http://fabfile.org/ +.. _AsciiDoc: http://www.methods.co.nz/asciidoc/ From c875c27e83941ecd2bcd401efe07b35421de704c Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Wed, 7 Aug 2013 22:43:08 +0200 Subject: [PATCH 0071/1427] Don't warn too loud about missing dependencies. - Show the messages only once for each Reader - Decrease the logging level --- pelican/readers.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pelican/readers.py b/pelican/readers.py index 97a184d0..3f01a72c 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -372,15 +372,28 @@ class Readers(object): 'readers_init' signall for plugins. """ + + # used to warn about missing dependencies only once, at the first + # instanciation of a Readers object. + warn_missing_deps = True + def __init__(self, settings=None): self.settings = settings or {} self.readers = {} self.reader_classes = {} for cls in [BaseReader] + BaseReader.__subclasses__(): + if not cls.enabled: + if self.__class__.warn_missing_deps: + logger.debug('Missing dependencies for {}' + .format(', '.join(cls.file_extensions))) + continue + for ext in cls.file_extensions: self.reader_classes[ext] = cls + self.__class__.warn_missing_deps = False + if self.settings['READERS']: self.reader_classes.update(self.settings['READERS']) @@ -390,10 +403,6 @@ class Readers(object): if not reader_class: continue - if not reader_class.enabled: - logger.warning('Missing dependencies for {}'.format(fmt)) - continue - self.readers[fmt] = reader_class(self.settings) settings_key = '%s_EXTENSIONS' % fmt.upper() From f2aef81c963c8daaa29ac28dba84243e6198c30f Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 8 Aug 2013 12:08:30 -0700 Subject: [PATCH 0072/1427] Work around pytz & pip 1.4+ problem. Fixes #996. The latest version of pip (1.4) no longer installs pre-release versions (alpha, beta, etc.) by default. Because pytz uses an unorthodox version number scheme, pip thinks it's a pre-release and skips it. This change to setup.py should alleviate the problem until it is otherwise resolved. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 69f76b3c..f56a7c41 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup requires = ['feedgenerator >= 1.6', 'jinja2 >= 2.7', 'pygments', 'docutils', - 'pytz', 'blinker', 'unidecode', 'six'] + 'pytz >= 0a', 'blinker', 'unidecode', 'six'] entry_points = { 'console_scripts': [ From a7152716e2e068c69c4ecde7f2cedeb3547a3411 Mon Sep 17 00:00:00 2001 From: Talha Mansoor Date: Sun, 11 Aug 2013 01:43:27 +0500 Subject: [PATCH 0073/1427] Receive the two values returned from get_instance() `get_instance()` returns two values. Old code, instead of unpacking two values in two variables, placed the tuple in a single variable `pelican`. Later in the same block when `pelican.run()` was called, it resulted in error. ``` -> Modified: content, theme, settings. re-generating... CRITICAL: ("'tuple' object has no attribute 'run'",) CRITICAL: 'tuple' object has no attribute 'run' Traceback (most recent call last): File "/Users/talha/Repos/VirtualEnvs/pelican-dev/bin/pelican", line 8, in load_entry_point('pelican==3.3', 'console_scripts', 'pelican')() File "/Users/talha/Repos/VirtualEnvs/pelican-dev/lib/python2.7/site-packages/pelican-3.3-py2.7.egg/pelican/__init__.py", line 353, in main pelican.run() ``` Either the returned value should be unpacked properly or `pelican[0].run` should be called. --- pelican/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 8ba79e0a..f2bd2e07 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -338,7 +338,7 @@ def main(): modified = {k: next(v) for k, v in watchers.items()} if modified['settings']: - pelican = get_instance(args) + pelican, settings = get_instance(args) if any(modified.values()): print('\n-> Modified: {}. re-generating...'.format( From b0a13a851c5e6d5088023a2eef5f239c42b0a134 Mon Sep 17 00:00:00 2001 From: Talha Mansoor Date: Sun, 11 Aug 2013 02:18:25 +0500 Subject: [PATCH 0074/1427] Fix a typo in the docs getpelican/pelican@f2d6f77462976f5ea291481cba40e041d42d9e8c changed `article_generate_context` to `article_generator_context`. This commit updates the docs to reflect the change. --- docs/plugins.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index 582f40a7..6e292efd 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -72,8 +72,8 @@ finalized pelican object invoked after al - notify/ping search engines with an updated sitemap. generator_init generator invoked in the Generator.__init__ readers_init readers invoked in the Readers.__init__ -article_generate_context article_generator, metadata -article_generate_preread article_generator invoked before a article is read in ArticlesGenerator.generate_context; +article_generator_context article_generator, metadata +article_generator_preread article_generator invoked before a article is read in ArticlesGenerator.generate_context; use if code needs to do something before every article is parsed article_generator_init article_generator invoked in the ArticlesGenerator.__init__ article_generator_finalized article_generator invoked at the end of ArticlesGenerator.generate_context From 472abb6fd9e9e1a5cfaebd70ec64c8469ddba8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Mon, 12 Aug 2013 14:49:25 +0200 Subject: [PATCH 0075/1427] Activate coveralls.io support --- .travis.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 918fd3f9..0c6bac89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,11 @@ before_install: install: - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then ln -s /usr/share/asciidoc/asciidocapi.py ~/virtualenv/python2.7/lib/python2.7/site-packages/; fi - pip install mock + - pip install mock nose nose-cov - pip install . - pip install Markdown -script: python -m unittest discover +script: nosetests -sv --with-coverage --cover-package=pelican pelican +after_success: + # Report coverage results to coveralls.io + - pip install coveralls + - coveralls From 3b315fbc53efe48012662dbcb39c82c2c5117ca4 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Mon, 12 Aug 2013 19:23:57 +0100 Subject: [PATCH 0076/1427] Fixing #1038 by allowing nice URLs. --- pelican/server.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/pelican/server.py b/pelican/server.py index 24f3ae04..0f7472c5 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -1,5 +1,7 @@ from __future__ import print_function +import os import sys +import logging try: import SimpleHTTPServer as srvmod except ImportError: @@ -11,19 +13,36 @@ except ImportError: import socketserver # NOQA PORT = len(sys.argv) == 2 and int(sys.argv[1]) or 8000 +SUFFIXES = ['','.html','/index.html'] -Handler = srvmod.SimpleHTTPRequestHandler +class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler): + def do_GET(self): + # we are trying to detect the file by having a fallback mechanism + r = None + for suffix in SUFFIXES: + if not hasattr(self,'original_path'): + self.original_path = self.path + self.path = self.original_path + suffix + path = self.translate_path(self.path) + if os.path.exists(path): + r = srvmod.SimpleHTTPRequestHandler.do_GET(self) + if r is not None: + break + logging.warning("Unable to find %s file." % self.path) + return r + +Handler = ComplexHTTPRequestHandler try: httpd = socketserver.TCPServer(("", PORT), Handler) except OSError as e: - print("Could not listen on port", PORT) + logging.error("Could not listen on port %s" % PORT) sys.exit(getattr(e, 'exitcode', 1)) -print("serving at port", PORT) +logging.info("serving at port %s" % PORT) try: httpd.serve_forever() except KeyboardInterrupt as e: - print("shutting down server") - httpd.socket.close() + logging.info("shutting down server") + httpd.socket.close() \ No newline at end of file From d9816be5deec20528d53adc5fc7455c973b8e63e Mon Sep 17 00:00:00 2001 From: Daniel Goldsmith Date: Tue, 13 Aug 2013 10:46:41 +0100 Subject: [PATCH 0077/1427] Tag Cloud made functional --- docs/settings.rst | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 55721c11..c709a339 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -522,14 +522,34 @@ Setting name (default value) What does it do? The default theme does not include a tag cloud, but it is pretty easy to add:: -

        + -You should then also define a CSS style with the appropriate classes (tag-0 to tag-N, where -N matches `TAG_CLOUD_STEPS` -1). +You should then also define CSS styles with appropriate classes (tag-0 to tag-N, where +N matches `TAG_CLOUD_STEPS` -1), tag-0 being the most frequent, and define a ul.tagcloud +class with appropriate list-style to create the cloud, for example:: + + ul.tagcloud { + list-style: none; + padding: 0; + } + + ul.tagcloud li { + display: inline-block; + } + + li.tag-0 { + font-size: 150%; + } + + li.tag-1 { + font-size: 120%; + } + + ... Translations ============ From 6191b2919e39d2ed61411b2defb99af2c4dee816 Mon Sep 17 00:00:00 2001 From: Ken Jung Date: Fri, 16 Aug 2013 13:31:14 -0700 Subject: [PATCH 0078/1427] StaticGenerator now stores file list in context. This allows plugins and other generators to easily add files to be simply copied over. --- pelican/generators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pelican/generators.py b/pelican/generators.py index 72c76b32..26fa40ea 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -562,13 +562,14 @@ class StaticGenerator(Generator): context_sender=self) self.staticfiles.append(static) self.add_source_path(static) + self._update_context(('staticfiles',)) def generate_output(self, writer): self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme, self.settings['THEME_STATIC_DIR'], self.output_path, os.curdir) # copy all Static files - for sc in self.staticfiles: + for sc in self.context['staticfiles']: source_path = os.path.join(self.path, sc.source_path) save_as = os.path.join(self.output_path, sc.save_as) mkdir_p(os.path.dirname(save_as)) From 73b37989f74c8f7f5b9c7cd4c625cece4cbef048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Sat, 17 Aug 2013 01:02:07 +0200 Subject: [PATCH 0079/1427] update the readers tests --- pelican/tests/test_readers.py | 41 +++++++++++++++-------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index cb7db575..1b3a2055 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -14,17 +14,6 @@ CONTENT_PATH = os.path.join(CUR_DIR, 'content') def _path(*args): return os.path.join(CONTENT_PATH, *args) -class ReaderTests(unittest.TestCase): - def test_readfile_unknown_extension(self): - f = _path('article_with_metadata.unknownextension') - with self.assertRaises(TypeError) as cm: - readers.read_file(f) - ex = cm.exception - self.assertEqual('Pelican does not know how to parse ' + f, ex.message) - #, setattr, root.c1.c2, 'text', "test") - # self.assertTrue(1 == 0) - # except TypeError: - # self.assertTrue(1 == 1) class ReaderTest(unittest.TestCase): @@ -34,6 +23,15 @@ class ReaderTest(unittest.TestCase): return r.read_file(base_path=CONTENT_PATH, path=path) +class DefaultReaderTest(ReaderTest): + + def test_readfile_unknown_extension(self): + with self.assertRaises(TypeError) as cm: + self.read_file(path='article_with_metadata.unknownextension') + ex = cm.exception + self.assertIn('Pelican does not know how to parse', ex.message) + + class RstReaderTest(ReaderTest): def test_article_with_metadata(self): @@ -98,11 +96,11 @@ class RstReaderTest(ReaderTest): # Keys of metadata should be lowercase. reader = readers.RstReader(settings=get_settings()) content, metadata = reader.read( - _path('article_with_uppercase_metadata.rst')) + _path('article_with_uppercase_metadata.rst')) self.assertIn('category', metadata, 'Key should be lowercase.') self.assertEqual('Yeah', metadata.get('category'), - 'Value keeps case.') + 'Value keeps case.') def test_typogrify(self): # if nothing is specified in the settings, the content should be @@ -158,7 +156,6 @@ class MdReaderTest(ReaderTest): for key, value in metadata.items(): self.assertEqual(value, expected[key], key) - @unittest.skipUnless(readers.Markdown, "markdown isn't installed") def test_article_with_footnote(self): reader = readers.MarkdownReader(settings=get_settings()) @@ -179,8 +176,8 @@ class MdReaderTest(ReaderTest): 'title="Jump back to footnote 1 in the text">↩

        \n' '\n
      • \n' '

        Named footnote ' - '

        \n' + '

        \n' '
      • \n
    \n
    ') expected_metadata = { 'title': 'Article with markdown containing footnotes', @@ -194,7 +191,6 @@ class MdReaderTest(ReaderTest): for key, value in metadata.items(): self.assertEqual(value, expected_metadata[key], key) - @unittest.skipUnless(readers.Markdown, "markdown isn't installed") def test_article_with_file_extensions(self): reader = readers.MarkdownReader(settings=get_settings()) @@ -203,9 +199,9 @@ class MdReaderTest(ReaderTest): content, metadata = reader.read( _path('article_with_md_extension.md')) expected = ( - "

    Test Markdown File Header

    \n" - "

    Used for pelican test

    \n" - "

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

    ") + "

    Test Markdown File Header

    \n" + "

    Used for pelican test

    \n" + "

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

    ") self.assertEqual(content, expected) # test to ensure the mkd file extension is being processed by the # correct reader @@ -319,7 +315,7 @@ class AdReaderTest(ReaderTest): def test_article_with_asc_options(self): # test to ensure the ASCIIDOC_OPTIONS is being used reader = readers.AsciiDocReader( - dict(ASCIIDOC_OPTIONS=["-a revision=1.0.42"])) + dict(ASCIIDOC_OPTIONS=["-a revision=1.0.42"])) content, metadata = reader.read(_path('article_with_asc_options.asc')) expected = ('
    \n

    Used for' ' pelican test

    \n

    version 1.0.42

    \n' @@ -375,7 +371,6 @@ class HTMLReaderTest(ReaderTest): for key, value in expected.items(): self.assertEqual(value, page.metadata[key], key) - def test_article_with_null_attributes(self): page = self.read_file(path='article_with_null_attributes.html') @@ -389,4 +384,4 @@ class HTMLReaderTest(ReaderTest): page = self.read_file(path='article_with_uppercase_metadata.html') self.assertIn('category', page.metadata, 'Key should be lowercase.') self.assertEqual('Yeah', page.metadata.get('category'), - 'Value keeps cases.') + 'Value keeps cases.') From aae56fee3935b771619c3fcf68976aae79bbfde0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Sat, 17 Aug 2013 01:12:39 +0200 Subject: [PATCH 0080/1427] exception.message was removed in py 3.X --- pelican/tests/test_readers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index 1b3a2055..b6652d03 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -26,10 +26,8 @@ class ReaderTest(unittest.TestCase): class DefaultReaderTest(ReaderTest): def test_readfile_unknown_extension(self): - with self.assertRaises(TypeError) as cm: + with self.assertRaises(TypeError): self.read_file(path='article_with_metadata.unknownextension') - ex = cm.exception - self.assertIn('Pelican does not know how to parse', ex.message) class RstReaderTest(ReaderTest): From 2a599b864624f483149739bb2c86e812cb74a2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Sat, 17 Aug 2013 01:23:09 +0200 Subject: [PATCH 0081/1427] Reactivate travis IRC reports. --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0c6bac89..1df32baa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,12 +8,15 @@ before_install: - sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8 install: - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then ln -s /usr/share/asciidoc/asciidocapi.py ~/virtualenv/python2.7/lib/python2.7/site-packages/; fi - - pip install mock - - pip install mock nose nose-cov + - pip install mock nose nose-cov Markdown - pip install . - - pip install Markdown script: nosetests -sv --with-coverage --cover-package=pelican pelican after_success: # Report coverage results to coveralls.io - pip install coveralls - coveralls +notifications: + irc: + channels: + - "irc.freenode.org#pelican" + on_success: change From 339955376e1f84fb5209dad9f18bc802f006d0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Sat, 17 Aug 2013 12:48:34 +0200 Subject: [PATCH 0082/1427] Add a content_written signal --- docs/plugins.rst | 1 + pelican/signals.py | 3 +++ pelican/writers.py | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/docs/plugins.rst b/docs/plugins.rst index 6e292efd..29d67e24 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -84,6 +84,7 @@ page_generate_context page_generator, metadata page_generator_init page_generator invoked in the PagesGenerator.__init__ page_generator_finalized page_generator invoked at the end of PagesGenerator.generate_context content_object_init content_object invoked at the end of Content.__init__ (see note below) +content_written path, context invoked each time a content file is written. ============================= ============================ =========================================================================== The list is currently small, so don't hesitate to add signals and make a pull diff --git a/pelican/signals.py b/pelican/signals.py index 77802e88..e92272c9 100644 --- a/pelican/signals.py +++ b/pelican/signals.py @@ -37,3 +37,6 @@ static_generator_preread = signal('static_generator_preread') static_generator_context = signal('static_generator_context') content_object_init = signal('content_object_init') + +# Writers signals +content_written = signal('content_written') diff --git a/pelican/writers.py b/pelican/writers.py index 59642cd9..da105929 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -11,8 +11,10 @@ if not six.PY3: from feedgenerator import Atom1Feed, Rss201rev2Feed from jinja2 import Markup + from pelican.paginator import Paginator from pelican.utils import get_relative_path, path_to_url, set_date_tzinfo +from pelican import signals logger = logging.getLogger(__name__) @@ -151,10 +153,15 @@ class Writer(object): os.makedirs(os.path.dirname(path)) except Exception: pass + with self._open_w(path, 'utf-8', override=override) as f: f.write(output) logger.info('writing {}'.format(path)) + # Send a signal to say we're writing a file with some specific + # local context. + signals.content_written.send(path, context=localcontext) + localcontext = context.copy() if relative_urls: relative_url = path_to_url(get_relative_path(name)) From e2ca6d76086eb3bbde961dfa2e7e1dd3a9a8452d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Sat, 17 Aug 2013 17:36:13 +0200 Subject: [PATCH 0083/1427] Add categories and tags to the replacement mechanism. --- docs/getting_started.rst | 4 ++++ pelican/contents.py | 4 ++++ pelican/tests/test_contents.py | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 6d9a8c41..bad9bbda 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -459,6 +459,10 @@ following to ``pelicanconf.py``:: And then the ``pdfs`` directory would also be copied to ``output/static/``. +You can also link to categories or tags, using the `|tag|tagname` and +`|category|foobar` syntax. + + Importing an existing blog -------------------------- diff --git a/pelican/contents.py b/pelican/contents.py index ed213c31..900049a2 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -216,6 +216,10 @@ class Content(object): else: logger.warning("Unable to find {fn}, skipping url" " replacement".format(fn=value)) + elif what == 'category': + origin = Category(value, self.settings).url + elif what == 'tag': + origin = Tag(value, self.settings).url return ''.join((m.group('markup'), m.group('quote'), origin, m.group('quote'))) diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index af97db3f..936903c1 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -180,6 +180,30 @@ class TestPage(unittest.TestCase): Page(**self.page_kwargs) self.assertTrue(content_object_init.has_receivers_for(Page)) + def test_get_content(self): + # Test that the content is updated with the relative links to + # filenames, tags and categories. + settings = get_settings() + args = self.page_kwargs.copy() + args['settings'] = settings + + # Tag + args['content'] = ('A simple test, with a ' + 'link') + page = Page(**args) + content = page.get_content('http://notmyidea.org') + self.assertEquals(content, ('A simple test, with a ' + 'link')) + + # Category + args['content'] = ('A simple test, with a ' + 'link') + page = Page(**args) + content = page.get_content('http://notmyidea.org') + self.assertEquals(content, + ('A simple test, with a ' + 'link')) + class TestArticle(TestPage): def test_template(self): From 56371aaf0f9e7b3e756160147486f29838820f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Sat, 17 Aug 2013 17:42:54 +0200 Subject: [PATCH 0084/1427] update the changelog --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 0804c8b0..61cae1d9 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,6 +9,7 @@ Release history longer article-specific * Deprecate ``FILES_TO_COPY`` in favor of ``STATIC_PATHS`` and ``EXTRA_PATH_METADATA`` +* Add support for |tag| and |category| relative links. 3.2.1 and 3.2.2 =============== From 895945e3cc1b25a00708227307bdeee5e0ed20f9 Mon Sep 17 00:00:00 2001 From: Kura Date: Mon, 19 Aug 2013 11:45:59 +0100 Subject: [PATCH 0085/1427] Removed YouTube directive as it's been moved to a Plugin --- pelican/rstdirectives.py | 57 ---------------------------------------- 1 file changed, 57 deletions(-) diff --git a/pelican/rstdirectives.py b/pelican/rstdirectives.py index fb4a6c93..07c40f5e 100644 --- a/pelican/rstdirectives.py +++ b/pelican/rstdirectives.py @@ -41,63 +41,6 @@ directives.register_directive('code-block', Pygments) directives.register_directive('sourcecode', Pygments) -class YouTube(Directive): - """ Embed YouTube video in posts. - - Courtesy of Brian Hsu: https://gist.github.com/1422773 - - VIDEO_ID is required, with / height are optional integer, - and align could be left / center / right. - - Usage: - .. youtube:: VIDEO_ID - :width: 640 - :height: 480 - :align: center - """ - - def align(argument): - """Conversion function for the "align" option.""" - return directives.choice(argument, ('left', 'center', 'right')) - - required_arguments = 1 - optional_arguments = 2 - option_spec = { - 'width': directives.positive_int, - 'height': directives.positive_int, - 'align': align - } - - final_argument_whitespace = False - has_content = False - - def run(self): - videoID = self.arguments[0].strip() - width = 420 - height = 315 - align = 'left' - - if 'width' in self.options: - width = self.options['width'] - - if 'height' in self.options: - height = self.options['height'] - - if 'align' in self.options: - align = self.options['align'] - - url = 'http://www.youtube.com/embed/%s' % videoID - div_block = '
    ' % align - embed_block = '' % (width, height, url) - - return [ - nodes.raw('', div_block, format='html'), - nodes.raw('', embed_block, format='html'), - nodes.raw('', '
    ', format='html')] - -directives.register_directive('youtube', YouTube) - _abbr_re = re.compile('\((.*)\)$') From d11b33030f4220e16b62bdfe94b80dc1290ead4a Mon Sep 17 00:00:00 2001 From: Axel Haustant Date: Wed, 27 Mar 2013 09:15:35 +0100 Subject: [PATCH 0086/1427] Use .dev suffix for development versions --- pelican/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index f2bd2e07..2e1763ac 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -25,7 +25,7 @@ from pelican.writers import Writer __major__ = 3 __minor__ = 2 __micro__ = 0 -__version__ = "{0}.{1}.{2}".format(__major__, __minor__, __micro__) +__version__ = "{0}.{1}.{2}.dev".format(__major__, __minor__, __micro__) DEFAULT_CONFIG_NAME = 'pelicanconf.py' From 469c531ae4fbc9f8093b3f50607755e3d43e3e26 Mon Sep 17 00:00:00 2001 From: Axel Haustant Date: Wed, 27 Mar 2013 09:16:19 +0100 Subject: [PATCH 0087/1427] Include version in doc and warn if it's a dev version. --- docs/conf.py | 16 ++++++++++++---- docs/index.rst | 15 ++++++++++++--- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 40de84c7..84531182 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,18 +4,26 @@ import sys, os sys.path.append(os.path.abspath(os.pardir)) -from pelican import __version__, __major__ +from pelican import __version__, __major__, __minor__ # -- General configuration ----------------------------------------------------- templates_path = ['_templates'] -extensions = ['sphinx.ext.autodoc',] +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.ifconfig', 'sphinx.ext.extlinks'] source_suffix = '.rst' master_doc = 'index' project = 'Pelican' copyright = '2010, Alexis Metaireau and contributors' exclude_patterns = ['_build'] -version = __version__ -release = __major__ +version = '%s.%s' % (__major__, __minor__) +release = __version__ +last_stable = '3.1.1' +rst_prolog = ''' +.. |last_stable| replace:: :pelican-doc:`{0}` +'''.format(last_stable) + +extlinks = { + 'pelican-doc': ('http://docs.getpelican.com/%s/', '') +} # -- Options for HTML output --------------------------------------------------- diff --git a/docs/index.rst b/docs/index.rst index 914c2a7e..eb8883ce 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,14 @@ -Pelican -======= +Pelican |release| +================= + + +.. ifconfig:: release.endswith('.dev') + + .. warning:: + + This documentation is for the version of Pelican currently under development. + Were you looking for version |last_stable| documentation? + Pelican is a static site generator, written in Python_. @@ -12,7 +21,7 @@ Pelican is a static site generator, written in Python_. Features -------- -Pelican currently supports: +Pelican |version| currently supports: * Articles (e.g., blog posts) and pages (e.g., "About", "Projects", "Contact") * Comments, via an external service (Disqus). (Please note that while From e6e99ffbb9c671da9a80c61cf7d47c12d77f2ef4 Mon Sep 17 00:00:00 2001 From: Axel Haustant Date: Wed, 27 Mar 2013 10:11:45 +0100 Subject: [PATCH 0088/1427] Change current release section with a constant title --- docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 61cae1d9..c020d7f4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,7 +1,7 @@ Release history ############### -3.3 (XXXX-XX-XX) +Next release ================ * Rename signals for better consistency (some plugins may need to be updated) From 9d583961e700667b7f1e78c4f2e0c88954becf15 Mon Sep 17 00:00:00 2001 From: bmcorser Date: Wed, 21 Aug 2013 19:57:55 +0100 Subject: [PATCH 0089/1427] Fix for THEME_STATIC_PATHS by copying sensitively If more than one path is defined in THEME_STATIC_PATHS, the theme's static directory in output is deleted and replaced by the following path's files. Using `shutil.rmtree` to remove the entire destination tree if overwrite is `True` assumes that we didn't want anything at all that was there. We should recurse through the directory and their subdirs instead, leaving things put there by the previous path where they were. I lazily copied almost verbatim the solution for recursively copying a diectory from http://stackoverflow.com/a/1994840. The reason for this is patch is that without it, my plugin is broken! It also makes my code a lot less crazy: https://github.com/bmcorser/pelicanfly/commit/a83f066 --- pelican/utils.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/pelican/utils.py b/pelican/utils.py index 054c1f40..e028c84c 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -275,16 +275,24 @@ def copy(path, source, destination, destination_path=None, overwrite=False): destination_ = os.path.abspath( os.path.expanduser(os.path.join(destination, destination_path))) + def recurse(source, destination): + for entry in os.listdir(source): + entry_path = os.path.join(source, entry) + if os.path.isdir(entry_path): + entry_dest = os.path.join(destination, entry) + if os.path.exists(entry_dest): + if not os.path.isdir(entry_dest): + raise IOError('Failed to copy {0} a directory.' + .format(entry_dest)) + recurse(entry_path, entry_dest) + else: + shutil.copytree(entry_path, entry_dest) + else: + shutil.copy(entry_path, destination) + + if os.path.isdir(source_): - try: - shutil.copytree(source_, destination_) - logger.info('copying %s to %s' % (source_, destination_)) - except OSError: - if overwrite: - shutil.rmtree(destination_) - shutil.copytree(source_, destination_) - logger.info('replacement of %s with %s' % (source_, - destination_)) + recurse(source_, destination_) elif os.path.isfile(source_): dest_dir = os.path.dirname(destination_) From 2826c1a5580508932b4b27b5b0bb38cea4a45240 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Wed, 21 Aug 2013 22:58:50 -0400 Subject: [PATCH 0090/1427] Added metadata information. Refs #1028 --- pelican/themes/simple/templates/article.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pelican/themes/simple/templates/article.html b/pelican/themes/simple/templates/article.html index 98cc852c..8da555a1 100644 --- a/pelican/themes/simple/templates/article.html +++ b/pelican/themes/simple/templates/article.html @@ -1,4 +1,14 @@ {% extends "base.html" %} +{% block head %} + {% for keyword in article.keywords %} + + {% endfor %} + + {% for description in article.description %} + + {% endfor %} +{% endblock %} + {% block content %}
    From d22b0892418b60b752551ebc95c4616926bb513f Mon Sep 17 00:00:00 2001 From: bmcorser Date: Sat, 24 Aug 2013 13:41:22 +0100 Subject: [PATCH 0091/1427] Adding placeholder files for regression test --- samples/theme_standard/a_stylesheet | 0 samples/theme_standard/a_template | 0 samples/very/exciting/new/files/bap! | 0 samples/very/exciting/new/files/boom! | 0 samples/very/exciting/new/files/wow! | 0 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 samples/theme_standard/a_stylesheet create mode 100644 samples/theme_standard/a_template create mode 100644 samples/very/exciting/new/files/bap! create mode 100644 samples/very/exciting/new/files/boom! create mode 100644 samples/very/exciting/new/files/wow! diff --git a/samples/theme_standard/a_stylesheet b/samples/theme_standard/a_stylesheet new file mode 100644 index 00000000..e69de29b diff --git a/samples/theme_standard/a_template b/samples/theme_standard/a_template new file mode 100644 index 00000000..e69de29b diff --git a/samples/very/exciting/new/files/bap! b/samples/very/exciting/new/files/bap! new file mode 100644 index 00000000..e69de29b diff --git a/samples/very/exciting/new/files/boom! b/samples/very/exciting/new/files/boom! new file mode 100644 index 00000000..e69de29b diff --git a/samples/very/exciting/new/files/wow! b/samples/very/exciting/new/files/wow! new file mode 100644 index 00000000..e69de29b From 0f4058a3170724cc1709837c1d8f89d364c08bf5 Mon Sep 17 00:00:00 2001 From: bmcorser Date: Sat, 24 Aug 2013 13:42:55 +0100 Subject: [PATCH 0092/1427] Add regressison test for recursively copying files --- pelican/tests/test_pelican.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index d6c0d8e9..db2d0e6b 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -92,3 +92,22 @@ class TestPelican(LoggedTestCase): mute(True)(pelican.run)() dcmp = dircmp(self.temp_path, os.path.join(OUTPUT_PATH, 'custom')) self.assertFilesEqual(recursiveDiff(dcmp)) + + def test_theme_static_paths_copy(self): + # the same thing with a specified set of settings should work + settings = read_settings(path=SAMPLE_CONFIG, override={ + 'PATH': INPUT_PATH, + 'OUTPUT_PATH': self.temp_path, + 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, 'very'), + os.path.join(SAMPLES_PATH, 'theme_standard')] + }) + pelican = Pelican(settings=settings) + mute(True)(pelican.run)() + theme_output = os.path.join(self.temp_path, 'theme') + extra_path = os.path.join(theme_output, 'exciting', 'new', 'files') + + for file in ['a_stylesheet', 'a_template']: + self.assertTrue(os.path.exists(os.path.join(theme_output, file))) + + for file in ['wow!', 'boom!', 'bap!']: + self.assertTrue(os.path.exists(os.path.join(extra_path, file))) From b144c3cfbd6ea0e5bd1bba5d30d6bf3605499794 Mon Sep 17 00:00:00 2001 From: bmcorser Date: Sat, 24 Aug 2013 14:38:06 +0100 Subject: [PATCH 0093/1427] Hack out `overwrite` param --- pelican/generators.py | 2 +- pelican/utils.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index 26fa40ea..d695c7c8 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -544,7 +544,7 @@ class StaticGenerator(Generator): """Copy all the paths from source to destination""" for path in paths: copy(path, source, os.path.join(output_path, destination), - final_path, overwrite=True) + final_path) def generate_context(self): self.staticfiles = [] diff --git a/pelican/utils.py b/pelican/utils.py index e028c84c..d306de65 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -256,7 +256,7 @@ def slugify(value, substitutions=()): return value.decode('ascii') -def copy(path, source, destination, destination_path=None, overwrite=False): +def copy(path, source, destination, destination_path=None): """Copy path from origin to destination. The function is able to copy either files or directories. @@ -265,8 +265,6 @@ def copy(path, source, destination, destination_path=None, overwrite=False): :param source: the source dir :param destination: the destination dir :param destination_path: the destination path (optional) - :param overwrite: whether to overwrite the destination if already exists - or not """ if not destination_path: destination_path = path From c61f6f402a417e807c9cb6f01c9293fced678892 Mon Sep 17 00:00:00 2001 From: Axel Haustant Date: Wed, 27 Mar 2013 09:30:54 +0100 Subject: [PATCH 0094/1427] Prepare for bumpr --- bumpr.rc | 30 ++++++++++++++++++++++++++++++ docs/changelog.rst | 2 +- docs/conf.py | 6 +++--- pelican/__init__.py | 5 +---- 4 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 bumpr.rc diff --git a/bumpr.rc b/bumpr.rc new file mode 100644 index 00000000..cfc90fd7 --- /dev/null +++ b/bumpr.rc @@ -0,0 +1,30 @@ +[bumpr] +file = pelican/__init__.py +vcs = git +clean = + python setup.py clean + rm -rf *egg-info build dist +tests = tox +publish = python setup.py sdist register upload +files = README.rst + +[bump] +unsuffix = true +message = Bump version {version} + +[prepare] +part = patch +suffix = dev +message = Prepare version {version} for next development cycle + +[changelog] +file = docs/changelog.rst +separator = = +bump = {version} ({date:%Y-%m-%d}) +prepare = Next release + +[readthedoc] +url = http://docs.getpelican.com/{tag} + +[commands] +bump = sed -i "s/last_stable\s*=.*/last_stable = '{version}'/" docs/conf.py diff --git a/docs/changelog.rst b/docs/changelog.rst index c020d7f4..27938d4d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,7 +2,7 @@ Release history ############### Next release -================ +============ * Rename signals for better consistency (some plugins may need to be updated) * Move metadata extraction from generators to readers; metadata extraction no diff --git a/docs/conf.py b/docs/conf.py index 84531182..5ac81b9e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,7 +4,7 @@ import sys, os sys.path.append(os.path.abspath(os.pardir)) -from pelican import __version__, __major__, __minor__ +from pelican import __version__ # -- General configuration ----------------------------------------------------- templates_path = ['_templates'] @@ -14,9 +14,9 @@ master_doc = 'index' project = 'Pelican' copyright = '2010, Alexis Metaireau and contributors' exclude_patterns = ['_build'] -version = '%s.%s' % (__major__, __minor__) release = __version__ -last_stable = '3.1.1' +version = '.'.join(release.split('.')[:1]) +last_stable = '3.2.2' rst_prolog = ''' .. |last_stable| replace:: :pelican-doc:`{0}` '''.format(last_stable) diff --git a/pelican/__init__.py b/pelican/__init__.py index 2e1763ac..cecab54b 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -22,10 +22,7 @@ from pelican.settings import read_settings from pelican.utils import clean_output_dir, folder_watcher, file_watcher from pelican.writers import Writer -__major__ = 3 -__minor__ = 2 -__micro__ = 0 -__version__ = "{0}.{1}.{2}.dev".format(__major__, __minor__, __micro__) +__version__ = "3.2.3.dev" DEFAULT_CONFIG_NAME = 'pelicanconf.py' From bad8cfb3d9883845c054772b75a5934a96aeb394 Mon Sep 17 00:00:00 2001 From: Axel Haustant Date: Sat, 24 Aug 2013 20:20:37 +0200 Subject: [PATCH 0095/1427] Added bumpr as a development requirement to perform release --- dev_requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev_requirements.txt b/dev_requirements.txt index fa2634a0..0a4a4589 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -6,3 +6,6 @@ Markdown BeautifulSoup4 lxml typogrify + +# To perform release +bumpr From 8d41d6ba2419e5d380f42f98e530a898c428f516 Mon Sep 17 00:00:00 2001 From: Alex Stephen Date: Sat, 24 Aug 2013 23:36:07 -0400 Subject: [PATCH 0096/1427] Refs #1028. Now iterating over tags --- pelican/themes/simple/templates/article.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pelican/themes/simple/templates/article.html b/pelican/themes/simple/templates/article.html index 8da555a1..2356c2f6 100644 --- a/pelican/themes/simple/templates/article.html +++ b/pelican/themes/simple/templates/article.html @@ -7,6 +7,11 @@ {% for description in article.description %} {% endfor %} + + {% for tag in article.tags %} + Date: Sun, 25 Aug 2013 14:14:43 +0200 Subject: [PATCH 0097/1427] Pin bumpr version to avoid config breakage with new versions --- dev_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 0a4a4589..c90ac630 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -8,4 +8,4 @@ lxml typogrify # To perform release -bumpr +bumpr==0.2.0 From 71cca7a444f8ac81be3dd55ba678eb69f5d1269a Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Mon, 26 Aug 2013 23:22:08 +0200 Subject: [PATCH 0098/1427] Fix the behavior of Markdown extensions. There was several issues here: - `self.extensions` was adding 'meta' multiple times (ref #1058) - `self.extensions` was keeping a reference to `self.settings['MD_EXTENSIONS']`, so adding 'meta' to it. - the `%s_EXTENSIONS` block coming after, it was overriding `self.extensions` with `self.settings['EXTENSIONS']` (while it was a reference, it was working, but ...). As this is currently used only for Mardown, the simplest solution is to remove this, and let each reader manage its `_EXTENSIONS` setting. --- pelican/readers.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/pelican/readers.py b/pelican/readers.py index 3f01a72c..067bbb85 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -188,8 +188,9 @@ class MarkdownReader(BaseReader): def __init__(self, *args, **kwargs): super(MarkdownReader, self).__init__(*args, **kwargs) - self.extensions = self.settings['MD_EXTENSIONS'] - self.extensions.append('meta') + self.extensions = list(self.settings['MD_EXTENSIONS']) + if 'meta' not in self.extensions: + self.extensions.append('meta') def _parse_metadata(self, meta): """Return the dict containing document metadata""" @@ -405,11 +406,6 @@ class Readers(object): self.readers[fmt] = reader_class(self.settings) - settings_key = '%s_EXTENSIONS' % fmt.upper() - - if settings_key in self.settings: - self.readers[fmt].extensions = self.settings[settings_key] - @property def extensions(self): return self.readers.keys() From 2f34307e129a8ae14b29262b9c63c0ab091f0f7f Mon Sep 17 00:00:00 2001 From: Talha Mansoor Date: Thu, 29 Aug 2013 23:14:47 +0500 Subject: [PATCH 0099/1427] Change the regex so that it parse |filename| and {filename} equally Updates getpelican/pelican#1061 --- pelican/contents.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/contents.py b/pelican/contents.py index 900049a2..94790612 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -187,7 +187,7 @@ class Content(object): (?:href|src)\s*=) (?P["\']) # require value to be quoted - (?P\|(?P.*?)\|(?P.*?)) # the url value + (?P[|{](?P.*?)[|}](?P.*?)) # the url value \2""", re.X) def replacer(m): From 8d352d9a7863fde988645b9b401c76ab2f8f512d Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 29 Aug 2013 17:35:48 -0700 Subject: [PATCH 0100/1427] Correct upload targets in Makefile.in --- pelican/tools/templates/Makefile.in | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in index 4ca0af6a..524bf82e 100644 --- a/pelican/tools/templates/Makefile.in +++ b/pelican/tools/templates/Makefile.in @@ -27,7 +27,7 @@ DROPBOX_DIR=$dropbox_dir DEBUG ?= 0 ifeq ($(DEBUG), 1) PELICANOPTS += -D -endif +endif help: @echo 'Makefile for a pelican Web site ' @@ -40,13 +40,13 @@ help: @echo ' make serve [PORT=8000] serve site at http://localhost:8000' @echo ' make devserver [PORT=8000] start/restart develop_server.sh ' @echo ' make stopserver stop local server ' - @echo ' ssh_upload upload the web site via SSH ' - @echo ' rsync_upload upload the web site via rsync+ssh ' - @echo ' dropbox_upload upload the web site via Dropbox ' - @echo ' ftp_upload upload the web site via FTP ' - @echo ' s3_upload upload the web site via S3 ' - @echo ' cf_upload upload the web site via Cloud Files' - @echo ' github upload the web site via gh-pages ' + @echo ' make ssh_upload upload the web site via SSH ' + @echo ' make rsync_upload upload the web site via rsync+ssh ' + @echo ' make dropbox_upload upload the web site via Dropbox ' + @echo ' make ftp_upload upload the web site via FTP ' + @echo ' make s3_upload upload the web site via S3 ' + @echo ' make cf_upload upload the web site via Cloud Files' + @echo ' make github upload the web site via gh-pages ' @echo ' ' @echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html' @echo ' ' From ef16c915d4dfec668788bcff367a860214d01e55 Mon Sep 17 00:00:00 2001 From: bmcorser Date: Sat, 31 Aug 2013 21:48:19 +0100 Subject: [PATCH 0101/1427] Clarification of THEME_STATIC_PATHS behaviour --- docs/settings.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index c709a339..075bea83 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -597,7 +597,10 @@ Setting name (default value) What does it do? `THEME_STATIC_PATHS`. Default is `theme`. `THEME_STATIC_PATHS` (``['static']``) Static theme paths you want to copy. Default value is `static`, but if your theme has - other static paths, you can put them here. + other static paths, you can put them here. If files + or directories with the same names are included in + the paths defined in this settings, they will be + progressively overwritten. `CSS_FILE` (``'main.css'``) Specify the CSS file you want to load. ================================================ ===================================================== From 10c62b27dd3ca59972978ee4d57022739769bb2b Mon Sep 17 00:00:00 2001 From: bmcorser Date: Sat, 31 Aug 2013 22:00:52 +0100 Subject: [PATCH 0102/1427] More exciting files --- samples/kinda/exciting/new/files/zap! | 0 samples/kinda/exciting/old | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 samples/kinda/exciting/new/files/zap! create mode 100644 samples/kinda/exciting/old diff --git a/samples/kinda/exciting/new/files/zap! b/samples/kinda/exciting/new/files/zap! new file mode 100644 index 00000000..e69de29b diff --git a/samples/kinda/exciting/old b/samples/kinda/exciting/old new file mode 100644 index 00000000..e69de29b From 089059aec611b0827b925b987cb0f59bbfe4c485 Mon Sep 17 00:00:00 2001 From: bmcorser Date: Sat, 31 Aug 2013 22:11:03 +0100 Subject: [PATCH 0103/1427] Clarify revised functinality --- pelican/tests/test_pelican.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index db2d0e6b..e829e1a5 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -99,6 +99,7 @@ class TestPelican(LoggedTestCase): 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, 'very'), + os.path.join(SAMPLES_PATH, 'kinda'), os.path.join(SAMPLES_PATH, 'theme_standard')] }) pelican = Pelican(settings=settings) @@ -109,5 +110,5 @@ class TestPelican(LoggedTestCase): for file in ['a_stylesheet', 'a_template']: self.assertTrue(os.path.exists(os.path.join(theme_output, file))) - for file in ['wow!', 'boom!', 'bap!']: + for file in ['wow!', 'boom!', 'bap!', 'zap!']: self.assertTrue(os.path.exists(os.path.join(extra_path, file))) From b7bc5702154e0e5d7d95ae61dfc22dd9a7ba5c62 Mon Sep 17 00:00:00 2001 From: jeekajoo Date: Mon, 2 Sep 2013 19:25:46 +0200 Subject: [PATCH 0104/1427] add async and defer attributes to piwik.js inspired from http://piwik.org/docs/javascript-tracking/#toc-where-can-i-find-the-piwik-tracking-code --- pelican/themes/notmyidea/templates/piwik.html | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/pelican/themes/notmyidea/templates/piwik.html b/pelican/themes/notmyidea/templates/piwik.html index ff459af7..f9126264 100644 --- a/pelican/themes/notmyidea/templates/piwik.html +++ b/pelican/themes/notmyidea/templates/piwik.html @@ -1,16 +1,19 @@ {% if PIWIK_URL and PIWIK_SITE_ID %} - -{% endif %} \ No newline at end of file + +{% endif %} From 6813cd923fed8b6a4f30a6951327049a6420e5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Sun, 8 Sep 2013 17:07:30 +0200 Subject: [PATCH 0105/1427] Close some HTML tags. Fix #1076 --- pelican/tests/output/basic/a-markdown-powered-article.html | 4 ++-- pelican/tests/output/basic/archives.html | 4 ++-- pelican/tests/output/basic/article-1.html | 4 ++-- pelican/tests/output/basic/article-2.html | 4 ++-- pelican/tests/output/basic/article-3.html | 4 ++-- pelican/tests/output/basic/author/alexis-metaireau.html | 4 ++-- pelican/tests/output/basic/authors.html | 4 ++-- pelican/tests/output/basic/categories.html | 4 ++-- pelican/tests/output/basic/category/bar.html | 4 ++-- pelican/tests/output/basic/category/cat1.html | 4 ++-- pelican/tests/output/basic/category/misc.html | 4 ++-- pelican/tests/output/basic/category/yeah.html | 4 ++-- pelican/tests/output/basic/filename_metadata-example.html | 4 ++-- pelican/tests/output/basic/index.html | 4 ++-- pelican/tests/output/basic/oh-yeah.html | 4 ++-- pelican/tests/output/basic/override/index.html | 4 ++-- .../output/basic/pages/this-is-a-test-hidden-page.html | 4 ++-- pelican/tests/output/basic/pages/this-is-a-test-page.html | 4 ++-- pelican/tests/output/basic/second-article-fr.html | 4 ++-- pelican/tests/output/basic/second-article.html | 4 ++-- pelican/tests/output/basic/tag/bar.html | 4 ++-- pelican/tests/output/basic/tag/baz.html | 4 ++-- pelican/tests/output/basic/tag/foo.html | 4 ++-- pelican/tests/output/basic/tag/foobar.html | 4 ++-- pelican/tests/output/basic/tag/oh.html | 4 ++-- pelican/tests/output/basic/tag/yeah.html | 4 ++-- pelican/tests/output/basic/tags.html | 4 ++-- pelican/tests/output/basic/this-is-a-super-article.html | 4 ++-- pelican/tests/output/basic/unbelievable.html | 4 ++-- pelican/tests/output/custom/a-markdown-powered-article.html | 4 ++-- pelican/tests/output/custom/archives.html | 4 ++-- pelican/tests/output/custom/article-1.html | 4 ++-- pelican/tests/output/custom/article-2.html | 4 ++-- pelican/tests/output/custom/article-3.html | 4 ++-- pelican/tests/output/custom/author/alexis-metaireau.html | 4 ++-- pelican/tests/output/custom/author/alexis-metaireau2.html | 4 ++-- pelican/tests/output/custom/author/alexis-metaireau3.html | 4 ++-- pelican/tests/output/custom/authors.html | 4 ++-- pelican/tests/output/custom/categories.html | 4 ++-- pelican/tests/output/custom/category/bar.html | 4 ++-- pelican/tests/output/custom/category/cat1.html | 4 ++-- pelican/tests/output/custom/category/misc.html | 4 ++-- pelican/tests/output/custom/category/yeah.html | 4 ++-- pelican/tests/output/custom/drafts/a-draft-article.html | 4 ++-- pelican/tests/output/custom/filename_metadata-example.html | 4 ++-- pelican/tests/output/custom/index.html | 4 ++-- pelican/tests/output/custom/index2.html | 4 ++-- pelican/tests/output/custom/index3.html | 4 ++-- pelican/tests/output/custom/jinja2_template.html | 4 ++-- pelican/tests/output/custom/oh-yeah-fr.html | 4 ++-- pelican/tests/output/custom/oh-yeah.html | 4 ++-- pelican/tests/output/custom/override/index.html | 4 ++-- .../output/custom/pages/this-is-a-test-hidden-page.html | 4 ++-- pelican/tests/output/custom/pages/this-is-a-test-page.html | 4 ++-- pelican/tests/output/custom/second-article-fr.html | 4 ++-- pelican/tests/output/custom/second-article.html | 4 ++-- pelican/tests/output/custom/tag/bar.html | 4 ++-- pelican/tests/output/custom/tag/baz.html | 4 ++-- pelican/tests/output/custom/tag/foo.html | 4 ++-- pelican/tests/output/custom/tag/foobar.html | 4 ++-- pelican/tests/output/custom/tag/oh.html | 4 ++-- pelican/tests/output/custom/tag/yeah.html | 4 ++-- pelican/tests/output/custom/tags.html | 4 ++-- pelican/tests/output/custom/this-is-a-super-article.html | 4 ++-- pelican/tests/output/custom/unbelievable.html | 4 ++-- pelican/themes/notmyidea/templates/base.html | 4 ++-- pelican/themes/simple/templates/article.html | 6 +++--- 67 files changed, 135 insertions(+), 135 deletions(-) diff --git a/pelican/tests/output/basic/a-markdown-powered-article.html b/pelican/tests/output/basic/a-markdown-powered-article.html index b7a90f02..6bc29eba 100644 --- a/pelican/tests/output/basic/a-markdown-powered-article.html +++ b/pelican/tests/output/basic/a-markdown-powered-article.html @@ -1,9 +1,9 @@ - + A markdown powered article - + diff --git a/pelican/tests/output/basic/feeds/all-en.atom.xml b/pelican/tests/output/basic/feeds/all-en.atom.xml index 5b8eb591..da537344 100644 --- a/pelican/tests/output/basic/feeds/all-en.atom.xml +++ b/pelican/tests/output/basic/feeds/all-en.atom.xml @@ -31,7 +31,14 @@ YEAH !</p> <h2>Testing sourcecode directive</h2> <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> -</td></tr></table><p>Lovely.</p> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> @@ -43,5 +50,12 @@ YEAH !</p> <span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> <p>Lovely.</p> </div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00Ztag:,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/basic/feeds/all.atom.xml b/pelican/tests/output/basic/feeds/all.atom.xml index d19b0c30..d837c895 100644 --- a/pelican/tests/output/basic/feeds/all.atom.xml +++ b/pelican/tests/output/basic/feeds/all.atom.xml @@ -32,7 +32,14 @@ YEAH !</p> <h2>Testing sourcecode directive</h2> <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> -</td></tr></table><p>Lovely.</p> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> @@ -44,5 +51,12 @@ YEAH !</p> <span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> <p>Lovely.</p> </div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00Ztag:,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/basic/feeds/misc.atom.xml b/pelican/tests/output/basic/feeds/misc.atom.xml index 34b6b4fb..8b988614 100644 --- a/pelican/tests/output/basic/feeds/misc.atom.xml +++ b/pelican/tests/output/basic/feeds/misc.atom.xml @@ -8,7 +8,14 @@ <h2>Testing sourcecode directive</h2> <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> -</td></tr></table><p>Lovely.</p> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> <h2>Testing more sourcecode directives</h2> @@ -20,5 +27,12 @@ <span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> <p>Lovely.</p> </div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00Ztag:,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/basic/index.html b/pelican/tests/output/basic/index.html index 08c4ad09..24b00606 100644 --- a/pelican/tests/output/basic/index.html +++ b/pelican/tests/output/basic/index.html @@ -219,11 +219,11 @@ YEAH !

    Testing sourcecode directive

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

    Lovely.

    - -
    -

    Testing more sourcecode directives

    -
     8 def run(self):
    self.assert_has_content()
    10 try:
    lexer = get_lexer_by_name(self.arguments[0])
    12 except ValueError ...
    + +
    +

    Testing another case

    +

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

    read more diff --git a/pelican/tests/output/basic/unbelievable.html b/pelican/tests/output/basic/unbelievable.html index db8d0bdf..aef78474 100644 --- a/pelican/tests/output/basic/unbelievable.html +++ b/pelican/tests/output/basic/unbelievable.html @@ -47,7 +47,14 @@

    Testing sourcecode directive

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

    Lovely.

    + +
    +

    Testing another case

    +

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

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

    Lovely.

    Testing more sourcecode directives

    @@ -58,6 +65,13 @@

    Testing even more sourcecode directives

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

    Lovely.

    +
    +
    +

    Testing overriding config defaults

    +

    Even if the default is line numbers, we can override it here

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

    Lovely.

    diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index 4b598914..77c9cdfe 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -55,11 +55,11 @@

    Testing sourcecode directive

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

    Lovely.

    - -
    -

    Testing more sourcecode directives

    -
     8 def run(self):
    self.assert_has_content()
    10 try:
    lexer = get_lexer_by_name(self.arguments[0])
    12 except ValueError ...
    + +
    +

    Testing another case

    +

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

    read more

    There are comments.

    diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html index f32e04c3..36479803 100644 --- a/pelican/tests/output/custom/category/misc.html +++ b/pelican/tests/output/custom/category/misc.html @@ -99,11 +99,11 @@

    Testing sourcecode directive

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

    Lovely.

    - -
    -

    Testing more sourcecode directives

    -
     8 def run(self):
    self.assert_has_content()
    10 try:
    lexer = get_lexer_by_name(self.arguments[0])
    12 except ValueError ...
    + +
    +

    Testing another case

    +

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

    read more

    There are comments.

    diff --git a/pelican/tests/output/custom/feeds/all-en.atom.xml b/pelican/tests/output/custom/feeds/all-en.atom.xml index ce25290d..69ba08c6 100644 --- a/pelican/tests/output/custom/feeds/all-en.atom.xml +++ b/pelican/tests/output/custom/feeds/all-en.atom.xml @@ -31,6 +31,13 @@ YEAH !</p> <h2>Testing sourcecode directive</h2> <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> @@ -43,5 +50,12 @@ YEAH !</p> <span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> <p>Lovely.</p> </div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/all.atom.xml b/pelican/tests/output/custom/feeds/all.atom.xml index 68986d40..2eb31731 100644 --- a/pelican/tests/output/custom/feeds/all.atom.xml +++ b/pelican/tests/output/custom/feeds/all.atom.xml @@ -33,6 +33,13 @@ YEAH !</p> <h2>Testing sourcecode directive</h2> <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> @@ -45,5 +52,12 @@ YEAH !</p> <span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> <p>Lovely.</p> </div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/all.rss.xml b/pelican/tests/output/custom/feeds/all.rss.xml index 7fee491a..69e30bfd 100644 --- a/pelican/tests/output/custom/feeds/all.rss.xml +++ b/pelican/tests/output/custom/feeds/all.rss.xml @@ -33,6 +33,13 @@ YEAH !</p> <h2>Testing sourcecode directive</h2> <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> @@ -45,5 +52,12 @@ YEAH !</p> <span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> <p>Lovely.</p> </div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:unbelievable.htmlThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:tag/baz.html \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/misc.atom.xml b/pelican/tests/output/custom/feeds/misc.atom.xml index 9328e05a..91d6b28f 100644 --- a/pelican/tests/output/custom/feeds/misc.atom.xml +++ b/pelican/tests/output/custom/feeds/misc.atom.xml @@ -8,6 +8,13 @@ <h2>Testing sourcecode directive</h2> <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> @@ -20,5 +27,12 @@ <span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> <p>Lovely.</p> </div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/misc.rss.xml b/pelican/tests/output/custom/feeds/misc.rss.xml index b708c70d..3493d2a3 100644 --- a/pelican/tests/output/custom/feeds/misc.rss.xml +++ b/pelican/tests/output/custom/feeds/misc.rss.xml @@ -8,6 +8,13 @@ <h2>Testing sourcecode directive</h2> <table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> </pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> </td></tr></table><p>Lovely.</p> </div> <div class="section" id="testing-more-sourcecode-directives"> @@ -20,5 +27,12 @@ <span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> <p>Lovely.</p> </div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:unbelievable.htmlThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:tag/baz.html \ No newline at end of file diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index 69f9d20c..b4d9ffc6 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -55,11 +55,11 @@

    Testing sourcecode directive

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

    Lovely.

    - -
    -

    Testing more sourcecode directives

    -
     8 def run(self):
    self.assert_has_content()
    10 try:
    lexer = get_lexer_by_name(self.arguments[0])
    12 except ValueError ...
    + +
    +

    Testing another case

    +

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

    read more

    There are comments.

    diff --git a/pelican/tests/output/custom/unbelievable.html b/pelican/tests/output/custom/unbelievable.html index 74bfa83c..03b533bb 100644 --- a/pelican/tests/output/custom/unbelievable.html +++ b/pelican/tests/output/custom/unbelievable.html @@ -54,6 +54,13 @@

    Testing sourcecode directive

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

    Testing another case

    +

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

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

    Lovely.

    @@ -65,6 +72,13 @@

    Testing even more sourcecode directives

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

    Lovely.

    +
    +
    +

    Testing overriding config defaults

    +

    Even if the default is line numbers, we can override it here

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

    Lovely.

    diff --git a/samples/content/unbelievable.rst b/samples/content/unbelievable.rst index b990d20c..209e3557 100644 --- a/samples/content/unbelievable.rst +++ b/samples/content/unbelievable.rst @@ -12,9 +12,20 @@ Testing sourcecode directive ---------------------------- .. sourcecode:: python - :linenos: + :linenos: - formatter = self.options and VARIANTS[self.options.keys()[0]] + formatter = self.options and VARIANTS[self.options.keys()[0]] + + +Testing another case +-------------------- + +This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default. + +.. sourcecode:: python + + formatter = self.options and VARIANTS[self.options.keys()[0]] Lovely. @@ -71,3 +82,17 @@ Testing even more sourcecode directives Lovely. + +Testing overriding config defaults +---------------------------------- + +Even if the default is line numbers, we can override it here + +.. sourcecode:: python + :linenos: none + + + formatter = self.options and VARIANTS[self.options.keys()[0]] + + +Lovely. diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py index 4d5cd06d..d6a87923 100755 --- a/samples/pelican.conf.py +++ b/samples/pelican.conf.py @@ -48,6 +48,9 @@ STATIC_PATHS = [ # custom page generated with a jinja2 template TEMPLATE_PAGES = {'pages/jinja2_template.html': 'jinja2_template.html'} +# code blocks with line numbers +PYGMENTS_RST_OPTIONS = {'linenos': 'table'} + # foobar will not be used, because it's not in caps. All configuration keys # have to be in caps foobar = "barbaz" From 22da74211d1874edf4e6d3c01f7526d5b633a0ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Tue, 17 Sep 2013 17:32:08 +0200 Subject: [PATCH 0114/1427] Fix a broken link --- docs/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.rst b/docs/faq.rst index c1d4f5a0..f7ee6826 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -49,7 +49,7 @@ themes to style code syntax highlighting via CSS. Specifically, you can customize the appearance of your syntax highlighting via the ``.highlight pre`` class in your theme's CSS file. To see how various styles can be used to render Django code, for example, use the style selector drop-down at top-right on the -`Pygments project demo site `_. +`Pygments project demo site `_. You can use the following example commands to generate a starting CSS file from a Pygments built-in style (in this case, "monokai") and then copy the generated From 07f87969ea7a66a89f6a54ea575659f839d6d1d7 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 23 Sep 2013 19:28:06 +0200 Subject: [PATCH 0115/1427] Update changelog --- docs/changelog.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 064c8fd2..ebf84f34 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,13 +4,27 @@ Release history Next release ============ +* Drop Python 3.2 support in favor of Python 3.3 +* Add ``Fabfile`` so Fabric can be used for workflow automation instead of Make +* ``OUTPUT_RETENTION`` setting can be used to preserve metadata (e.g., VCS + data such as ``.hg`` and ``.git``) from being removed from output directory +* Tumblr import +* Improve logic and consistency when cleaning output folder +* Improve documentation versioning and release automation +* Improve pagination flexibility * Rename signals for better consistency (some plugins may need to be updated) * Move metadata extraction from generators to readers; metadata extraction no longer article-specific * Deprecate ``FILES_TO_COPY`` in favor of ``STATIC_PATHS`` and ``EXTRA_PATH_METADATA`` +* Summaries in Markdown posts no longer include footnotes +* Remove unnecessary whitespace in output via ``lstrip_blocks`` Jinja parameter +* Move PDF generation from core to plugin +* Replace ``MARKUP`` setting with ``READERS`` +* Add warning if img tag is missing ``alt`` attribute * Add support for ``{}`` in relative links syntax, besides ``||`` * Add support for ``{tag}`` and ``{category}`` relative links +* Add a ``content_written`` signal 3.2.1 and 3.2.2 =============== From f6c9237a0115491b7db3206717e63a4250c6c2b8 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 23 Sep 2013 19:30:51 +0200 Subject: [PATCH 0116/1427] Enhance and correct documentation --- docs/faq.rst | 13 +++---- docs/getting_started.rst | 60 ++++++++++++++++--------------- docs/index.rst | 10 ++++-- docs/plugins.rst | 16 ++++----- docs/settings.rst | 77 ++++++++++++++++++++-------------------- docs/themes.rst | 23 ++++++------ docs/tips.rst | 48 +++++++++++++------------ 7 files changed, 129 insertions(+), 118 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index f7ee6826..da37af04 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -23,8 +23,8 @@ 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 check the existing issue list first in order to avoid submitting -a duplicate 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 @@ -96,7 +96,8 @@ This metadata can then be accessed in templates such as ``article.html`` via:: Last modified: {{ article.modified }} {% endif %} -If you want to include metadata in templates outside the article context (e.g., ``base.html``), the ``if`` statement should instead be:: +If you want to include metadata in templates outside the article context (e.g., +``base.html``), the ``if`` statement should instead be:: {% if article and article.modified %} @@ -196,10 +197,10 @@ Is Pelican only suitable for blogs? =================================== No. Pelican can be easily configured to create and maintain any type of static site. -This may require little customization of your theme and Pelican configuration. +This may require a little customization of your theme and Pelican configuration. For example, if you are building a launch site for your product and do not need -tags on your site. You can hide tags by removing relevant html code from your theme. -You can also disable generation of tags pages:: +tags on your site, you could remove the relevant HTML code from your theme. +You can also disable generation of tag-related pages via:: TAGS_SAVE_AS = '' TAG_SAVE_AS = '' diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 2605c3a1..201b9ad4 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -83,15 +83,20 @@ Viewing the generated files --------------------------- The files generated by Pelican are static files, so you don't actually need -anything special to view them. You can either use your browser to open the -files on your disk:: +anything special to view them. You can use your browser to open the generated +HTML files directly:: firefox output/index.html -Or run a simple web server using Python:: +Because the above method may have trouble locating your CSS and other linked +assets, running a simple web server using Python will often provide a more +reliable previewing experience:: cd output && python -m SimpleHTTPServer +Once the ``SimpleHTTPServer`` has been started, you can preview your site at +http://localhost:8000/ + Upgrading --------- @@ -452,25 +457,24 @@ And ``image-test.md`` would include:: ![Alt Text]({filename}/images/han.jpg) Any content can be linked in this way. What happens is that the ``images`` -directory gets copied to ``output/static/`` upon publishing. This is -because ``images`` is in the ``settings["STATIC_PATHS"]`` list by default. If -you want to have another directory, say ``pdfs`` you would need to add the -following to ``pelicanconf.py``:: +directory gets copied to ``output/`` during site generation because Pelican +includes ``images`` in the ``STATIC_PATHS`` setting's list by default. If +you want to have another directory, say ``pdfs``, copied from your content to +your output during site generation, you would need to add the following to +your settings file:: STATIC_PATHS = ['images', 'pdfs'] -And then the ``pdfs`` directory would also be copied to ``output/static/``. +After the above line has been added, subsequent site generation should copy the +``content/pdfs/`` directory to ``output/pdfs/``. You can also link to categories or tags, using the ``{tag}tagname`` and ``{category}foobar`` syntax. -For backward compatibility, Pelican also supports bars ``||``, besides ``{}``, -i.e. the ``filename``, ``tag`` and ``category`` identifiers can be enclosed -in bars ``|`` instead of braces ``{}``, for example, ``|filename|an_article.rst``, -``|tag|tagname``, ``|category|foobar``. - -Using ``{}`` ensures that the syntax will not collide with markdown extensions or -reST directives. +For backward compatibility, Pelican also supports bars (``||``) in addition to +curly braces (``{}``). For example: ``|filename|an_article.rst``, +``|tag|tagname``, ``|category|foobar``. The syntax was changed from ``||`` to +``{}`` to avoid collision with Markdown extensions or reST directives. Importing an existing blog -------------------------- @@ -590,12 +594,12 @@ tagsfile string ctags file to use for name definitions. tagurlformat string format for the ctag links. ============= ============ ========================================= -Note that, depending on its version, your pygments module might not have -all of these available. See the `Pygments documentation -`_ for the HTML formatter for more +Note that, depending on the version, your Pygments module might not have +all of these options available. Refer to the *HtmlFormatter* section of the +`Pygments documentation `_ for more details on each of the options. -for example the below code block enables line numbers, starting at 153, +For example, the following code block enables line numbers, starting at 153, and prefixes the Pygments CSS classes with *pgcss* to make the names more unique and avoid possible CSS conflicts:: @@ -606,25 +610,25 @@ more unique and avoid possible CSS conflicts:: -It is also possible to specify the ``PYGMENTS_RST_OPTIONS`` variable -in your Pelican configuration file for settings that will be -automatically applied to every code block. +It is also possible to specify the ``PYGMENTS_RST_OPTIONS`` variable in your +Pelican settings file to include options that will be automatically applied to +every code block. -For example, if you wanted to have line numbers on for every code block +For example, if you want to have line numbers displayed for every code block and a CSS prefix you would set this variable to:: - PYGMENTS_RST_OPTIONS = { 'classprefix': 'pgcss', 'linenos': 'table'} + PYGMENTS_RST_OPTIONS = {'classprefix': 'pgcss', 'linenos': 'table'} -If specified, settings for individual code blocks will override the -defaults in the configuration file. +If specified, settings for individual code blocks will override the defaults in +your settings file. Publishing drafts ----------------- If you want to publish an article as a draft (for friends to review before -publishing, for example), you can add a ``status: draft`` attribute to its +publishing, for example), you can add a ``Status: draft`` attribute to its metadata. That article will then be output to the ``drafts`` folder and not -listed on the index page nor on any category page. +listed on the index page nor on any category or tag page. .. _virtualenv: http://www.virtualenv.org/ .. _W3C ISO 8601: http://www.w3.org/TR/NOTE-datetime diff --git a/docs/index.rst b/docs/index.rst index eb8883ce..35e859d5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -51,9 +51,13 @@ If you want to see new features in Pelican, don't hesitate to offer suggestions, clone the repository, etc. There are many ways to :doc:`contribute`. That's open source, dude! -Send a message to "authors at getpelican dot com" with any requests/feedback! You -can also join the team at `#pelican on Freenode`_ (or if you don't have an IRC -client handy, use the webchat_ for quick feedback. +Send a message to "authors at getpelican dot com" with any requests/feedback. +For a more immediate response, you can also join the team via IRC at +`#pelican on Freenode`_ — if you don't have an IRC client handy, use the +webchat_ for quick feedback. If you ask a question via IRC and don't get an +immediate response, don't leave the channel! It may take a few hours because +of time zone differences, but f you are patient and remain in the channel, +someone will almost always respond to your inquiry. Documentation ------------- diff --git a/docs/plugins.rst b/docs/plugins.rst index 29d67e24..6de01d05 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -135,15 +135,15 @@ shared in the documentation somewhere, so here they are! How to create a new reader -------------------------- -One thing you might want is to add the support for your very own input -format. While it might make sense to add this feature in pelican core, we -wisely chose to avoid this situation, and have the different readers defined in -plugins. +One thing you might want is to add support for your very own input format. +While it might make sense to add this feature in Pelican core, we +wisely chose to avoid this situation and instead have the different readers +defined via plugins. The rationale behind this choice is mainly that plugins are really easy to -write and don't slow down pelican itself when they're not active. +write and don't slow down Pelican itself when they're not active. -No more talking, here is the example:: +No more talking — here is an example:: from pelican import signals from pelican.readers import BaseReader @@ -153,7 +153,7 @@ No more talking, here is the example:: enabled = True # Yeah, you probably want that :-) # The list of file extensions you want this reader to match with. - # In the case multiple readers use the same extensions, the latest will + # If multiple readers were to use the same extension, the latest will # win (so the one you're defining here, most probably). file_extensions = ['yeah'] @@ -173,7 +173,7 @@ No more talking, here is the example:: def add_reader(readers): readers.reader_classes['yeah'] = NewReader - # this is how pelican works. + # This is how pelican works. def register(): signals.readers_init.connect(add_reader) diff --git a/docs/settings.rst b/docs/settings.rst index e8965731..b6c18fa9 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -84,10 +84,10 @@ Setting name (default value) What doe here or a single string representing one locale. When providing a list, all the locales will be tried until one works. -`READERS` (``{}``) A dict of file extensions / Reader classes to overwrite or - add file readers. for instance, to avoid processing .html files: - ``READERS = {'html': None}``. Or to add a custom reader for the - `foo` extension: ``READERS = {'foo': FooReader}`` +`READERS` (``{}``) A dictionary of file extensions / Reader classes for Pelican to + process or ignore. For example, to avoid processing .html files, + set: ``READERS = {'html': None}``. To add a custom reader for the + `foo` extension, set: ``READERS = {'foo': FooReader}`` `IGNORE_FILES` (``['.#*']``) A list of file globbing patterns to match against the source files to be ignored by the processor. For example, the default ``['.#*']`` will ignore emacs lock files. @@ -106,11 +106,9 @@ Setting name (default value) What doe `PAGE_EXCLUDES` (``()``) A list of directories to exclude when looking for pages. `ARTICLE_DIR` (``''``) Directory to look at for articles, relative to `PATH`. `ARTICLE_EXCLUDES`: (``('pages',)``) A list of directories to exclude when looking for articles. -`PDF_GENERATOR` (``False``) Set to ``True`` if you want PDF versions of your documents to be. - generated. You will need to install ``rst2pdf``. `OUTPUT_SOURCES` (``False``) Set to True if you want to copy the articles and pages in their original format (e.g. Markdown or reStructuredText) to the - specified OUTPUT_PATH. + specified ``OUTPUT_PATH``. `OUTPUT_SOURCES_EXTENSION` (``.text``) Controls the extension that will be used by the SourcesGenerator. Defaults to ``.text``. If not a valid string the default value will be used. @@ -144,7 +142,7 @@ Setting name (default value) What doe are not needed, set ``DIRECT_TEMPLATES = ('index', 'archives')`` `PAGINATED_DIRECT_TEMPLATES` (``('index',)``) Provides the direct templates that should be paginated. `SUMMARY_MAX_LENGTH` (``50``) When creating a short summary of an article, this will - be the default length in words of the text created. + be the default length (measured in words) of the text created. This only applies if your content does not otherwise specify a summary. Setting to ``None`` will cause the summary to be a copy of the original content. @@ -155,9 +153,9 @@ Setting name (default value) What doe `ASCIIDOC_OPTIONS` (``[]``) A list of options to pass to AsciiDoc. See the `manpage `_ `WITH_FUTURE_DATES` (``True``) If disabled, content with dates in the future will get a - default status of draft. + default status of ``draft``. `INTRASITE_LINK_REGEX` (``'[{|](?P.*?)[|}]'``) Regular expression that is used to parse internal links. - Default syntax of links to internal files, tags, etc. is + Default syntax of links to internal files, tags, etc., is to enclose the identifier, say ``filename``, in ``{}`` or ``||``. Identifier between ``{`` and ``}`` goes into the ``what`` capturing group. For details see :ref:`ref-linking-to-internal-content`. @@ -173,7 +171,7 @@ URL settings ------------ The first thing to understand is that there are currently two supported methods -for URL formation: *relative* and *absolute*. Document-relative URLs are useful +for URL formation: *relative* and *absolute*. Relative URLs are useful when testing locally, and absolute URLs are reliable and most useful when publishing. One method of supporting both is to have one Pelican configuration file for local development and another for publishing. To see an example of this @@ -181,16 +179,17 @@ type of setup, use the ``pelican-quickstart`` script as described at the top of the :doc:`Getting Started ` page, which will produce two separate configuration files for local development and publishing, respectively. -You can customize the URLs and locations where files will be saved. The URLs and -SAVE_AS variables use Python's format strings. These variables allow you to place -your articles in a location such as ``{slug}/index.html`` and link to them as -``{slug}`` for clean URLs. These settings give you the flexibility to place your -articles and pages anywhere you want. +You can customize the URLs and locations where files will be saved. The +``*_URL`` and ``*_SAVE_AS`` variables use Python's format strings. These +variables allow you to place your articles in a location such as +``{slug}/index.html`` and link to them as ``{slug}`` for clean URLs. These +settings give you the flexibility to place your articles and pages anywhere you +want. .. note:: - If you specify a datetime directive, it will be substituted using the + If you specify a ``datetime`` directive, it will be substituted using the input files' date metadata attribute. If the date is not specified for a - particular file, Pelican will rely on the file's mtime timestamp. + particular file, Pelican will rely on the file's ``mtime`` timestamp. Check the Python datetime documentation at http://bit.ly/cNcJUC for more information. @@ -213,7 +212,7 @@ and the URL to this would be ``/posts/2011/Aug/07/sample-post/``. Pelican can optionally create per-year, per-month, and per-day archives of your posts. These secondary archives are disabled by default but are automatically -enabled if you supply format strings for their respective `_SAVE_AS` settings. +enabled if you supply format strings for their respective ``_SAVE_AS`` settings. Period archives fit intuitively with the hierarchical model of web URLs and can make it easier for readers to navigate through the posts you've written over time. @@ -222,12 +221,12 @@ Example usage: * YEAR_ARCHIVE_SAVE_AS = ``'posts/{date:%Y}/index.html'`` * MONTH_ARCHIVE_SAVE_AS = ``'posts/{date:%Y}/{date:%b}/index.html'`` -With these settings, Pelican will create an archive of all your posts for the year -at (for instance) 'posts/2011/index.html', and an archive of all your posts for -the month at 'posts/2011/Aug/index.html'. +With these settings, Pelican will create an archive of all your posts for the +year at (for instance) ``posts/2011/index.html`` and an archive of all your +posts for the month at ``posts/2011/Aug/index.html``. .. note:: - Period archives work best when the final path segment is 'index.html'. + Period archives work best when the final path segment is ``index.html``. This way a reader can remove a portion of your URL and automatically arrive at an appropriate archive of posts, without having to specify a page name. @@ -299,10 +298,11 @@ Have a look at `the wikipedia page`_ to get a list of valid timezone values. Date format and locale ---------------------- -If no DATE_FORMATS are set, Pelican will fall back to DEFAULT_DATE_FORMAT. If -you need to maintain multiple languages with different date formats, you can -set this dict using the language name (``lang`` metadata in your post content) -as the key. Regarding available format codes, see `strftime document of python`_ : +If no ``DATE_FORMATS`` are set, Pelican will fall back to +``DEFAULT_DATE_FORMAT``. If you need to maintain multiple languages with +different date formats, you can set the ``DATE_FORMATS`` dictionary using the +language name (``lang`` metadata in your post content) as the key. Regarding +available format codes, see `strftime document of python`_ : .. parsed-literal:: @@ -320,8 +320,8 @@ You can set locale to further control date format: ) Also, it is possible to set different locale settings for each language. If you -put (locale, format) tuples in the dict, this will override the LOCALE setting -above: +put (locale, format) tuples in the dict, this will override the ``LOCALE`` +setting above: .. parsed-literal:: # On Unix/Linux @@ -473,9 +473,9 @@ Pagination ========== The default behaviour of Pelican is to list all the article titles along -with a short description on the index page. While it works pretty well -for small-to-medium blogs, for sites with large quantity of articles it would -be convenient to have a way to paginate the list. +with a short description on the index page. While this works well for +small-to-medium sites, sites with a large quantity of articles will probably +benefit from paginating this list. You can use the following settings to configure the pagination. @@ -483,8 +483,8 @@ You can use the following settings to configure the pagination. Setting name (default value) What does it do? ================================================ ===================================================== `DEFAULT_ORPHANS` (``0``) The minimum number of articles allowed on the - last page. Use this when you don't want to - have a last page with very few articles. + last page. Use this when you don't want the last page + to only contain a handful of articles. `DEFAULT_PAGINATION` (``False``) The maximum number of articles to include on a page, not including orphans. False to disable pagination. @@ -528,7 +528,7 @@ Setting name (default value) What does it do? `TAG_CLOUD_MAX_ITEMS` (``100``) Maximum number of tags in the cloud. ================================================ ===================================================== -The default theme does not include a tag cloud, but it is pretty easy to add:: +The default theme does not include a tag cloud, but it is pretty easy to add one::
      {% for tag in tag_cloud %} @@ -536,9 +536,10 @@ The default theme does not include a tag cloud, but it is pretty easy to add:: {% endfor %}
    -You should then also define CSS styles with appropriate classes (tag-0 to tag-N, where -N matches `TAG_CLOUD_STEPS` -1), tag-0 being the most frequent, and define a ul.tagcloud -class with appropriate list-style to create the cloud, for example:: +You should then also define CSS styles with appropriate classes (tag-0 to tag-N, +where N matches ``TAG_CLOUD_STEPS`` -1), tag-0 being the most frequent, and +define a ``ul.tagcloud`` class with appropriate list-style to create the cloud. +For example:: ul.tagcloud { list-style: none; diff --git a/docs/themes.rst b/docs/themes.rst index ddf509f8..c5aafb46 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -30,12 +30,12 @@ To make your own theme, you must follow the following structure:: └── tags.html // must list all the tags. Can be a tag cloud. * `static` contains all the static assets, which will be copied to the output - `theme` folder. I've put the CSS and image folders here, but they are - just examples. Put what you need here. + `theme` folder. The above filesystem layout includes CSS and image folders, + but those are just examples. Put what you need here. * `templates` contains all the templates that will be used to generate the content. - I've just put the mandatory templates here; you can define your own if it helps - you keep things organized while creating your theme. + The template files listed above are mandatory; you can add your own templates + if it helps you keep things organized while creating your theme. Templates and variables ======================= @@ -44,8 +44,8 @@ The idea is to use a simple syntax that you can embed into your HTML pages. This document describes which templates should exist in a theme, and which variables will be passed to each template at generation time. -All templates will receive the variables defined in your settings file, if they -are in all-caps. You can access them directly. +All templates will receive the variables defined in your settings file, as long +as they are in all-caps. You can access them directly. Common variables ---------------- @@ -58,19 +58,18 @@ Variable Description output_file The name of the file currently being generated. For instance, when Pelican is rendering the homepage, output_file will be "index.html". -articles The list of articles, ordered descending by date +articles The list of articles, ordered descending by date. All the elements are `Article` objects, so you can access their attributes (e.g. title, summary, author etc.). Sometimes this is shadowed (for instance in the tags page). You will then find info about it in the `all_articles` variable. dates The same list of articles, but ordered by date, - ascending + ascending. tags A list of (tag, articles) tuples, containing all the tags. categories A list of (category, articles) tuples, containing - all the categories. - and the list of respective articles (values) + all the categories and corresponding articles (values) pages The list of pages ============= =================================================== @@ -91,9 +90,9 @@ __ http://jinja.pocoo.org/docs/templates/#sort Date Formatting --------------- -Pelican formats the date with according to your settings and locale +Pelican formats the date according to your settings and locale (``DATE_FORMATS``/``DEFAULT_DATE_FORMAT``) and provides a -``locale_date`` attribute. On the other hand, ``date`` attribute will +``locale_date`` attribute. On the other hand, the ``date`` attribute will be a `datetime`_ object. If you need custom formatting for a date different than your settings, use the Jinja filter ``strftime`` that comes with Pelican. Usage is same as Python `strftime`_ format, diff --git a/docs/tips.rst b/docs/tips.rst index 1864f0dd..7629481f 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -15,16 +15,16 @@ Project Pages and User Pages. Project Pages ------------- -To publish a Pelican site as Project Pages you need to *push* the content of +To publish a Pelican site as a Project Page you need to *push* the content of the ``output`` dir generated by Pelican to a repository's ``gh-pages`` branch on GitHub. The excellent `ghp-import `_, which can be installed with ``easy_install`` or ``pip``, makes this process really easy. -For example, if the sources of your Pelican site are contained in a GitHub -repository, and if you want to publish your Pelican site as Project Pages of -this repository, you can then use the following:: +For example, if the source of your Pelican site is contained in a GitHub +repository, and if you want to publish that Pelican site in the form of Project +Pages to this repository, you can then use the following:: $ pelican content -o output -s pelicanconf.py $ ghp-import output @@ -38,28 +38,28 @@ already exist). The ``git push origin gh-pages`` command updates the remote .. note:: The ``github`` target of the Makefile created by the ``pelican-quickstart`` - command publishes the Pelican site as Project Pages as described above. + command publishes the Pelican site as Project Pages, as described above. User Pages ---------- -To publish a Pelican site as User Pages you need to *push* the content of the -``output`` dir generated by Pelican to the ``master`` branch of your -``.github.com`` repository on GitHub. +To publish a Pelican site in the form of User Pages, you need to *push* the +content of the ``output`` dir generated by Pelican to the ``master`` branch of +your ``.github.io`` repository on GitHub. Again, you can take advantage of ``ghp-import``:: $ pelican content -o output -s pelicanconf.py $ ghp-import output - $ git push git@github.com:elemoine/elemoine.github.com.git gh-pages:master + $ git push git@github.com:elemoine/elemoine.github.io.git gh-pages:master The ``git push`` command pushes the local ``gh-pages`` branch (freshly updated -by the ``ghp-import`` command) to the ``elemoine.github.com`` repository's +by the ``ghp-import`` command) to the ``elemoine.github.io`` repository's ``master`` branch on GitHub. .. note:: - To publish your Pelican site as User Pages feel free to adjust the the + To publish your Pelican site as User Pages, feel free to adjust the ``github`` target of the Makefile. Extra Tips @@ -67,28 +67,30 @@ Extra Tips Tip #1: -To automatically update your Pelican site on each commit you can create +To automatically update your Pelican site on each commit, you can create a post-commit hook. For example, you can add the following to ``.git/hooks/post-commit``:: - pelican pelican content -o output -s pelicanconf.py && ghp-import output && git push origin gh-pages + pelican content -o output -s pelicanconf.py && ghp-import output && git push origin gh-pages Tip #2: To use a `custom domain `_ with -GitHub Pages you need to have a ``CNAME`` file at the root of your pages. For -that you will add ``CNAME`` file to your ``content``, dir and use the -``FILES_TO_COPY`` setting variable to tell Pelican to copy that file -to the ``output`` dir. For example:: +GitHub Pages, you need to put the domain of your site (e.g., +``blog.example.com``) inside a ``CNAME`` file at the root of your site. To do +this, create the ``content/extras/`` directory and add a ``CNAME`` file to it. +Then use the ``STATIC_PATHS`` setting to tell Pelican to copy this file to your +output directory. For example:: - FILES_TO_COPY = (('extra/CNAME', 'CNAME'),) + STATIC_PATHS = ['images', 'extra/CNAME'] + EXTRA_PATH_METADATA = {'extra/CNAME': {'path': 'CNAME'},} -How to add Youtube or Vimeo Videos +How to add YouTube or Vimeo Videos ================================== -The easiest way is to paste embed code of the video from these sites in your -markup file. +The easiest way is to paste the embed code of the video from these sites +directly into your source content. -Alternatively, you can also use Pelican plugins like ``liquid_tags`` or ``pelican_youtube`` -or ``pelican_vimeo`` to embed videos in your blog. +Alternatively, you can also use Pelican plugins like ``liquid_tags``, +``pelican_youtube``, or ``pelican_vimeo`` to embed videos in your content. From e5c0a54c57eb7d9cd718a382f66977ea8becf24a Mon Sep 17 00:00:00 2001 From: Honza Javorek Date: Tue, 24 Sep 2013 14:19:47 +0200 Subject: [PATCH 0117/1427] Removed obsolete comment. --- pelican/contents.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index b453f61b..1858013c 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -197,10 +197,6 @@ class Content(object): value = m.group('value') origin = m.group('path') - # we support only filename for now. the plan is to support - # categories, tags, etc. in the future, but let's keep things - # simple for now. - # XXX Put this in a different location. if what == 'filename': if value.startswith('/'): From 7d43c4fa00fa31c9ef445a5f923255da870bf650 Mon Sep 17 00:00:00 2001 From: Honza Javorek Date: Tue, 24 Sep 2013 15:18:09 +0200 Subject: [PATCH 0118/1427] Support for params and fragments in intrasite links. Adresses #1063. --- pelican/contents.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 1858013c..83fbad8f 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -5,6 +5,7 @@ import six import copy import locale import logging +import urlparse import functools import os import re @@ -194,30 +195,36 @@ class Content(object): def replacer(m): what = m.group('what') - value = m.group('value') + value = urlparse.urlparse(m.group('value')) + path = value.path origin = m.group('path') # XXX Put this in a different location. if what == 'filename': - if value.startswith('/'): - value = value[1:] + if path.startswith('/'): + path = path[1:] else: # relative to the source path of this content - value = self.get_relative_source_path( - os.path.join(self.relative_dir, value) + path = self.get_relative_source_path( + os.path.join(self.relative_dir, path) ) - if value in self._context['filenames']: + if path in self._context['filenames']: origin = '/'.join((siteurl, - self._context['filenames'][value].url)) - origin = origin.replace('\\', '/') # Fow windows paths. + self._context['filenames'][path].url)) + origin = origin.replace('\\', '/') # for Windows paths. else: logger.warning("Unable to find {fn}, skipping url" - " replacement".format(fn=value)) + " replacement".format(fn=path)) elif what == 'category': - origin = Category(value, self.settings).url + origin = Category(path, self.settings).url elif what == 'tag': - origin = Tag(value, self.settings).url + origin = Tag(path, self.settings).url + + # keep all other parts, such as query, fragment, etc. + parts = list(value) + parts[2] = origin + origin = urlparse.urlunparse(parts) return ''.join((m.group('markup'), m.group('quote'), origin, m.group('quote'))) From 5f23aab8072bb28631043e6c0f8585fde6c2dd93 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 24 Sep 2013 21:28:59 +0200 Subject: [PATCH 0119/1427] Change Bumpr test from tox to unittest There are currently some minor problems when running tox that are not present when running "python -m unittest discover". Once those problems have been resolved, we should probably change this setting back to tox. --- bumpr.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumpr.rc b/bumpr.rc index cfc90fd7..fd35b042 100644 --- a/bumpr.rc +++ b/bumpr.rc @@ -4,7 +4,7 @@ vcs = git clean = python setup.py clean rm -rf *egg-info build dist -tests = tox +tests = python -m unittest discover publish = python setup.py sdist register upload files = README.rst From 2f57b86560f93037ead2466016cdd5e358cae872 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 24 Sep 2013 21:37:53 +0200 Subject: [PATCH 0120/1427] Modify sed command in bumpr.rc to support BSD It appears that BSD sed, unlike Linux, has a requirement that you provide an extension to the -i option. So, while Linux allows: sed -i "sed-command" ... to edit in-place, the BSD variant needs to have: sed -i "" "sed-command" i.e., with an empty backup suffix. --- bumpr.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bumpr.rc b/bumpr.rc index fd35b042..969fdb24 100644 --- a/bumpr.rc +++ b/bumpr.rc @@ -27,4 +27,4 @@ prepare = Next release url = http://docs.getpelican.com/{tag} [commands] -bump = sed -i "s/last_stable\s*=.*/last_stable = '{version}'/" docs/conf.py +bump = sed -i "" "s/last_stable\s*=.*/last_stable = '{version}'/" docs/conf.py From b35ce43b7fc2cb96d6534dd0392bb41d2de1d933 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 24 Sep 2013 21:57:07 +0200 Subject: [PATCH 0121/1427] Bump version 3.3.0 --- README.rst | 2 +- docs/changelog.rst | 4 ++-- pelican/__init__.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 20c3f217..278f3a44 100644 --- a/README.rst +++ b/README.rst @@ -61,7 +61,7 @@ client handy, use the webchat_ for quick feedback. .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _Markdown: http://daringfireball.net/projects/markdown/ .. _Jinja2: http://jinja.pocoo.org/ -.. _`Pelican documentation`: http://docs.getpelican.com/latest/ +.. _`Pelican documentation`: http://docs.getpelican.com/3.3.0/ .. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html .. _`#pelican on Freenode`: irc://irc.freenode.net/pelican .. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4 diff --git a/docs/changelog.rst b/docs/changelog.rst index ebf84f34..a5f0c1f3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,8 +1,8 @@ Release history ############### -Next release -============ +3.3.0 (2013-09-24) +================== * Drop Python 3.2 support in favor of Python 3.3 * Add ``Fabfile`` so Fabric can be used for workflow automation instead of Make diff --git a/pelican/__init__.py b/pelican/__init__.py index cecab54b..9daeed76 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -22,7 +22,7 @@ from pelican.settings import read_settings from pelican.utils import clean_output_dir, folder_watcher, file_watcher from pelican.writers import Writer -__version__ = "3.2.3.dev" +__version__ = "3.3.0" DEFAULT_CONFIG_NAME = 'pelicanconf.py' From 2c468f091a587257f3d455f5727fa4c05cbbca87 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 24 Sep 2013 21:57:28 +0200 Subject: [PATCH 0122/1427] Prepare version 3.3.1.dev for next development cycle --- README.rst | 2 +- docs/changelog.rst | 5 +++++ pelican/__init__.py | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 278f3a44..20c3f217 100644 --- a/README.rst +++ b/README.rst @@ -61,7 +61,7 @@ client handy, use the webchat_ for quick feedback. .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _Markdown: http://daringfireball.net/projects/markdown/ .. _Jinja2: http://jinja.pocoo.org/ -.. _`Pelican documentation`: http://docs.getpelican.com/3.3.0/ +.. _`Pelican documentation`: http://docs.getpelican.com/latest/ .. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html .. _`#pelican on Freenode`: irc://irc.freenode.net/pelican .. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4 diff --git a/docs/changelog.rst b/docs/changelog.rst index a5f0c1f3..54b0d871 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Release history ############### +Next release +============ + +- Nothing yet + 3.3.0 (2013-09-24) ================== diff --git a/pelican/__init__.py b/pelican/__init__.py index 9daeed76..b3ffe21a 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -22,7 +22,7 @@ from pelican.settings import read_settings from pelican.utils import clean_output_dir, folder_watcher, file_watcher from pelican.writers import Writer -__version__ = "3.3.0" +__version__ = "3.3.1.dev" DEFAULT_CONFIG_NAME = 'pelicanconf.py' From a987b65bd2abe6faa37425c4f061362435125382 Mon Sep 17 00:00:00 2001 From: "M. Utku Altinkaya" Date: Wed, 25 Sep 2013 04:43:06 +0300 Subject: [PATCH 0123/1427] Watch static folders in Autoreload mode --- pelican/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pelican/__init__.py b/pelican/__init__.py index b3ffe21a..5cf89927 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -320,6 +320,9 @@ def main(): pelican.ignore_files), 'settings': file_watcher(args.settings)} + for static_path in settings.get("STATIC_PATHS", []): + watchers[static_path] = file_watcher(static_path) + try: if args.autoreload: print(' --- AutoReload Mode: Monitoring `content`, `theme` and' From 6fb0335269e9d9dbd79c2f4cb020cd9a6b03313a Mon Sep 17 00:00:00 2001 From: Honza Javorek Date: Wed, 25 Sep 2013 11:39:29 +0200 Subject: [PATCH 0124/1427] Attempt to be compilant with Python 3. --- pelican/contents.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 83fbad8f..dbc33716 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -5,12 +5,16 @@ import six import copy import locale import logging -import urlparse import functools import os import re import sys +try: + from urlparse import urlparse, urlunparse +except ImportError: + from urllib.parse import urlparse, urlunparse + from datetime import datetime @@ -195,7 +199,7 @@ class Content(object): def replacer(m): what = m.group('what') - value = urlparse.urlparse(m.group('value')) + value = urlparse(m.group('value')) path = value.path origin = m.group('path') @@ -224,7 +228,7 @@ class Content(object): # keep all other parts, such as query, fragment, etc. parts = list(value) parts[2] = origin - origin = urlparse.urlunparse(parts) + origin = urlunparse(parts) return ''.join((m.group('markup'), m.group('quote'), origin, m.group('quote'))) From 7415d370e6a9c6f9b96da927685ffe9a6bc6eada Mon Sep 17 00:00:00 2001 From: Honza Javorek Date: Wed, 25 Sep 2013 16:13:28 +0200 Subject: [PATCH 0125/1427] Added tests. --- pelican/tests/test_contents.py | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 936903c1..2e44253a 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -204,6 +204,66 @@ class TestPage(unittest.TestCase): ('A simple test, with a ' 'link')) + def test_intrasite_link(self): + article = type(b'_DummyArticle', (object,), {'url': 'article.html'}) + + args = self.page_kwargs.copy() + args['settings'] = get_settings() + args['source_path'] = 'content' + args['context']['filenames'] = {'article.rst': article} + + # Classic intrasite link via filename + args['content'] = ( + 'A simple test, with a ' + 'link' + ) + content = Page(**args).get_content('http://notmyidea.org') + self.assertEquals( + content, + 'A simple test, with a ' + 'link' + ) + + # fragment + args['content'] = ( + 'A simple test, with a ' + 'link' + ) + content = Page(**args).get_content('http://notmyidea.org') + self.assertEquals( + content, + 'A simple test, with a ' + 'link' + ) + + # query + args['content'] = ( + 'A simple test, with a ' + 'link' + ) + content = Page(**args).get_content('http://notmyidea.org') + self.assertEquals( + content, + 'A simple test, with a ' + 'link' + ) + + # combination + args['content'] = ( + 'A simple test, with a ' + 'link' + ) + content = Page(**args).get_content('http://notmyidea.org') + self.assertEquals( + content, + 'A simple test, with a ' + 'link' + ) + class TestArticle(TestPage): def test_template(self): From 6ed23fec7dc1c907b9dcc3eb3d08eca61c1bb9b5 Mon Sep 17 00:00:00 2001 From: Honza Javorek Date: Wed, 25 Sep 2013 16:31:23 +0200 Subject: [PATCH 0126/1427] Tuned the tests so they are PY3 compilant. --- pelican/tests/test_contents.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 2e44253a..437d0228 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import six from datetime import datetime from sys import platform @@ -205,7 +206,10 @@ class TestPage(unittest.TestCase): 'link')) def test_intrasite_link(self): - article = type(b'_DummyArticle', (object,), {'url': 'article.html'}) + # type does not take unicode in PY2 and bytes in PY3, which in + # combination with unicode literals leads to following insane line: + cls_name = '_DummyArticle' if six.PY3 else b'_DummyArticle' + article = type(cls_name, (object,), {'url': 'article.html'}) args = self.page_kwargs.copy() args['settings'] = get_settings() From a49b744e951a810040baca27ff04fda23f973e26 Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Thu, 26 Sep 2013 00:37:35 +0200 Subject: [PATCH 0127/1427] Fix tests with latest versions of smartypants. smartypants is now py3 compatible but the default settings for double quotes has been changed (http://pythonhosted.org/smartypants/changes.html). This commit: - update the typogrify test (change quotes, and add more test casesi: caps word, ellipsis) - install typogrify on travis - uses upstream version of smartypants in tox instead of dmdm's fork for py3 --- .travis.yml | 2 ++ pelican/tests/content/article.rst | 2 +- pelican/tests/test_readers.py | 13 +++++++------ tox.ini | 1 - 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1df32baa..62373e68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ before_install: - sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8 install: - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then ln -s /usr/share/asciidoc/asciidocapi.py ~/virtualenv/python2.7/lib/python2.7/site-packages/; fi + - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install typogrify ; fi + - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install git+https://github.com/dmdm/typogrify.git@py3k#egg=typogrify; fi - pip install mock nose nose-cov Markdown - pip install . script: nosetests -sv --with-coverage --cover-package=pelican pelican diff --git a/pelican/tests/content/article.rst b/pelican/tests/content/article.rst index 7109c29b..793e6869 100644 --- a/pelican/tests/content/article.rst +++ b/pelican/tests/content/article.rst @@ -1,6 +1,6 @@ Article title ############# -This is some content. With some stuff to "typogrify". +THIS is some content. With some stuff to "typogrify"... Now with added support for :abbr:`TLA (three letter acronym)`. diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index b6652d03..841e3d34 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -104,8 +104,8 @@ class RstReaderTest(ReaderTest): # if nothing is specified in the settings, the content should be # unmodified page = self.read_file(path='article.rst') - expected = ('

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

    \n

    Now with added ' + expected = ('

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

    \n

    Now with added ' 'support for ' 'TLA.

    \n') @@ -114,10 +114,11 @@ class RstReaderTest(ReaderTest): try: # otherwise, typogrify should be applied page = self.read_file(path='article.rst', TYPOGRIFY=True) - expected = ('

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

    \n

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

    \n') + expected = ( + '

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

    \n' + '

    Now with added support for TLA.

    \n') self.assertEqual(page.content, expected) except ImportError: diff --git a/tox.ini b/tox.ini index 8763c963..440216cf 100644 --- a/tox.ini +++ b/tox.ini @@ -22,6 +22,5 @@ deps = mock Markdown BeautifulSoup4 - git+https://github.com/dmdm/smartypants.git#egg=smartypants git+https://github.com/dmdm/typogrify.git@py3k#egg=typogrify lxml From cb82e486369479432624b293aa1176f11c11f074 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 4 Oct 2013 16:23:19 +0200 Subject: [PATCH 0128/1427] None, not False, in *_SAVE_AS docs. Fixes #1106. --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index b6c18fa9..82752436 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -278,7 +278,7 @@ Setting name (default value) What does it do? If you do not want one or more of the default pages to be created (e.g., you are the only author on your site and thus do not need an Authors page), - set the corresponding ``*_SAVE_AS`` setting to ``False`` to prevent the + set the corresponding ``*_SAVE_AS`` setting to ``None`` to prevent the relevant page from being generated. Timezone From 9657071301ffb7029d17a675f9544e7ec458b202 Mon Sep 17 00:00:00 2001 From: Tshepang Lekhonkhobe Date: Sun, 6 Oct 2013 15:30:14 +0200 Subject: [PATCH 0129/1427] Python 3.3 got mock --- pelican/tests/test_generators.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index f47ce7d3..e821bb86 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -3,7 +3,10 @@ from __future__ import unicode_literals import os from codecs import open -from mock import MagicMock +try: + from unittest.mock import MagicMock +except ImportError: + from mock import MagicMock from shutil import rmtree from tempfile import mkdtemp From 67d3ab8883ba072cc10da214db25dceedb0000a1 Mon Sep 17 00:00:00 2001 From: Tshepang Lekhonkhobe Date: Sun, 6 Oct 2013 16:15:43 +0200 Subject: [PATCH 0130/1427] assertEquals is deprecated in favor of assertEqual --- pelican/tests/test_contents.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 437d0228..9c894ffc 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -193,17 +193,17 @@ class TestPage(unittest.TestCase): 'link') page = Page(**args) content = page.get_content('http://notmyidea.org') - self.assertEquals(content, ('A simple test, with a ' - 'link')) + self.assertEqual(content, ('A simple test, with a ' + 'link')) # Category args['content'] = ('A simple test, with a ' 'link') page = Page(**args) content = page.get_content('http://notmyidea.org') - self.assertEquals(content, - ('A simple test, with a ' - 'link')) + self.assertEqual(content, + ('A simple test, with a ' + 'link')) def test_intrasite_link(self): # type does not take unicode in PY2 and bytes in PY3, which in @@ -222,7 +222,7 @@ class TestPage(unittest.TestCase): 'link' ) content = Page(**args).get_content('http://notmyidea.org') - self.assertEquals( + self.assertEqual( content, 'A simple test, with a ' 'link' @@ -234,7 +234,7 @@ class TestPage(unittest.TestCase): 'link' ) content = Page(**args).get_content('http://notmyidea.org') - self.assertEquals( + self.assertEqual( content, 'A simple test, with a ' 'link' @@ -247,7 +247,7 @@ class TestPage(unittest.TestCase): '?utm_whatever=234&highlight=word">link' ) content = Page(**args).get_content('http://notmyidea.org') - self.assertEquals( + self.assertEqual( content, 'A simple test, with a ' 'link' ) content = Page(**args).get_content('http://notmyidea.org') - self.assertEquals( + self.assertEqual( content, 'A simple test, with a ' ' Date: Tue, 8 Oct 2013 13:20:56 +0200 Subject: [PATCH 0132/1427] Add Tumblr and Posterous to importer description --- pelican/tools/pelican_import.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 8cc5b083..69e1f1b4 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -98,7 +98,7 @@ def decode_wp_content(content, br=True): def wp2fields(xml): - """Opens a wordpress XML file, and yield pelican fields""" + """Opens a wordpress XML file, and yield Pelican fields""" try: from bs4 import BeautifulSoup except ImportError: @@ -551,8 +551,9 @@ def fields2pelican(fields, out_markup, output_path, def main(): parser = argparse.ArgumentParser( - description="Transform feed, Wordpress or Dotclear files to reST (rst) " - "or Markdown (md) files. Be sure to have pandoc installed.", + description="Transform feed, WordPress, Tumblr, Dotclear, or Posterous " + "files into reST (rst) or Markdown (md) files. Be sure to " + "have pandoc installed.", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument(dest='input', help='The input file to read') From 00150f3556b99f68ef8290bd65d1e7865d9a278e Mon Sep 17 00:00:00 2001 From: David Branner Date: Wed, 9 Oct 2013 11:53:11 -0400 Subject: [PATCH 0133/1427] xml => lxml for bs4, in pelican-import.wp2fields() --- pelican/tools/pelican_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 69e1f1b4..54a2be2f 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -109,7 +109,7 @@ def wp2fields(xml): with open(xml, encoding='utf-8') as infile: xmlfile = infile.read() - soup = BeautifulSoup(xmlfile, "xml") + soup = BeautifulSoup(xmlfile, "lxml") items = soup.rss.channel.findAll('item') for item in items: From 6dafe69ac66a9732c4bb5bbcb889bb297e9e4964 Mon Sep 17 00:00:00 2001 From: Torrance Date: Thu, 10 Oct 2013 14:29:42 +1300 Subject: [PATCH 0134/1427] Ensure headers from base.html are included. --- pelican/themes/simple/templates/article.html | 1 + 1 file changed, 1 insertion(+) diff --git a/pelican/themes/simple/templates/article.html b/pelican/themes/simple/templates/article.html index 5025a5e7..79124725 100644 --- a/pelican/themes/simple/templates/article.html +++ b/pelican/themes/simple/templates/article.html @@ -1,5 +1,6 @@ {% extends "base.html" %} {% block head %} + {{ super() }} {% for keyword in article.keywords %} {% endfor %} From caa833877dc11362090c97552135668240b0b588 Mon Sep 17 00:00:00 2001 From: Adrien Oliva Date: Fri, 11 Oct 2013 15:52:47 +0200 Subject: [PATCH 0135/1427] Change StandardError to RuntimeError Since built-in exception "StandardError" does not exist in the latest python version (at least in version 3.3), use RuntimeError instead (which exists from python2.6 to python3.4) --- pelican/writers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/writers.py b/pelican/writers.py index da105929..6cf5232d 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -60,7 +60,7 @@ class Writer(object): """ if filename in self._overridden_files: if override: - raise StandardError('File %s is set to be overridden twice' + raise RuntimeError('File %s is set to be overridden twice' % filename) else: logger.info('skipping %s' % filename) @@ -69,7 +69,7 @@ class Writer(object): if override: logger.info('overwriting %s' % filename) else: - raise StandardError('File %s is to be overwritten' % filename) + raise RuntimeError('File %s is to be overwritten' % filename) if override: self._overridden_files.add(filename) self._written_files.add(filename) From 2b5db0321b021e54f8a966c1aa01a1f8f34d9ddd Mon Sep 17 00:00:00 2001 From: Jed Brown Date: Fri, 11 Oct 2013 22:28:43 -0500 Subject: [PATCH 0136/1427] docs/plugins.rst: fix signal name "page_generator_context" The old "pages_generate_context" dates from before pelican-3.2 standardization, but AFAIK, "page_generate_context" was never correct. --- docs/plugins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index 6de01d05..5e311fb6 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -80,7 +80,7 @@ article_generator_finalized article_generator invoked at the e get_generators generators invoked in Pelican.get_generator_classes, can return a Generator, or several generator in a tuple or in a list. -page_generate_context page_generator, metadata +page_generator_context page_generator, metadata page_generator_init page_generator invoked in the PagesGenerator.__init__ page_generator_finalized page_generator invoked at the end of PagesGenerator.generate_context content_object_init content_object invoked at the end of Content.__init__ (see note below) From eb6d4bb0087bcc218795625ee3bfe5a6d230dc75 Mon Sep 17 00:00:00 2001 From: zhouji Date: Tue, 15 Oct 2013 10:37:03 +0800 Subject: [PATCH 0137/1427] Preserve file metadata (esp. timestamps) when copy static files to output folder. --- pelican/generators.py | 2 +- pelican/utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index d695c7c8..c55cdc37 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -573,7 +573,7 @@ class StaticGenerator(Generator): source_path = os.path.join(self.path, sc.source_path) save_as = os.path.join(self.output_path, sc.save_as) mkdir_p(os.path.dirname(save_as)) - shutil.copy(source_path, save_as) + shutil.copy2(source_path, save_as) logger.info('copying {} to {}'.format(sc.source_path, sc.save_as)) diff --git a/pelican/utils.py b/pelican/utils.py index f222f63c..4b25ec7f 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -289,7 +289,7 @@ def copy(path, source, destination, destination_path=None): else: shutil.copytree(entry_path, entry_dest) else: - shutil.copy(entry_path, destination) + shutil.copy2(entry_path, destination) if os.path.isdir(source_): @@ -299,7 +299,7 @@ def copy(path, source, destination, destination_path=None): dest_dir = os.path.dirname(destination_) if not os.path.exists(dest_dir): os.makedirs(dest_dir) - shutil.copy(source_, destination_) + shutil.copy2(source_, destination_) logger.info('copying %s to %s' % (source_, destination_)) else: logger.warning('skipped copy %s to %s' % (source_, destination_)) From 04dba17b800b7c67e43706a8a164ebf697bba10f Mon Sep 17 00:00:00 2001 From: zhouji Date: Wed, 16 Oct 2013 17:06:56 +0800 Subject: [PATCH 0138/1427] Fix #1117 Make intra-link support all url-value HTML attributes. --- pelican/contents.py | 58 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index dbc33716..39322e99 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -125,6 +125,52 @@ class Content(object): if 'summary' in metadata: self._summary = metadata['summary'] + # prepare the list of HTML tag attributes which have a URL value. + # refer: http://stackoverflow.com/questions/2725156/complete-list-of-html-tag-attributes-which-have-a-url-value + self._url_attributes = { # each item in this set is a tuple composed by tag_name, attr_name + # HTML4 tags + ('a', 'href'), + ('applet', 'codebase'), + ('area', 'href'), + ('base', 'href'), + ('blockquote', 'cite'), + ('body', 'background'), + ('del', 'cite'), + ('form', 'action'), + ('frame', 'longdesc'), + ('frame', 'src'), + ('head', 'profile'), + ('iframe', 'longdesc'), + ('iframe', 'src'), + ('img', 'longdesc'), + ('img', 'src'), + ('img', 'usemap'), + ('input', 'src'), + ('input', 'usemap'), + ('ins', 'cite'), + ('link', 'href'), + ('object', 'classid'), + ('object', 'codebase'), + ('object', 'data'), + ('object', 'usemap'), + ('q', 'cite'), + ('script', 'src'), + + # HTML5 tags + ('audio', 'src'), + ('button', 'formaction'), + ('command', 'icon'), + ('embed', 'src'), + ('html', 'manifest'), + ('input', 'formaction'), + ('source', 'src'), + ('video', 'poster'), + ('video', 'src'), + } + """:type: set of (tuple of (string, string)""" + attribute_names = set(pair[1] for pair in self._url_attributes) + self._url_attr_pattern = '|'.join(attribute_names) + signals.content_object_init.send(self) def __str__(self): @@ -189,12 +235,12 @@ class Content(object): instrasite_link_regex = self.settings['INTRASITE_LINK_REGEX'] regex = r""" - (?P<\s*[^\>]* # match tag with src and href attr - (?:href|src)\s*=) + (?P<\s*(?P[^\s\>]+)[^\>]* # match tag with all url-value attributes + (?P{1})\s*=) (?P["\']) # require value to be quoted (?P{0}(?P.*?)) # the url value - \2""".format(instrasite_link_regex) + \4""".format(instrasite_link_regex, self._url_attr_pattern) hrefs = re.compile(regex, re.X) def replacer(m): @@ -203,6 +249,12 @@ class Content(object): path = value.path origin = m.group('path') + # verify HTML tag and attribute pair to avoid miss-replacing + tag = m.group('tag') + attr = m.group('attr') + if attr != 'href' and attr != 'src' and (tag, attr) not in self._url_attributes: + return m.group(0) + # XXX Put this in a different location. if what == 'filename': if path.startswith('/'): From e538aa2cdeb4eed2df40bcf0a414c0930ab05e25 Mon Sep 17 00:00:00 2001 From: zhouji Date: Thu, 17 Oct 2013 11:33:34 +0800 Subject: [PATCH 0139/1427] Fine-tune url-value HTML attributes list. --- pelican/contents.py | 58 ++-------------------------------- pelican/tests/test_contents.py | 55 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 55 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 39322e99..059c54a7 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -125,52 +125,6 @@ class Content(object): if 'summary' in metadata: self._summary = metadata['summary'] - # prepare the list of HTML tag attributes which have a URL value. - # refer: http://stackoverflow.com/questions/2725156/complete-list-of-html-tag-attributes-which-have-a-url-value - self._url_attributes = { # each item in this set is a tuple composed by tag_name, attr_name - # HTML4 tags - ('a', 'href'), - ('applet', 'codebase'), - ('area', 'href'), - ('base', 'href'), - ('blockquote', 'cite'), - ('body', 'background'), - ('del', 'cite'), - ('form', 'action'), - ('frame', 'longdesc'), - ('frame', 'src'), - ('head', 'profile'), - ('iframe', 'longdesc'), - ('iframe', 'src'), - ('img', 'longdesc'), - ('img', 'src'), - ('img', 'usemap'), - ('input', 'src'), - ('input', 'usemap'), - ('ins', 'cite'), - ('link', 'href'), - ('object', 'classid'), - ('object', 'codebase'), - ('object', 'data'), - ('object', 'usemap'), - ('q', 'cite'), - ('script', 'src'), - - # HTML5 tags - ('audio', 'src'), - ('button', 'formaction'), - ('command', 'icon'), - ('embed', 'src'), - ('html', 'manifest'), - ('input', 'formaction'), - ('source', 'src'), - ('video', 'poster'), - ('video', 'src'), - } - """:type: set of (tuple of (string, string)""" - attribute_names = set(pair[1] for pair in self._url_attributes) - self._url_attr_pattern = '|'.join(attribute_names) - signals.content_object_init.send(self) def __str__(self): @@ -235,12 +189,12 @@ class Content(object): instrasite_link_regex = self.settings['INTRASITE_LINK_REGEX'] regex = r""" - (?P<\s*(?P[^\s\>]+)[^\>]* # match tag with all url-value attributes - (?P{1})\s*=) + (?P<\s*[^\>]* # match tag with all url-value attributes + (?:href|src|poster|data|cite|formaction|action)\s*=) (?P["\']) # require value to be quoted (?P{0}(?P.*?)) # the url value - \4""".format(instrasite_link_regex, self._url_attr_pattern) + \2""".format(instrasite_link_regex) hrefs = re.compile(regex, re.X) def replacer(m): @@ -249,12 +203,6 @@ class Content(object): path = value.path origin = m.group('path') - # verify HTML tag and attribute pair to avoid miss-replacing - tag = m.group('tag') - attr = m.group('attr') - if attr != 'href' and attr != 'src' and (tag, attr) not in self._url_attributes: - return m.group(0) - # XXX Put this in a different location. if what == 'filename': if path.startswith('/'): diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 9c894ffc..92e61355 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -268,6 +268,61 @@ class TestPage(unittest.TestCase): '?utm_whatever=234&highlight=word#section-2">link' ) + def test_intrasite_link_more(self): + # type does not take unicode in PY2 and bytes in PY3, which in + # combination with unicode literals leads to following insane line: + cls_name = '_DummyAsset' if six.PY3 else b'_DummyAsset' + + args = self.page_kwargs.copy() + 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'}), + } + + # video.poster + args['content'] = ( + 'There is a video with poster ' + '' + ) + content = Page(**args).get_content('http://notmyidea.org') + self.assertEqual( + 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 + args['content'] = ( + 'There is a blockquote with cite attribute ' + '
    blah blah
    ' + ) + content = Page(**args).get_content('http://notmyidea.org') + self.assertEqual( + content, + 'There is a blockquote with cite attribute ' + '
    blah blah
    ' + ) + class TestArticle(TestPage): def test_template(self): From ae2afa27fc6841a9785f13f14bfd17bebbd1f516 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sat, 19 Oct 2013 17:20:13 +0200 Subject: [PATCH 0140/1427] Clarify FAQ entry re: need to install Markdown Folks keep running into this error, which probably signals a need to change this behavior. After all, it wouldn't be hard for us to detect what's going on and provide a better error message, such as: "It looks like you're trying to process Markdown, but the Markdown library is not currently installed. Please install the Python-Markdown library via 'pip install markdown'." Until we implement something akin to the above, this should serve as a slightly-improved FAQ entry in the interim. --- docs/faq.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index da37af04..80e14d21 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -65,12 +65,13 @@ How do I create my own theme? Please refer to :ref:`theming-pelican`. -I want to use Markdown, but I got an error. -=========================================== +I'm using Markdown and getting ``No valid files found in content`` errors. +========================================================================== -Markdown is not a hard dependency for Pelican, so you will need to explicitly -install it. You can do so by typing the following command, prepending ``sudo`` -if permissions require it:: +Markdown is not a hard dependency for Pelican, so if you have content in +Markdown format, you will need to explicitly install the Markdown library. +You can do so by typing the following command, prepending ``sudo`` if +permissions require it:: pip install markdown From 9331e42ee10c0a3537d93b000c0a41508758d401 Mon Sep 17 00:00:00 2001 From: Jon Chen Date: Thu, 24 Oct 2013 14:36:03 -0400 Subject: [PATCH 0141/1427] use // instead of explicitly defining http for twitter as well update sample output --- pelican/tests/output/custom/a-markdown-powered-article.html | 4 ++-- pelican/tests/output/custom/archives.html | 2 +- pelican/tests/output/custom/article-1.html | 4 ++-- pelican/tests/output/custom/article-2.html | 4 ++-- pelican/tests/output/custom/article-3.html | 4 ++-- pelican/tests/output/custom/author/alexis-metaireau.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau2.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom/authors.html | 2 +- pelican/tests/output/custom/categories.html | 2 +- pelican/tests/output/custom/category/bar.html | 2 +- pelican/tests/output/custom/category/cat1.html | 2 +- pelican/tests/output/custom/category/misc.html | 2 +- pelican/tests/output/custom/category/yeah.html | 2 +- pelican/tests/output/custom/drafts/a-draft-article.html | 2 +- pelican/tests/output/custom/filename_metadata-example.html | 4 ++-- pelican/tests/output/custom/index.html | 2 +- pelican/tests/output/custom/index2.html | 2 +- pelican/tests/output/custom/index3.html | 2 +- pelican/tests/output/custom/jinja2_template.html | 2 +- pelican/tests/output/custom/oh-yeah-fr.html | 4 ++-- pelican/tests/output/custom/oh-yeah.html | 4 ++-- pelican/tests/output/custom/override/index.html | 2 +- .../tests/output/custom/pages/this-is-a-test-hidden-page.html | 2 +- pelican/tests/output/custom/pages/this-is-a-test-page.html | 2 +- pelican/tests/output/custom/second-article-fr.html | 4 ++-- pelican/tests/output/custom/second-article.html | 4 ++-- pelican/tests/output/custom/tag/bar.html | 2 +- pelican/tests/output/custom/tag/baz.html | 4 ++-- pelican/tests/output/custom/tag/foo.html | 2 +- pelican/tests/output/custom/tag/foobar.html | 2 +- pelican/tests/output/custom/tag/oh.html | 2 +- pelican/tests/output/custom/tag/yeah.html | 2 +- pelican/tests/output/custom/tags.html | 2 +- pelican/tests/output/custom/this-is-a-super-article.html | 4 ++-- pelican/tests/output/custom/unbelievable.html | 4 ++-- pelican/themes/notmyidea/templates/article.html | 2 +- pelican/themes/notmyidea/templates/disqus_script.html | 2 +- pelican/themes/notmyidea/templates/twitter.html | 4 ++-- 39 files changed, 52 insertions(+), 52 deletions(-) diff --git a/pelican/tests/output/custom/a-markdown-powered-article.html b/pelican/tests/output/custom/a-markdown-powered-article.html index 4d1fe16f..515b0298 100644 --- a/pelican/tests/output/custom/a-markdown-powered-article.html +++ b/pelican/tests/output/custom/a-markdown-powered-article.html @@ -59,7 +59,7 @@ var disqus_url = "./a-markdown-powered-article.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -105,7 +105,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/archives.html b/pelican/tests/output/custom/archives.html index a2ab7430..13c3d980 100644 --- a/pelican/tests/output/custom/archives.html +++ b/pelican/tests/output/custom/archives.html @@ -92,7 +92,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/article-1.html b/pelican/tests/output/custom/article-1.html index 89e12914..6faed3be 100644 --- a/pelican/tests/output/custom/article-1.html +++ b/pelican/tests/output/custom/article-1.html @@ -58,7 +58,7 @@ var disqus_url = "./article-1.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -104,7 +104,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/article-2.html b/pelican/tests/output/custom/article-2.html index 1113e5e8..d1e89db8 100644 --- a/pelican/tests/output/custom/article-2.html +++ b/pelican/tests/output/custom/article-2.html @@ -58,7 +58,7 @@ var disqus_url = "./article-2.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -104,7 +104,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/article-3.html b/pelican/tests/output/custom/article-3.html index c7306605..5c287a13 100644 --- a/pelican/tests/output/custom/article-3.html +++ b/pelican/tests/output/custom/article-3.html @@ -58,7 +58,7 @@ var disqus_url = "./article-3.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -104,7 +104,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/author/alexis-metaireau.html b/pelican/tests/output/custom/author/alexis-metaireau.html index d2350bd5..19c254a0 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau.html +++ b/pelican/tests/output/custom/author/alexis-metaireau.html @@ -165,7 +165,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/author/alexis-metaireau2.html b/pelican/tests/output/custom/author/alexis-metaireau2.html index 91ef0696..772d2939 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom/author/alexis-metaireau2.html @@ -175,7 +175,7 @@ YEAH !

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index 77c9cdfe..f3d985c3 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -130,7 +130,7 @@ pelican.conf, it ...

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/authors.html b/pelican/tests/output/custom/authors.html index adb3d992..eb2becfd 100644 --- a/pelican/tests/output/custom/authors.html +++ b/pelican/tests/output/custom/authors.html @@ -71,7 +71,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/categories.html b/pelican/tests/output/custom/categories.html index 5d839648..17d9de76 100644 --- a/pelican/tests/output/custom/categories.html +++ b/pelican/tests/output/custom/categories.html @@ -72,7 +72,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/category/bar.html b/pelican/tests/output/custom/category/bar.html index 3f1bbc4a..30545518 100644 --- a/pelican/tests/output/custom/category/bar.html +++ b/pelican/tests/output/custom/category/bar.html @@ -95,7 +95,7 @@ YEAH !

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/category/cat1.html b/pelican/tests/output/custom/category/cat1.html index 81718322..9a737b76 100644 --- a/pelican/tests/output/custom/category/cat1.html +++ b/pelican/tests/output/custom/category/cat1.html @@ -162,7 +162,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html index 36479803..b70afcdf 100644 --- a/pelican/tests/output/custom/category/misc.html +++ b/pelican/tests/output/custom/category/misc.html @@ -173,7 +173,7 @@ pelican.conf, it ...

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/category/yeah.html b/pelican/tests/output/custom/category/yeah.html index 53666b0b..02d07413 100644 --- a/pelican/tests/output/custom/category/yeah.html +++ b/pelican/tests/output/custom/category/yeah.html @@ -99,7 +99,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/drafts/a-draft-article.html b/pelican/tests/output/custom/drafts/a-draft-article.html index b8306208..440780a3 100644 --- a/pelican/tests/output/custom/drafts/a-draft-article.html +++ b/pelican/tests/output/custom/drafts/a-draft-article.html @@ -92,7 +92,7 @@ listed anywhere else.

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/filename_metadata-example.html b/pelican/tests/output/custom/filename_metadata-example.html index fcbda9c2..1252aba8 100644 --- a/pelican/tests/output/custom/filename_metadata-example.html +++ b/pelican/tests/output/custom/filename_metadata-example.html @@ -58,7 +58,7 @@ var disqus_url = "./filename_metadata-example.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -104,7 +104,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/index.html b/pelican/tests/output/custom/index.html index 3fa1d5c3..c7174715 100644 --- a/pelican/tests/output/custom/index.html +++ b/pelican/tests/output/custom/index.html @@ -165,7 +165,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/index2.html b/pelican/tests/output/custom/index2.html index 8769d098..b349b3ca 100644 --- a/pelican/tests/output/custom/index2.html +++ b/pelican/tests/output/custom/index2.html @@ -175,7 +175,7 @@ YEAH !

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index b4d9ffc6..c668dba6 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -130,7 +130,7 @@ pelican.conf, it ...

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/jinja2_template.html b/pelican/tests/output/custom/jinja2_template.html index 31beac32..0eafa913 100644 --- a/pelican/tests/output/custom/jinja2_template.html +++ b/pelican/tests/output/custom/jinja2_template.html @@ -69,7 +69,7 @@ Some text (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/oh-yeah-fr.html b/pelican/tests/output/custom/oh-yeah-fr.html index 410c721f..4450514a 100644 --- a/pelican/tests/output/custom/oh-yeah-fr.html +++ b/pelican/tests/output/custom/oh-yeah-fr.html @@ -60,7 +60,7 @@ Translations: var disqus_url = "./oh-yeah-fr.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -106,7 +106,7 @@ Translations: (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/oh-yeah.html b/pelican/tests/output/custom/oh-yeah.html index dab28171..95cf0317 100644 --- a/pelican/tests/output/custom/oh-yeah.html +++ b/pelican/tests/output/custom/oh-yeah.html @@ -65,7 +65,7 @@ YEAH !

    var disqus_url = "./oh-yeah.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -111,7 +111,7 @@ YEAH !

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/override/index.html b/pelican/tests/output/custom/override/index.html index 9c48d76f..e84d79fe 100644 --- a/pelican/tests/output/custom/override/index.html +++ b/pelican/tests/output/custom/override/index.html @@ -73,7 +73,7 @@ at a custom location.

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html index a061b7ee..dced8107 100644 --- a/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html @@ -73,7 +73,7 @@ Anyone can see this page but it's not linked to anywhere!

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/pages/this-is-a-test-page.html b/pelican/tests/output/custom/pages/this-is-a-test-page.html index af50adf8..46ea4fef 100644 --- a/pelican/tests/output/custom/pages/this-is-a-test-page.html +++ b/pelican/tests/output/custom/pages/this-is-a-test-page.html @@ -73,7 +73,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/second-article-fr.html b/pelican/tests/output/custom/second-article-fr.html index cbeef437..b4a04ee8 100644 --- a/pelican/tests/output/custom/second-article-fr.html +++ b/pelican/tests/output/custom/second-article-fr.html @@ -60,7 +60,7 @@ var disqus_url = "./second-article-fr.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -106,7 +106,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/second-article.html b/pelican/tests/output/custom/second-article.html index 57009066..04f038c8 100644 --- a/pelican/tests/output/custom/second-article.html +++ b/pelican/tests/output/custom/second-article.html @@ -60,7 +60,7 @@ var disqus_url = "./second-article.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -106,7 +106,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/tag/bar.html b/pelican/tests/output/custom/tag/bar.html index 9560b712..7a90eff2 100644 --- a/pelican/tests/output/custom/tag/bar.html +++ b/pelican/tests/output/custom/tag/bar.html @@ -148,7 +148,7 @@ YEAH !

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/tag/baz.html b/pelican/tests/output/custom/tag/baz.html index c6ebe542..f794b351 100644 --- a/pelican/tests/output/custom/tag/baz.html +++ b/pelican/tests/output/custom/tag/baz.html @@ -58,7 +58,7 @@ var disqus_url = "../tag/baz.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -104,7 +104,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/tag/foo.html b/pelican/tests/output/custom/tag/foo.html index d5b0d413..d99df63b 100644 --- a/pelican/tests/output/custom/tag/foo.html +++ b/pelican/tests/output/custom/tag/foo.html @@ -118,7 +118,7 @@ as well as inline markup.

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/tag/foobar.html b/pelican/tests/output/custom/tag/foobar.html index 0b4d2471..ded91f12 100644 --- a/pelican/tests/output/custom/tag/foobar.html +++ b/pelican/tests/output/custom/tag/foobar.html @@ -99,7 +99,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/tag/oh.html b/pelican/tests/output/custom/tag/oh.html index 3b30a39c..21c8e352 100644 --- a/pelican/tests/output/custom/tag/oh.html +++ b/pelican/tests/output/custom/tag/oh.html @@ -72,7 +72,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/tag/yeah.html b/pelican/tests/output/custom/tag/yeah.html index a6764575..523358b5 100644 --- a/pelican/tests/output/custom/tag/yeah.html +++ b/pelican/tests/output/custom/tag/yeah.html @@ -95,7 +95,7 @@ YEAH !

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/tags.html b/pelican/tests/output/custom/tags.html index bc9b9d20..2e70c9e8 100644 --- a/pelican/tests/output/custom/tags.html +++ b/pelican/tests/output/custom/tags.html @@ -76,7 +76,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/this-is-a-super-article.html b/pelican/tests/output/custom/this-is-a-super-article.html index 0a580e25..d251b0ec 100644 --- a/pelican/tests/output/custom/this-is-a-super-article.html +++ b/pelican/tests/output/custom/this-is-a-super-article.html @@ -69,7 +69,7 @@ var disqus_url = "./this-is-a-super-article.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -115,7 +115,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/unbelievable.html b/pelican/tests/output/custom/unbelievable.html index 03b533bb..0d87bbf6 100644 --- a/pelican/tests/output/custom/unbelievable.html +++ b/pelican/tests/output/custom/unbelievable.html @@ -90,7 +90,7 @@ pelican.conf, it will have nothing in default.

    var disqus_url = "./unbelievable.html"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://blog-notmyidea.disqus.com/embed.js'; + dsq.src = '//blog-notmyidea.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); @@ -136,7 +136,7 @@ pelican.conf, it will have nothing in default.

    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/themes/notmyidea/templates/article.html b/pelican/themes/notmyidea/templates/article.html index 516fd3b5..367222b2 100644 --- a/pelican/themes/notmyidea/templates/article.html +++ b/pelican/themes/notmyidea/templates/article.html @@ -23,7 +23,7 @@ var disqus_url = "{{ SITEURL }}/{{ article.url }}"; (function() { var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; - dsq.src = 'http://{{ DISQUS_SITENAME }}.disqus.com/embed.js'; + dsq.src = '//{{ DISQUS_SITENAME }}.disqus.com/embed.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); })(); diff --git a/pelican/themes/notmyidea/templates/disqus_script.html b/pelican/themes/notmyidea/templates/disqus_script.html index c4f442c8..4ee419bb 100644 --- a/pelican/themes/notmyidea/templates/disqus_script.html +++ b/pelican/themes/notmyidea/templates/disqus_script.html @@ -4,7 +4,7 @@ (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = 'http://' + disqus_shortname + '.disqus.com/count.js'; + s.src = '//' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/themes/notmyidea/templates/twitter.html b/pelican/themes/notmyidea/templates/twitter.html index c6b159f4..7247a0c6 100644 --- a/pelican/themes/notmyidea/templates/twitter.html +++ b/pelican/themes/notmyidea/templates/twitter.html @@ -1,3 +1,3 @@ {% if TWITTER_USERNAME %} - -{% endif %} \ No newline at end of file + +{% endif %} From 6c808e426fe5287b49fdd6a56067aa90eaaba963 Mon Sep 17 00:00:00 2001 From: Kevin Deldycke Date: Mon, 21 Oct 2013 23:38:25 +0200 Subject: [PATCH 0142/1427] Document video support in Markdown and reST. --- THANKS | 1 + docs/tips.rst | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/THANKS b/THANKS index e4eed231..1d867471 100644 --- a/THANKS +++ b/THANKS @@ -88,6 +88,7 @@ Joseph Reagle Joshua Adelman Julian Berman Justin Mayer +Kevin Deldycke Kyle Fuller Laureline Guerin Leonard Huang diff --git a/docs/tips.rst b/docs/tips.rst index 7629481f..b140ea3c 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -94,3 +94,8 @@ directly into your source content. Alternatively, you can also use Pelican plugins like ``liquid_tags``, ``pelican_youtube``, or ``pelican_vimeo`` to embed videos in your content. + +Moreover, markup languages like reST and Markdown have plugins that let you +embed videos in the markup. You can use `reST video directive +`_ for reST or `mdx_video plugin +`_ for Markdown. From 5e5510cfcf65b873881d9e08adcb54ac1db9c730 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 27 Oct 2013 09:27:30 -0700 Subject: [PATCH 0143/1427] Improve Disqus embed code in notmyidea theme According to Disqus, the disqus_shortname variable is a required field. Also added a
    -

    Authors on A Pelican Blog

  • Alexis Métaireau (2)
  • +

    Authors on A Pelican Blog

    +
    @@ -48,4 +51,4 @@
    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/tags.html b/pelican/tests/output/basic/tags.html index 8172f6ae..0eda47d7 100644 --- a/pelican/tests/output/basic/tags.html +++ b/pelican/tests/output/basic/tags.html @@ -26,12 +26,15 @@
    -

    Tags for A Pelican Blog

  • bar (3)
  • +

    Tags for A Pelican Blog

    +
    @@ -53,4 +56,4 @@
    - \ No newline at end of file + diff --git a/pelican/tests/output/custom/authors.html b/pelican/tests/output/custom/authors.html index eb2becfd..d9aaef34 100644 --- a/pelican/tests/output/custom/authors.html +++ b/pelican/tests/output/custom/authors.html @@ -30,7 +30,10 @@
    -

    Authors on Alexis' log

  • Alexis Métaireau (10)
  • +

    Authors on Alexis' log

    +
    @@ -76,4 +79,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/tags.html b/pelican/tests/output/custom/tags.html index 2e70c9e8..ba8d53a4 100644 --- a/pelican/tests/output/custom/tags.html +++ b/pelican/tests/output/custom/tags.html @@ -30,12 +30,15 @@
    -

    Tags for Alexis' log

  • bar (3)
  • +

    Tags for Alexis' log

    +
    @@ -81,4 +84,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/themes/notmyidea/templates/authors.html b/pelican/themes/notmyidea/templates/authors.html index b145902e..e61a332f 100644 --- a/pelican/themes/notmyidea/templates/authors.html +++ b/pelican/themes/notmyidea/templates/authors.html @@ -6,9 +6,11 @@

    Authors on {{ SITENAME }}

    - {%- for author, articles in authors|sort %} +
      + {% for author, articles in authors|sort %}
    • {{ author }} ({{ articles|count }})
    • {% endfor %} +
    {% endblock %} diff --git a/pelican/themes/notmyidea/templates/tags.html b/pelican/themes/notmyidea/templates/tags.html index 76955f27..fb099557 100644 --- a/pelican/themes/notmyidea/templates/tags.html +++ b/pelican/themes/notmyidea/templates/tags.html @@ -6,10 +6,11 @@

    Tags for {{ SITENAME }}

    - - {%- for tag, articles in tags|sort %} +
      + {% for tag, articles in tags|sort %}
    • {{ tag }} ({{ articles|count }})
    • {% endfor %} +
    {% endblock %} From cff9d0aa5832e2d4c296a8c9b34ac5f88c9b0d3d Mon Sep 17 00:00:00 2001 From: Mario Lang Date: Wed, 12 Feb 2014 12:42:27 +0100 Subject: [PATCH 0209/1427] Fix stray and
    if only one article is displayed. We already check if loop.length > 1 before outputting
    and
      tags, but we neglected to do the same check when outputting the corresponding end tags. Also, since I had to read the code when I touched it, simplified a conditional: if (a) if (a and (b or not b and c)) can be simplified to if (a) if (b or c) Note the "b or not b", it was just too ugly to not fix. --- pelican/tests/output/basic/category/bar.html | 4 +--- pelican/tests/output/basic/category/yeah.html | 4 +--- pelican/tests/output/basic/tag/foobar.html | 4 +--- pelican/tests/output/basic/tag/yeah.html | 4 +--- pelican/tests/output/custom/category/bar.html | 4 +--- pelican/tests/output/custom/category/yeah.html | 4 +--- pelican/tests/output/custom/tag/foobar.html | 4 +--- pelican/tests/output/custom/tag/yeah.html | 4 +--- pelican/themes/notmyidea/templates/index.html | 11 +++++++---- 9 files changed, 15 insertions(+), 28 deletions(-) diff --git a/pelican/tests/output/basic/category/bar.html b/pelican/tests/output/basic/category/bar.html index 54b1db3e..763cfe77 100644 --- a/pelican/tests/output/basic/category/bar.html +++ b/pelican/tests/output/basic/category/bar.html @@ -46,8 +46,6 @@ YEAH !

      -
    -
    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/category/yeah.html b/pelican/tests/output/basic/category/yeah.html index 51fada93..225eae6e 100644 --- a/pelican/tests/output/basic/category/yeah.html +++ b/pelican/tests/output/basic/category/yeah.html @@ -56,8 +56,6 @@ - -
    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/tag/foobar.html b/pelican/tests/output/basic/tag/foobar.html index 66aa05e8..f3035f8e 100644 --- a/pelican/tests/output/basic/tag/foobar.html +++ b/pelican/tests/output/basic/tag/foobar.html @@ -56,8 +56,6 @@ - -
    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/tag/yeah.html b/pelican/tests/output/basic/tag/yeah.html index c064d8a0..cd9d01b2 100644 --- a/pelican/tests/output/basic/tag/yeah.html +++ b/pelican/tests/output/basic/tag/yeah.html @@ -46,8 +46,6 @@ YEAH !

    - -
    - \ No newline at end of file + diff --git a/pelican/tests/output/custom/category/bar.html b/pelican/tests/output/custom/category/bar.html index 113b8841..80c46de5 100644 --- a/pelican/tests/output/custom/category/bar.html +++ b/pelican/tests/output/custom/category/bar.html @@ -55,8 +55,6 @@ YEAH !

    Page 1 / 1

    - -

    blogroll

    @@ -100,4 +98,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/category/yeah.html b/pelican/tests/output/custom/category/yeah.html index c8e066c8..11fd181d 100644 --- a/pelican/tests/output/custom/category/yeah.html +++ b/pelican/tests/output/custom/category/yeah.html @@ -63,8 +63,6 @@ Page 1 / 1

    - -

    blogroll

    @@ -108,4 +106,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/tag/foobar.html b/pelican/tests/output/custom/tag/foobar.html index c62d3418..c224992e 100644 --- a/pelican/tests/output/custom/tag/foobar.html +++ b/pelican/tests/output/custom/tag/foobar.html @@ -63,8 +63,6 @@ Page 1 / 1

    - -

    blogroll

    @@ -108,4 +106,4 @@ }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom/tag/yeah.html b/pelican/tests/output/custom/tag/yeah.html index 288f4ec7..49c78359 100644 --- a/pelican/tests/output/custom/tag/yeah.html +++ b/pelican/tests/output/custom/tag/yeah.html @@ -55,8 +55,6 @@ YEAH !

    Page 1 / 1

    - -

    blogroll

    @@ -100,4 +98,4 @@ YEAH !

    }()); - \ No newline at end of file + diff --git a/pelican/themes/notmyidea/templates/index.html b/pelican/themes/notmyidea/templates/index.html index 2d45bb2a..c8982476 100644 --- a/pelican/themes/notmyidea/templates/index.html +++ b/pelican/themes/notmyidea/templates/index.html @@ -42,12 +42,15 @@ {% endif %} {% if loop.last %} - - {% if loop.last and (articles_page.has_previous() - or not articles_page.has_previous() and loop.length > 1) %} + {% if loop.length > 1 %} + + {% endif %} + {% if articles_page.has_previous() or loop.length > 1 %} {% include 'pagination.html' %} {% endif %} -
    + {% if loop.length > 1 %} + + {% endif %} {% endif %} {% endfor %} {% else %} From e500e64ebd888d61c555e0eefba8b3ae529b5e63 Mon Sep 17 00:00:00 2001 From: Mario Lang Date: Sun, 9 Feb 2014 15:17:23 +0100 Subject: [PATCH 0210/1427] Some browsers like Lynx render adjacent links without implicit spaces. Put an extra space at the end of each link to a tag so that Lynx doesnt render the tags as a single word. --- pelican/themes/notmyidea/templates/taglist.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/themes/notmyidea/templates/taglist.html b/pelican/themes/notmyidea/templates/taglist.html index c792fd7d..b8f4ba95 100644 --- a/pelican/themes/notmyidea/templates/taglist.html +++ b/pelican/themes/notmyidea/templates/taglist.html @@ -1,2 +1,2 @@ -{% if article.tags %}

    tags: {% for tag in article.tags %}{{ tag }}{% endfor %}

    {% endif %} +{% if article.tags %}

    tags: {% for tag in article.tags %}{{ tag }} {% endfor %}

    {% endif %} {% if PDF_PROCESSOR %}

    get the pdf

    {% endif %} From a7ca52dee05819be269b95556da01f965d107a50 Mon Sep 17 00:00:00 2001 From: Mario Lang Date: Tue, 18 Feb 2014 15:01:31 +0100 Subject: [PATCH 0211/1427] Run tag name through escape filter to avoid invalid HTML If a tag contains characters like <> or &, we currently generate invalid HTML. This is easily fixed by sending the tag through the jinja escape filter. (This bug is not theoretical, I hit it when using C++ template names for tags, like "boost::variant<>".) --- pelican/themes/notmyidea/templates/taglist.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/themes/notmyidea/templates/taglist.html b/pelican/themes/notmyidea/templates/taglist.html index b8f4ba95..1e0b95a7 100644 --- a/pelican/themes/notmyidea/templates/taglist.html +++ b/pelican/themes/notmyidea/templates/taglist.html @@ -1,2 +1,2 @@ -{% if article.tags %}

    tags: {% for tag in article.tags %}{{ tag }} {% endfor %}

    {% endif %} +{% if article.tags %}

    tags: {% for tag in article.tags %}{{ tag | escape }} {% endfor %}

    {% endif %} {% if PDF_PROCESSOR %}

    get the pdf

    {% endif %} From 05d357e98e9c948eaddfcfe3db9895b7ab5e9bf0 Mon Sep 17 00:00:00 2001 From: Ben Bridts Date: Tue, 18 Feb 2014 17:56:57 +0100 Subject: [PATCH 0212/1427] Split multiple authors on ',' --- pelican/readers.py | 2 +- pelican/tests/content/article_with_multiple_authors.html | 6 ++++++ pelican/tests/test_readers.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 pelican/tests/content/article_with_multiple_authors.html diff --git a/pelican/readers.py b/pelican/readers.py index 1e00aefa..fb800b2b 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -46,7 +46,7 @@ METADATA_PROCESSORS = { 'status': lambda x, y: x.strip(), 'category': Category, 'author': Author, - 'authors': lambda x, y: [Author(author, y) for author in x], + 'authors': lambda x, y: [Author(author.strip(), y) for author in x.split(',')], } logger = logging.getLogger(__name__) diff --git a/pelican/tests/content/article_with_multiple_authors.html b/pelican/tests/content/article_with_multiple_authors.html new file mode 100644 index 00000000..a74442c9 --- /dev/null +++ b/pelican/tests/content/article_with_multiple_authors.html @@ -0,0 +1,6 @@ + + + This is an article with multiple authors! + + + diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index acb268fb..6274bdc5 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -361,7 +361,7 @@ class HTMLReaderTest(ReaderTest): self.assertEqual(value, page.metadata[key], key) def test_article_with_multiple_authors(self): - page = self.read_file(path='article_with_multiple_authors.rst') + page = self.read_file(path='article_with_multiple_authors.html') expected = { 'authors': ['First Author', 'Second Author'] } From 7f2bc2a23b6e8c8f0865cb293942a560a06d74e0 Mon Sep 17 00:00:00 2001 From: Stefan hr Berder Date: Sun, 16 Feb 2014 12:51:52 +0100 Subject: [PATCH 0213/1427] change date metadata parsing to dateutil.parser --- docs/getting_started.rst | 2 ++ pelican/tests/test_utils.py | 32 +++++++++++++++++++++++++++--- pelican/utils.py | 39 +++++-------------------------------- setup.py | 2 +- 4 files changed, 37 insertions(+), 38 deletions(-) diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 6655d8d6..8ee37162 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -130,6 +130,8 @@ automatically installed without any action on your part: utilities * `MarkupSafe `_, for a markup safe string implementation +* `python-dateutil `_, to read + the date metadata If you want the following optional packages, you will need to install them manually via ``pip``: diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index f6f96a1c..9047593f 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -41,6 +41,12 @@ class TestUtils(LoggedTestCase): date = datetime.datetime(year=2012, month=11, day=22) date_hour = datetime.datetime( year=2012, month=11, day=22, hour=22, minute=11) + date_hour_z = datetime.datetime( + year=2012, month=11, day=22, hour=22, minute=11, + tzinfo=pytz.timezone('UTC')) + date_hour_est = datetime.datetime( + year=2012, month=11, day=22, hour=22, minute=11, + tzinfo=pytz.timezone('EST')) date_hour_sec = datetime.datetime( year=2012, month=11, day=22, hour=22, minute=11, second=10) date_hour_sec_z = datetime.datetime( @@ -61,22 +67,42 @@ class TestUtils(LoggedTestCase): '22/11/2012': date, '22.11.2012': date, '22.11.2012 22:11': date_hour, + '2012-11-22T22:11Z': date_hour_z, + '2012-11-22T22:11-0500': date_hour_est, '2012-11-22 22:11:10': date_hour_sec, '2012-11-22T22:11:10Z': date_hour_sec_z, '2012-11-22T22:11:10-0500': date_hour_sec_est, '2012-11-22T22:11:10.123Z': date_hour_sec_frac_z, } + # examples from http://www.w3.org/TR/NOTE-datetime + iso_8601_date = datetime.datetime(year=1997, month=7, day=16) + iso_8601_date_hour_tz = datetime.datetime( + year=1997, month=7, day=16, hour=19, minute=20, + tzinfo=pytz.timezone('CET')) + iso_8601_date_hour_sec_tz = datetime.datetime( + year=1997, month=7, day=16, hour=19, minute=20, second=30, + tzinfo=pytz.timezone('CET')) + iso_8601_date_hour_sec_ms_tz = datetime.datetime( + year=1997, month=7, day=16, hour=19, minute=20, second=30, + microsecond=450000, tzinfo=pytz.timezone('CET')) + iso_8601 = { + '1997-07-16': iso_8601_date, + '1997-07-16T19:20+01:00': iso_8601_date_hour_tz, + '1997-07-16T19:20:30+01:00': iso_8601_date_hour_sec_tz, + '1997-07-16T19:20:30.45+01:00': iso_8601_date_hour_sec_ms_tz, + } + # invalid ones invalid_dates = ['2010-110-12', 'yay'] - if version_info < (3, 2): - dates.pop('2012-11-22T22:11:10-0500') - invalid_dates.append('2012-11-22T22:11:10-0500') for value, expected in dates.items(): self.assertEqual(utils.get_date(value), expected, value) + for value, expected in iso_8601.items(): + self.assertEqual(utils.get_date(value), expected, value) + for item in invalid_dates: self.assertRaises(ValueError, utils.get_date, item) diff --git a/pelican/utils.py b/pelican/utils.py index 822e50e9..c5aacaa3 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -15,7 +15,7 @@ import traceback from collections import Hashable from contextlib import contextmanager -from datetime import datetime +import dateutil.parser from functools import partial from itertools import groupby from jinja2 import Markup @@ -181,39 +181,10 @@ def get_date(string): If no format matches the given date, raise a ValueError. """ string = re.sub(' +', ' ', string) - formats = [ - # ISO 8601 - '%Y', - '%Y-%m', - '%Y-%m-%d', - '%Y-%m-%dT%H:%M%z', - '%Y-%m-%dT%H:%MZ', - '%Y-%m-%dT%H:%M', - '%Y-%m-%dT%H:%M:%S%z', - '%Y-%m-%dT%H:%M:%SZ', - '%Y-%m-%dT%H:%M:%S', - '%Y-%m-%dT%H:%M:%S.%f%z', - '%Y-%m-%dT%H:%M:%S.%fZ', - '%Y-%m-%dT%H:%M:%S.%f', - # end ISO 8601 forms - '%Y-%m-%d %H:%M', - '%Y-%m-%d %H:%M:%S', - '%Y/%m/%d %H:%M', - '%Y/%m/%d', - '%d-%m-%Y', - '%d.%m.%Y %H:%M', - '%d.%m.%Y', - '%d/%m/%Y', - ] - for date_format in formats: - try: - date = datetime.strptime(string, date_format) - except ValueError: - continue - if date_format.endswith('Z'): - date = date.replace(tzinfo=pytz.timezone('UTC')) - return date - raise ValueError('{0!r} is not a valid date'.format(string)) + try: + return dateutil.parser.parse(string) + except (TypeError, ValueError): + raise ValueError('{0!r} is not a valid date'.format(string)) @contextmanager diff --git a/setup.py b/setup.py index f56a7c41..e989d549 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup requires = ['feedgenerator >= 1.6', 'jinja2 >= 2.7', 'pygments', 'docutils', - 'pytz >= 0a', 'blinker', 'unidecode', 'six'] + 'pytz >= 0a', 'blinker', 'unidecode', 'six', 'python-dateutil'] entry_points = { 'console_scripts': [ From 652eb3686f15a24f8e8830f2d4345ab8c50cf761 Mon Sep 17 00:00:00 2001 From: Stefan hr Berder Date: Thu, 26 Dec 2013 19:30:55 +0100 Subject: [PATCH 0214/1427] add lang support for drafts (#826 & #1107) Fix #826 Fix #1107 --- docs/settings.rst | 10 +++++-- pelican/__init__.py | 12 ++++++--- pelican/contents.py | 5 ++++ pelican/generators.py | 27 +++++++++++++------ pelican/settings.py | 4 +++ .../output/custom/drafts/a-draft-article.html | 4 +-- 6 files changed, 46 insertions(+), 16 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index ca92a19e..a8e96d71 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -237,12 +237,18 @@ posts for the month at ``posts/2011/Aug/index.html``. ==================================================== ===================================================== Setting name (default value) What does it do? ==================================================== ===================================================== -`ARTICLE_URL` (``'{slug}.html'``) The URL to refer to an ARTICLE. +`ARTICLE_URL` (``'{slug}.html'``) The URL to refer to an article. `ARTICLE_SAVE_AS` (``'{slug}.html'``) The place where we will save an article. -`ARTICLE_LANG_URL` (``'{slug}-{lang}.html'``) The URL to refer to an ARTICLE which doesn't use the +`ARTICLE_LANG_URL` (``'{slug}-{lang}.html'``) The URL to refer to an article which doesn't use the default language. `ARTICLE_LANG_SAVE_AS` (``'{slug}-{lang}.html'``) The place where we will save an article which doesn't use the default language. +`DRAFT_URL` (``'drafts/{slug}.html'``) The URL to refer to an article draft. +`DRAFT_SAVE_AS` (``'drafts/{slug}.html'``) The place where we will save an article draft. +`DRAFT_LANG_URL` (``'drafts/{slug}-{lang}.html'``) The URL to refer to an article draft which doesn't + use the default language. +`DRAFT_LANG_SAVE_AS` (``'drafts/{slug}-{lang}.html'``) The place where we will save an article draft which + doesn't use the default language. `PAGE_URL` (``'pages/{slug}.html'``) The URL we will use to link to a page. `PAGE_SAVE_AS` (``'pages/{slug}.html'``) The location we will save the page. This value has to be the same as PAGE_URL or you need to use a rewrite in diff --git a/pelican/__init__.py b/pelican/__init__.py index 47260551..08dd484e 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -114,9 +114,10 @@ class Pelican(object): structure = re.sub('^/', '', structure) for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL', - 'PAGE_LANG_URL', 'ARTICLE_SAVE_AS', - 'ARTICLE_LANG_SAVE_AS', 'PAGE_SAVE_AS', - 'PAGE_LANG_SAVE_AS'): + 'PAGE_LANG_URL', 'DRAFT_URL', 'DRAFT_LANG_URL', + 'ARTICLE_SAVE_AS', 'ARTICLE_LANG_SAVE_AS', + 'DRAFT_SAVE_AS', 'DRAFT_LANG_SAVE_AS', + 'PAGE_SAVE_AS', 'PAGE_LANG_SAVE_AS'): self.settings[setting] = os.path.join(structure, self.settings[setting]) logger.warning("%s = '%s'" % (setting, self.settings[setting])) @@ -174,8 +175,11 @@ class Pelican(object): pages_generator = next(g for g in generators if isinstance(g, PagesGenerator)) - print('Done: Processed {} articles and {} pages in {:.2f} seconds.'.format( + print('Done: Processed {} article(s), {} draft(s) and {} page(s) in ' \ + '{:.2f} seconds.'.format( len(articles_generator.articles) + len(articles_generator.translations), + len(articles_generator.drafts) + \ + len(articles_generator.drafts_translations), len(pages_generator.pages) + len(pages_generator.translations), time.time() - start_time)) diff --git a/pelican/contents.py b/pelican/contents.py index 69b7fa69..2ba81c1d 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -328,6 +328,11 @@ class Article(Page): default_template = 'article' +class Draft(Page): + mandatory_properties = ('title', 'category') + default_template = 'article' + + class Quote(Page): base_properties = ('author', 'date') diff --git a/pelican/generators.py b/pelican/generators.py index d1034eb0..bfdac1a5 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -18,7 +18,7 @@ from operator import attrgetter, itemgetter from jinja2 import (Environment, FileSystemLoader, PrefixLoader, ChoiceLoader, BaseLoader, TemplateNotFound) -from pelican.contents import Article, Page, Static, is_valid_content +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 from pelican import signals @@ -190,7 +190,8 @@ class ArticlesGenerator(Generator): self.categories = defaultdict(list) self.related_posts = [] self.authors = defaultdict(list) - self.drafts = [] + self.drafts = [] # only drafts in default language + self.drafts_translations = [] super(ArticlesGenerator, self).__init__(*args, **kwargs) signals.article_generator_init.send(self) @@ -376,11 +377,11 @@ class ArticlesGenerator(Generator): def generate_drafts(self, write): """Generate drafts pages.""" - for article in self.drafts: - write(os.path.join('drafts', '%s.html' % article.slug), - self.get_template(article.template), self.context, - article=article, category=article.category, - all_articles=self.articles) + 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'), + all_articles=self.articles) def generate_pages(self, writer): """Generate the pages on the disk""" @@ -403,6 +404,7 @@ class ArticlesGenerator(Generator): """Add the articles into the shared context""" all_articles = [] + all_drafts = [] for f in self.get_files( self.settings['ARTICLE_DIR'], exclude=self.settings['ARTICLE_EXCLUDES']): @@ -426,13 +428,22 @@ class ArticlesGenerator(Generator): if article.status.lower() == "published": all_articles.append(article) elif article.status.lower() == "draft": - self.drafts.append(article) + draft = self.readers.read_file( + base_path=self.path, path=f, content_class=Draft, + context=self.context, + preread_signal=signals.article_generator_preread, + preread_sender=self, + context_signal=signals.article_generator_context, + context_sender=self) + all_drafts.append(draft) else: logger.warning("Unknown status %s for file %s, skipping it." % (repr(article.status), repr(f))) self.articles, self.translations = process_translations(all_articles) + self.drafts, self.drafts_translations = \ + process_translations(all_drafts) signals.article_generator_pretaxonomy.send(self) diff --git a/pelican/settings.py b/pelican/settings.py index 99828935..225a1e9d 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -59,6 +59,10 @@ DEFAULT_CONFIG = { 'ARTICLE_SAVE_AS': '{slug}.html', 'ARTICLE_LANG_URL': '{slug}-{lang}.html', 'ARTICLE_LANG_SAVE_AS': '{slug}-{lang}.html', + 'DRAFT_URL': 'drafts/{slug}.html', + 'DRAFT_SAVE_AS': os.path.join('drafts', '{slug}.html'), + 'DRAFT_LANG_URL': 'drafts/{slug}-{lang}.html', + 'DRAFT_LANG_SAVE_AS': os.path.join('drafts', '{slug}-{lang}.html'), 'PAGE_URL': 'pages/{slug}.html', 'PAGE_SAVE_AS': os.path.join('pages', '{slug}.html'), 'PAGE_LANG_URL': 'pages/{slug}-{lang}.html', diff --git a/pelican/tests/output/custom/drafts/a-draft-article.html b/pelican/tests/output/custom/drafts/a-draft-article.html index 9050c04e..57eea18c 100644 --- a/pelican/tests/output/custom/drafts/a-draft-article.html +++ b/pelican/tests/output/custom/drafts/a-draft-article.html @@ -32,7 +32,7 @@

    - A draft article

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

    }()); - \ No newline at end of file + From 011cd50e2e768a80234781584bde0cddc971ca55 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sat, 1 Mar 2014 18:25:07 +0100 Subject: [PATCH 0215/1427] Minor improvements to Settings docs --- docs/settings.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index a8e96d71..b75ce53d 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -6,7 +6,8 @@ the command line:: $ pelican content -s path/to/your/settingsfile.py -(If you used the `pelican-quickstart` command, your primary settings file will be named `pelicanconf.py` by default.) +(If you used the `pelican-quickstart` command, your primary settings file will +be named `pelicanconf.py` by default.) Settings are configured in the form of a Python module (a file). There is an `example settings file @@ -193,9 +194,10 @@ want. If you specify a ``datetime`` directive, it will be substituted using the input files' date metadata attribute. If the date is not specified for a particular file, Pelican will rely on the file's ``mtime`` timestamp. + Check the `Python datetime documentation`_ for more information. -Check the Python datetime documentation at http://bit.ly/cNcJUC for more -information. +.. _Python datetime documentation: + http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior Also, you can use other file metadata attributes as well: From 56b0061393d682f2487901b27bcafd5f0fbbd4a7 Mon Sep 17 00:00:00 2001 From: th3aftermath Date: Fri, 31 Jan 2014 19:42:20 -0500 Subject: [PATCH 0216/1427] Add the setting SLUGIFY_ATTRIBUTE --- docs/settings.rst | 4 ++++ pelican/contents.py | 12 +++++++++--- pelican/settings.py | 1 + pelican/tests/test_contents.py | 11 +++++++++-- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index b75ce53d..3873a479 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -166,6 +166,10 @@ Setting name (default value) `PYGMENTS_RST_OPTIONS` (``[]``) A list of default Pygments settings for your reStructuredText code blocks. See :ref:`internal_pygments_options` for a list of supported options. + +`SLUGIFY_SOURCE` (``'input'``) Specifies where you want the slug to be automatically generated + from. Can be set to 'title' to use the Title: metadata tag or + 'basename' to use the articles basename to make a slug. =============================================================================== ===================================================================== .. [#] Default is the system locale. diff --git a/pelican/contents.py b/pelican/contents.py index 2ba81c1d..66602666 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -96,9 +96,15 @@ class Content(object): self.in_default_lang = (self.lang == default_lang) - # create the slug if not existing, from the title - if not hasattr(self, 'slug') and hasattr(self, 'title'): - self.slug = slugify(self.title, + # 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'): + 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', ())) self.source_path = source_path diff --git a/pelican/settings.py b/pelican/settings.py index 225a1e9d..7ab43a82 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -116,6 +116,7 @@ DEFAULT_CONFIG = { 'IGNORE_FILES': ['.#*'], 'SLUG_SUBSTITUTIONS': (), 'INTRASITE_LINK_REGEX': '[{|](?P.*?)[|}]', + 'SLUGIFY_SOURCE': 'title' } PYGMENTS_RST_OPTIONS = None diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index f831f061..4c6f8ed1 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -32,6 +32,7 @@ class TestPage(unittest.TestCase): 'title': 'foo bar', 'author': 'Blogger', }, + 'source_path': '/path/to/file/foo.ext' } def test_use_args(self): @@ -77,9 +78,15 @@ class TestPage(unittest.TestCase): self.assertEqual(page.summary, '') def test_slug(self): - # If a title is given, it should be used to generate the slug. - page = Page(**self.page_kwargs) + page_kwargs = self._copy_page_kwargs() + settings = get_settings() + page_kwargs['settings'] = settings + settings['SLUGIFY_SOURCE'] = "title" + page = Page(**page_kwargs) self.assertEqual(page.slug, 'foo-bar') + settings['SLUGIFY_SOURCE'] = "basename" + page = Page(**page_kwargs) + self.assertEqual(page.slug, 'foo') def test_defaultlang(self): # If no lang is given, default to the default one. From 990ddb5a5e020cd68117e6c17b310e51f69c804e Mon Sep 17 00:00:00 2001 From: th3aftermath Date: Sat, 8 Mar 2014 12:56:30 -0500 Subject: [PATCH 0217/1427] Fix Issue #1165 allows extensions to be set by certain settings PAGINATION_PATTERNS was hard coded so that all files had a ".html" extension. This fixes that and add a test to ensure that the pagination code is not changing the filename incorrectly. --- pelican/paginator.py | 3 +- pelican/settings.py | 2 +- pelican/tests/test_paginator.py | 50 +++++++++++++++++++++++++++++++++ pelican/writers.py | 3 +- 4 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 pelican/tests/test_paginator.py diff --git a/pelican/paginator.py b/pelican/paginator.py index df8606ec..757c9120 100644 --- a/pelican/paginator.py +++ b/pelican/paginator.py @@ -69,7 +69,7 @@ class Paginator(object): class Page(object): def __init__(self, name, object_list, number, paginator, settings): - self.name = name + self.name, self.extension = os.path.splitext(name) self.object_list = object_list self.number = number self.paginator = paginator @@ -143,6 +143,7 @@ class Page(object): 'settings': self.settings, 'base_name': os.path.dirname(self.name), 'number_sep': '/', + 'extension': self.extension, } if self.number == 1: diff --git a/pelican/settings.py b/pelican/settings.py index 7ab43a82..796678e0 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -79,7 +79,7 @@ DEFAULT_CONFIG = { 'AUTHOR_URL': 'author/{slug}.html', 'AUTHOR_SAVE_AS': os.path.join('author', '{slug}.html'), 'PAGINATION_PATTERNS': [ - (0, '{name}{number}.html', '{name}{number}.html'), + (0, '{name}{number}{extension}', '{name}{number}{extension}'), ], 'YEAR_ARCHIVE_SAVE_AS': False, 'MONTH_ARCHIVE_SAVE_AS': False, diff --git a/pelican/tests/test_paginator.py b/pelican/tests/test_paginator.py new file mode 100644 index 00000000..f454d47d --- /dev/null +++ b/pelican/tests/test_paginator.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals, absolute_import +import six + +from pelican.tests.support import unittest, get_settings + +from pelican.paginator import Paginator +from pelican.contents import Article +from pelican.settings import DEFAULT_CONFIG +from jinja2.utils import generate_lorem_ipsum + +# generate one paragraph, enclosed with

    +TEST_CONTENT = str(generate_lorem_ipsum(n=1)) +TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False) + +class TestPage(unittest.TestCase): + def setUp(self): + super(TestPage, self).setUp() + self.page_kwargs = { + 'content': TEST_CONTENT, + 'context': { + 'localsiteurl': '', + }, + 'metadata': { + 'summary': TEST_SUMMARY, + 'title': 'foo bar', + 'author': 'Blogger', + }, + 'source_path': '/path/to/file/foo.ext' + } + + def test_save_as_preservation(self): + settings = get_settings() + # fix up pagination rules + from pelican.paginator import PaginationRule + pagination_rules = [ + PaginationRule(*r) for r in settings.get( + 'PAGINATION_PATTERNS', + DEFAULT_CONFIG['PAGINATION_PATTERNS'], + ) + ] + settings['PAGINATION_PATTERNS'] = sorted( + pagination_rules, + key=lambda r: r[0], + ) + + object_list = [Article(**self.page_kwargs), Article(**self.page_kwargs)] + paginator = Paginator('foobar.foo', object_list, settings) + page = paginator.page(1) + self.assertEqual(page.save_as, 'foobar.foo') \ No newline at end of file diff --git a/pelican/writers.py b/pelican/writers.py index 580a3990..63d74126 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -179,10 +179,9 @@ class Writer(object): # pagination if paginated: - name_root = os.path.splitext(name)[0] # pagination needed, init paginators - paginators = {key: Paginator(name_root, val, self.settings) + paginators = {key: Paginator(name, val, self.settings) for key, val in paginated.items()} # generated pages, and write From 91d576eb45d416d8cbaeee2bc72765c573c824e6 Mon Sep 17 00:00:00 2001 From: Jean Lauliac Date: Mon, 24 Mar 2014 14:50:49 -0400 Subject: [PATCH 0218/1427] Apply typogrify on article summary as well --- pelican/readers.py | 9 ++++--- .../tests/content/article_with_metadata.rst | 2 +- pelican/tests/test_readers.py | 27 ++++++++++++++++++- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/pelican/readers.py b/pelican/readers.py index 1e00aefa..20f391eb 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -462,10 +462,13 @@ class Readers(object): find_empty_alt(content, path) # eventually filter the content with typogrify if asked so - if content and self.settings['TYPOGRIFY']: + if self.settings['TYPOGRIFY']: from typogrify.filters import typogrify - content = typogrify(content) - metadata['title'] = typogrify(metadata['title']) + if content: + content = typogrify(content) + metadata['title'] = typogrify(metadata['title']) + if 'summary' in metadata: + metadata['summary'] = typogrify(metadata['summary']) if context_signal: logger.debug('signal {}.send({}, )'.format( diff --git a/pelican/tests/content/article_with_metadata.rst b/pelican/tests/content/article_with_metadata.rst index c5768cfb..9b65a4b0 100644 --- a/pelican/tests/content/article_with_metadata.rst +++ b/pelican/tests/content/article_with_metadata.rst @@ -9,5 +9,5 @@ This is a super article ! :author: Alexis Métaireau :summary: Multi-line metadata should be supported - as well as **inline markup**. + as well as **inline markup** and stuff to "typogrify"... :custom_field: http://notmyidea.org diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index acb268fb..d605d785 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -40,7 +40,8 @@ class RstReaderTest(ReaderTest): 'title': 'This is a super article !', 'summary': '

    Multi-line metadata should be' ' supported\nas well as inline' - ' markup.

    \n', + ' markup and stuff to "typogrify' + '"...

    \n', 'date': datetime.datetime(2010, 12, 2, 10, 14), 'modified': datetime.datetime(2010, 12, 2, 10, 20), 'tags': ['foo', 'bar', 'foobar'], @@ -125,6 +126,30 @@ class RstReaderTest(ReaderTest): except ImportError: return unittest.skip('need the typogrify distribution') + def test_typogrify_summary(self): + # if nothing is specified in the settings, the summary should be + # unmodified + page = self.read_file(path='article_with_metadata.rst') + expected = ('

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

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

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

    \n') + + self.assertEqual(page.metadata['summary'], expected) + except ImportError: + return unittest.skip('need the typogrify distribution') + class MdReaderTest(ReaderTest): From 01ad449f29b8f9cdd17ce204312384a5f15b6c44 Mon Sep 17 00:00:00 2001 From: th3aftermath Date: Sun, 30 Mar 2014 14:35:22 -0400 Subject: [PATCH 0219/1427] Fix indentation error in the settings doc This was accidentally caused by me in #1248 As a result the Basic Settings table was not being updated. --- docs/settings.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 3873a479..b579ae95 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -167,9 +167,9 @@ Setting name (default value) code blocks. See :ref:`internal_pygments_options` for a list of supported options. -`SLUGIFY_SOURCE` (``'input'``) Specifies where you want the slug to be automatically generated - from. Can be set to 'title' to use the Title: metadata tag or - 'basename' to use the articles basename to make a slug. +`SLUGIFY_SOURCE` (``'input'``) Specifies where you want the slug to be automatically generated + from. Can be set to 'title' to use the 'Title:' metadata tag or + 'basename' to use the articles basename when creating the slug. =============================================================================== ===================================================================== .. [#] Default is the system locale. From 382c09c187293310dca226cc0b873564a5993c7b Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 30 Mar 2014 11:59:32 -0700 Subject: [PATCH 0220/1427] Fix docs last_stable version and copyright date --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5ac81b9e..6db0f3d1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,11 +12,11 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.ifconfig', 'sphinx.ext.extlinks' source_suffix = '.rst' master_doc = 'index' project = 'Pelican' -copyright = '2010, Alexis Metaireau and contributors' +copyright = '2014, Alexis Metaireau and contributors' exclude_patterns = ['_build'] release = __version__ version = '.'.join(release.split('.')[:1]) -last_stable = '3.2.2' +last_stable = '3.3.0' rst_prolog = ''' .. |last_stable| replace:: :pelican-doc:`{0}` '''.format(last_stable) From 3f304a2e9277a5c512ac722279cc278bb866ea5c Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Mon, 31 Mar 2014 19:38:49 +0200 Subject: [PATCH 0221/1427] change the inhibition value of *_SAVE_AS to '' Previously, the documentation claimed the value of None for this purpose even though False was used for certain defaults. The values False and None cause warnings to be emitted from URLWrapper._from_settings though, so the new way of inhibiting page generation is to set a *_SAVE_AS value to the empty string. --- docs/settings.rst | 8 ++++---- pelican/settings.py | 6 +++--- pelican/writers.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index b579ae95..0c16db3c 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -269,9 +269,9 @@ Setting name (default value) What does it do? `TAG_SAVE_AS` (``'tag/{slug}.html'``) The location to save the tag page. `AUTHOR_URL` (``'author/{slug}.html'``) The URL to use for an author. `AUTHOR_SAVE_AS` (``'author/{slug}.html'``) The location to save an author. -`YEAR_ARCHIVE_SAVE_AS` (False) The location to save per-year archives of your posts. -`MONTH_ARCHIVE_SAVE_AS` (False) The location to save per-month archives of your posts. -`DAY_ARCHIVE_SAVE_AS` (False) The location to save per-day archives of your posts. +`YEAR_ARCHIVE_SAVE_AS` (``''``) The location to save per-year archives of your posts. +`MONTH_ARCHIVE_SAVE_AS` (``''``) The location to save per-month archives of your posts. +`DAY_ARCHIVE_SAVE_AS` (``''``) The location to save per-day archives of your posts. `SLUG_SUBSTITUTIONS` (``()``) Substitutions to make prior to stripping out non-alphanumerics when generating slugs. Specified as a list of 2-tuples of ``(from, to)`` which are @@ -282,7 +282,7 @@ Setting name (default value) What does it do? If you do not want one or more of the default pages to be created (e.g., you are the only author on your site and thus do not need an Authors page), - set the corresponding ``*_SAVE_AS`` setting to ``None`` to prevent the + set the corresponding ``*_SAVE_AS`` setting to ``''`` to prevent the relevant page from being generated. `DIRECT_TEMPLATES` diff --git a/pelican/settings.py b/pelican/settings.py index 796678e0..ffd0bc8f 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -81,9 +81,9 @@ DEFAULT_CONFIG = { 'PAGINATION_PATTERNS': [ (0, '{name}{number}{extension}', '{name}{number}{extension}'), ], - 'YEAR_ARCHIVE_SAVE_AS': False, - 'MONTH_ARCHIVE_SAVE_AS': False, - 'DAY_ARCHIVE_SAVE_AS': False, + 'YEAR_ARCHIVE_SAVE_AS': '', + 'MONTH_ARCHIVE_SAVE_AS': '', + 'DAY_ARCHIVE_SAVE_AS': '', 'RELATIVE_URLS': False, 'DEFAULT_LANG': 'en', 'TAG_CLOUD_STEPS': 4, diff --git a/pelican/writers.py b/pelican/writers.py index 63d74126..19e36e39 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -140,7 +140,7 @@ class Writer(object): :param **kwargs: additional variables to pass to the templates """ - if name is False: + if name is False or name == "": return elif not name: # other stuff, just return for now From d9b0091357565391854e6a00563a551b8b7bfe13 Mon Sep 17 00:00:00 2001 From: Rogdham Date: Tue, 1 Apr 2014 20:44:09 +0200 Subject: [PATCH 0222/1427] Limit and filter logs Drop duplicates logs. Allow for logs to be grouped, enforcing a maximum number of logs per group. Add the LOG_FILTER setting to ask from the configuration file to ignore some logs (of level up to warning). --- docs/contribute.rst | 38 ++++++++++++++++++++ docs/settings.rst | 20 +++++++++++ pelican/__init__.py | 5 ++- pelican/contents.py | 6 ++-- pelican/log.py | 65 ++++++++++++++++++++++++++++++++--- pelican/readers.py | 34 ++++++------------ pelican/settings.py | 9 ++++- pelican/tests/test_pelican.py | 2 +- 8 files changed, 146 insertions(+), 33 deletions(-) diff --git a/docs/contribute.rst b/docs/contribute.rst index 304d1de8..28df1fcd 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -143,3 +143,41 @@ and Python 3 at the same time: changed it where I felt necessary. - Changed xrange() back to range(), so it is valid in both Python versions. + + +Logging tips +============ + +Try to use logging with appropriate levels. + +For logging messages that are not repeated, use the usual Python way: + + # at top of file + import logging + logger = logging.getLogger(__name__) + + # when needed + logger.warning('A warning that could occur only once") + +However, if you want to log messages that may occur several times, instead of +a string, gives a tuple to the logging method, with two arguments: + + 1. The message to log for this very execution + 2. A generic message that will appear if the previous one would occur to many + times. + +For example, if you want to log missing resources, use the following code: + + for ressource in ressources: + if ressource.is_missing: + logger.warning(( + 'The resource {r} is missing'.format(r=ressource.name), + 'Other resources were missing')) + +The logs will be displayed as follows: + + WARNING: The resource prettiest_cat.jpg is missing + WARNING: The resource best_cat_ever.jpg is missing + WARNING: The resource cutest_cat.jpg is missing + WARNING: The resource lolcat.jpg is missing + WARNING: Other resources were missing diff --git a/docs/settings.rst b/docs/settings.rst index 0c16db3c..f7cfa69d 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -88,6 +88,9 @@ Setting name (default value) here or a single string representing one locale. When providing a list, all the locales will be tried until one works. +`LOG_FILTER` (``[]``) A list of tuples containing the logging level (up to warning) + and the message to be ignored. + For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]`` `READERS` (``{}``) A dictionary of file extensions / Reader classes for Pelican to process or ignore. For example, to avoid processing .html files, set: ``READERS = {'html': None}``. To add a custom reader for the @@ -694,6 +697,23 @@ adding the following to your configuration:: CSS_FILE = "wide.css" + +Logging +======= + +Sometimes, useless lines of log appears while the generation occurs. Finding +**the** meaningful error message in the middle of tons of annoying log outputs +can be quite tricky. To be able to filter out all useless log messages, Pelican +comes with the ``LOG_FILTER`` setting. + +``LOG_FILTER`` should be a list of tuples ``(level, msg)``, each of them being +composed of the logging level (up to warning) and the message to be ignored. +Simply populate the list with the logs you want to hide and they will be +filtered out. + +For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]`` + + Example settings ================ diff --git a/pelican/__init__.py b/pelican/__init__.py index 08dd484e..494e7e43 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -11,12 +11,15 @@ import argparse import locale import collections +# 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 import signals from pelican.generators import (ArticlesGenerator, PagesGenerator, StaticGenerator, SourceFileGenerator, TemplatePagesGenerator) -from pelican.log import init from pelican.readers import Readers from pelican.settings import read_settings from pelican.utils import clean_output_dir, folder_watcher, file_watcher diff --git a/pelican/contents.py b/pelican/contents.py index 66602666..3096a064 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -239,8 +239,10 @@ class Content(object): self._context['filenames'][path].url)) origin = origin.replace('\\', '/') # for Windows paths. else: - logger.warning("Unable to find {fn}, skipping url" - " replacement".format(fn=path)) + logger.warning(("Unable to find {fn}, skipping url" + " replacement".format(fn=value), + "Other ressources were not found" + " and their urls not replaced")) elif what == 'category': origin = Category(path, self.settings).url elif what == 'tag': diff --git a/pelican/log.py b/pelican/log.py index bde8037e..d3aae012 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -9,7 +9,7 @@ import os import sys import logging -from logging import Formatter, getLogger, StreamHandler, DEBUG +from collections import defaultdict RESET_TERM = '\033[0;m' @@ -30,7 +30,7 @@ def ansi(color, text): return '\033[1;{0}m{1}{2}'.format(code, text, RESET_TERM) -class ANSIFormatter(Formatter): +class ANSIFormatter(logging.Formatter): """Convert a `logging.LogRecord' object into colored text, using ANSI escape sequences. @@ -51,7 +51,7 @@ class ANSIFormatter(Formatter): return ansi('white', record.levelname) + ': ' + msg -class TextFormatter(Formatter): +class TextFormatter(logging.Formatter): """ Convert a `logging.LogRecord' object into text. """ @@ -63,7 +63,62 @@ class TextFormatter(Formatter): return record.levelname + ': ' + record.getMessage() -def init(level=None, logger=getLogger(), handler=StreamHandler()): +class LimitFilter(logging.Filter): + """ + Remove duplicates records, and limit the number of records in the same + group. + + Groups are specified by the message to use when the number of records in + the same group hit the limit. + E.g.: log.warning(('43 is not the answer', 'More erroneous answers')) + """ + + ignore = set() + threshold = 5 + group_count = defaultdict(int) + + def filter(self, record): + # don't limit levels over warnings + if record.levelno > logging.WARN: + return record + # extract group + group = None + if len(record.msg) == 2: + record.msg, group = record.msg + # ignore record if it was already raised + # use .getMessage() and not .msg for string formatting + ignore_key = (record.levelno, record.getMessage()) + to_ignore = ignore_key in LimitFilter.ignore + LimitFilter.ignore.add(ignore_key) + if to_ignore: + return False + # check if we went over threshold + if group: + key = (record.levelno, group) + LimitFilter.group_count[key] += 1 + if LimitFilter.group_count[key] == LimitFilter.threshold: + record.msg = group + if LimitFilter.group_count[key] > LimitFilter.threshold: + return False + return record + + +class LimitLogger(logging.Logger): + """ + A logger which add LimitFilter automatically + """ + + limit_filter = LimitFilter() + + def __init__(self, *args, **kwargs): + super(LimitLogger, self).__init__(*args, **kwargs) + self.addFilter(LimitLogger.limit_filter) + +logging.setLoggerClass(LimitLogger) + + +def init(level=None, handler=logging.StreamHandler()): + logger = logging.getLogger() if (os.isatty(sys.stdout.fileno()) @@ -79,7 +134,7 @@ def init(level=None, logger=getLogger(), handler=StreamHandler()): if __name__ == '__main__': - init(level=DEBUG) + init(level=logging.DEBUG) root_logger = logging.getLogger() root_logger.debug('debug') diff --git a/pelican/readers.py b/pelican/readers.py index 26329af6..35c38220 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -318,7 +318,11 @@ class HTMLReader(BaseReader): if not contents: contents = self._attr_value(attrs, 'contents', '') if contents: - logger.warning("Meta tag attribute 'contents' used in file %s, should be changed to 'content'", self._filename) + logger.warning(( + "Meta tag attribute 'contents' used in file {}, should" + " be changed to 'content'".format(self._filename), + "Other files have meta tag attribute 'contents' that" + " should be changed to 'content'")) if name == 'keywords': name = 'tags' @@ -385,10 +389,6 @@ class Readers(object): """ - # used to warn about missing dependencies only once, at the first - # instanciation of a Readers object. - warn_missing_deps = True - def __init__(self, settings=None): self.settings = settings or {} self.readers = {} @@ -396,16 +396,13 @@ class Readers(object): for cls in [BaseReader] + BaseReader.__subclasses__(): if not cls.enabled: - if self.__class__.warn_missing_deps: - logger.debug('Missing dependencies for {}' - .format(', '.join(cls.file_extensions))) + logger.debug('Missing dependencies for {}' + .format(', '.join(cls.file_extensions))) continue for ext in cls.file_extensions: self.reader_classes[ext] = cls - self.__class__.warn_missing_deps = False - if self.settings['READERS']: self.reader_classes.update(self.settings['READERS']) @@ -505,19 +502,10 @@ def find_empty_alt(content, path): src=(['"])(.*)\5 ) """, re.X) - matches = re.findall(imgs, content) - # find a correct threshold - nb_warnings = 10 - if len(matches) == nb_warnings + 1: - nb_warnings += 1 # avoid bad looking case - # print one warning per image with empty alt until threshold - for match in matches[:nb_warnings]: - logger.warning('Empty alt attribute for image {} in {}'.format( - os.path.basename(match[1] + match[5]), path)) - # print one warning for the other images with empty alt - if len(matches) > nb_warnings: - logger.warning('{} other images with empty alt attributes' - .format(len(matches) - nb_warnings)) + for match in re.findall(imgs, content): + logger.warning(('Empty alt attribute for image {} in {}'.format( + os.path.basename(match[1] + match[5]), path), + 'Other images have empty alt attributes')) def default_metadata(settings=None, process=None): diff --git a/pelican/settings.py b/pelican/settings.py index ffd0bc8f..f70f74a8 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -19,6 +19,8 @@ except ImportError: from os.path import isabs +from pelican.log import LimitFilter + logger = logging.getLogger(__name__) @@ -98,6 +100,7 @@ DEFAULT_CONFIG = { 'MD_EXTENSIONS': ['codehilite(css_class=highlight)', 'extra'], 'JINJA_EXTENSIONS': [], 'JINJA_FILTERS': {}, + 'LOG_FILTER': [], 'LOCALE': [''], # defaults to user locale 'DEFAULT_PAGINATION': False, 'DEFAULT_ORPHANS': 0, @@ -170,12 +173,16 @@ def get_settings_from_file(path, default_settings=DEFAULT_CONFIG): def configure_settings(settings): """Provide optimizations, error checking and warnings for the given settings. - + Set up the logs to be ignored as well. """ if not 'PATH' 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)') + # set up logs to be ignored + LimitFilter.ignore.update(set(settings.get('LOG_FILTER', + DEFAULT_CONFIG['LOG_FILTER']))) + # lookup the theme in "pelican/themes" if the given one doesn't exist if not os.path.isdir(settings['THEME']): theme_path = os.path.join( diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 21a77e6b..2d4bbdfc 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -83,7 +83,7 @@ class TestPelican(LoggedTestCase): mute(True)(pelican.run)() self.assertDirsEqual(self.temp_path, os.path.join(OUTPUT_PATH, 'basic')) self.assertLogCountEqual( - count=4, + count=3, msg="Unable to find.*skipping url replacement", level=logging.WARNING) From 80842cbc0e2bbaed991c23de318752ca866a32a9 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 2 Apr 2014 12:38:49 -0700 Subject: [PATCH 0223/1427] Fix deprecated logger warning for Python 3 logger.warn() has been deprecated in Python 3 in favor of logger.warning() --- pelican/tools/pelican_import.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index d6b57c47..30d6346c 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -135,7 +135,7 @@ def wp2fields(xml, wp_custpost=False): title = HTMLParser().unescape(item.title.contents[0]) except IndexError: title = 'No title [%s]' % item.find('post_name').string - logger.warn('Post "%s" is lacking a proper title' % title) + logger.warning('Post "%s" is lacking a proper title' % title) filename = item.find('post_name').string post_id = item.find('post_id').string @@ -601,11 +601,11 @@ def download_attachments(output_path, urls): except URLError as e: error = ("No file could be downloaded from {}; Error {}" .format(url, e)) - logger.warn(error) + logger.warning(error) except IOError as e: #Python 2.7 throws an IOError rather Than URLError error = ("No file could be downloaded from {}; Error {}" .format(url, e)) - logger.warn(error) + logger.warning(error) return locations From c6ff88d0fce7f7ab5e05f2c414a365aa9faa6454 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 7 Apr 2014 14:29:21 -0700 Subject: [PATCH 0224/1427] Minor correction to settings documentation --- docs/settings.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 0c16db3c..53a25f8c 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -36,7 +36,7 @@ Setting name (default value) =============================================================================== ===================================================================== `AUTHOR` Default author (put your name) `DATE_FORMATS` (``{}``) If you manage multiple languages, you can set the date formatting - here. See the "Date format and locales" section below for details. + here. See the "Date format and locale" section below for details. `USE_FOLDER_AS_CATEGORY` (``True``) When you don't specify a category in your post metadata, set this setting to ``True``, and organize your articles in subfolders, the subfolder will become the category of your post. If set to ``False``, @@ -167,9 +167,9 @@ Setting name (default value) code blocks. See :ref:`internal_pygments_options` for a list of supported options. -`SLUGIFY_SOURCE` (``'input'``) Specifies where you want the slug to be automatically generated - from. Can be set to 'title' to use the 'Title:' metadata tag or - 'basename' to use the articles basename when creating the slug. +`SLUGIFY_SOURCE` (``'input'``) Specifies where you want the slug to be automatically generated + from. Can be set to 'title' to use the 'Title:' metadata tag or + 'basename' to use the articles basename when creating the slug. =============================================================================== ===================================================================== .. [#] Default is the system locale. From 03976b650d0e96251a63a9510d23fff7d9aec2d1 Mon Sep 17 00:00:00 2001 From: Antoine Brenner Date: Mon, 14 Apr 2014 20:43:19 +0200 Subject: [PATCH 0225/1427] Fix unittest issue related to python2/python3 differences The test_datetime test passed on python3 but not python2 because datetime.strftime is a byte string in python2, and a unicode string in python3 This patch allows the test to pass in both python2 and python3 (3.3+ only) --- pelican/tests/test_contents.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 4c6f8ed1..27d2a897 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -129,9 +129,15 @@ class TestPage(unittest.TestCase): page_kwargs['metadata']['date'] = dt page = Page(**page_kwargs) - self.assertEqual(page.locale_date, - dt.strftime(DEFAULT_CONFIG['DEFAULT_DATE_FORMAT'])) + # 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') + self.assertEqual(page.locale_date, dt_date ) page_kwargs['settings'] = get_settings() # I doubt this can work on all platforms ... From 7e06912bcad1eeb71d8f154dff21555a5ed865ca Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 14 Apr 2014 16:18:07 -0400 Subject: [PATCH 0226/1427] Minor text changes to log message limitation --- docs/contribute.rst | 16 ++++++++-------- docs/settings.rst | 14 +++++++------- pelican/contents.py | 2 +- pelican/log.py | 4 ++-- pelican/settings.py | 6 +++--- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/contribute.rst b/docs/contribute.rst index 28df1fcd..57349156 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -157,24 +157,24 @@ For logging messages that are not repeated, use the usual Python way: logger = logging.getLogger(__name__) # when needed - logger.warning('A warning that could occur only once") + logger.warning("A warning that would usually occur only once") However, if you want to log messages that may occur several times, instead of -a string, gives a tuple to the logging method, with two arguments: +a string, give a tuple to the logging method, with two arguments: - 1. The message to log for this very execution - 2. A generic message that will appear if the previous one would occur to many + 1. The message to log for the initial execution + 2. A generic message that will appear if the previous one would occur too many times. For example, if you want to log missing resources, use the following code: - for ressource in ressources: - if ressource.is_missing: + for resource in resources: + if resource.is_missing: logger.warning(( - 'The resource {r} is missing'.format(r=ressource.name), + 'The resource {r} is missing'.format(r=resource.name), 'Other resources were missing')) -The logs will be displayed as follows: +The log messages will be displayed as follows: WARNING: The resource prettiest_cat.jpg is missing WARNING: The resource best_cat_ever.jpg is missing diff --git a/docs/settings.rst b/docs/settings.rst index c35bf08d..36cc3f9a 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -88,7 +88,7 @@ Setting name (default value) here or a single string representing one locale. When providing a list, all the locales will be tried until one works. -`LOG_FILTER` (``[]``) A list of tuples containing the logging level (up to warning) +`LOG_FILTER` (``[]``) A list of tuples containing the logging level (up to ``warning``) and the message to be ignored. For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]`` `READERS` (``{}``) A dictionary of file extensions / Reader classes for Pelican to @@ -701,15 +701,15 @@ adding the following to your configuration:: Logging ======= -Sometimes, useless lines of log appears while the generation occurs. Finding -**the** meaningful error message in the middle of tons of annoying log outputs -can be quite tricky. To be able to filter out all useless log messages, Pelican +Sometimes, a long list of warnings may appear during site generation. Finding +the **meaningful** error message in the middle of tons of annoying log output +can be quite tricky. In order to filter out redundant log messages, Pelican comes with the ``LOG_FILTER`` setting. ``LOG_FILTER`` should be a list of tuples ``(level, msg)``, each of them being -composed of the logging level (up to warning) and the message to be ignored. -Simply populate the list with the logs you want to hide and they will be -filtered out. +composed of the logging level (up to ``warning``) and the message to be ignored. +Simply populate the list with the log messages you want to hide, and they will +be filtered out. For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]`` diff --git a/pelican/contents.py b/pelican/contents.py index 3096a064..615a7fd8 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -241,7 +241,7 @@ class Content(object): else: logger.warning(("Unable to find {fn}, skipping url" " replacement".format(fn=value), - "Other ressources were not found" + "Other resources were not found" " and their urls not replaced")) elif what == 'category': origin = Category(path, self.settings).url diff --git a/pelican/log.py b/pelican/log.py index d3aae012..fdf41cb0 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -78,7 +78,7 @@ class LimitFilter(logging.Filter): group_count = defaultdict(int) def filter(self, record): - # don't limit levels over warnings + # don't limit log messages for anything above "warning" if record.levelno > logging.WARN: return record # extract group @@ -105,7 +105,7 @@ class LimitFilter(logging.Filter): class LimitLogger(logging.Logger): """ - A logger which add LimitFilter automatically + A logger which adds LimitFilter automatically """ limit_filter = LimitFilter() diff --git a/pelican/settings.py b/pelican/settings.py index f70f74a8..7277c121 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -171,15 +171,15 @@ def get_settings_from_file(path, default_settings=DEFAULT_CONFIG): def configure_settings(settings): - """Provide optimizations, error checking and warnings for the given + """Provide optimizations, error checking, and warnings for the given settings. - Set up the logs to be ignored as well. + Also, specify the log messages to be ignored. """ if not 'PATH' 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)') - # set up logs to be ignored + # specify the log messages to be ignored LimitFilter.ignore.update(set(settings.get('LOG_FILTER', DEFAULT_CONFIG['LOG_FILTER']))) From aabb7f9345853c7afb73e781ac1cc65f6a2094dd Mon Sep 17 00:00:00 2001 From: Antoine Brenner Date: Mon, 14 Apr 2014 22:28:25 +0200 Subject: [PATCH 0227/1427] Fix error in download_attachments() triggered by python2 unit test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The download_attachments error is triggered in the unit tests by a japanese error message (接続を拒否されました) (connexion denied), that python is not able to serialize the into a byte string. This error weirdly does not appear every time the unit tests are run. It might be related to the order in which the tests are run. This error was found and fixed during the PyconUS 2014 pelican sprint. It was discovered on a Linux Fedora20 computer running Python2.7 in virtualenv --- pelican/tools/pelican_import.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 30d6346c..27e47754 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -603,8 +603,18 @@ def download_attachments(output_path, urls): .format(url, e)) logger.warning(error) except IOError as e: #Python 2.7 throws an IOError rather Than URLError - error = ("No file could be downloaded from {}; Error {}" - .format(url, e)) + # For japanese, the error might look kind of like this: + # e = IOError( 'socket error', socket.error(111, u'\u63a5\u7d9a\u3092\u62d2\u5426\u3055\u308c\u307e\u3057\u305f') ) + # and not be suitable to use in "{}".format(e) , raising UnicodeDecodeError + # (This is at least the case on my Fedora running Python 2.7.5 + # (default, Feb 19 2014, 13:47:28) [GCC 4.8.2 20131212 (Red Hat 4.8.2-7)] on linux2 + try: + error = ("No file could be downloaded from {}; Error {}" + .format(url, e)) + except UnicodeDecodeError: + # For lack of a better log message because we could not decode e, let's use repr(e) + error = ("No file could be downloaded from {}; Error {}" + .format(url, repr(e))) logger.warning(error) return locations From fd779267000ac539ee0a9ba5856d103fbbc7cd7c Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Sat, 15 Feb 2014 21:20:51 +0100 Subject: [PATCH 0228/1427] Cache content to speed up reading. Fixes #224. Cache read content so that it doesn't have to be read next time if its source has not been modified. --- docs/faq.rst | 19 ++++++ docs/settings.rst | 64 ++++++++++++++++- pelican/__init__.py | 19 ++++++ pelican/contents.py | 7 ++ pelican/generators.py | 72 ++++++++++++-------- pelican/settings.py | 7 +- pelican/tests/test_generators.py | 59 ++++++++++++++++ pelican/tests/test_pelican.py | 6 ++ pelican/utils.py | 113 +++++++++++++++++++++++++++++++ 9 files changed, 334 insertions(+), 32 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index 80e14d21..bb9377e6 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -205,3 +205,22 @@ You can also disable generation of tag-related pages via:: TAGS_SAVE_AS = '' TAG_SAVE_AS = '' + +Why does Pelican always write all HTML files even with content caching enabled? +=============================================================================== + +In order to reliably determine whether the HTML output is different +before writing it, a large part of the generation environment +including the template contexts, imported plugins, etc. would have to +be saved and compared, at least in the form of a hash (which would +require special handling of unhashable types), because of all the +possible combinations of plugins, pagination, etc. which may change in +many different ways. This would require a lot more processing time +and memory and storage space. Simply writing the files each time is a +lot faster and a lot more reliable. + +However, this means that the modification time of the files changes +every time, so a ``rsync`` based upload will transfer them even if +their content hasn't changed. A simple solution is to make ``rsync`` +use the ``--checksum`` option, which will make it compare the file +checksums in a much faster way than Pelican would. diff --git a/docs/settings.rst b/docs/settings.rst index 36cc3f9a..d8690230 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -173,6 +173,12 @@ Setting name (default value) `SLUGIFY_SOURCE` (``'input'``) Specifies where you want the slug to be automatically generated from. Can be set to 'title' to use the 'Title:' metadata tag or 'basename' to use the articles basename when creating the slug. +`CACHE_CONTENT` (``True``) If ``True``, save read content in a cache file. + See :ref:`reading_only_modified_content` for details about caching. +`CACHE_DIRECTORY` (``cache``) Directory in which to store cache files. +`CHECK_MODIFIED_METHOD` (``mtime``) Controls how files are checked for modifications. +`LOAD_CONTENT_CACHE` (``True``) If ``True``, load unmodified content from cache. +`GZIP_CACHE` (``True``) If ``True``, use gzip to (de)compress the cache files. =============================================================================== ===================================================================== .. [#] Default is the system locale. @@ -602,7 +608,7 @@ Setting name (default value) What does it do? .. [3] %s is the language Ordering content -================= +================ ================================================ ===================================================== Setting name (default value) What does it do? @@ -697,7 +703,6 @@ adding the following to your configuration:: CSS_FILE = "wide.css" - Logging ======= @@ -713,6 +718,61 @@ be filtered out. For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]`` +.. _reading_only_modified_content: + +Reading only modified content +============================= + +To speed up the build process, pelican can optionally read only articles +and pages with modified content. + +When Pelican is about to read some content source file: + +1. The hash or modification time information for the file from a + previous build are loaded from a cache file if `LOAD_CONTENT_CACHE` + is ``True``. These files are stored in the `CACHE_DIRECTORY` + directory. If the file has no record in the cache file, it is read + as usual. +2. The file is checked according to `CHECK_MODIFIED_METHOD`: + + - If set to ``'mtime'``, the modification time of the file is + checked. + - If set to a name of a function provided by the ``hashlib`` + module, e.g. ``'md5'``, the file hash is checked. + - If set to anything else or the necessary information about the + file cannot be found in the cache file, the content is read as + usual. + +3. If the file is considered unchanged, the content object saved in a + previous build corresponding to the file is loaded from the cache + and the file is not read. +4. If the file is considered changed, the file is read and the new + modification information and the content object are saved to the + cache if `CACHE_CONTENT` is ``True``. + +Modification time based checking is faster than comparing file hashes, +but is not as reliable, because mtime information can be lost when +e.g. copying the content sources using the ``cp`` or ``rsync`` +commands without the mtime preservation mode (invoked e.g. by +``--archive``). + +The cache files are Python pickles, so they may not be readable by +different versions of Python as the pickle format often changes. If +such an error is encountered, the cache files have to be rebuilt +using the pelican command-line option ``--full-rebuild``. +The cache files also have to be rebuilt when changing the +`GZIP_CACHE` setting for cache file reading to work. + +The ``--full-rebuild`` command-line option is also useful when the +whole site needs to be regenerated due to e.g. modifications to the +settings file or theme files. When pelican runs in autorealod mode, +modification of the settings file or theme will trigger a full rebuild +automatically. + +Note that even when using cached content, all output is always +written, so the modification times of the ``*.html`` files always +change. Therefore, ``rsync`` based upload may benefit from the +``--checksum`` option. Example settings ================ diff --git a/pelican/__init__.py b/pelican/__init__.py index 494e7e43..b6bfe326 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -260,6 +260,10 @@ def parse_arguments(): action='store_true', help='Relaunch pelican each time a modification occurs' ' on the content files.') + + parser.add_argument('-f', '--full-rebuild', action='store_true', + dest='full_rebuild', help='Rebuild everything by not loading from cache') + return parser.parse_args() @@ -275,6 +279,8 @@ def get_config(args): config['THEME'] = abstheme if os.path.exists(abstheme) else args.theme if args.delete_outputdir is not None: config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir + if args.full_rebuild: + config['LOAD_CONTENT_CACHE'] = False # argparse returns bytes in Py2. There is no definite answer as to which # encoding argparse (or sys.argv) uses. @@ -327,6 +333,7 @@ def main(): print(' --- AutoReload Mode: Monitoring `content`, `theme` and' ' `settings` for changes. ---') + first_run = True # load cache on first run while True: try: # Check source dir for changed files ending with the given @@ -335,9 +342,14 @@ def main(): # have changed, no matter what extension the filenames # have. modified = {k: next(v) for k, v in watchers.items()} + original_load_cache = settings['LOAD_CONTENT_CACHE'] if modified['settings']: pelican, settings = get_instance(args) + if not first_run: + original_load_cache = settings['LOAD_CONTENT_CACHE'] + # invalidate cache + pelican.settings['LOAD_CONTENT_CACHE'] = False if any(modified.values()): print('\n-> Modified: {}. re-generating...'.format( @@ -349,8 +361,15 @@ def main(): if modified['theme'] is None: logger.warning('Empty theme folder. Using `basic` ' 'theme.') + elif modified['theme']: + # theme modified, needs full rebuild -> no cache + if not first_run: # but not on first run + pelican.settings['LOAD_CONTENT_CACHE'] = False pelican.run() + first_run = False + # restore original caching policy + pelican.settings['LOAD_CONTENT_CACHE'] = original_load_cache except KeyboardInterrupt: logger.warning("Keyboard interrupt, quitting.") diff --git a/pelican/contents.py b/pelican/contents.py index 615a7fd8..c02047b8 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -325,6 +325,13 @@ class Content(object): os.path.abspath(self.settings['PATH'])) ) + def __eq__(self, other): + """Compare with metadata and content of other Content object""" + return other and self.metadata == other.metadata and self.content == other.content + + # keep basic hashing functionality for caching to work + __hash__ = object.__hash__ + class Page(Content): mandatory_properties = ('title',) diff --git a/pelican/generators.py b/pelican/generators.py index bfdac1a5..7c2dbbf2 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -20,14 +20,15 @@ from jinja2 import (Environment, FileSystemLoader, PrefixLoader, ChoiceLoader, 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 +from pelican.utils import (copy, process_translations, mkdir_p, DateFormatter, + FileStampDataCacher) from pelican import signals logger = logging.getLogger(__name__) -class Generator(object): +class Generator(FileStampDataCacher): """Baseclass generator""" def __init__(self, context, settings, path, theme, output_path, **kwargs): @@ -73,6 +74,10 @@ class Generator(object): custom_filters = self.settings['JINJA_FILTERS'] self.env.filters.update(custom_filters) + # set up caching + super(Generator, self).__init__(settings, 'CACHE_CONTENT', + 'LOAD_CONTENT_CACHE') + signals.generator_init.send(self) def get_template(self, name): @@ -408,20 +413,24 @@ class ArticlesGenerator(Generator): for f in self.get_files( self.settings['ARTICLE_DIR'], exclude=self.settings['ARTICLE_EXCLUDES']): - try: - article = self.readers.read_file( - base_path=self.path, path=f, content_class=Article, - context=self.context, - preread_signal=signals.article_generator_preread, - preread_sender=self, - context_signal=signals.article_generator_context, - context_sender=self) - except Exception as e: - logger.warning('Could not process {}\n{}'.format(f, e)) - continue + article = self.get_cached_data(f, None) + if article is None: + try: + article = self.readers.read_file( + base_path=self.path, path=f, content_class=Article, + context=self.context, + preread_signal=signals.article_generator_preread, + preread_sender=self, + context_signal=signals.article_generator_context, + context_sender=self) + except Exception as e: + logger.warning('Could not process {}\n{}'.format(f, e)) + continue - if not is_valid_content(article, f): - continue + if not is_valid_content(article, f): + continue + + self.cache_data(f, article) self.add_source_path(article) @@ -502,7 +511,7 @@ class ArticlesGenerator(Generator): self._update_context(('articles', 'dates', 'tags', 'categories', 'tag_cloud', 'authors', 'related_posts')) - + self.save_cache() signals.article_generator_finalized.send(self) def generate_output(self, writer): @@ -527,20 +536,24 @@ class PagesGenerator(Generator): for f in self.get_files( self.settings['PAGE_DIR'], exclude=self.settings['PAGE_EXCLUDES']): - try: - page = self.readers.read_file( - base_path=self.path, path=f, content_class=Page, - context=self.context, - preread_signal=signals.page_generator_preread, - preread_sender=self, - context_signal=signals.page_generator_context, - context_sender=self) - except Exception as e: - logger.warning('Could not process {}\n{}'.format(f, e)) - continue + page = self.get_cached_data(f, None) + if page is None: + try: + page = self.readers.read_file( + base_path=self.path, path=f, content_class=Page, + context=self.context, + preread_signal=signals.page_generator_preread, + preread_sender=self, + context_signal=signals.page_generator_context, + context_sender=self) + except Exception as e: + logger.warning('Could not process {}\n{}'.format(f, e)) + continue - if not is_valid_content(page, f): - continue + if not is_valid_content(page, f): + continue + + self.cache_data(f, page) self.add_source_path(page) @@ -560,6 +573,7 @@ class PagesGenerator(Generator): self._update_context(('pages', )) self.context['PAGES'] = self.pages + self.save_cache() signals.page_generator_finalized.send(self) def generate_output(self, writer): diff --git a/pelican/settings.py b/pelican/settings.py index 7277c121..baf2a497 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -119,7 +119,12 @@ DEFAULT_CONFIG = { 'IGNORE_FILES': ['.#*'], 'SLUG_SUBSTITUTIONS': (), 'INTRASITE_LINK_REGEX': '[{|](?P.*?)[|}]', - 'SLUGIFY_SOURCE': 'title' + 'SLUGIFY_SOURCE': 'title', + 'CACHE_CONTENT': True, + 'CACHE_DIRECTORY': 'cache', + 'GZIP_CACHE': True, + 'CHECK_MODIFIED_METHOD': 'mtime', + 'LOAD_CONTENT_CACHE': True, } PYGMENTS_RST_OPTIONS = None diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 6f13aeb6..a500f87a 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -42,6 +42,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 cls.generator = ArticlesGenerator( context=settings.copy(), settings=settings, @@ -50,8 +51,15 @@ class TestArticlesGenerator(unittest.TestCase): cls.articles = [[page.title, page.status, page.category.name, page.template] for page in cls.generator.articles] + def setUp(self): + self.temp_cache = mkdtemp(prefix='pelican_cache.') + + def tearDown(self): + rmtree(self.temp_cache) + def test_generate_feeds(self): settings = get_settings() + settings['CACHE_DIRECTORY'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=None, theme=settings['THEME'], output_path=None) @@ -127,6 +135,7 @@ class TestArticlesGenerator(unittest.TestCase): settings['DEFAULT_CATEGORY'] = 'Default' settings['DEFAULT_DATE'] = (1970, 1, 1) settings['USE_FOLDER_AS_CATEGORY'] = False + settings['CACHE_DIRECTORY'] = self.temp_cache settings['READERS'] = {'asc': None} settings['filenames'] = {} generator = ArticlesGenerator( @@ -151,6 +160,7 @@ class TestArticlesGenerator(unittest.TestCase): def test_direct_templates_save_as_default(self): settings = get_settings(filenames={}) + settings['CACHE_DIRECTORY'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=None, theme=settings['THEME'], output_path=None) @@ -165,6 +175,7 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings() settings['DIRECT_TEMPLATES'] = ['archives'] settings['ARCHIVES_SAVE_AS'] = 'archives/index.html' + settings['CACHE_DIRECTORY'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=None, theme=settings['THEME'], output_path=None) @@ -180,6 +191,7 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings() settings['DIRECT_TEMPLATES'] = ['archives'] settings['ARCHIVES_SAVE_AS'] = 'archives/index.html' + settings['CACHE_DIRECTORY'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=None, theme=settings['THEME'], output_path=None) @@ -206,6 +218,7 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings(filenames={}) settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html' + settings['CACHE_DIRECTORY'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=CONTENT_DIR, theme=settings['THEME'], output_path=None) @@ -268,6 +281,25 @@ class TestArticlesGenerator(unittest.TestCase): authors_expected = ['alexis-metaireau', 'first-author', 'second-author'] self.assertEqual(sorted(authors), sorted(authors_expected)) + def test_content_caching(self): + """Test that the articles are read only once when caching""" + settings = get_settings(filenames={}) + settings['CACHE_DIRECTORY'] = self.temp_cache + settings['READERS'] = {'asc': None} + + generator = ArticlesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + generator.generate_context() + self.assertTrue(hasattr(generator, '_cache')) + + generator = ArticlesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + generator.readers.read_file = MagicMock() + generator.generate_context() + generator.readers.read_file.assert_called_count == 0 + class TestPageGenerator(unittest.TestCase): # Note: Every time you want to test for a new field; Make sure the test @@ -275,12 +307,19 @@ class TestPageGenerator(unittest.TestCase): # distill_pages Then update the assertEqual in test_generate_context # to match expected + def setUp(self): + self.temp_cache = mkdtemp(prefix='pelican_cache.') + + def tearDown(self): + rmtree(self.temp_cache) + def distill_pages(self, pages): return [[page.title, page.status, page.template] for page in pages] def test_generate_context(self): settings = get_settings(filenames={}) settings['PAGE_DIR'] = 'TestPages' # relative to CUR_DIR + settings['CACHE_DIRECTORY'] = self.temp_cache settings['DEFAULT_DATE'] = (1970, 1, 1) generator = PagesGenerator( @@ -306,6 +345,26 @@ class TestPageGenerator(unittest.TestCase): self.assertEqual(sorted(pages_expected), sorted(pages)) self.assertEqual(sorted(hidden_pages_expected), sorted(hidden_pages)) + def test_content_caching(self): + """Test that the pages are read only once when caching""" + settings = get_settings(filenames={}) + settings['CACHE_DIRECTORY'] = 'cache_dir' #TODO + settings['CACHE_DIRECTORY'] = self.temp_cache + settings['READERS'] = {'asc': None} + + generator = PagesGenerator( + context=settings.copy(), settings=settings, + path=CUR_DIR, theme=settings['THEME'], output_path=None) + generator.generate_context() + self.assertTrue(hasattr(generator, '_cache')) + + generator = PagesGenerator( + context=settings.copy(), settings=settings, + path=CUR_DIR, theme=settings['THEME'], output_path=None) + generator.readers.read_file = MagicMock() + generator.generate_context() + generator.readers.read_file.assert_called_count == 0 + class TestTemplatePagesGenerator(unittest.TestCase): diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 2d4bbdfc..15876095 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -43,12 +43,14 @@ class TestPelican(LoggedTestCase): def setUp(self): super(TestPelican, self).setUp() self.temp_path = mkdtemp(prefix='pelicantests.') + self.temp_cache = mkdtemp(prefix='pelican_cache.') self.old_locale = locale.setlocale(locale.LC_ALL) self.maxDiff = None locale.setlocale(locale.LC_ALL, str('C')) def tearDown(self): rmtree(self.temp_path) + rmtree(self.temp_cache) locale.setlocale(locale.LC_ALL, self.old_locale) super(TestPelican, self).tearDown() @@ -77,6 +79,7 @@ class TestPelican(LoggedTestCase): settings = read_settings(path=None, override={ 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, + 'CACHE_DIRECTORY': self.temp_cache, 'LOCALE': locale.normalize('en_US'), }) pelican = Pelican(settings=settings) @@ -92,6 +95,7 @@ class TestPelican(LoggedTestCase): settings = read_settings(path=SAMPLE_CONFIG, override={ 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, + 'CACHE_DIRECTORY': self.temp_cache, 'LOCALE': locale.normalize('en_US'), }) pelican = Pelican(settings=settings) @@ -103,6 +107,7 @@ class TestPelican(LoggedTestCase): settings = read_settings(path=SAMPLE_CONFIG, override={ 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, + 'CACHE_DIRECTORY': self.temp_cache, 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, 'very'), os.path.join(SAMPLES_PATH, 'kinda'), os.path.join(SAMPLES_PATH, 'theme_standard')] @@ -123,6 +128,7 @@ class TestPelican(LoggedTestCase): settings = read_settings(path=SAMPLE_CONFIG, override={ 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, + 'CACHE_DIRECTORY': self.temp_cache, 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, 'theme_standard')] }) diff --git a/pelican/utils.py b/pelican/utils.py index c5aacaa3..8c416921 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -12,6 +12,8 @@ import pytz import re import shutil import traceback +import pickle +import hashlib from collections import Hashable from contextlib import contextmanager @@ -545,3 +547,114 @@ def split_all(path): break path = head return components + + +class FileDataCacher(object): + '''Class that can cache data contained in files''' + + def __init__(self, settings, cache_policy_key, load_policy_key): + '''Load the specified cache within CACHE_DIRECTORY + + only if load_policy_key in setttings is True, + May use gzip if GZIP_CACHE. + Sets caching policy according to *cache_policy_key* + in *settings* + ''' + self.settings = settings + name = self.__class__.__name__ + self._cache_path = os.path.join(self.settings['CACHE_DIRECTORY'], name) + self._cache_data_policy = self.settings[cache_policy_key] + if not self.settings[load_policy_key]: + self._cache = {} + return + if self.settings['GZIP_CACHE']: + import gzip + self._cache_open = gzip.open + else: + self._cache_open = open + try: + with self._cache_open(self._cache_path, 'rb') as f: + self._cache = pickle.load(f) + except Exception as e: + self._cache = {} + + def cache_data(self, filename, data): + '''Cache data for given file''' + if not self._cache_data_policy: + return + self._cache[filename] = data + + def get_cached_data(self, filename, default={}): + '''Get cached data for the given file + + if no data is cached, return the default object + ''' + return self._cache.get(filename, default) + + def save_cache(self): + '''Save the updated cache''' + if not self._cache_data_policy: + return + try: + mkdir_p(self.settings['CACHE_DIRECTORY']) + with self._cache_open(self._cache_path, 'wb') as f: + pickle.dump(self._cache, f) + except Exception as e: + logger.warning('Could not save cache {}\n{}'.format( + self._cache_path, e)) + + +class FileStampDataCacher(FileDataCacher): + '''Subclass that also caches the stamp of the file''' + + def __init__(self, settings, cache_policy_key, load_policy_key): + '''This sublcass additionaly sets filestamp function''' + super(FileStampDataCacher, self).__init__(settings, cache_policy_key, + load_policy_key) + + method = self.settings['CHECK_MODIFIED_METHOD'] + if method == 'mtime': + self._filestamp_func = os.path.getmtime + else: + try: + hash_func = getattr(hashlib, method) + def filestamp_func(buf): + return hash_func(buf).digest() + self._filestamp_func = filestamp_func + except ImportError: + self._filestamp_func = None + + def cache_data(self, filename, data): + '''Cache stamp and data for the given file''' + stamp = self._get_file_stamp(filename) + super(FileStampDataCacher, self).cache_data(filename, (stamp, data)) + + def _get_file_stamp(self, filename): + '''Check if the given file has been modified + since the previous build. + + depending on CHECK_MODIFIED_METHOD + a float may be returned for 'mtime', + a hash for a function name in the hashlib module + or an empty bytes string otherwise + ''' + filename = os.path.join(self.path, filename) + try: + with open(filename, 'rb') as f: + return self._filestamp_func(f.read()) + except Exception: + return b'' + + def get_cached_data(self, filename, default=None): + '''Get the cached data for the given filename + if the file has not been modified. + + If no record exists or file has been modified, return default. + Modification is checked by compaing the cached + and current file stamp. + ''' + stamp, data = super(FileStampDataCacher, self).get_cached_data( + filename, (None, default)) + if stamp != self._get_file_stamp(filename): + return default + return data From 5b4381c19cb372866f5e2343d108c3193206195d Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Sun, 9 Mar 2014 22:04:43 +0000 Subject: [PATCH 0229/1427] Add s3cmd MIME type detection --- pelican/tools/templates/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in index fe7a60a4..c542e588 100644 --- a/pelican/tools/templates/Makefile.in +++ b/pelican/tools/templates/Makefile.in @@ -97,7 +97,7 @@ ftp_upload: publish lftp ftp://$$(FTP_USER)@$$(FTP_HOST) -e "mirror -R $$(OUTPUTDIR) $$(FTP_TARGET_DIR) ; quit" s3_upload: publish - s3cmd sync $(OUTPUTDIR)/ s3://$(S3_BUCKET) --acl-public --delete-removed + s3cmd sync $(OUTPUTDIR)/ s3://$(S3_BUCKET) --acl-public --delete-removed --guess-mime-type cf_upload: publish cd $(OUTPUTDIR) && swift -v -A https://auth.api.rackspacecloud.com/v1.0 -U $(CLOUDFILES_USERNAME) -K $(CLOUDFILES_API_KEY) upload -c $(CLOUDFILES_CONTAINER) . From 4cae9ea88f552ec3166629ec1e9f004567245a06 Mon Sep 17 00:00:00 2001 From: Lonewolf Date: Mon, 31 Mar 2014 14:28:46 +0530 Subject: [PATCH 0230/1427] Added new sphinxtheme as requirement for docs Modified docs conf to support the theme update --- dev_requirements.txt | 3 +++ docs/conf.py | 34 +++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index c90ac630..01fe2507 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -9,3 +9,6 @@ typogrify # To perform release bumpr==0.2.0 + +# For docs theme +sphinx_rtd_theme diff --git a/docs/conf.py b/docs/conf.py index 6db0f3d1..99acd1b6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,6 +2,8 @@ from __future__ import unicode_literals import sys, os +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + sys.path.append(os.path.abspath(os.pardir)) from pelican import __version__ @@ -21,29 +23,43 @@ rst_prolog = ''' .. |last_stable| replace:: :pelican-doc:`{0}` '''.format(last_stable) +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + extlinks = { 'pelican-doc': ('http://docs.getpelican.com/%s/', '') } # -- Options for HTML output --------------------------------------------------- -html_theme_path = ['_themes'] -html_theme = 'pelican' - -html_theme_options = { - 'nosidebar': True, - 'index_logo': 'pelican.png', - 'github_fork': 'getpelican/pelican', -} +html_theme = 'default' +if not on_rtd: + try: + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + except ImportError: + pass html_static_path = ['_static'] # Output file base name for HTML help builder. htmlhelp_basename = 'Pelicandoc' +html_use_smartypants = True + +# If false, no module index is generated. +html_use_modindex = False + +# If false, no index is generated. +html_use_index = False + +# If true, links to the reST sources are added to the pages. +html_show_sourcelink = False + # -- Options for LaTeX output -------------------------------------------------- latex_documents = [ - ('index', 'Pelican.tex', 'Pelican Documentation', + ('index', 'Pelican.tex', 'Pelican Documentation', 'Alexis Métaireau', 'manual'), ] From e97e9b5ae5fa5494f136521afb3594cf3e65fc83 Mon Sep 17 00:00:00 2001 From: Antoine Brenner Date: Tue, 15 Apr 2014 00:04:40 +0200 Subject: [PATCH 0231/1427] Fix unittest issue related to python2/python3 differences Under python 2, with non-ascii locales, u"{:%b}".format(date) can raise UnicodeDecodeError because u"{:%b}".format(date) will call date.__format__(u"%b"), which will return a byte string and not a unicode string. eg: locale.setlocale(locale.LC_ALL, 'ja_JP.utf8') date.__format__(u"%b") == '12\xe6\x9c\x88' # True This commit catches UnicodeDecodeError and calls date.__format__() with byte strings instead of characters, since it to work with character strings --- pelican/generators.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pelican/generators.py b/pelican/generators.py index 7c2dbbf2..1b584d3f 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -309,7 +309,20 @@ class ArticlesGenerator(Generator): # format string syntax can be used for specifying the # period archive dates date = archive[0].date - save_as = save_as_fmt.format(date=date) + # Under python 2, with non-ascii locales, u"{:%b}".format(date) might raise UnicodeDecodeError + # because u"{:%b}".format(date) will call date.__format__(u"%b"), which will return a byte string + # and not a unicode string. + # eg: + # locale.setlocale(locale.LC_ALL, 'ja_JP.utf8') + # date.__format__(u"%b") == '12\xe6\x9c\x88' # True + try: + save_as = save_as_fmt.format(date=date) + except UnicodeDecodeError: + # Python2 only: + # Let date.__format__() work with byte strings instead of characters since it fails to work with characters + bytes_save_as_fmt = save_as_fmt.encode('utf8') + bytes_save_as = bytes_save_as_fmt.format(date=date) + save_as = unicode(bytes_save_as,'utf8') context = self.context.copy() if key == period_date_key['year']: From 7277c95fb588463d035decefcf86f59d41a4e7c0 Mon Sep 17 00:00:00 2001 From: Antoine Brenner Date: Tue, 15 Apr 2014 16:36:29 +0200 Subject: [PATCH 0232/1427] Make sure locale is what we want before/after the tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The locale is a global state, and it was not properly reset to whatever it was before the unitttest possibly changed it. This is now fixed. Not restoring the locale led to weird issues: depending on the order chosen by "python -m unittest discover" to run the unit tests, some tests would apparently randomly fail due to the locale not being what was expected. For example, test_period_in_timeperiod_archive would call mock('posts/1970/ 1月/index.html',...) instead of expected mock('posts/1970/Jan/index.html',...) and fail. --- pelican/tests/test_contents.py | 6 ++++++ pelican/tests/test_generators.py | 11 +++++++++++ pelican/tests/test_importer.py | 11 +++++++++++ pelican/tests/test_paginator.py | 8 +++++++- pelican/tests/test_pelican.py | 2 +- pelican/tests/test_settings.py | 5 +++++ pelican/tests/test_utils.py | 3 +++ 7 files changed, 44 insertions(+), 2 deletions(-) diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 27d2a897..3c0f8d75 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals, absolute_import import six from datetime import datetime from sys import platform +import locale from pelican.tests.support import unittest, get_settings @@ -22,6 +23,8 @@ class TestPage(unittest.TestCase): def setUp(self): super(TestPage, self).setUp() + self.old_locale = locale.setlocale(locale.LC_ALL) + locale.setlocale(locale.LC_ALL, str('C')) self.page_kwargs = { 'content': TEST_CONTENT, 'context': { @@ -35,6 +38,9 @@ class TestPage(unittest.TestCase): 'source_path': '/path/to/file/foo.ext' } + def tearDown(self): + locale.setlocale(locale.LC_ALL, self.old_locale) + def test_use_args(self): # Creating a page with arguments passed to the constructor should use # them to initialise object's attributes. diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index a500f87a..ff487c3e 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -14,6 +14,7 @@ from pelican.generators import (Generator, ArticlesGenerator, PagesGenerator, 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') @@ -21,11 +22,17 @@ CONTENT_DIR = os.path.join(CUR_DIR, 'content') class TestGenerator(unittest.TestCase): def setUp(self): + self.old_locale = locale.setlocale(locale.LC_ALL) + locale.setlocale(locale.LC_ALL, str('C')) self.settings = get_settings() self.settings['READERS'] = {'asc': None} self.generator = Generator(self.settings.copy(), self.settings, CUR_DIR, self.settings['THEME'], None) + def tearDown(self): + locale.setlocale(locale.LC_ALL, self.old_locale) + + def test_include_path(self): filename = os.path.join(CUR_DIR, 'content', 'article.rst') include_path = self.generator._include_path @@ -373,10 +380,14 @@ class TestTemplatePagesGenerator(unittest.TestCase): def setUp(self): self.temp_content = mkdtemp(prefix='pelicantests.') self.temp_output = mkdtemp(prefix='pelicantests.') + self.old_locale = locale.setlocale(locale.LC_ALL) + locale.setlocale(locale.LC_ALL, str('C')) + def tearDown(self): rmtree(self.temp_content) rmtree(self.temp_output) + locale.setlocale(locale.LC_ALL, self.old_locale) def test_generate_output(self): diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index 8412c75b..65193bf5 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals, print_function import os import re +import locale 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) @@ -30,9 +31,14 @@ except ImportError: class TestWordpressXmlImporter(unittest.TestCase): def setUp(self): + self.old_locale = locale.setlocale(locale.LC_ALL) + locale.setlocale(locale.LC_ALL, str('C')) self.posts = list(wp2fields(WORDPRESS_XML_SAMPLE)) self.custposts = list(wp2fields(WORDPRESS_XML_SAMPLE, True)) + def tearDown(self): + locale.setlocale(locale.LC_ALL, self.old_locale) + def test_ignore_empty_posts(self): self.assertTrue(self.posts) for title, content, fname, date, author, categ, tags, kind, format in self.posts: @@ -261,8 +267,13 @@ class TestBuildHeader(unittest.TestCase): @unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module') class TestWordpressXMLAttachements(unittest.TestCase): def setUp(self): + self.old_locale = locale.setlocale(locale.LC_ALL) + locale.setlocale(locale.LC_ALL, str('C')) self.attachments = get_attachments(WORDPRESS_XML_SAMPLE) + def tearDown(self): + locale.setlocale(locale.LC_ALL, self.old_locale) + def test_recognise_attachments(self): self.assertTrue(self.attachments) self.assertTrue(len(self.attachments.keys()) == 3) diff --git a/pelican/tests/test_paginator.py b/pelican/tests/test_paginator.py index f454d47d..108dc791 100644 --- a/pelican/tests/test_paginator.py +++ b/pelican/tests/test_paginator.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals, absolute_import import six +import locale from pelican.tests.support import unittest, get_settings @@ -16,6 +17,8 @@ TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False) class TestPage(unittest.TestCase): def setUp(self): super(TestPage, self).setUp() + self.old_locale = locale.setlocale(locale.LC_ALL) + locale.setlocale(locale.LC_ALL, str('C')) self.page_kwargs = { 'content': TEST_CONTENT, 'context': { @@ -29,6 +32,9 @@ class TestPage(unittest.TestCase): 'source_path': '/path/to/file/foo.ext' } + def tearDown(self): + locale.setlocale(locale.LC_ALL, self.old_locale) + def test_save_as_preservation(self): settings = get_settings() # fix up pagination rules @@ -47,4 +53,4 @@ class TestPage(unittest.TestCase): object_list = [Article(**self.page_kwargs), Article(**self.page_kwargs)] paginator = Paginator('foobar.foo', object_list, settings) page = paginator.page(1) - self.assertEqual(page.save_as, 'foobar.foo') \ No newline at end of file + self.assertEqual(page.save_as, 'foobar.foo') diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 15876095..974986cd 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -44,8 +44,8 @@ class TestPelican(LoggedTestCase): super(TestPelican, self).setUp() self.temp_path = mkdtemp(prefix='pelicantests.') self.temp_cache = mkdtemp(prefix='pelican_cache.') - self.old_locale = locale.setlocale(locale.LC_ALL) self.maxDiff = None + self.old_locale = locale.setlocale(locale.LC_ALL) locale.setlocale(locale.LC_ALL, str('C')) def tearDown(self): diff --git a/pelican/tests/test_settings.py b/pelican/tests/test_settings.py index 7907a551..930e0fea 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -16,10 +16,15 @@ class TestSettingsConfiguration(unittest.TestCase): optimizations. """ def setUp(self): + self.old_locale = locale.setlocale(locale.LC_ALL) + locale.setlocale(locale.LC_ALL, str('C')) self.PATH = abspath(dirname(__file__)) default_conf = join(self.PATH, 'default_conf.py') self.settings = read_settings(default_conf) + def tearDown(self): + locale.setlocale(locale.LC_ALL, self.old_locale) + def test_overwrite_existing_settings(self): self.assertEqual(self.settings.get('SITENAME'), "Alexis' log") self.assertEqual(self.settings.get('SITEURL'), diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 9047593f..02398336 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -354,9 +354,12 @@ class TestCopy(unittest.TestCase): def setUp(self): self.root_dir = mkdtemp(prefix='pelicantests.') + self.old_locale = locale.setlocale(locale.LC_ALL) + locale.setlocale(locale.LC_ALL, str('C')) def tearDown(self): shutil.rmtree(self.root_dir) + locale.setlocale(locale.LC_ALL, self.old_locale) def _create_file(self, *path): with open(os.path.join(self.root_dir, *path), 'w') as f: From dd70f1b24ece9bba33fb115336a6e159c867f5b2 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 15 Apr 2014 11:13:10 -0400 Subject: [PATCH 0233/1427] Fix settings table in docs --- docs/settings.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index d8690230..9599ee10 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -249,9 +249,9 @@ posts for the month at ``posts/2011/Aug/index.html``. arrive at an appropriate archive of posts, without having to specify a page name. -==================================================== ===================================================== +====================================================== ===================================================== Setting name (default value) What does it do? -==================================================== ===================================================== +====================================================== ===================================================== `ARTICLE_URL` (``'{slug}.html'``) The URL to refer to an article. `ARTICLE_SAVE_AS` (``'{slug}.html'``) The place where we will save an article. `ARTICLE_LANG_URL` (``'{slug}-{lang}.html'``) The URL to refer to an article which doesn't use the @@ -262,7 +262,7 @@ Setting name (default value) What does it do? `DRAFT_SAVE_AS` (``'drafts/{slug}.html'``) The place where we will save an article draft. `DRAFT_LANG_URL` (``'drafts/{slug}-{lang}.html'``) The URL to refer to an article draft which doesn't use the default language. -`DRAFT_LANG_SAVE_AS` (``'drafts/{slug}-{lang}.html'``) The place where we will save an article draft which +`DRAFT_LANG_SAVE_AS` (``'drafts/{slug}-{lang}.html'``) The place where we will save an article draft which doesn't use the default language. `PAGE_URL` (``'pages/{slug}.html'``) The URL we will use to link to a page. `PAGE_SAVE_AS` (``'pages/{slug}.html'``) The location we will save the page. This value has to be @@ -285,7 +285,7 @@ Setting name (default value) What does it do? non-alphanumerics when generating slugs. Specified as a list of 2-tuples of ``(from, to)`` which are applied in order. -==================================================== ===================================================== +====================================================== ===================================================== .. note:: From fd7cb9e2132e0b66adeff40c5d780a842f84fb6c Mon Sep 17 00:00:00 2001 From: Antoine Brenner Date: Tue, 15 Apr 2014 22:01:20 +0200 Subject: [PATCH 0234/1427] Test to reproduce an issue that occurs with python3.3 under macos10 only This test passes fine under linux --- pelican/tests/test_utils.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 02398336..3c12a15b 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -458,6 +458,25 @@ class TestDateFormatter(unittest.TestCase): locale.setlocale(locale.LC_ALL, '') + @unittest.skipUnless(locale_available('fr_FR.UTF-8') or + locale_available('French'), + 'French locale needed') + def test_french_strftime(self): + # This test tries to reproduce an issue that occured with python3.3 under macos10 only + locale.setlocale(locale.LC_ALL, str('fr_FR.UTF-8')) + date = datetime.datetime(2014,8,14) + # we compare the lower() dates since macos10 returns "Jeudi" for %A whereas linux reports "jeudi" + self.assertEqual( u'jeudi, 14 août 2014', utils.strftime(date, date_format="%A, %d %B %Y").lower() ) + df = utils.DateFormatter() + self.assertEqual( u'jeudi, 14 août 2014', df(date, date_format="%A, %d %B %Y").lower() ) + # Let us now set the global locale to C: + locale.setlocale(locale.LC_ALL, str('C')) + # DateFormatter should still work as expected since it is the whole point of DateFormatter + # (This is where pre-2014/4/15 code fails on macos10) + df_date = df(date, date_format="%A, %d %B %Y").lower() + self.assertEqual( u'jeudi, 14 août 2014', df_date ) + + @unittest.skipUnless(locale_available('fr_FR.UTF-8') or locale_available('French'), 'French locale needed') From 6703950abec63a25d412bd96d6aa419c6f449c97 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Thu, 17 Apr 2014 16:28:22 +0200 Subject: [PATCH 0235/1427] enable writing of only selected output paths - add WRITE_SELECTED setting - add --write-selected commandline option --- docs/faq.rst | 5 +++++ docs/settings.rst | 19 +++++++++++++++++++ pelican/__init__.py | 6 ++++++ pelican/settings.py | 7 +++++++ pelican/tests/test_pelican.py | 23 +++++++++++++++++++++++ pelican/utils.py | 14 ++++++++++++++ pelican/writers.py | 9 +++++++-- 7 files changed, 81 insertions(+), 2 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index bb9377e6..bf468c51 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -224,3 +224,8 @@ every time, so a ``rsync`` based upload will transfer them even if their content hasn't changed. A simple solution is to make ``rsync`` use the ``--checksum`` option, which will make it compare the file checksums in a much faster way than Pelican would. + +When only several specific output files are of interest (e.g. when +working on some specific page or the theme templates), the +`WRITE_SELECTED` option may help, see +:ref:`writing_only_selected_content`. diff --git a/docs/settings.rst b/docs/settings.rst index 9599ee10..8d8f9a16 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -179,6 +179,10 @@ Setting name (default value) `CHECK_MODIFIED_METHOD` (``mtime``) Controls how files are checked for modifications. `LOAD_CONTENT_CACHE` (``True``) If ``True``, load unmodified content from cache. `GZIP_CACHE` (``True``) If ``True``, use gzip to (de)compress the cache files. +`WRITE_SELECTED` (``[]``) If this list is not empty, **only** output files with their paths + in this list are written. Paths should be either relative to the current + working directory of Pelican or absolute. For possible use cases see + :ref:`writing_only_selected_content`. =============================================================================== ===================================================================== .. [#] Default is the system locale. @@ -774,6 +778,21 @@ written, so the modification times of the ``*.html`` files always change. Therefore, ``rsync`` based upload may benefit from the ``--checksum`` option. +.. _writing_only_selected_content: + +Writing only selected content +============================= + +When one article or page or the theme is being worked on it is often +desirable to display selected output files as soon as possible. In +such cases generating and writing all output is often unnecessary. +These selected output files can be given as output paths in the +`WRITE_SELECTED` list and **only** those files will be written. This +list can be also specified on the command-line using the +``--write-selected`` option which accepts a comma separated list +of output file paths. By default the list is empty so all output is +written. + Example settings ================ diff --git a/pelican/__init__.py b/pelican/__init__.py index b6bfe326..1ed98fc3 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -264,6 +264,10 @@ def parse_arguments(): parser.add_argument('-f', '--full-rebuild', action='store_true', dest='full_rebuild', help='Rebuild everything by not loading from cache') + parser.add_argument('-w', '--write-selected', type=str, + dest='selected_paths', default=None, + help='Comma separated list of selected paths to write') + return parser.parse_args() @@ -281,6 +285,8 @@ def get_config(args): config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir if args.full_rebuild: config['LOAD_CONTENT_CACHE'] = False + if args.selected_paths: + config['WRITE_SELECTED'] = args.selected_paths.split(',') # argparse returns bytes in Py2. There is no definite answer as to which # encoding argparse (or sys.argv) uses. diff --git a/pelican/settings.py b/pelican/settings.py index baf2a497..7615c25c 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -125,6 +125,7 @@ DEFAULT_CONFIG = { 'GZIP_CACHE': True, 'CHECK_MODIFIED_METHOD': 'mtime', 'LOAD_CONTENT_CACHE': True, + 'WRITE_SELECTED': [], } PYGMENTS_RST_OPTIONS = None @@ -200,6 +201,12 @@ def configure_settings(settings): raise Exception("Could not find the theme %s" % settings['THEME']) + # make paths selected for writing absolute if necessary + settings['WRITE_SELECTED'] = [ + os.path.abspath(path) for path in + settings.get('WRITE_SELECTED', DEFAULT_CONFIG['WRITE_SELECTED']) + ] + # standardize strings to lowercase strings for key in [ 'DEFAULT_LANG', diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 974986cd..294cf399 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -138,3 +138,26 @@ class TestPelican(LoggedTestCase): for file in ['a_stylesheet', 'a_template']: self.assertTrue(os.path.exists(os.path.join(theme_output, file))) + + def test_write_only_selected(self): + """Test that only the selected files are written""" + settings = read_settings(path=None, override={ + 'PATH': INPUT_PATH, + 'OUTPUT_PATH': self.temp_path, + 'CACHE_DIRECTORY': self.temp_cache, + 'WRITE_SELECTED': [ + os.path.join(self.temp_path, 'oh-yeah.html'), + os.path.join(self.temp_path, 'categories.html'), + ], + 'LOCALE': locale.normalize('en_US'), + }) + pelican = Pelican(settings=settings) + logger = logging.getLogger() + orig_level = logger.getEffectiveLevel() + logger.setLevel(logging.INFO) + mute(True)(pelican.run)() + logger.setLevel(orig_level) + self.assertLogCountEqual( + count=2, + msg="writing .*", + level=logging.INFO) diff --git a/pelican/utils.py b/pelican/utils.py index 8c416921..cd942fd5 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -658,3 +658,17 @@ class FileStampDataCacher(FileDataCacher): if stamp != self._get_file_stamp(filename): return default return data + + +def is_selected_for_writing(settings, path): + '''Check whether path is selected for writing + according to the WRITE_SELECTED list + + If WRITE_SELECTED is an empty list (default), + any path is selected for writing. + ''' + if settings['WRITE_SELECTED']: + return path in settings['WRITE_SELECTED'] + else: + return True + diff --git a/pelican/writers.py b/pelican/writers.py index 19e36e39..a92feee4 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -16,7 +16,8 @@ from feedgenerator import Atom1Feed, Rss201rev2Feed from jinja2 import Markup from pelican.paginator import Paginator -from pelican.utils import get_relative_path, path_to_url, set_date_tzinfo +from pelican.utils import (get_relative_path, path_to_url, set_date_tzinfo, + is_selected_for_writing) from pelican import signals logger = logging.getLogger(__name__) @@ -92,6 +93,8 @@ class Writer(object): :param path: the path to output. :param feed_type: the feed type to use (atom or rss) """ + if not is_selected_for_writing(self.settings, path): + return old_locale = locale.setlocale(locale.LC_ALL) locale.setlocale(locale.LC_ALL, str('C')) try: @@ -140,7 +143,9 @@ class Writer(object): :param **kwargs: additional variables to pass to the templates """ - if name is False or name == "": + if name is False or name == "" or\ + not is_selected_for_writing(self.settings,\ + os.path.join(self.output_path, name)): return elif not name: # other stuff, just return for now From 6972261261a4eed164728943a037e1382d772f21 Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Thu, 17 Apr 2014 22:57:26 +0200 Subject: [PATCH 0236/1427] Add python 3.4 to tox config. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a72aea21..5dd36c36 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ # depends on some external libraries that aren't released yet. [tox] -envlist = py27,py33 +envlist = py27,py33,py34 [testenv] commands = From 676981c62110779e3d6adb04d08492f9f32f7af3 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Fri, 18 Apr 2014 06:57:59 +0200 Subject: [PATCH 0237/1427] set _cache_open func even if not loading cache, fixes autoreload The _cache_open attribute of the FileDataCacher class was not set when settings[load_policy_key] was not True, so saving later failed. As a precaution, replaced the `if ...: return` style with a plain if structure to prevent such readability issues and added tests. --- pelican/tests/test_generators.py | 48 ++++++++++++++++++++++++++++++++ pelican/utils.py | 36 +++++++++++------------- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index ff487c3e..f951f0cb 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -307,6 +307,30 @@ class TestArticlesGenerator(unittest.TestCase): generator.generate_context() generator.readers.read_file.assert_called_count == 0 + def test_full_rebuild(self): + """Test that all the articles are read again when not loading cache + + used in --full-rebuild or autoreload mode""" + settings = get_settings(filenames={}) + settings['CACHE_DIRECTORY'] = self.temp_cache + settings['READERS'] = {'asc': None} + + generator = ArticlesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + generator.readers.read_file = MagicMock() + generator.generate_context() + self.assertTrue(hasattr(generator, '_cache_open')) + orig_call_count = generator.readers.read_file.call_count + + settings['LOAD_CONTENT_CACHE'] = False + generator = ArticlesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + generator.readers.read_file = MagicMock() + generator.generate_context() + generator.readers.read_file.assert_called_count == orig_call_count + class TestPageGenerator(unittest.TestCase): # Note: Every time you want to test for a new field; Make sure the test @@ -372,6 +396,30 @@ class TestPageGenerator(unittest.TestCase): generator.generate_context() generator.readers.read_file.assert_called_count == 0 + def test_full_rebuild(self): + """Test that all the pages are read again when not loading cache + + used in --full-rebuild or autoreload mode""" + settings = get_settings(filenames={}) + settings['CACHE_DIRECTORY'] = self.temp_cache + settings['READERS'] = {'asc': None} + + generator = PagesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + generator.readers.read_file = MagicMock() + generator.generate_context() + self.assertTrue(hasattr(generator, '_cache_open')) + orig_call_count = generator.readers.read_file.call_count + + settings['LOAD_CONTENT_CACHE'] = False + generator = PagesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + generator.readers.read_file = MagicMock() + generator.generate_context() + generator.readers.read_file.assert_called_count == orig_call_count + class TestTemplatePagesGenerator(unittest.TestCase): diff --git a/pelican/utils.py b/pelican/utils.py index 8c416921..e76c559f 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -564,25 +564,24 @@ class FileDataCacher(object): name = self.__class__.__name__ self._cache_path = os.path.join(self.settings['CACHE_DIRECTORY'], name) self._cache_data_policy = self.settings[cache_policy_key] - if not self.settings[load_policy_key]: - self._cache = {} - return if self.settings['GZIP_CACHE']: import gzip self._cache_open = gzip.open else: self._cache_open = open - try: - with self._cache_open(self._cache_path, 'rb') as f: - self._cache = pickle.load(f) - except Exception as e: + if self.settings[load_policy_key]: + try: + with self._cache_open(self._cache_path, 'rb') as f: + self._cache = pickle.load(f) + except Exception as e: + self._cache = {} + else: self._cache = {} def cache_data(self, filename, data): '''Cache data for given file''' - if not self._cache_data_policy: - return - self._cache[filename] = data + if self._cache_data_policy: + self._cache[filename] = data def get_cached_data(self, filename, default={}): '''Get cached data for the given file @@ -593,15 +592,14 @@ class FileDataCacher(object): def save_cache(self): '''Save the updated cache''' - if not self._cache_data_policy: - return - try: - mkdir_p(self.settings['CACHE_DIRECTORY']) - with self._cache_open(self._cache_path, 'wb') as f: - pickle.dump(self._cache, f) - except Exception as e: - logger.warning('Could not save cache {}\n{}'.format( - self._cache_path, e)) + if self._cache_data_policy: + try: + mkdir_p(self.settings['CACHE_DIRECTORY']) + with self._cache_open(self._cache_path, 'wb') as f: + pickle.dump(self._cache, f) + except Exception as e: + logger.warning('Could not save cache {}\n{}'.format( + self._cache_path, e)) class FileStampDataCacher(FileDataCacher): From 22484983e911daec0234e924574e5b2f52683f70 Mon Sep 17 00:00:00 2001 From: James Lee Date: Sat, 19 Apr 2014 03:37:47 +0900 Subject: [PATCH 0238/1427] Handle list metadata as list of string in MarkdownReader --- pelican/readers.py | 6 ++++++ .../tests/content/article_with_markdown_and_footnote.md | 6 ++++++ pelican/tests/test_readers.py | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/pelican/readers.py b/pelican/readers.py index 35c38220..3f8a551e 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -204,12 +204,18 @@ class MarkdownReader(BaseReader): for name, value in meta.items(): name = name.lower() if name == "summary": + # handle summary metadata as markdown + # summary metadata is special case and join all list values summary_values = "\n".join(value) # reset the markdown instance to clear any state self._md.reset() summary = self._md.convert(summary_values) output[name] = self.process_metadata(name, summary) + elif len(value) > 1: + # handle list metadata as list of string + output[name] = self.process_metadata(name, value) else: + # otherwise, handle metadata as single string output[name] = self.process_metadata(name, value[0]) return output diff --git a/pelican/tests/content/article_with_markdown_and_footnote.md b/pelican/tests/content/article_with_markdown_and_footnote.md index 332ccea6..6fea2d6e 100644 --- a/pelican/tests/content/article_with_markdown_and_footnote.md +++ b/pelican/tests/content/article_with_markdown_and_footnote.md @@ -2,6 +2,12 @@ Title: Article with markdown containing footnotes Date: 2012-10-31 Modified: 2012-11-01 Summary: Summary with **inline** markup *should* be supported. +Multiline: Line Metadata should be handle properly. + See syntax of Meta-Data extension of Python Markdown package: + If a line is indented by 4 or more spaces, + that line is assumed to be an additional line of the value + for the previous keyword. + A keyword may have as many lines as desired. This is some content[^1] with some footnotes[^footnote] diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index d4201a5e..fd30e9b9 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -214,6 +214,14 @@ class MdReaderTest(ReaderTest): 'date': datetime.datetime(2012, 10, 31), 'modified': datetime.datetime(2012, 11, 1), 'slug': 'article-with-markdown-containing-footnotes', + 'multiline': [ + 'Line Metadata should be handle properly.', + 'See syntax of Meta-Data extension of Python Markdown package:', + 'If a line is indented by 4 or more spaces,', + 'that line is assumed to be an additional line of the value', + 'for the previous keyword.', + 'A keyword may have as many lines as desired.', + ] } self.assertEqual(content, expected_content) for key, value in metadata.items(): From c386e29d0c21e17895eef545e1ba0936ccc9c30a Mon Sep 17 00:00:00 2001 From: Lonewolf Date: Sun, 2 Mar 2014 19:21:22 +0530 Subject: [PATCH 0239/1427] Ability to specify PLUGIN_PATH as list PLUGIN_PATH added to settings table --- docs/plugins.rst | 2 +- pelican/__init__.py | 3 ++- pelican/settings.py | 14 +++++++++++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index c03b1251..9dddce70 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -24,7 +24,7 @@ If your plugins are not in an importable path, you can specify a ``PLUGIN_PATH`` in the settings. ``PLUGIN_PATH`` can be an absolute path or a path relative to the settings file:: - PLUGIN_PATH = "plugins" + PLUGIN_PATH = ["list", "of", plugins path"] PLUGINS = ["list", "of", "plugins"] Where to find plugins diff --git a/pelican/__init__.py b/pelican/__init__.py index 1ed98fc3..077859bb 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -65,7 +65,8 @@ class Pelican(object): self.plugins = [] logger.debug('Temporarily adding PLUGIN_PATH to system path') _sys_path = sys.path[:] - sys.path.insert(0, self.settings['PLUGIN_PATH']) + for pluginpath in self.settings['PLUGIN_PATH']: + sys.path.insert(0, pluginpath) for plugin in self.settings['PLUGINS']: # if it's a string, then import it if isinstance(plugin, six.string_types): diff --git a/pelican/settings.py b/pelican/settings.py index 7615c25c..7caffa61 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -112,7 +112,7 @@ DEFAULT_CONFIG = { 'ARTICLE_PERMALINK_STRUCTURE': '', 'TYPOGRIFY': False, 'SUMMARY_MAX_LENGTH': 50, - 'PLUGIN_PATH': '', + 'PLUGIN_PATH': [], 'PLUGINS': [], 'PYGMENTS_RST_OPTIONS': {}, 'TEMPLATE_PAGES': {}, @@ -135,13 +135,21 @@ def read_settings(path=None, override=None): if path: local_settings = get_settings_from_file(path) # Make the paths relative to the settings file - for p in ['PATH', 'OUTPUT_PATH', 'THEME', 'PLUGIN_PATH']: + for p in ['PATH', 'OUTPUT_PATH', 'THEME']: if p in local_settings and local_settings[p] is not None \ and not isabs(local_settings[p]): absp = os.path.abspath(os.path.normpath(os.path.join( os.path.dirname(path), local_settings[p]))) - if p not in ('THEME', 'PLUGIN_PATH') or os.path.exists(absp): + if p not in ('THEME') or os.path.exists(absp): local_settings[p] = absp + + if isinstance(local_settings['PLUGIN_PATH'], six.string_types): + logger.warning("Detected misconfiguration with %s setting ""(must be a list)" % 'PLUGIN_PATH') + local_settings['PLUGIN_PATH'] = [local_settings['PLUGIN_PATH']] + else: + if 'PLUGIN_PATH' in local_settings and local_settings['PLUGIN_PATH'] is not None: + local_settings['PLUGIN_PATH'] = [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_PATH']] else: local_settings = copy.deepcopy(DEFAULT_CONFIG) From f0802e8114b6edf6f239ecfb9ab3cd4db8cc9dfc Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 18 Apr 2014 13:21:06 -0700 Subject: [PATCH 0240/1427] Text tweaks for "PLUGIN_PATH as list" feature --- docs/plugins.rst | 10 +++++----- pelican/settings.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index 9dddce70..16d697fa 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -20,12 +20,12 @@ Alternatively, another method is to import them and add them to the list:: from package import myplugin PLUGINS = [myplugin,] -If your plugins are not in an importable path, you can specify a ``PLUGIN_PATH`` -in the settings. ``PLUGIN_PATH`` can be an absolute path or a path relative to -the settings file:: +If your plugins are not in an importable path, you can specify a list of paths +via the ``PLUGIN_PATH`` setting. As shown in the following example, paths in +the ``PLUGIN_PATH`` list can be absolute or relative to the settings file:: - PLUGIN_PATH = ["list", "of", plugins path"] - PLUGINS = ["list", "of", "plugins"] + PLUGIN_PATH = ["plugins", "/srv/pelican/plugins"] + PLUGINS = ["assets", "liquid_tags", "sitemap"] Where to find plugins ===================== diff --git a/pelican/settings.py b/pelican/settings.py index 7caffa61..ee337386 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -144,7 +144,7 @@ def read_settings(path=None, override=None): local_settings[p] = absp if isinstance(local_settings['PLUGIN_PATH'], six.string_types): - logger.warning("Detected misconfiguration with %s setting ""(must be a list)" % 'PLUGIN_PATH') + logger.warning("Defining %s setting as string has been deprecated (should be a list)" % 'PLUGIN_PATH') local_settings['PLUGIN_PATH'] = [local_settings['PLUGIN_PATH']] else: if 'PLUGIN_PATH' in local_settings and local_settings['PLUGIN_PATH'] is not None: From 260953da02bea53a68545060d354b3ed079fc988 Mon Sep 17 00:00:00 2001 From: Tastalian Date: Mon, 10 Mar 2014 04:16:38 +0100 Subject: [PATCH 0241/1427] Make docutils requirement explicit. Fixes #1243. Previously, the error returned by Python when docutils is not installed was not explicit, instead saying that HTMLTranslator is not defined (needed by FeedGenerator and such), forcing the user to go into readers.py to figure out that this happens because "import docutils" failed. This pull request makes the docutils dependency explicit, so that there is an ImportError if doctutils is not found. --- pelican/readers.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pelican/readers.py b/pelican/readers.py index 35c38220..43749dce 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -6,16 +6,13 @@ import logging import os import re -try: - import docutils - import docutils.core - import docutils.io - from docutils.writers.html4css1 import HTMLTranslator +import docutils +import docutils.core +import docutils.io +from docutils.writers.html4css1 import HTMLTranslator - # import the directives to have pygments support - from pelican import rstdirectives # NOQA -except ImportError: - docutils = False +# import the directives to have pygments support +from pelican import rstdirectives # NOQA try: from markdown import Markdown except ImportError: From e6be02264afc838276923933c676ef69676c320f Mon Sep 17 00:00:00 2001 From: Shauna Date: Sat, 5 Apr 2014 15:27:03 -0400 Subject: [PATCH 0242/1427] Add feeds for each author --- docs/settings.rst | 2 + pelican/generators.py | 12 ++++ pelican/settings.py | 3 + .../basic/feeds/alexis-metaireau.atom.xml | 20 ++++++ .../basic/feeds/alexis-metaireau.rss.xml | 20 ++++++ .../custom/feeds/alexis-metaireau.atom.xml | 61 +++++++++++++++++++ .../custom/feeds/alexis-metaireau.rss.xml | 61 +++++++++++++++++++ 7 files changed, 179 insertions(+) create mode 100644 pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml create mode 100644 pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml create mode 100644 pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml create mode 100644 pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml diff --git a/docs/settings.rst b/docs/settings.rst index 8d8f9a16..0de811ec 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -474,6 +474,8 @@ Setting name (default value) What does it do? language. `CATEGORY_FEED_ATOM` ('feeds/%s.atom.xml'[2]_) Where to put the category Atom feeds. `CATEGORY_FEED_RSS` (``None``, i.e. no RSS) Where to put the category RSS feeds. +`AUTHOR_FEED_ATOM` ('feeds/%s.atom.xml'[2]_) Where to put the author Atom feeds. +`AUTHOR_FEED_RSS` ('feeds/%s.rss.xml'[2]_) Where to put the author RSS feeds. `TAG_FEED_ATOM` (``None``, i.e. no tag feed) Relative URL to output the tag Atom feed. It should be defined using a "%s" match in the tag name. `TAG_FEED_RSS` (``None``, ie no RSS tag feed) Relative URL to output the tag RSS feed diff --git a/pelican/generators.py b/pelican/generators.py index 1b584d3f..a2d7320a 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -239,6 +239,18 @@ class ArticlesGenerator(Generator): self.settings['CATEGORY_FEED_RSS'] % cat.slug, feed_type='rss') + for auth, arts in self.authors: + arts.sort(key=attrgetter('date'), reverse=True) + if self.settings.get('AUTHOR_FEED_ATOM'): + writer.write_feed(arts, self.context, + self.settings['AUTHOR_FEED_ATOM'] + % auth.slug) + + if self.settings.get('AUTHOR_FEED_RSS'): + writer.write_feed(arts, self.context, + self.settings['AUTHOR_FEED_RSS'] + % auth.slug, feed_type='rss') + if (self.settings.get('TAG_FEED_ATOM') or self.settings.get('TAG_FEED_RSS')): for tag, arts in self.tags.items(): diff --git a/pelican/settings.py b/pelican/settings.py index ee337386..1d0ada0c 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -41,6 +41,8 @@ DEFAULT_CONFIG = { 'THEME_STATIC_PATHS': ['static', ], 'FEED_ALL_ATOM': os.path.join('feeds', 'all.atom.xml'), 'CATEGORY_FEED_ATOM': os.path.join('feeds', '%s.atom.xml'), + 'AUTHOR_FEED_ATOM': os.path.join('feeds', '%s.atom.xml'), + 'AUTHOR_FEED_RSS': os.path.join('feeds', '%s.rss.xml'), 'TRANSLATION_FEED_ATOM': os.path.join('feeds', 'all-%s.atom.xml'), 'FEED_MAX_ITEMS': '', 'SITEURL': '', @@ -269,6 +271,7 @@ def configure_settings(settings): 'FEED_ATOM', 'FEED_RSS', 'FEED_ALL_ATOM', 'FEED_ALL_RSS', 'CATEGORY_FEED_ATOM', 'CATEGORY_FEED_RSS', + 'AUTHOR_FEED_ATOM', 'AUTHOR_FEED_RSS', 'TAG_FEED_ATOM', 'TAG_FEED_RSS', 'TRANSLATION_FEED_ATOM', 'TRANSLATION_FEED_RSS', ] diff --git a/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml new file mode 100644 index 00000000..d87023b5 --- /dev/null +++ b/pelican/tests/output/basic/feeds/alexis-metaireau.atom.xml @@ -0,0 +1,20 @@ + +A Pelican Blog/2013-11-17T23:29:00ZThis is a super article !2013-11-17T23:29:00ZAlexis Métaireautag:,2010-12-02:this-is-a-super-article.html<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> +Oh yeah !2010-10-20T10:14:00ZAlexis Métaireautag:,2010-10-20:oh-yeah.html<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> + \ No newline at end of file diff --git a/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml new file mode 100644 index 00000000..09409217 --- /dev/null +++ b/pelican/tests/output/basic/feeds/alexis-metaireau.rss.xml @@ -0,0 +1,20 @@ + +A Pelican Blog/Sun, 17 Nov 2013 23:29:00 -0000This is a super article !/this-is-a-super-article.html<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> +Alexis MétaireauSun, 17 Nov 2013 23:29:00 -0000tag:,2010-12-02:this-is-a-super-article.htmlfoobarfoobarOh yeah !/oh-yeah.html<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> +Alexis MétaireauWed, 20 Oct 2010 10:14:00 -0000tag:,2010-10-20:oh-yeah.htmlohbaryeah \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml new file mode 100644 index 00000000..cb746377 --- /dev/null +++ b/pelican/tests/output/custom/feeds/alexis-metaireau.atom.xml @@ -0,0 +1,61 @@ + +Alexis' loghttp://blog.notmyidea.org/2013-11-17T23:29:00+01:00FILENAME_METADATA example2012-11-30T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-11-30:filename_metadata-example.html<p>Some cool stuff!</p> +Second article2012-02-29T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-02-29:second-article.html<p>This is some article, in english</p> +A markdown powered article2011-04-20T00:00:00+02:00Alexis Métaireautag:blog.notmyidea.org,2011-04-20:a-markdown-powered-article.html<p>You're mutually oblivious.</p> +<p><a href="http://blog.notmyidea.org/unbelievable.html">a root-relative link to unbelievable</a> +<a href="http://blog.notmyidea.org/unbelievable.html">a file-relative link to unbelievable</a></p>Article 12011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:article-1.html<p>Article 1</p> +Article 22011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:article-2.html<p>Article 2</p> +Article 32011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:article-3.html<p>Article 3</p> +This is a super article !2013-11-17T23:29:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-12-02:this-is-a-super-article.html<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> +Oh yeah !2010-10-20T10:14:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-20:oh-yeah.html<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> +Unbelievable !2010-10-15T20:30:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-15:unbelievable.html<p>Or completely awesome. Depends the needs.</p> +<p><a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a root-relative link to markdown-article</a> +<a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> +The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> + \ No newline at end of file diff --git a/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml new file mode 100644 index 00000000..2c4b1160 --- /dev/null +++ b/pelican/tests/output/custom/feeds/alexis-metaireau.rss.xml @@ -0,0 +1,61 @@ + +Alexis' loghttp://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100FILENAME_METADATA examplehttp://blog.notmyidea.org/filename_metadata-example.html<p>Some cool stuff!</p> +Alexis MétaireauFri, 30 Nov 2012 00:00:00 +0100tag:blog.notmyidea.org,2012-11-30:filename_metadata-example.htmlSecond articlehttp://blog.notmyidea.org/second-article.html<p>This is some article, in english</p> +Alexis MétaireauWed, 29 Feb 2012 00:00:00 +0100tag:blog.notmyidea.org,2012-02-29:second-article.htmlfoobarbazA markdown powered articlehttp://blog.notmyidea.org/a-markdown-powered-article.html<p>You're mutually oblivious.</p> +<p><a href="http://blog.notmyidea.org/unbelievable.html">a root-relative link to unbelievable</a> +<a href="http://blog.notmyidea.org/unbelievable.html">a file-relative link to unbelievable</a></p>Alexis MétaireauWed, 20 Apr 2011 00:00:00 +0200tag:blog.notmyidea.org,2011-04-20:a-markdown-powered-article.htmlArticle 1http://blog.notmyidea.org/article-1.html<p>Article 1</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:article-1.htmlArticle 2http://blog.notmyidea.org/article-2.html<p>Article 2</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:article-2.htmlArticle 3http://blog.notmyidea.org/article-3.html<p>Article 3</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:article-3.htmlThis is a super article !http://blog.notmyidea.org/this-is-a-super-article.html<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> +Alexis MétaireauSun, 17 Nov 2013 23:29:00 +0100tag:blog.notmyidea.org,2010-12-02:this-is-a-super-article.htmlfoobarfoobarOh yeah !http://blog.notmyidea.org/oh-yeah.html<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> +Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0200tag:blog.notmyidea.org,2010-10-20:oh-yeah.htmlohbaryeahUnbelievable !http://blog.notmyidea.org/unbelievable.html<p>Or completely awesome. Depends the needs.</p> +<p><a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a root-relative link to markdown-article</a> +<a class="reference external" href="http://blog.notmyidea.org/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> +Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:unbelievable.htmlThe baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> +Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:tag/baz.html \ No newline at end of file From c1324b0206a70b8179689d2305c8de678d5e7b1d Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Sun, 20 Apr 2014 14:34:52 +0200 Subject: [PATCH 0243/1427] split content caching into two layers This is a reworked and improved version of content caching. Notable changes: - by default only raw content and metadata returned by readers are cached which should prevent conficts with plugins, the speed benefit of content objects caching is not very big with a simple setup - renamed --full-rebuild to --ignore-cache - added more elaborate logging to caching code --- README.rst | 1 + docs/index.rst | 1 + docs/settings.rst | 41 +++++++++++++------ pelican/__init__.py | 24 +++++------ pelican/generators.py | 46 ++++++++++++++++----- pelican/readers.py | 20 +++++++-- pelican/settings.py | 10 +++++ pelican/tests/test_generators.py | 67 ++++++++++++++++++++++++++----- pelican/utils.py | 69 ++++++++++++++++++-------------- 9 files changed, 199 insertions(+), 80 deletions(-) diff --git a/README.rst b/README.rst index 20c3f217..bf506c5f 100644 --- a/README.rst +++ b/README.rst @@ -29,6 +29,7 @@ Pelican currently supports: * Code syntax highlighting * Import from WordPress, Dotclear, or RSS feeds * Integration with external tools: Twitter, Google Analytics, etc. (optional) +* Fast rebuild times thanks to content caching and selective output writing. Have a look at the `Pelican documentation`_ for more information. diff --git a/docs/index.rst b/docs/index.rst index 43193e9e..c2deb6de 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -33,6 +33,7 @@ Pelican |version| currently supports: * Code syntax highlighting * Import from WordPress, Dotclear, or RSS feeds * Integration with external tools: Twitter, Google Analytics, etc. (optional) +* Fast rebuild times thanks to content caching and selective output writing. Why the name "Pelican"? ----------------------- diff --git a/docs/settings.rst b/docs/settings.rst index 0de811ec..1b4bae94 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -161,6 +161,7 @@ Setting name (default value) `_ `WITH_FUTURE_DATES` (``True``) If disabled, content with dates in the future will get a default status of ``draft``. + see :ref:`reading_only_modified_content` for details. `INTRASITE_LINK_REGEX` (``'[{|](?P.*?)[|}]'``) Regular expression that is used to parse internal links. Default syntax of links to internal files, tags, etc., is to enclose the identifier, say ``filename``, in ``{}`` or ``||``. @@ -173,12 +174,16 @@ Setting name (default value) `SLUGIFY_SOURCE` (``'input'``) Specifies where you want the slug to be automatically generated from. Can be set to 'title' to use the 'Title:' metadata tag or 'basename' to use the articles basename when creating the slug. -`CACHE_CONTENT` (``True``) If ``True``, save read content in a cache file. +`CACHE_CONTENT` (``True``) If ``True``, save content in a cache file. See :ref:`reading_only_modified_content` for details about caching. +`CONTENT_CACHING_LAYER` (``'reader'``) If set to ``'reader'``, save only the raw content and metadata returned + by readers, if set to ``'generator'``, save processed content objects. `CACHE_DIRECTORY` (``cache``) Directory in which to store cache files. +`GZIP_CACHE` (``True``) If ``True``, use gzip to (de)compress the cache files. `CHECK_MODIFIED_METHOD` (``mtime``) Controls how files are checked for modifications. `LOAD_CONTENT_CACHE` (``True``) If ``True``, load unmodified content from cache. -`GZIP_CACHE` (``True``) If ``True``, use gzip to (de)compress the cache files. +`AUTORELOAD_IGNORE_CACHE` (``False``) If ``True``, do not load content cache in autoreload mode + when the settings file changes. `WRITE_SELECTED` (``[]``) If this list is not empty, **only** output files with their paths in this list are written. Paths should be either relative to the current working directory of Pelican or absolute. For possible use cases see @@ -749,13 +754,21 @@ When Pelican is about to read some content source file: file cannot be found in the cache file, the content is read as usual. -3. If the file is considered unchanged, the content object saved in a +3. If the file is considered unchanged, the content data saved in a previous build corresponding to the file is loaded from the cache and the file is not read. 4. If the file is considered changed, the file is read and the new - modification information and the content object are saved to the + modification information and the content data are saved to the cache if `CACHE_CONTENT` is ``True``. +Depending on `CONTENT_CACHING_LAYER` either the raw content and +metadata returned by a reader are cached if set to ``'reader'``, or +the processed content object is cached if set to ``'generator'``. +Caching the processed content object may conflict with plugins (as +some reading related signals may be skipped) or e.g. the +`WITH_FUTURE_DATES` functionality (as the ``draft`` status of the +cached content objects would not change automatically over time). + Modification time based checking is faster than comparing file hashes, but is not as reliable, because mtime information can be lost when e.g. copying the content sources using the ``cp`` or ``rsync`` @@ -764,16 +777,18 @@ commands without the mtime preservation mode (invoked e.g. by The cache files are Python pickles, so they may not be readable by different versions of Python as the pickle format often changes. If -such an error is encountered, the cache files have to be rebuilt -using the pelican command-line option ``--full-rebuild``. -The cache files also have to be rebuilt when changing the -`GZIP_CACHE` setting for cache file reading to work. +such an error is encountered, the cache files have to be rebuilt by +running pelican after removing them or by using the pelican +command-line option ``--ignore-cache``. The cache files also have to +be rebuilt when changing the `GZIP_CACHE` setting for cache file +reading to work. -The ``--full-rebuild`` command-line option is also useful when the -whole site needs to be regenerated due to e.g. modifications to the -settings file or theme files. When pelican runs in autorealod mode, -modification of the settings file or theme will trigger a full rebuild -automatically. +The ``--ignore-cache`` command-line option is also useful when the +whole cache needs to be regenerated due to e.g. modifications to the +settings file which should change the cached content or just for +debugging purposes. When pelican runs in autoreload mode, modification +of the settings file will make it ignore the cache automatically if +`AUTORELOAD_IGNORE_CACHE` is ``True``. Note that even when using cached content, all output is always written, so the modification times of the ``*.html`` files always diff --git a/pelican/__init__.py b/pelican/__init__.py index 077859bb..8cae468c 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -262,8 +262,9 @@ def parse_arguments(): help='Relaunch pelican each time a modification occurs' ' on the content files.') - parser.add_argument('-f', '--full-rebuild', action='store_true', - dest='full_rebuild', help='Rebuild everything by not loading from cache') + parser.add_argument('-c', '--ignore-cache', action='store_true', + dest='ignore_cache', help='Ignore content cache ' + 'from previous runs by not loading cache files.') parser.add_argument('-w', '--write-selected', type=str, dest='selected_paths', default=None, @@ -284,7 +285,7 @@ def get_config(args): config['THEME'] = abstheme if os.path.exists(abstheme) else args.theme if args.delete_outputdir is not None: config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir - if args.full_rebuild: + if args.ignore_cache: config['LOAD_CONTENT_CACHE'] = False if args.selected_paths: config['WRITE_SELECTED'] = args.selected_paths.split(',') @@ -340,7 +341,10 @@ def main(): print(' --- AutoReload Mode: Monitoring `content`, `theme` and' ' `settings` for changes. ---') - first_run = True # load cache on first run + def _ignore_cache(pelican_obj): + if pelican_obj.settings['AUTORELOAD_IGNORE_CACHE']: + pelican_obj.settings['LOAD_CONTENT_CACHE'] = False + while True: try: # Check source dir for changed files ending with the given @@ -353,10 +357,9 @@ def main(): if modified['settings']: pelican, settings = get_instance(args) - if not first_run: - original_load_cache = settings['LOAD_CONTENT_CACHE'] - # invalidate cache - pelican.settings['LOAD_CONTENT_CACHE'] = False + original_load_cache = settings['LOAD_CONTENT_CACHE'] + print(pelican.settings['AUTORELOAD_IGNORE_CACHE']) + _ignore_cache(pelican) if any(modified.values()): print('\n-> Modified: {}. re-generating...'.format( @@ -368,13 +371,8 @@ def main(): if modified['theme'] is None: logger.warning('Empty theme folder. Using `basic` ' 'theme.') - elif modified['theme']: - # theme modified, needs full rebuild -> no cache - if not first_run: # but not on first run - pelican.settings['LOAD_CONTENT_CACHE'] = False pelican.run() - first_run = False # restore original caching policy pelican.settings['LOAD_CONTENT_CACHE'] = original_load_cache diff --git a/pelican/generators.py b/pelican/generators.py index a2d7320a..3cc84fa8 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -28,10 +28,11 @@ from pelican import signals logger = logging.getLogger(__name__) -class Generator(FileStampDataCacher): +class Generator(object): """Baseclass generator""" - def __init__(self, context, settings, path, theme, output_path, **kwargs): + def __init__(self, context, settings, path, theme, output_path, + readers_cache_name='', **kwargs): self.context = context self.settings = settings self.path = path @@ -41,7 +42,7 @@ class Generator(FileStampDataCacher): for arg, value in kwargs.items(): setattr(self, arg, value) - self.readers = Readers(self.settings) + self.readers = Readers(self.settings, readers_cache_name) # templates cache self._templates = {} @@ -74,10 +75,6 @@ class Generator(FileStampDataCacher): custom_filters = self.settings['JINJA_FILTERS'] self.env.filters.update(custom_filters) - # set up caching - super(Generator, self).__init__(settings, 'CACHE_CONTENT', - 'LOAD_CONTENT_CACHE') - signals.generator_init.send(self) def get_template(self, name): @@ -153,6 +150,35 @@ class Generator(FileStampDataCacher): self.context[item] = value +class CachingGenerator(Generator, FileStampDataCacher): + '''Subclass of Generator and FileStampDataCacher classes + + enables content caching, either at the generator or reader level + ''' + + def __init__(self, *args, **kwargs): + '''Initialize the generator, then set up caching + + note the multiple inheritance structure + ''' + cls_name = self.__class__.__name__ + Generator.__init__(self, *args, + readers_cache_name=(cls_name + '-Readers'), + **kwargs) + + cache_this_level = self.settings['CONTENT_CACHING_LAYER'] == 'generator' + caching_policy = cache_this_level and self.settings['CACHE_CONTENT'] + load_policy = cache_this_level and self.settings['LOAD_CONTENT_CACHE'] + FileStampDataCacher.__init__(self, self.settings, cls_name, + caching_policy, load_policy + ) + + def _get_file_stamp(self, filename): + '''Get filestamp for path relative to generator.path''' + filename = os.path.join(self.path, filename) + return super(Generator, self)._get_file_stamp(filename) + + class _FileLoader(BaseLoader): def __init__(self, path, basedir): @@ -183,7 +209,7 @@ class TemplatePagesGenerator(Generator): del self.env.loader.loaders[0] -class ArticlesGenerator(Generator): +class ArticlesGenerator(CachingGenerator): """Generate blog articles""" def __init__(self, *args, **kwargs): @@ -537,6 +563,7 @@ class ArticlesGenerator(Generator): self._update_context(('articles', 'dates', 'tags', 'categories', 'tag_cloud', 'authors', 'related_posts')) self.save_cache() + self.readers.save_cache() signals.article_generator_finalized.send(self) def generate_output(self, writer): @@ -545,7 +572,7 @@ class ArticlesGenerator(Generator): signals.article_writer_finalized.send(self, writer=writer) -class PagesGenerator(Generator): +class PagesGenerator(CachingGenerator): """Generate pages""" def __init__(self, *args, **kwargs): @@ -599,6 +626,7 @@ class PagesGenerator(Generator): self.context['PAGES'] = self.pages self.save_cache() + self.readers.save_cache() signals.page_generator_finalized.send(self) def generate_output(self, writer): diff --git a/pelican/readers.py b/pelican/readers.py index fa9d92ae..c63b8981 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -33,7 +33,7 @@ except ImportError: from pelican import signals from pelican.contents import Page, Category, Tag, Author -from pelican.utils import get_date, pelican_open +from pelican.utils import get_date, pelican_open, FileStampDataCacher METADATA_PROCESSORS = { @@ -382,7 +382,7 @@ class AsciiDocReader(BaseReader): return content, metadata -class Readers(object): +class Readers(FileStampDataCacher): """Interface for all readers. This class contains a mapping of file extensions / Reader classes, to know @@ -392,7 +392,7 @@ class Readers(object): """ - def __init__(self, settings=None): + def __init__(self, settings=None, cache_name=''): self.settings = settings or {} self.readers = {} self.reader_classes = {} @@ -417,6 +417,15 @@ class Readers(object): self.readers[fmt] = reader_class(self.settings) + # set up caching + cache_this_level = (cache_name != '' and + self.settings['CONTENT_CACHING_LAYER'] == 'reader') + caching_policy = cache_this_level and self.settings['CACHE_CONTENT'] + load_policy = cache_this_level and self.settings['LOAD_CONTENT_CACHE'] + super(Readers, self).__init__(settings, cache_name, + caching_policy, load_policy, + ) + @property def extensions(self): return self.readers.keys() @@ -455,7 +464,10 @@ class Readers(object): source_path=source_path, settings=self.settings, process=reader.process_metadata)) - content, reader_metadata = reader.read(path) + content, reader_metadata = self.get_cached_data(path, (None, None)) + if content is None: + content, reader_metadata = reader.read(path) + self.cache_data(path, (content, reader_metadata)) metadata.update(reader_metadata) if content: diff --git a/pelican/settings.py b/pelican/settings.py index 1d0ada0c..abf16b32 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -123,10 +123,12 @@ DEFAULT_CONFIG = { 'INTRASITE_LINK_REGEX': '[{|](?P.*?)[|}]', 'SLUGIFY_SOURCE': 'title', 'CACHE_CONTENT': True, + 'CONTENT_CACHING_LAYER': 'reader', 'CACHE_DIRECTORY': 'cache', 'GZIP_CACHE': True, 'CHECK_MODIFIED_METHOD': 'mtime', 'LOAD_CONTENT_CACHE': True, + 'AUTORELOAD_IGNORE_CACHE': False, 'WRITE_SELECTED': [], } @@ -266,6 +268,14 @@ def configure_settings(settings): if not 'FEED_DOMAIN' 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") + # Warn if feeds are generated with both SITEURL & FEED_DOMAIN undefined feed_keys = [ 'FEED_ATOM', 'FEED_RSS', diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index f951f0cb..9463047e 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -288,10 +288,11 @@ class TestArticlesGenerator(unittest.TestCase): authors_expected = ['alexis-metaireau', 'first-author', 'second-author'] self.assertEqual(sorted(authors), sorted(authors_expected)) - def test_content_caching(self): - """Test that the articles are read only once when caching""" + def test_article_object_caching(self): + """Test Article objects caching at the generator level""" settings = get_settings(filenames={}) settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CONTENT_CACHING_LAYER'] = 'generator' settings['READERS'] = {'asc': None} generator = ArticlesGenerator( @@ -307,10 +308,32 @@ class TestArticlesGenerator(unittest.TestCase): generator.generate_context() generator.readers.read_file.assert_called_count == 0 - def test_full_rebuild(self): + def test_reader_content_caching(self): + """Test raw content caching at the reader level""" + settings = get_settings(filenames={}) + settings['CACHE_DIRECTORY'] = self.temp_cache + settings['READERS'] = {'asc': None} + + generator = ArticlesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + generator.generate_context() + self.assertTrue(hasattr(generator.readers, '_cache')) + + generator = ArticlesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + readers = generator.readers.readers + for reader in readers.values(): + reader.read = MagicMock() + generator.generate_context() + for reader in readers.values(): + reader.read.assert_called_count == 0 + + def test_ignore_cache(self): """Test that all the articles are read again when not loading cache - used in --full-rebuild or autoreload mode""" + used in --ignore-cache or autoreload mode""" settings = get_settings(filenames={}) settings['CACHE_DIRECTORY'] = self.temp_cache settings['READERS'] = {'asc': None} @@ -376,30 +399,52 @@ class TestPageGenerator(unittest.TestCase): self.assertEqual(sorted(pages_expected), sorted(pages)) self.assertEqual(sorted(hidden_pages_expected), sorted(hidden_pages)) - def test_content_caching(self): - """Test that the pages are read only once when caching""" + def test_page_object_caching(self): + """Test Page objects caching at the generator level""" settings = get_settings(filenames={}) - settings['CACHE_DIRECTORY'] = 'cache_dir' #TODO settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CONTENT_CACHING_LAYER'] = 'generator' settings['READERS'] = {'asc': None} generator = PagesGenerator( context=settings.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) generator.generate_context() self.assertTrue(hasattr(generator, '_cache')) generator = PagesGenerator( context=settings.copy(), settings=settings, - path=CUR_DIR, theme=settings['THEME'], output_path=None) + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) generator.readers.read_file = MagicMock() generator.generate_context() generator.readers.read_file.assert_called_count == 0 - def test_full_rebuild(self): + def test_reader_content_caching(self): + """Test raw content caching at the reader level""" + settings = get_settings(filenames={}) + settings['CACHE_DIRECTORY'] = self.temp_cache + settings['READERS'] = {'asc': None} + + generator = PagesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + generator.generate_context() + self.assertTrue(hasattr(generator.readers, '_cache')) + + generator = PagesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + readers = generator.readers.readers + for reader in readers.values(): + reader.read = MagicMock() + generator.generate_context() + for reader in readers.values(): + reader.read.assert_called_count == 0 + + def test_ignore_cache(self): """Test that all the pages are read again when not loading cache - used in --full-rebuild or autoreload mode""" + used in --ignore_cache or autoreload mode""" settings = get_settings(filenames={}) settings['CACHE_DIRECTORY'] = self.temp_cache settings['READERS'] = {'asc': None} diff --git a/pelican/utils.py b/pelican/utils.py index cda3108e..7b58a231 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -552,28 +552,30 @@ def split_all(path): class FileDataCacher(object): '''Class that can cache data contained in files''' - def __init__(self, settings, cache_policy_key, load_policy_key): - '''Load the specified cache within CACHE_DIRECTORY + def __init__(self, settings, cache_name, caching_policy, load_policy): + '''Load the specified cache within CACHE_DIRECTORY in settings - only if load_policy_key in setttings is True, - May use gzip if GZIP_CACHE. - Sets caching policy according to *cache_policy_key* - in *settings* + only if *load_policy* is True, + May use gzip if GZIP_CACHE ins settings is True. + Sets caching policy according to *caching_policy*. ''' self.settings = settings - name = self.__class__.__name__ - self._cache_path = os.path.join(self.settings['CACHE_DIRECTORY'], name) - self._cache_data_policy = self.settings[cache_policy_key] + self._cache_path = os.path.join(self.settings['CACHE_DIRECTORY'], + cache_name) + self._cache_data_policy = caching_policy if self.settings['GZIP_CACHE']: import gzip self._cache_open = gzip.open else: self._cache_open = open - if self.settings[load_policy_key]: + if load_policy: try: - with self._cache_open(self._cache_path, 'rb') as f: - self._cache = pickle.load(f) - except Exception as e: + with self._cache_open(self._cache_path, 'rb') as fhandle: + self._cache = pickle.load(fhandle) + except (IOError, OSError, pickle.UnpicklingError) as err: + logger.warning(('Cannot load cache {}, ' + 'proceeding with empty cache.\n{}').format( + self._cache_path, err)) self._cache = {} else: self._cache = {} @@ -583,7 +585,7 @@ class FileDataCacher(object): if self._cache_data_policy: self._cache[filename] = data - def get_cached_data(self, filename, default={}): + def get_cached_data(self, filename, default=None): '''Get cached data for the given file if no data is cached, return the default object @@ -595,20 +597,23 @@ class FileDataCacher(object): if self._cache_data_policy: try: mkdir_p(self.settings['CACHE_DIRECTORY']) - with self._cache_open(self._cache_path, 'wb') as f: - pickle.dump(self._cache, f) - except Exception as e: + with self._cache_open(self._cache_path, 'wb') as fhandle: + pickle.dump(self._cache, fhandle) + except (IOError, OSError, pickle.PicklingError) as err: logger.warning('Could not save cache {}\n{}'.format( - self._cache_path, e)) + self._cache_path, err)) class FileStampDataCacher(FileDataCacher): '''Subclass that also caches the stamp of the file''' - def __init__(self, settings, cache_policy_key, load_policy_key): - '''This sublcass additionaly sets filestamp function''' - super(FileStampDataCacher, self).__init__(settings, cache_policy_key, - load_policy_key) + def __init__(self, settings, cache_name, caching_policy, load_policy): + '''This sublcass additionaly sets filestamp function + and base path for filestamping operations + ''' + super(FileStampDataCacher, self).__init__(settings, cache_name, + caching_policy, + load_policy) method = self.settings['CHECK_MODIFIED_METHOD'] if method == 'mtime': @@ -616,10 +621,14 @@ class FileStampDataCacher(FileDataCacher): else: try: hash_func = getattr(hashlib, method) - def filestamp_func(buf): - return hash_func(buf).digest() + def filestamp_func(filename): + '''return hash of file contents''' + with open(filename, 'rb') as fhandle: + return hash_func(fhandle.read()).digest() self._filestamp_func = filestamp_func - except ImportError: + except AttributeError as err: + logger.warning('Could not get hashing function\n{}'.format( + err)) self._filestamp_func = None def cache_data(self, filename, data): @@ -636,11 +645,11 @@ class FileStampDataCacher(FileDataCacher): a hash for a function name in the hashlib module or an empty bytes string otherwise ''' - filename = os.path.join(self.path, filename) try: - with open(filename, 'rb') as f: - return self._filestamp_func(f.read()) - except Exception: + return self._filestamp_func(filename) + except (IOError, OSError, TypeError) as err: + logger.warning('Cannot get modification stamp for {}\n{}'.format( + filename, err)) return b'' def get_cached_data(self, filename, default=None): @@ -648,7 +657,7 @@ class FileStampDataCacher(FileDataCacher): if the file has not been modified. If no record exists or file has been modified, return default. - Modification is checked by compaing the cached + Modification is checked by comparing the cached and current file stamp. ''' stamp, data = super(FileStampDataCacher, self).get_cached_data( From 30e2cac7539630ba7ad69c548f3551e5a2390721 Mon Sep 17 00:00:00 2001 From: Bernhard Scheirle Date: Thu, 24 Apr 2014 15:30:34 +0200 Subject: [PATCH 0244/1427] send the static_generator_{init, finalized} signals. Note: The two signals were already present but were never sent. --- docs/plugins.rst | 8 ++++++++ pelican/generators.py | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/docs/plugins.rst b/docs/plugins.rst index c03b1251..4e0bdb26 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -87,8 +87,16 @@ get_generators generators invoked in Pe can return a Generator, or several generator in a tuple or in a list. page_generator_context page_generator, metadata +page_generator_preread page_generator invoked before a page is read in PageGenerator.generate_context; + use if code needs to do something before every page is parsed. page_generator_init page_generator invoked in the PagesGenerator.__init__ page_generator_finalized page_generator invoked at the end of PagesGenerator.generate_context +static_generator_context static_generator, metadata +static_generator_preread static_generator invoked before a static file is read in StaticGenerator.generate_context; + use if code needs to do something before every static file is added to the + staticfiles list. +static_generator_init static_generator invoked in the StaticGenerator.__init__ +static_generator_finalized static_generator invoked at the end of StaticGenerator.generate_context content_object_init content_object invoked at the end of Content.__init__ (see note below) content_written path, context invoked each time a content file is written. ================================= ============================ =========================================================================== diff --git a/pelican/generators.py b/pelican/generators.py index bfdac1a5..01f7db8f 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -576,6 +576,10 @@ class StaticGenerator(Generator): """copy static paths (what you want to copy, like images, medias etc. to output""" + def __init__(self, *args, **kwargs): + super(StaticGenerator, self).__init__(*args, **kwargs) + signals.static_generator_init.send(self) + def _copy_paths(self, paths, source, destination, output_path, final_path=None): """Copy all the paths from source to destination""" @@ -604,6 +608,7 @@ class StaticGenerator(Generator): self.staticfiles.append(static) self.add_source_path(static) self._update_context(('staticfiles',)) + signals.static_generator_finalized.send(self) def generate_output(self, writer): self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme, From ad6dc3f8ba6c63820869cdf7fa34110ad0fe0e5d Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Fri, 25 Apr 2014 19:44:26 +0200 Subject: [PATCH 0245/1427] use correct CachingGenerator class in super() call This was a leftover from code moving in c1324b0. Detected by pylint. --- pelican/generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/generators.py b/pelican/generators.py index 3cc84fa8..45447183 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -176,7 +176,7 @@ class CachingGenerator(Generator, FileStampDataCacher): def _get_file_stamp(self, filename): '''Get filestamp for path relative to generator.path''' filename = os.path.join(self.path, filename) - return super(Generator, self)._get_file_stamp(filename) + return super(CachingGenerator, self)._get_file_stamp(filename) class _FileLoader(BaseLoader): From 5a3daae72f78334e9994d527fc09452de75b3474 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Sun, 27 Apr 2014 14:25:08 +0200 Subject: [PATCH 0246/1427] add get_writer signal and unify with get_generators Fix outdated docs of get_generators to unify. --- docs/plugins.rst | 10 ++++++---- pelican/__init__.py | 15 ++++++++++++++- pelican/signals.py | 1 + 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index 7fe497e6..717019a8 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -83,9 +83,11 @@ article_generator_finalized article_generator invoked at th article_generator_write_article article_generator, content invoked before writing each article, the article is passed as content article_writer_finalized article_generator, writer invoked after all articles and related pages have been written, but before the article generator is closed. -get_generators generators invoked in Pelican.get_generator_classes, +get_generators pelican object invoked in Pelican.get_generator_classes, can return a Generator, or several - generator in a tuple or in a list. + generators in a tuple or in a list. +get_writer pelican object invoked in Pelican.get_writer, + can return a custom Writer. page_generator_context page_generator, metadata page_generator_preread page_generator invoked before a page is read in PageGenerator.generate_context; use if code needs to do something before every page is parsed. @@ -200,8 +202,8 @@ Adding a new generator is also really easy. You might want to have a look at :: - def get_generators(generators): + def get_generators(pelican_object): # define a new generator here if you need to - return generators + return MyGenerator signals.get_generators.connect(get_generators) diff --git a/pelican/__init__.py b/pelican/__init__.py index 8cae468c..5208c317 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -209,7 +209,20 @@ class Pelican(object): return generators def get_writer(self): - return Writer(self.output_path, settings=self.settings) + 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) + else: + _, writer = writers[0] + if writers_found == 1: + logger.debug('Found writer: {}'.format(writer)) + else: + logger.warning( + '{} writers found, using only first one: {}'.format( + writers_found, writer)) + return writer(self.output_path, settings=self.settings) def parse_arguments(): diff --git a/pelican/signals.py b/pelican/signals.py index 9eb907dc..e06b89ac 100644 --- a/pelican/signals.py +++ b/pelican/signals.py @@ -6,6 +6,7 @@ from blinker import signal initialized = signal('pelican_initialized') get_generators = signal('get_generators') +get_writer = signal('get_writer') finalized = signal('pelican_finalized') # Reader-level signals From 00a5a0f0dfed1e9f65101e81a72ca37c12b6faa8 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Mon, 28 Apr 2014 20:37:49 +0200 Subject: [PATCH 0247/1427] Fix #1311 wide tables in RTD theme, remove old theme files This works by adding a CSS overrides file to the Sphinx app stylesheets. --- docs/_static/pelican.gif | Bin 16941 -> 0 bytes docs/_static/pelican.png | Bin 6441 -> 0 bytes docs/_static/theme_overrides.css | 12 + docs/_themes/.gitignore | 3 - docs/_themes/pelican/layout.html | 22 -- docs/_themes/pelican/static/pelican.css_t | 254 ---------------------- docs/_themes/pelican/theme.conf | 10 - docs/conf.py | 6 + 8 files changed, 18 insertions(+), 289 deletions(-) delete mode 100644 docs/_static/pelican.gif delete mode 100644 docs/_static/pelican.png create mode 100644 docs/_static/theme_overrides.css delete mode 100644 docs/_themes/.gitignore delete mode 100644 docs/_themes/pelican/layout.html delete mode 100644 docs/_themes/pelican/static/pelican.css_t delete mode 100644 docs/_themes/pelican/theme.conf diff --git a/docs/_static/pelican.gif b/docs/_static/pelican.gif deleted file mode 100644 index d9208590b7cce4c7f9284dfc4686b940eb1912bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16941 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%0DPy!XW zK`zk!N_@kizdbUkd`j%YEx a=5G={=Xbv6e?I7kzUYrW>Hkqc0028+la#Cg diff --git a/docs/_static/pelican.png b/docs/_static/pelican.png deleted file mode 100644 index c2d61c88997a3b853a1afec2aa2e2ce84102fbf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6441 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 j4}To;|DHdOxViP7=V+AmQfdJFje+x2O - {% endif %} -{% endblock %} -{% block footer %} - {% if pagename == 'index' %} - - {% endif %} -{% endblock %} -{# do not display relbars #} -{% block relbar1 %}{% endblock %} -{% block relbar2 %} - {% if theme_github_fork %} - Fork me on GitHub - {% endif %} -{% endblock %} -{% block sidebar1 %}{% endblock %} -{% block sidebar2 %}{% endblock %} diff --git a/docs/_themes/pelican/static/pelican.css_t b/docs/_themes/pelican/static/pelican.css_t deleted file mode 100644 index 3cb2a3c1..00000000 --- a/docs/_themes/pelican/static/pelican.css_t +++ /dev/null @@ -1,254 +0,0 @@ -/* - * pelican.css_t - * ~~~~~~~~~~~~ - * - * Sphinx stylesheet -- pelican theme, based on the nature theme - * - * :copyright: Copyright 2011 by Alexis Metaireau. - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: Arial, sans-serif; - font-size: 100%; - background-color: white; - color: #555; - margin: 0; - padding: 0; -} - -div.documentwrapper { - width: 70%; - margin: auto; -} - -div.bodywrapper { - margin: 0 0 0 230px; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.document { -} - -div.body { - background-color: #ffffff; - color: #3E4349; - padding: 0 30px 30px 30px; - font-size: 0.9em; -} - -div.footer { - color: #555; - width: 100%; - padding: 13px 0; - text-align: center; - font-size: 75%; -} - -div.footer a { - color: #444; - text-decoration: underline; -} - -div.related { - background-color: #6BA81E; - line-height: 32px; - color: #fff; - text-shadow: 0px 1px 0 #444; - font-size: 0.9em; -} - -div.related a { - color: #E2F3CC; -} - -div.sphinxsidebar { - font-size: 0.75em; - line-height: 1.5em; -} - -div.sphinxsidebarwrapper{ - padding: 20px 0; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: Arial, sans-serif; - color: #222; - font-size: 1.2em; - font-weight: normal; - margin: 0; - padding: 5px 10px; - background-color: #ddd; - text-shadow: 1px 1px 0 white -} - -div.sphinxsidebar h4{ - font-size: 1.1em; -} - -div.sphinxsidebar h3 a { - color: #444; -} - - -div.sphinxsidebar p { - color: #888; - padding: 5px 20px; -} - -div.sphinxsidebar p.topless { -} - -div.sphinxsidebar ul { - margin: 10px 20px; - padding: 0; - color: #000; -} - -div.sphinxsidebar a { - color: #444; -} - -div.sphinxsidebar input { - border: 1px solid #ccc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar input[type=text]{ - margin-left: 20px; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #005B81; - text-decoration: none; -} - -a:hover { - color: #E32E00; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: Arial, sans-serif; - font-weight: normal; - color: #212224; - margin: 30px 0px 10px 0px; - padding: 5px 0 5px 10px; - text-shadow: 0px 1px 0 white -} - -{% if theme_index_logo %} -div.indexwrapper h1 { - text-indent: -999999px; - background: url({{ theme_index_logo }}) no-repeat center center; - height: {{ theme_index_logo_height }}; -} -{% endif %} -div.body h1 { - border-top: 20px solid white; - margin-top: 0; - font-size: 250%; - text-align: center; -} - -div.body h2 { font-size: 150%; background-color: #C8D5E3; } -div.body h3 { font-size: 120%; background-color: #D8DEE3; } -div.body h4 { font-size: 110%; background-color: #D8DEE3; } -div.body h5 { font-size: 100%; background-color: #D8DEE3; } -div.body h6 { font-size: 100%; background-color: #D8DEE3; } - -a.headerlink { - color: #c60f0f; - font-size: 0.8em; - padding: 0 4px 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - background-color: #c60f0f; - color: white; -} - -div.body p, div.body dd, div.body li { - line-height: 1.5em; -} - -div.admonition p.admonition-title + p { - display: inline; -} - -div.highlight{ - background-color: #111; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre { - padding: 10px; - background-color: #111; - color: #fff; - line-height: 1.2em; - border: 1px solid #C6C9CB; - font-size: 1.1em; - margin: 1.5em 0 1.5em 0; - -webkit-box-shadow: 1px 1px 1px #d8d8d8; - -moz-box-shadow: 1px 1px 1px #d8d8d8; -} - -tt { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ - font-size: 1.1em; - font-family: monospace; -} - -.viewcode-back { - font-family: Arial, sans-serif; -} - -div.viewcode-block:target { - background-color: #f4debf; - border-top: 1px solid #ac9; - border-bottom: 1px solid #ac9; -} diff --git a/docs/_themes/pelican/theme.conf b/docs/_themes/pelican/theme.conf deleted file mode 100644 index ffbb7945..00000000 --- a/docs/_themes/pelican/theme.conf +++ /dev/null @@ -1,10 +0,0 @@ -[theme] -inherit = basic -stylesheet = pelican.css -nosidebar = true -pygments_style = fruity - -[options] -index_logo_height = 120px -index_logo = -github_fork = diff --git a/docs/conf.py b/docs/conf.py index 99acd1b6..d4efcb38 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -57,6 +57,12 @@ html_use_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False + +def setup(app): + # overrides for wide tables in RTD theme + app.add_stylesheet('theme_overrides.css') # path relative to _static + + # -- Options for LaTeX output -------------------------------------------------- latex_documents = [ ('index', 'Pelican.tex', 'Pelican Documentation', From 46c865f295d9bbb1230e763ad5ed69777a477b43 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Mon, 28 Apr 2014 21:39:20 +0200 Subject: [PATCH 0248/1427] Really fix #1311 by declaring CSS overrides as !important this is needed because on RTD the common hosted theme stylesheets get added after the overrides. --- docs/_static/theme_overrides.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/_static/theme_overrides.css b/docs/_static/theme_overrides.css index d4b31645..83afc78e 100644 --- a/docs/_static/theme_overrides.css +++ b/docs/_static/theme_overrides.css @@ -1,12 +1,12 @@ /* override table width restrictions */ .wy-table-responsive table td, .wy-table-responsive table th { - white-space: normal; + /* !important prevents the common CSS stylesheets from + overriding this as on RTD they are loaded after this stylesheet */ + white-space: normal !important; } .wy-table-responsive { - margin-bottom: 24px; - max-width: 100%; - overflow: visible; + overflow: visible !important; } From b0e388747c5f5667dc38030f6b20fada49627e61 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Mon, 28 Apr 2014 21:47:18 +0200 Subject: [PATCH 0249/1427] Fix #1277 use rsync -c option as all output is rewritten Because Pelican always rewrites all output, the mtimes always change, so rsync would always transfer all the files which defeats the purpose of rsync. The '-c' option (--checksum) compares the checksums rather than mtimes. --- pelican/tools/templates/Makefile.in | 2 +- pelican/tools/templates/fabfile.py.in | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in index c542e588..6bfa7b0c 100644 --- a/pelican/tools/templates/Makefile.in +++ b/pelican/tools/templates/Makefile.in @@ -88,7 +88,7 @@ ssh_upload: publish scp -P $$(SSH_PORT) -r $$(OUTPUTDIR)/* $$(SSH_USER)@$$(SSH_HOST):$$(SSH_TARGET_DIR) rsync_upload: publish - rsync -e "ssh -p $(SSH_PORT)" -P -rvz --delete $(OUTPUTDIR)/ $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) --cvs-exclude + rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --delete $(OUTPUTDIR)/ $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) --cvs-exclude dropbox_upload: publish cp -r $$(OUTPUTDIR)/* $$(DROPBOX_DIR) diff --git a/pelican/tools/templates/fabfile.py.in b/pelican/tools/templates/fabfile.py.in index fb56ae85..fbd03e2c 100644 --- a/pelican/tools/templates/fabfile.py.in +++ b/pelican/tools/templates/fabfile.py.in @@ -68,5 +68,6 @@ def publish(): remote_dir=dest_path, exclude=".DS_Store", local_dir=DEPLOY_PATH.rstrip('/') + '/', - delete=True + delete=True, + extra_opts='-c', ) From 3d53b4ca17d298e7147403d176c566eec9d30d79 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 30 Apr 2014 06:44:55 -0700 Subject: [PATCH 0250/1427] Add Python 3.4 to Travic CI configuration --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 93a7ab54..41ad82b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: python python: - "2.7" - "3.3" + - "3.4" before_install: - sudo apt-get update -qq - sudo apt-get install -qq --no-install-recommends asciidoc From 81cd781c455f9e1e3e09dfc4ccfdac5f96e60742 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 30 Apr 2014 12:35:13 -0700 Subject: [PATCH 0251/1427] Show setting defaults as actual code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some reason, setting names on the Settings page have long been wrapped in single back-ticks (usually meant for linking in reST) instead of double back-ticks (meant for denoting code). This seems to be widespread throughout the docs, and it's not clear if this is intentional or simply a reST formatting error that got propagated by others in order to stay consistent. This commit applies double back-ticks in any case where something resembling code is shown, with the idea that single back-ticks should only be used when linking. More importantly, the settings denoted their default values in parentheses, which hapless users often included when copying and pasting these values into their config files. As one can imagine, confusion — not hilarity — ensued. Setting defaults are now shown as they would actually appear in one's settings file, with an equal sign and without parentheses. During this spelunking expedition, many other minor improvements were concurrently conducted. --- docs/settings.rst | 422 +++++++++++++++++++++++----------------------- 1 file changed, 210 insertions(+), 212 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 1b4bae94..c9eddfba 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -6,8 +6,8 @@ the command line:: $ pelican content -s path/to/your/settingsfile.py -(If you used the `pelican-quickstart` command, your primary settings file will -be named `pelicanconf.py` by default.) +(If you used the ``pelican-quickstart`` command, your primary settings file will +be named ``pelicanconf.py`` by default.) Settings are configured in the form of a Python module (a file). There is an `example settings file @@ -32,33 +32,33 @@ Basic settings ============== =============================================================================== ===================================================================== -Setting name (default value) What does it do? +Setting name (followed by default value, if any) What does it do? =============================================================================== ===================================================================== -`AUTHOR` Default author (put your name) -`DATE_FORMATS` (``{}``) If you manage multiple languages, you can set the date formatting +``AUTHOR`` Default author (put your name) +``DATE_FORMATS = {}`` If you manage multiple languages, you can set the date formatting here. See the "Date format and locale" section below for details. -`USE_FOLDER_AS_CATEGORY` (``True``) When you don't specify a category in your post metadata, set this +``USE_FOLDER_AS_CATEGORY = True`` When you don't specify a category in your post metadata, set this setting to ``True``, and organize your articles in subfolders, the subfolder will become the category of your post. If set to ``False``, ``DEFAULT_CATEGORY`` will be used as a fallback. -`DEFAULT_CATEGORY` (``'misc'``) The default category to fall back on. -`DEFAULT_DATE_FORMAT` (``'%a %d %B %Y'``) The default date format you want to use. -`DISPLAY_PAGES_ON_MENU` (``True``) Whether to display pages on the menu of the +``DEFAULT_CATEGORY = 'misc'`` The default category to fall back on. +``DEFAULT_DATE_FORMAT = '%a %d %B %Y'`` The default date format you want to use. +``DISPLAY_PAGES_ON_MENU = True`` Whether to display pages on the menu of the template. Templates may or may not honor this setting. -`DISPLAY_CATEGORIES_ON_MENU` (``True``) Whether to display categories on the menu of the +``DISPLAY_CATEGORIES_ON_MENU = True`` Whether to display categories on the menu of the template. Templates may or not honor this setting. -`DEFAULT_DATE` (``None``) The default date you want to use. +``DEFAULT_DATE = None`` The default date you want to use. If ``fs``, Pelican will use the file system timestamp information (mtime) if it can't get date information from the metadata. If set to a tuple object, the default datetime object will instead be generated by passing the tuple to the ``datetime.datetime`` constructor. -`DEFAULT_METADATA` (``()``) The default metadata you want to use for all articles +``DEFAULT_METADATA = ()`` The default metadata you want to use for all articles and pages. -`FILENAME_METADATA` (``'(?P\d{4}-\d{2}-\d{2}).*'``) The regexp that will be used to extract any metadata +``FILENAME_METADATA =`` ``'(?P\d{4}-\d{2}-\d{2}).*'`` The regexp that will be used to extract any metadata from the filename. All named groups that are matched will be set in the metadata object. The default value will only extract the date from @@ -67,38 +67,38 @@ Setting name (default value) date and the slug, you could set something like: ``'(?P\d{4}-\d{2}-\d{2})_(?P.*)'``. See :ref:`path_metadata`. -`PATH_METADATA` (``''``) Like ``FILENAME_METADATA``, but parsed from a page's +``PATH_METADATA = ''`` Like ``FILENAME_METADATA``, but parsed from a page's full path relative to the content source directory. See :ref:`path_metadata`. -`EXTRA_PATH_METADATA` (``{}``) Extra metadata dictionaries keyed by relative path. +``EXTRA_PATH_METADATA = {}`` Extra metadata dictionaries keyed by relative path. See :ref:`path_metadata`. -`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the output directory, and **all** of its contents, before +``DELETE_OUTPUT_DIRECTORY = False`` Delete the output directory, and **all** of its contents, before generating new files. This can be useful in preventing older, unnecessary files from persisting in your output. However, **this is a destructive setting and should be handled with extreme care.** -`OUTPUT_RETENTION` (``()``) A tuple of filenames that should be retained and not deleted from the +``OUTPUT_RETENTION = ()`` A tuple of filenames that should be retained and not deleted from the output directory. One use case would be the preservation of version control data. For example: ``(".hg", ".git", ".bzr")`` -`JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use. -`JINJA_FILTERS` (``{}``) A list of custom Jinja2 filters you want to use. +``JINJA_EXTENSIONS = []`` A list of any Jinja2 extensions you want to use. +``JINJA_FILTERS = {}`` A list of custom Jinja2 filters you want to use. The dictionary should map the filtername to the filter function. For example: ``{'urlencode': urlencode_filter}`` See `Jinja custom filters documentation`_. -`LOCALE` (''[#]_) Change the locale. A list of locales can be provided +``LOCALE`` [#]_ Change the locale. A list of locales can be provided here or a single string representing one locale. When providing a list, all the locales will be tried until one works. -`LOG_FILTER` (``[]``) A list of tuples containing the logging level (up to ``warning``) +``LOG_FILTER = []`` A list of tuples containing the logging level (up to ``warning``) and the message to be ignored. For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]`` -`READERS` (``{}``) A dictionary of file extensions / Reader classes for Pelican to +``READERS = {}`` A dictionary of file extensions / Reader classes for Pelican to process or ignore. For example, to avoid processing .html files, set: ``READERS = {'html': None}``. To add a custom reader for the `foo` extension, set: ``READERS = {'foo': FooReader}`` -`IGNORE_FILES` (``['.#*']``) A list of file globbing patterns to match against the +``IGNORE_FILES = ['.#*']`` A list of file globbing patterns to match against the source files to be ignored by the processor. For example, the default ``['.#*']`` will ignore emacs lock files. -`MD_EXTENSIONS` (``['codehilite(css_class=highlight)','extra']``) A list of the extensions that the Markdown processor +``MD_EXTENSIONS =`` ``['codehilite(css_class=highlight)','extra']`` A list of the extensions that the Markdown processor will use. Refer to the Python Markdown documentation's `Extensions section `_ for a complete list of supported extensions. (Note that @@ -107,87 +107,87 @@ Setting name (default value) to the default values for this setting, you'll need to include them explicitly and enumerate the full list of desired Markdown extensions.) -`OUTPUT_PATH` (``'output/'``) Where to output the generated files. -`PATH` (``None``) Path to content directory to be processed by Pelican. -`PAGE_DIR` (``'pages'``) Directory to look at for pages, relative to `PATH`. -`PAGE_EXCLUDES` (``()``) A list of directories to exclude when looking for pages. -`ARTICLE_DIR` (``''``) Directory to look at for articles, relative to `PATH`. -`ARTICLE_EXCLUDES`: (``('pages',)``) A list of directories to exclude when looking for articles. -`OUTPUT_SOURCES` (``False``) Set to True if you want to copy the articles and pages in their +``OUTPUT_PATH = 'output/'`` Where to output the generated files. +``PATH = None`` Path to content directory to be processed by Pelican. +``PAGE_DIR = 'pages'`` Directory to look at for pages, relative to `PATH`. +``PAGE_EXCLUDES = ()`` A list of directories to exclude when looking for pages. +``ARTICLE_DIR = ''`` Directory to look at for articles, relative to `PATH`. +``ARTICLE_EXCLUDES` = ('pages',)`` A list of directories to exclude when looking for articles. +``OUTPUT_SOURCES = False`` Set to True if you want to copy the articles and pages in their original format (e.g. Markdown or reStructuredText) to the specified ``OUTPUT_PATH``. -`OUTPUT_SOURCES_EXTENSION` (``.text``) Controls the extension that will be used by the SourcesGenerator. +``OUTPUT_SOURCES_EXTENSION = '.text'`` Controls the extension that will be used by the SourcesGenerator. Defaults to ``.text``. If not a valid string the default value will be used. -`RELATIVE_URLS` (``False``) Defines whether Pelican should use document-relative URLs or +``RELATIVE_URLS = False`` Defines whether Pelican should use document-relative URLs or not. Only set this to ``True`` when developing/testing and only if you fully understand the effect it can have on links/feeds. -`PLUGINS` (``[]``) The list of plugins to load. See :ref:`plugins`. -`SITENAME` (``'A Pelican Blog'``) Your site name -`SITEURL` Base URL of your website. Not defined by default, +``PLUGINS = []`` The list of plugins to load. See :ref:`plugins`. +``SITENAME = 'A Pelican Blog'`` Your site name +``SITEURL`` Base URL of your website. Not defined by default, so it is best to specify your SITEURL; if you do not, feeds will not be generated with properly-formed URLs. You should include ``http://`` and your domain, with no trailing slash at the end. Example: ``SITEURL = 'http://mydomain.com'`` -`TEMPLATE_PAGES` (``None``) A mapping containing template pages that will be rendered with +``TEMPLATE_PAGES = None`` A mapping containing template pages that will be rendered with the blog entries. See :ref:`template_pages`. -`STATIC_PATHS` (``['images']``) The static paths you want to have accessible +``STATIC_PATHS = ['images']`` The static paths you want to have accessible on the output path "static". By default, Pelican will copy the "images" folder to the output folder. -`TIMEZONE` The timezone used in the date information, to +``TIMEZONE`` The timezone used in the date information, to generate Atom and RSS feeds. See the *Timezone* section below for more info. -`TYPOGRIFY` (``False``) If set to True, several typographical improvements will be +``TYPOGRIFY = False`` If set to True, several typographical improvements will be incorporated into the generated HTML via the `Typogrify `_ library, which can be installed via: ``pip install typogrify`` -`DIRECT_TEMPLATES` (``('index', 'tags', 'categories', 'authors', 'archives')``) List of templates that are used directly to render +``DIRECT_TEMPLATES =`` ``('index', 'categories', 'authors', 'archives')`` List of templates that are used directly to render content. Typically direct templates are used to generate index pages for collections of content (e.g., tags and category index pages). If the tag and category collections are not needed, set ``DIRECT_TEMPLATES = ('index', 'archives')`` -`PAGINATED_DIRECT_TEMPLATES` (``('index',)``) Provides the direct templates that should be paginated. -`SUMMARY_MAX_LENGTH` (``50``) When creating a short summary of an article, this will +``PAGINATED_DIRECT_TEMPLATES = ('index',)`` Provides the direct templates that should be paginated. +``SUMMARY_MAX_LENGTH = 50`` When creating a short summary of an article, this will be the default length (measured in words) of the text created. This only applies if your content does not otherwise specify a summary. Setting to ``None`` will cause the summary to be a copy of the original content. -`EXTRA_TEMPLATES_PATHS` (``[]``) A list of paths you want Jinja2 to search for templates. +``EXTRA_TEMPLATES_PATHS = []`` A list of paths you want Jinja2 to search for templates. Can be used to separate templates from the theme. Example: projects, resume, profile ... These templates need to use ``DIRECT_TEMPLATES`` setting. -`ASCIIDOC_OPTIONS` (``[]``) A list of options to pass to AsciiDoc. See the `manpage - `_ -`WITH_FUTURE_DATES` (``True``) If disabled, content with dates in the future will get a - default status of ``draft``. - see :ref:`reading_only_modified_content` for details. -`INTRASITE_LINK_REGEX` (``'[{|](?P.*?)[|}]'``) Regular expression that is used to parse internal links. - Default syntax of links to internal files, tags, etc., is - to enclose the identifier, say ``filename``, in ``{}`` or ``||``. - Identifier between ``{`` and ``}`` goes into the ``what`` capturing group. +``ASCIIDOC_OPTIONS = []`` A list of options to pass to AsciiDoc. See the `manpage + `_. +``WITH_FUTURE_DATES = True`` If disabled, content with dates in the future will get a default + status of ``draft``. See :ref:`reading_only_modified_content` + for caveats. +``INTRASITE_LINK_REGEX = '[{|](?P.*?)[|}]'`` Regular expression that is used to parse internal links. Default + syntax when linking to internal files, tags, etc., is to enclose + the identifier, say ``filename``, in ``{}`` or ``||``. Identifier + between ``{`` and ``}`` goes into the ``what`` capturing group. For details see :ref:`ref-linking-to-internal-content`. -`PYGMENTS_RST_OPTIONS` (``[]``) A list of default Pygments settings for your reStructuredText +``PYGMENTS_RST_OPTIONS = []`` A list of default Pygments settings for your reStructuredText code blocks. See :ref:`internal_pygments_options` for a list of supported options. - -`SLUGIFY_SOURCE` (``'input'``) Specifies where you want the slug to be automatically generated - from. Can be set to 'title' to use the 'Title:' metadata tag or - 'basename' to use the articles basename when creating the slug. -`CACHE_CONTENT` (``True``) If ``True``, save content in a cache file. +``SLUGIFY_SOURCE = 'input'`` Specifies where you want the slug to be automatically generated + from. Can be set to ``title`` to use the 'Title:' metadata tag or + ``basename`` to use the article's basename when creating the slug. +``CACHE_CONTENT = True`` If ``True``, save content in a cache file. See :ref:`reading_only_modified_content` for details about caching. -`CONTENT_CACHING_LAYER` (``'reader'``) If set to ``'reader'``, save only the raw content and metadata returned - by readers, if set to ``'generator'``, save processed content objects. -`CACHE_DIRECTORY` (``cache``) Directory in which to store cache files. -`GZIP_CACHE` (``True``) If ``True``, use gzip to (de)compress the cache files. -`CHECK_MODIFIED_METHOD` (``mtime``) Controls how files are checked for modifications. -`LOAD_CONTENT_CACHE` (``True``) If ``True``, load unmodified content from cache. -`AUTORELOAD_IGNORE_CACHE` (``False``) If ``True``, do not load content cache in autoreload mode +``CONTENT_CACHING_LAYER = 'reader'`` If set to ``'reader'``, save only the raw content and metadata + returned by readers. If set to ``'generator'``, save processed + content objects. +``CACHE_DIRECTORY = 'cache'`` Directory in which to store cache files. +``GZIP_CACHE = True`` If ``True``, use gzip to (de)compress the cache files. +``CHECK_MODIFIED_METHOD = 'mtime'`` Controls how files are checked for modifications. +``LOAD_CONTENT_CACHE = True`` If ``True``, load unmodified content from cache. +``AUTORELOAD_IGNORE_CACHE = False`` If ``True``, do not load content cache in autoreload mode when the settings file changes. -`WRITE_SELECTED` (``[]``) If this list is not empty, **only** output files with their paths - in this list are written. Paths should be either relative to the current - working directory of Pelican or absolute. For possible use cases see - :ref:`writing_only_selected_content`. +``WRITE_SELECTED = []`` If this list is not empty, **only** output files with their paths + in this list are written. Paths should be either absolute or relative + to the current Pelican working directory. For possible use cases see + :ref:`writing_only_selected_content`. =============================================================================== ===================================================================== .. [#] Default is the system locale. @@ -231,8 +231,8 @@ Also, you can use other file metadata attributes as well: Example usage: -* ARTICLE_URL = ``'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/'`` -* ARTICLE_SAVE_AS = ``'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/index.html'`` +* ``ARTICLE_URL = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/'`` +* ``ARTICLE_SAVE_AS = 'posts/{date:%Y}/{date:%b}/{date:%d}/{slug}/index.html'`` This would save your articles in something like ``/posts/2011/Aug/07/sample-post/index.html``, and the URL to this would be ``/posts/2011/Aug/07/sample-post/``. @@ -245,8 +245,8 @@ make it easier for readers to navigate through the posts you've written over tim Example usage: -* YEAR_ARCHIVE_SAVE_AS = ``'posts/{date:%Y}/index.html'`` -* MONTH_ARCHIVE_SAVE_AS = ``'posts/{date:%Y}/{date:%b}/index.html'`` +* ``YEAR_ARCHIVE_SAVE_AS = 'posts/{date:%Y}/index.html'`` +* ``MONTH_ARCHIVE_SAVE_AS = 'posts/{date:%Y}/{date:%b}/index.html'`` With these settings, Pelican will create an archive of all your posts for the year at (for instance) ``posts/2011/index.html`` and an archive of all your @@ -258,43 +258,43 @@ posts for the month at ``posts/2011/Aug/index.html``. arrive at an appropriate archive of posts, without having to specify a page name. -====================================================== ===================================================== -Setting name (default value) What does it do? -====================================================== ===================================================== -`ARTICLE_URL` (``'{slug}.html'``) The URL to refer to an article. -`ARTICLE_SAVE_AS` (``'{slug}.html'``) The place where we will save an article. -`ARTICLE_LANG_URL` (``'{slug}-{lang}.html'``) The URL to refer to an article which doesn't use the +====================================================== ======================================================== +Setting name (followed by default value, if any) What does it do? +====================================================== ======================================================== +``ARTICLE_URL = '{slug}.html'`` The URL to refer to an article. +``ARTICLE_SAVE_AS = '{slug}.html'`` The place where we will save an article. +``ARTICLE_LANG_URL = '{slug}-{lang}.html'`` The URL to refer to an article which doesn't use the default language. -`ARTICLE_LANG_SAVE_AS` (``'{slug}-{lang}.html'``) The place where we will save an article which +``ARTICLE_LANG_SAVE_AS = '{slug}-{lang}.html'`` The place where we will save an article which doesn't use the default language. -`DRAFT_URL` (``'drafts/{slug}.html'``) The URL to refer to an article draft. -`DRAFT_SAVE_AS` (``'drafts/{slug}.html'``) The place where we will save an article draft. -`DRAFT_LANG_URL` (``'drafts/{slug}-{lang}.html'``) The URL to refer to an article draft which doesn't +``DRAFT_URL = 'drafts/{slug}.html'`` The URL to refer to an article draft. +``DRAFT_SAVE_AS = 'drafts/{slug}.html'`` The place where we will save an article draft. +``DRAFT_LANG_URL = 'drafts/{slug}-{lang}.html'`` The URL to refer to an article draft which doesn't use the default language. -`DRAFT_LANG_SAVE_AS` (``'drafts/{slug}-{lang}.html'``) The place where we will save an article draft which +``DRAFT_LANG_SAVE_AS = 'drafts/{slug}-{lang}.html'`` The place where we will save an article draft which doesn't use the default language. -`PAGE_URL` (``'pages/{slug}.html'``) The URL we will use to link to a page. -`PAGE_SAVE_AS` (``'pages/{slug}.html'``) The location we will save the page. This value has to be +``PAGE_URL = 'pages/{slug}.html'`` The URL we will use to link to a page. +``PAGE_SAVE_AS = 'pages/{slug}.html'`` The location we will save the page. This value has to be the same as PAGE_URL or you need to use a rewrite in your server config. -`PAGE_LANG_URL` (``'pages/{slug}-{lang}.html'``) The URL we will use to link to a page which doesn't +``PAGE_LANG_URL = 'pages/{slug}-{lang}.html'`` The URL we will use to link to a page which doesn't use the default language. -`PAGE_LANG_SAVE_AS` (``'pages/{slug}-{lang}.html'``) The location we will save the page which doesn't +``PAGE_LANG_SAVE_AS = 'pages/{slug}-{lang}.html'`` The location we will save the page which doesn't use the default language. -`CATEGORY_URL` (``'category/{slug}.html'``) The URL to use for a category. -`CATEGORY_SAVE_AS` (``'category/{slug}.html'``) The location to save a category. -`TAG_URL` (``'tag/{slug}.html'``) The URL to use for a tag. -`TAG_SAVE_AS` (``'tag/{slug}.html'``) The location to save the tag page. -`AUTHOR_URL` (``'author/{slug}.html'``) The URL to use for an author. -`AUTHOR_SAVE_AS` (``'author/{slug}.html'``) The location to save an author. -`YEAR_ARCHIVE_SAVE_AS` (``''``) The location to save per-year archives of your posts. -`MONTH_ARCHIVE_SAVE_AS` (``''``) The location to save per-month archives of your posts. -`DAY_ARCHIVE_SAVE_AS` (``''``) The location to save per-day archives of your posts. -`SLUG_SUBSTITUTIONS` (``()``) Substitutions to make prior to stripping out +``CATEGORY_URL = 'category/{slug}.html'`` The URL to use for a category. +``CATEGORY_SAVE_AS = 'category/{slug}.html'`` The location to save a category. +``TAG_URL = 'tag/{slug}.html'`` The URL to use for a tag. +``TAG_SAVE_AS = 'tag/{slug}.html'`` The location to save the tag page. +``AUTHOR_URL = 'author/{slug}.html'`` The URL to use for an author. +``AUTHOR_SAVE_AS = 'author/{slug}.html'`` The location to save an author. +``YEAR_ARCHIVE_SAVE_AS = ''`` The location to save per-year archives of your posts. +``MONTH_ARCHIVE_SAVE_AS = ''`` The location to save per-month archives of your posts. +``DAY_ARCHIVE_SAVE_AS = ''`` The location to save per-day archives of your posts. +``SLUG_SUBSTITUTIONS` = ()`` Substitutions to make prior to stripping out non-alphanumerics when generating slugs. Specified as a list of 2-tuples of ``(from, to)`` which are applied in order. -====================================================== ===================================================== +====================================================== ======================================================== .. note:: @@ -303,23 +303,21 @@ Setting name (default value) What does it do? set the corresponding ``*_SAVE_AS`` setting to ``''`` to prevent the relevant page from being generated. -`DIRECT_TEMPLATES` -~~~~~~~~~~~~~~~~~~ - -These templates (``('index', 'tags', 'categories', 'archives')`` by default) -works a bit differently than above. Only the ``_SAVE_AS`` setting is available: +``DIRECT_TEMPLATES``, which are ``('index', 'tags', 'categories', 'archives')`` +by default, work a bit differently than noted above. Only the ``_SAVE_AS`` +settings are available: ============================================= =============================================== -Setting name (default value) What does it do? +Setting name (followed by default value) What does it do? ============================================= =============================================== -`ARCHIVES_SAVE_AS` (``'archives.html'``) The location to save the article archives page. -`AUTHORS_SAVE_AS` (``'authors.html'``) The location to save the author list. -`CATEGORIES_SAVE_AS` (``'categories.html'``) The location to save the category list. -`TAGS_SAVE_AS` (``'tags.html'``) The location to save the tag list. +``ARCHIVES_SAVE_AS = 'archives.html'`` The location to save the article archives page. +``AUTHORS_SAVE_AS = 'authors.html'`` The location to save the author list. +``CATEGORIES_SAVE_AS = 'categories.html'`` The location to save the category list. +``TAGS_SAVE_AS = 'tags.html'`` The location to save the tag list. ============================================= =============================================== -The corresponding urls are hard-coded in the themes: ``'archives.html'``, -``'authors.html'``, ``'categories.html'``, ``'tags.html'``. +URLs for direct template pages are theme-dependent. Some themes hard-code them: +``'archives.html'``, ``'authors.html'``, ``'categories.html'``, ``'tags.html'``. Timezone -------- @@ -460,33 +458,33 @@ Pelican generates category feeds as well as feeds for all your articles. It does not generate feeds for tags by default, but it is possible to do so using the ``TAG_FEED_ATOM`` and ``TAG_FEED_RSS`` settings: -================================================ ===================================================== -Setting name (default value) What does it do? -================================================ ===================================================== -`FEED_DOMAIN` (``None``, i.e. base URL is "/") The domain prepended to feed URLs. Since feed URLs +================================================= ===================================================== +Setting name (followed by default value, if any) What does it do? +================================================= ===================================================== +``FEED_DOMAIN = None``, i.e. base URL is "/" The domain prepended to feed URLs. Since feed URLs should always be absolute, it is highly recommended to define this (e.g., "http://feeds.example.com"). If you have already explicitly defined SITEURL (see above) and want to use the same domain for your feeds, you can just set: ``FEED_DOMAIN = SITEURL``. -`FEED_ATOM` (``None``, i.e. no Atom feed) Relative URL to output the Atom feed. -`FEED_RSS` (``None``, i.e. no RSS) Relative URL to output the RSS feed. -`FEED_ALL_ATOM` (``'feeds/all.atom.xml'``) Relative URL to output the all posts Atom feed: +``FEED_ATOM = None``, i.e. no Atom feed Relative URL to output the Atom feed. +``FEED_RSS = None``, i.e. no RSS Relative URL to output the RSS feed. +``FEED_ALL_ATOM = 'feeds/all.atom.xml'`` Relative URL to output the all-posts Atom feed: this feed will contain all posts regardless of their language. -`FEED_ALL_RSS` (``None``, i.e. no all RSS) Relative URL to output the all posts RSS feed: +``FEED_ALL_RSS = None``, i.e. no all-posts RSS Relative URL to output the all-posts RSS feed: this feed will contain all posts regardless of their language. -`CATEGORY_FEED_ATOM` ('feeds/%s.atom.xml'[2]_) Where to put the category Atom feeds. -`CATEGORY_FEED_RSS` (``None``, i.e. no RSS) Where to put the category RSS feeds. -`AUTHOR_FEED_ATOM` ('feeds/%s.atom.xml'[2]_) Where to put the author Atom feeds. -`AUTHOR_FEED_RSS` ('feeds/%s.rss.xml'[2]_) Where to put the author RSS feeds. -`TAG_FEED_ATOM` (``None``, i.e. no tag feed) Relative URL to output the tag Atom feed. It should +``CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml'`` [2]_ Where to put the category Atom feeds. +``CATEGORY_FEED_RSS = None``, i.e. no RSS Where to put the category RSS feeds. +``AUTHOR_FEED_ATOM = 'feeds/%s.atom.xml'`` [2]_ Where to put the author Atom feeds. +``AUTHOR_FEED_RSS = 'feeds/%s.rss.xml'`` [2]_ Where to put the author RSS feeds. +``TAG_FEED_ATOM = None``, i.e. no tag feed Relative URL to output the tag Atom feed. It should be defined using a "%s" match in the tag name. -`TAG_FEED_RSS` (``None``, ie no RSS tag feed) Relative URL to output the tag RSS feed -`FEED_MAX_ITEMS` Maximum number of items allowed in a feed. Feed item +``TAG_FEED_RSS = None``, i.e. no RSS tag feed Relative URL to output the tag RSS feed +``FEED_MAX_ITEMS`` Maximum number of items allowed in a feed. Feed item quantity is unrestricted by default. -================================================ ===================================================== +================================================= ===================================================== If you don't want to generate some or any of these feeds, set the above variables to ``None``. @@ -499,17 +497,17 @@ If you want to use FeedBurner for your feed, you will likely need to decide upon a unique identifier. For example, if your site were called "Thyme" and hosted on the www.example.com domain, you might use "thymefeeds" as your unique identifier, which we'll use throughout this section for illustrative -purposes. In your Pelican settings, set the `FEED_ATOM` attribute to -"thymefeeds/main.xml" to create an Atom feed with an original address of -`http://www.example.com/thymefeeds/main.xml`. Set the `FEED_DOMAIN` attribute -to `http://feeds.feedburner.com`, or `http://feeds.example.com` if you are -using a CNAME on your own domain (i.e., FeedBurner's "MyBrand" feature). +purposes. In your Pelican settings, set the ``FEED_ATOM`` attribute to +``thymefeeds/main.xml`` to create an Atom feed with an original address of +``http://www.example.com/thymefeeds/main.xml``. Set the ``FEED_DOMAIN`` +attribute to ``http://feeds.feedburner.com``, or ``http://feeds.example.com`` if +you are using a CNAME on your own domain (i.e., FeedBurner's "MyBrand" feature). There are two fields to configure in the `FeedBurner `_ interface: "Original Feed" and "Feed Address". In this example, the "Original Feed" would be -`http://www.example.com/thymefeeds/main.xml` and the "Feed Address" suffix -would be `thymefeeds/main.xml`. +``http://www.example.com/thymefeeds/main.xml`` and the "Feed Address" suffix +would be ``thymefeeds/main.xml``. Pagination ========== @@ -522,15 +520,15 @@ benefit from paginating this list. You can use the following settings to configure the pagination. ================================================ ===================================================== -Setting name (default value) What does it do? +Setting name (followed by default value, if any) What does it do? ================================================ ===================================================== -`DEFAULT_ORPHANS` (``0``) The minimum number of articles allowed on the +``DEFAULT_ORPHANS = 0`` The minimum number of articles allowed on the last page. Use this when you don't want the last page to only contain a handful of articles. -`DEFAULT_PAGINATION` (``False``) The maximum number of articles to include on a +``DEFAULT_PAGINATION = False`` The maximum number of articles to include on a page, not including orphans. False to disable pagination. -`PAGINATION_PATTERNS` A set of patterns that are used to determine advanced +``PAGINATION_PATTERNS`` A set of patterns that are used to determine advanced pagination output. ================================================ ===================================================== @@ -563,11 +561,11 @@ If you want to generate a tag cloud with all your tags, you can do so using the following settings. ================================================ ===================================================== -Setting name (default value) What does it do? +Setting name (followed by default value) What does it do? ================================================ ===================================================== -`TAG_CLOUD_STEPS` (``4``) Count of different font sizes in the tag +``TAG_CLOUD_STEPS = 4`` Count of different font sizes in the tag cloud. -`TAG_CLOUD_MAX_ITEMS` (``100``) Maximum number of tags in the cloud. +``TAG_CLOUD_MAX_ITEMS = 100`` Maximum number of tags in the cloud. ================================================ ===================================================== The default theme does not include a tag cloud, but it is pretty easy to add one:: @@ -608,13 +606,13 @@ Translations Pelican offers a way to translate articles. See the :doc:`Getting Started ` section for more information. -===================================================== ===================================================== -Setting name (default value) What does it do? -===================================================== ===================================================== -`DEFAULT_LANG` (``'en'``) The default language to use. -`TRANSLATION_FEED_ATOM` ('feeds/all-%s.atom.xml'[3]_) Where to put the Atom feed for translations. -`TRANSLATION_FEED_RSS` (``None``, i.e. no RSS) Where to put the RSS feed for translations. -===================================================== ===================================================== +======================================================== ===================================================== +Setting name (followed by default value, if any) What does it do? +======================================================== ===================================================== +``DEFAULT_LANG = 'en'`` The default language to use. +``TRANSLATION_FEED_ATOM = 'feeds/all-%s.atom.xml'`` [3]_ Where to put the Atom feed for translations. +``TRANSLATION_FEED_RSS = None``, i.e. no RSS Where to put the RSS feed for translations. +======================================================== ===================================================== .. [3] %s is the language @@ -622,11 +620,11 @@ Ordering content ================ ================================================ ===================================================== -Setting name (default value) What does it do? +Setting name (followed by default value) What does it do? ================================================ ===================================================== -`NEWEST_FIRST_ARCHIVES` (``True``) Order archives by newest first by date. (False: +``NEWEST_FIRST_ARCHIVES = True`` Order archives by newest first by date. (False: orders by date with older articles first.) -`REVERSE_CATEGORY_ORDER` (``False``) Reverse the category order. (True: lists by reverse +``REVERSE_CATEGORY_ORDER = False`` Reverse the category order. (True: lists by reverse alphabetical order; default lists alphabetically.) ================================================ ===================================================== @@ -637,32 +635,32 @@ Creating Pelican themes is addressed in a dedicated section (see :ref:`theming-p However, here are the settings that are related to themes. ================================================ ===================================================== -Setting name (default value) What does it do? +Setting name (followed by default value, if any) What does it do? ================================================ ===================================================== -`THEME` Theme to use to produce the output. Can be a relative +``THEME`` Theme to use to produce the output. Can be a relative or absolute path to a theme folder, or the name of a default theme or a theme installed via ``pelican-themes`` (see below). -`THEME_STATIC_DIR` (``'theme'``) Destination directory in the output path where +``THEME_STATIC_DIR = 'theme'`` Destination directory in the output path where Pelican will place the files collected from `THEME_STATIC_PATHS`. Default is `theme`. -`THEME_STATIC_PATHS` (``['static']``) Static theme paths you want to copy. Default +``THEME_STATIC_PATHS = ['static']`` Static theme paths you want to copy. Default value is `static`, but if your theme has other static paths, you can put them here. If files or directories with the same names are included in the paths defined in this settings, they will be progressively overwritten. -`CSS_FILE` (``'main.css'``) Specify the CSS file you want to load. +``CSS_FILE = 'main.css'`` Specify the CSS file you want to load. ================================================ ===================================================== -By default, two themes are available. You can specify them using the `THEME` setting or by passing the -``-t`` option to the ``pelican`` command: +By default, two themes are available. You can specify them using the ``THEME`` +setting or by passing the ``-t`` option to the ``pelican`` command: * notmyidea * simple (a synonym for "plain text" :) -There are a number of other themes available at http://github.com/getpelican/pelican-themes. +There are a number of other themes available at https://github.com/getpelican/pelican-themes. Pelican comes with :doc:`pelican-themes`, a small script for managing themes. You can define your own theme, either by starting from scratch or by duplicating @@ -677,34 +675,34 @@ Following are example ways to specify your preferred theme:: # Specify a customized theme, via path relative to the settings file THEME = "themes/mycustomtheme" # Specify a customized theme, via absolute path - THEME = "~/projects/mysite/themes/mycustomtheme" + THEME = "/home/myuser/projects/mysite/themes/mycustomtheme" The built-in ``notmyidea`` theme can make good use of the following settings. Feel free to use them in your themes as well. ======================= ======================================================= -Setting name What does it do ? +Setting name What does it do? ======================= ======================================================= -`SITESUBTITLE` A subtitle to appear in the header. -`DISQUS_SITENAME` Pelican can handle Disqus comments. Specify the +``SITESUBTITLE`` A subtitle to appear in the header. +``DISQUS_SITENAME`` Pelican can handle Disqus comments. Specify the Disqus sitename identifier here. -`GITHUB_URL` Your GitHub URL (if you have one). It will then +``GITHUB_URL`` Your GitHub URL (if you have one). It will then use this information to create a GitHub ribbon. -`GOOGLE_ANALYTICS` 'UA-XXXX-YYYY' to activate Google Analytics. -`GOSQUARED_SITENAME` 'XXX-YYYYYY-X' to activate GoSquared. -`MENUITEMS` A list of tuples (Title, URL) for additional menu +``GOOGLE_ANALYTICS`` Set to 'UA-XXXX-YYYY' to activate Google Analytics. +``GOSQUARED_SITENAME`` Set to 'XXX-YYYYYY-X' to activate GoSquared. +``MENUITEMS`` A list of tuples (Title, URL) for additional menu items to appear at the beginning of the main menu. -`PIWIK_URL` URL to your Piwik server - without 'http://' at the +``PIWIK_URL`` URL to your Piwik server - without 'http://' at the beginning. -`PIWIK_SSL_URL` If the SSL-URL differs from the normal Piwik-URL +``PIWIK_SSL_URL`` If the SSL-URL differs from the normal Piwik-URL you have to include this setting too. (optional) -`PIWIK_SITE_ID` ID for the monitored website. You can find the ID - in the Piwik admin interface > settings > websites. -`LINKS` A list of tuples (Title, URL) for links to appear on +``PIWIK_SITE_ID`` ID for the monitored website. You can find the ID + in the Piwik admin interface > Settings > Websites. +``LINKS`` A list of tuples (Title, URL) for links to appear on the header. -`SOCIAL` A list of tuples (Title, URL) to appear in the +``SOCIAL`` A list of tuples (Title, URL) to appear in the "social" section. -`TWITTER_USERNAME` Allows for adding a button to articles to encourage +``TWITTER_USERNAME`` Allows for adding a button to articles to encourage others to tweet about them. Add your Twitter username if you want this button to appear. ======================= ======================================================= @@ -734,17 +732,17 @@ For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]`` Reading only modified content ============================= -To speed up the build process, pelican can optionally read only articles +To speed up the build process, Pelican can optionally read only articles and pages with modified content. When Pelican is about to read some content source file: 1. The hash or modification time information for the file from a - previous build are loaded from a cache file if `LOAD_CONTENT_CACHE` - is ``True``. These files are stored in the `CACHE_DIRECTORY` + previous build are loaded from a cache file if ``LOAD_CONTENT_CACHE`` + is ``True``. These files are stored in the ``CACHE_DIRECTORY`` directory. If the file has no record in the cache file, it is read as usual. -2. The file is checked according to `CHECK_MODIFIED_METHOD`: +2. The file is checked according to ``CHECK_MODIFIED_METHOD``: - If set to ``'mtime'``, the modification time of the file is checked. @@ -755,60 +753,60 @@ When Pelican is about to read some content source file: usual. 3. If the file is considered unchanged, the content data saved in a - previous build corresponding to the file is loaded from the cache + previous build corresponding to the file is loaded from the cache, and the file is not read. 4. If the file is considered changed, the file is read and the new modification information and the content data are saved to the - cache if `CACHE_CONTENT` is ``True``. + cache if ``CACHE_CONTENT`` is ``True``. -Depending on `CONTENT_CACHING_LAYER` either the raw content and -metadata returned by a reader are cached if set to ``'reader'``, or -the processed content object is cached if set to ``'generator'``. -Caching the processed content object may conflict with plugins (as -some reading related signals may be skipped) or e.g. the -`WITH_FUTURE_DATES` functionality (as the ``draft`` status of the +If ``CONTENT_CACHING_LAYER`` is set to ``'reader'`` (the default), +the raw content and metadata returned by a reader are cached. If this +setting is instead set to ``'generator'``, the processed content +object is cached. Caching the processed content object may conflict +with plugins (as some reading related signals may be skipped) and the +``WITH_FUTURE_DATES`` functionality (as the ``draft`` status of the cached content objects would not change automatically over time). -Modification time based checking is faster than comparing file hashes, -but is not as reliable, because mtime information can be lost when -e.g. copying the content sources using the ``cp`` or ``rsync`` -commands without the mtime preservation mode (invoked e.g. by -``--archive``). +Checking modification times is faster than comparing file hashes, +but it is not as reliable because ``mtime`` information can be lost, +e.g., when copying content source files using the ``cp`` or ``rsync`` +commands without the ``mtime`` preservation mode (which for ``rsync`` +can be invoked by passing the ``--archive`` flag). The cache files are Python pickles, so they may not be readable by different versions of Python as the pickle format often changes. If such an error is encountered, the cache files have to be rebuilt by -running pelican after removing them or by using the pelican -command-line option ``--ignore-cache``. The cache files also have to -be rebuilt when changing the `GZIP_CACHE` setting for cache file -reading to work. +removing them and re-running Pelican, or by using the Pelican +command-line option ``--ignore-cache``. The cache files also have to +be rebuilt when changing the ``GZIP_CACHE`` setting for cache file +reading to work properly. The ``--ignore-cache`` command-line option is also useful when the -whole cache needs to be regenerated due to e.g. modifications to the -settings file which should change the cached content or just for -debugging purposes. When pelican runs in autoreload mode, modification +whole cache needs to be regenerated, such as when making modifications +to the settings file that will affect the cached content, or just for +debugging purposes. When Pelican runs in autoreload mode, modification of the settings file will make it ignore the cache automatically if -`AUTORELOAD_IGNORE_CACHE` is ``True``. +``AUTORELOAD_IGNORE_CACHE`` is ``True``. Note that even when using cached content, all output is always -written, so the modification times of the ``*.html`` files always -change. Therefore, ``rsync`` based upload may benefit from the -``--checksum`` option. +written, so the modification times of the generated ``*.html`` files +will always change. Therefore, ``rsync``-based uploading may benefit +from the ``--checksum`` option. .. _writing_only_selected_content: Writing only selected content ============================= -When one article or page or the theme is being worked on it is often -desirable to display selected output files as soon as possible. In -such cases generating and writing all output is often unnecessary. -These selected output files can be given as output paths in the -`WRITE_SELECTED` list and **only** those files will be written. This -list can be also specified on the command-line using the -``--write-selected`` option which accepts a comma separated list -of output file paths. By default the list is empty so all output is -written. +When only working on a single article or page, or making tweaks to +your theme, it is often desirable to generate and review your work +as quickly as possible. In such cases, generating and writing the +entire site output is often unnecessary. By specifying only the +desired files as output paths in the ``WRITE_SELECTED`` list, +**only** those files will be written. This list can be also specified +on the command line using the ``--write-selected`` option, which +accepts a comma-separated list of output file paths. By default this +list is empty, so all output is written. Example settings ================ From fd231b6ce2692fc186080a472876687018eff772 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Wed, 30 Apr 2014 13:35:10 -0700 Subject: [PATCH 0252/1427] Remove errant leading spaces from fabfile.py.in --- pelican/tools/templates/fabfile.py.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pelican/tools/templates/fabfile.py.in b/pelican/tools/templates/fabfile.py.in index fbd03e2c..e693bb48 100644 --- a/pelican/tools/templates/fabfile.py.in +++ b/pelican/tools/templates/fabfile.py.in @@ -36,13 +36,13 @@ def regenerate(): def serve(): os.chdir(env.deploy_path) - + PORT = 8000 class AddressReuseTCPServer(SocketServer.TCPServer): allow_reuse_address = True - + server = AddressReuseTCPServer(('', PORT), SimpleHTTPServer.SimpleHTTPRequestHandler) - + sys.stderr.write('Serving on port {0} ...\n'.format(PORT)) server.serve_forever() From f00fc944fb310f1a6531b0a68ed9bda23c40fb2c Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Thu, 1 May 2014 10:39:07 +0200 Subject: [PATCH 0253/1427] Fix get_writer signal received result unpacking --- pelican/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 5208c317..74f55c46 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -209,13 +209,13 @@ class Pelican(object): return generators def get_writer(self): - writers = [ w for w in signals.get_writer.send(self) + 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) else: - _, writer = writers[0] + writer = writers[0] if writers_found == 1: logger.debug('Found writer: {}'.format(writer)) else: From f2549650e6b2154d3484a72ee19732a75e3d4fe8 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 2 May 2014 07:34:27 -0700 Subject: [PATCH 0254/1427] Remove errant backtick --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index c9eddfba..d5802d1d 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -112,7 +112,7 @@ Setting name (followed by default value, if any) ``PAGE_DIR = 'pages'`` Directory to look at for pages, relative to `PATH`. ``PAGE_EXCLUDES = ()`` A list of directories to exclude when looking for pages. ``ARTICLE_DIR = ''`` Directory to look at for articles, relative to `PATH`. -``ARTICLE_EXCLUDES` = ('pages',)`` A list of directories to exclude when looking for articles. +``ARTICLE_EXCLUDES = ('pages',)`` A list of directories to exclude when looking for articles. ``OUTPUT_SOURCES = False`` Set to True if you want to copy the articles and pages in their original format (e.g. Markdown or reStructuredText) to the specified ``OUTPUT_PATH``. From 5bad061c19947fde41a98bccb56a9a00ef46a40a Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Sun, 27 Apr 2014 08:53:56 +0200 Subject: [PATCH 0255/1427] rename CACHE_DIR -> CACHE_PATH to unify with rest of Pelican CACHE_PATH can now be relative to settings file like OUTPUT_PATH. Also add --cache-path commandline option. Change cache loading warning to a less scary and more helpful message. --- docs/settings.rst | 4 ++-- pelican/__init__.py | 8 +++++++- pelican/settings.py | 4 ++-- pelican/tests/test_generators.py | 26 +++++++++++++------------- pelican/tests/test_pelican.py | 10 +++++----- pelican/utils.py | 18 ++++++++++++------ 6 files changed, 41 insertions(+), 29 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index d5802d1d..2782977c 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -178,7 +178,7 @@ Setting name (followed by default value, if any) ``CONTENT_CACHING_LAYER = 'reader'`` If set to ``'reader'``, save only the raw content and metadata returned by readers. If set to ``'generator'``, save processed content objects. -``CACHE_DIRECTORY = 'cache'`` Directory in which to store cache files. +``CACHE_PATH = 'cache'`` Directory in which to store cache files. ``GZIP_CACHE = True`` If ``True``, use gzip to (de)compress the cache files. ``CHECK_MODIFIED_METHOD = 'mtime'`` Controls how files are checked for modifications. ``LOAD_CONTENT_CACHE = True`` If ``True``, load unmodified content from cache. @@ -739,7 +739,7 @@ When Pelican is about to read some content source file: 1. The hash or modification time information for the file from a previous build are loaded from a cache file if ``LOAD_CONTENT_CACHE`` - is ``True``. These files are stored in the ``CACHE_DIRECTORY`` + is ``True``. These files are stored in the ``CACHE_PATH`` directory. If the file has no record in the cache file, it is read as usual. 2. The file is checked according to ``CHECK_MODIFIED_METHOD``: diff --git a/pelican/__init__.py b/pelican/__init__.py index 74f55c46..d6417391 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -275,7 +275,11 @@ def parse_arguments(): help='Relaunch pelican each time a modification occurs' ' on the content files.') - parser.add_argument('-c', '--ignore-cache', action='store_true', + parser.add_argument('--cache-path', dest='cache_path', + help=('Directory in which to store cache files. ' + 'If not specified, defaults to "cache".')) + + parser.add_argument('--ignore-cache', action='store_true', dest='ignore_cache', help='Ignore content cache ' 'from previous runs by not loading cache files.') @@ -300,6 +304,8 @@ def get_config(args): config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir if args.ignore_cache: config['LOAD_CONTENT_CACHE'] = False + if args.cache_path: + config['CACHE_PATH'] = args.cache_path if args.selected_paths: config['WRITE_SELECTED'] = args.selected_paths.split(',') diff --git a/pelican/settings.py b/pelican/settings.py index abf16b32..f759ff9e 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -124,7 +124,7 @@ DEFAULT_CONFIG = { 'SLUGIFY_SOURCE': 'title', 'CACHE_CONTENT': True, 'CONTENT_CACHING_LAYER': 'reader', - 'CACHE_DIRECTORY': 'cache', + 'CACHE_PATH': 'cache', 'GZIP_CACHE': True, 'CHECK_MODIFIED_METHOD': 'mtime', 'LOAD_CONTENT_CACHE': True, @@ -139,7 +139,7 @@ def read_settings(path=None, override=None): if path: local_settings = get_settings_from_file(path) # Make the paths relative to the settings file - for p in ['PATH', 'OUTPUT_PATH', 'THEME']: + for p in ['PATH', 'OUTPUT_PATH', 'THEME', 'CACHE_PATH']: if p in local_settings and local_settings[p] is not None \ and not isabs(local_settings[p]): absp = os.path.abspath(os.path.normpath(os.path.join( diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 9463047e..7b79e8f3 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -66,7 +66,7 @@ class TestArticlesGenerator(unittest.TestCase): def test_generate_feeds(self): settings = get_settings() - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=None, theme=settings['THEME'], output_path=None) @@ -142,7 +142,7 @@ class TestArticlesGenerator(unittest.TestCase): settings['DEFAULT_CATEGORY'] = 'Default' settings['DEFAULT_DATE'] = (1970, 1, 1) settings['USE_FOLDER_AS_CATEGORY'] = False - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache settings['READERS'] = {'asc': None} settings['filenames'] = {} generator = ArticlesGenerator( @@ -167,7 +167,7 @@ class TestArticlesGenerator(unittest.TestCase): def test_direct_templates_save_as_default(self): settings = get_settings(filenames={}) - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=None, theme=settings['THEME'], output_path=None) @@ -182,7 +182,7 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings() settings['DIRECT_TEMPLATES'] = ['archives'] settings['ARCHIVES_SAVE_AS'] = 'archives/index.html' - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=None, theme=settings['THEME'], output_path=None) @@ -198,7 +198,7 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings() settings['DIRECT_TEMPLATES'] = ['archives'] settings['ARCHIVES_SAVE_AS'] = 'archives/index.html' - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=None, theme=settings['THEME'], output_path=None) @@ -225,7 +225,7 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings(filenames={}) settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html' - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=CONTENT_DIR, theme=settings['THEME'], output_path=None) @@ -291,7 +291,7 @@ class TestArticlesGenerator(unittest.TestCase): def test_article_object_caching(self): """Test Article objects caching at the generator level""" settings = get_settings(filenames={}) - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache settings['CONTENT_CACHING_LAYER'] = 'generator' settings['READERS'] = {'asc': None} @@ -311,7 +311,7 @@ class TestArticlesGenerator(unittest.TestCase): def test_reader_content_caching(self): """Test raw content caching at the reader level""" settings = get_settings(filenames={}) - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache settings['READERS'] = {'asc': None} generator = ArticlesGenerator( @@ -335,7 +335,7 @@ class TestArticlesGenerator(unittest.TestCase): used in --ignore-cache or autoreload mode""" settings = get_settings(filenames={}) - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache settings['READERS'] = {'asc': None} generator = ArticlesGenerator( @@ -373,7 +373,7 @@ class TestPageGenerator(unittest.TestCase): def test_generate_context(self): settings = get_settings(filenames={}) settings['PAGE_DIR'] = 'TestPages' # relative to CUR_DIR - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache settings['DEFAULT_DATE'] = (1970, 1, 1) generator = PagesGenerator( @@ -402,7 +402,7 @@ class TestPageGenerator(unittest.TestCase): def test_page_object_caching(self): """Test Page objects caching at the generator level""" settings = get_settings(filenames={}) - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache settings['CONTENT_CACHING_LAYER'] = 'generator' settings['READERS'] = {'asc': None} @@ -422,7 +422,7 @@ class TestPageGenerator(unittest.TestCase): def test_reader_content_caching(self): """Test raw content caching at the reader level""" settings = get_settings(filenames={}) - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache settings['READERS'] = {'asc': None} generator = PagesGenerator( @@ -446,7 +446,7 @@ class TestPageGenerator(unittest.TestCase): used in --ignore_cache or autoreload mode""" settings = get_settings(filenames={}) - settings['CACHE_DIRECTORY'] = self.temp_cache + settings['CACHE_PATH'] = self.temp_cache settings['READERS'] = {'asc': None} generator = PagesGenerator( diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 294cf399..411fb7da 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -79,7 +79,7 @@ class TestPelican(LoggedTestCase): settings = read_settings(path=None, override={ 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, - 'CACHE_DIRECTORY': self.temp_cache, + 'CACHE_PATH': self.temp_cache, 'LOCALE': locale.normalize('en_US'), }) pelican = Pelican(settings=settings) @@ -95,7 +95,7 @@ class TestPelican(LoggedTestCase): settings = read_settings(path=SAMPLE_CONFIG, override={ 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, - 'CACHE_DIRECTORY': self.temp_cache, + 'CACHE_PATH': self.temp_cache, 'LOCALE': locale.normalize('en_US'), }) pelican = Pelican(settings=settings) @@ -107,7 +107,7 @@ class TestPelican(LoggedTestCase): settings = read_settings(path=SAMPLE_CONFIG, override={ 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, - 'CACHE_DIRECTORY': self.temp_cache, + 'CACHE_PATH': self.temp_cache, 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, 'very'), os.path.join(SAMPLES_PATH, 'kinda'), os.path.join(SAMPLES_PATH, 'theme_standard')] @@ -128,7 +128,7 @@ class TestPelican(LoggedTestCase): settings = read_settings(path=SAMPLE_CONFIG, override={ 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, - 'CACHE_DIRECTORY': self.temp_cache, + 'CACHE_PATH': self.temp_cache, 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, 'theme_standard')] }) @@ -144,7 +144,7 @@ class TestPelican(LoggedTestCase): settings = read_settings(path=None, override={ 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, - 'CACHE_DIRECTORY': self.temp_cache, + 'CACHE_PATH': self.temp_cache, 'WRITE_SELECTED': [ os.path.join(self.temp_path, 'oh-yeah.html'), os.path.join(self.temp_path, 'categories.html'), diff --git a/pelican/utils.py b/pelican/utils.py index 7b58a231..2af34ecf 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -553,14 +553,14 @@ class FileDataCacher(object): '''Class that can cache data contained in files''' def __init__(self, settings, cache_name, caching_policy, load_policy): - '''Load the specified cache within CACHE_DIRECTORY in settings + '''Load the specified cache within CACHE_PATH in settings only if *load_policy* is True, May use gzip if GZIP_CACHE ins settings is True. Sets caching policy according to *caching_policy*. ''' self.settings = settings - self._cache_path = os.path.join(self.settings['CACHE_DIRECTORY'], + self._cache_path = os.path.join(self.settings['CACHE_PATH'], cache_name) self._cache_data_policy = caching_policy if self.settings['GZIP_CACHE']: @@ -572,9 +572,15 @@ class FileDataCacher(object): try: with self._cache_open(self._cache_path, 'rb') as fhandle: self._cache = pickle.load(fhandle) - except (IOError, OSError, pickle.UnpicklingError) as err: - logger.warning(('Cannot load cache {}, ' - 'proceeding with empty cache.\n{}').format( + except (IOError, OSError) as err: + logger.debug(('Cannot load cache {} (this is normal on first ' + 'run). Proceeding with empty cache.\n{}').format( + self._cache_path, err)) + self._cache = {} + except pickle.UnpicklingError as err: + logger.warning(('Cannot unpickle cache {}, cache may be using ' + 'an incompatible protocol (see pelican caching docs). ' + 'Proceeding with empty cache.\n{}').format( self._cache_path, err)) self._cache = {} else: @@ -596,7 +602,7 @@ class FileDataCacher(object): '''Save the updated cache''' if self._cache_data_policy: try: - mkdir_p(self.settings['CACHE_DIRECTORY']) + mkdir_p(self.settings['CACHE_PATH']) with self._cache_open(self._cache_path, 'wb') as fhandle: pickle.dump(self._cache, fhandle) except (IOError, OSError, pickle.PicklingError) as err: From cd40105c40285d0faec7c63200df86bf4d609cc7 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Mon, 28 Apr 2014 20:43:19 +0200 Subject: [PATCH 0256/1427] Fix #1335 remove old and unmaintained French docs Unfortunately nobody keeps them up to date, they would just spread confusion. --- docs/fr/astuces.rst | 20 ---- docs/fr/bases.rst | 58 ----------- docs/fr/configuration.rst | 165 ------------------------------- docs/fr/conventions.rst | 18 ---- docs/fr/faq.rst | 40 -------- docs/fr/index.rst | 57 ----------- docs/fr/installation.rst | 67 ------------- docs/fr/parametres_article.rst | 106 -------------------- docs/fr/pelican-themes.rst | 172 --------------------------------- docs/fr/themes.rst | 171 -------------------------------- docs/index.rst | 2 - 11 files changed, 876 deletions(-) delete mode 100644 docs/fr/astuces.rst delete mode 100644 docs/fr/bases.rst delete mode 100644 docs/fr/configuration.rst delete mode 100644 docs/fr/conventions.rst delete mode 100644 docs/fr/faq.rst delete mode 100644 docs/fr/index.rst delete mode 100644 docs/fr/installation.rst delete mode 100644 docs/fr/parametres_article.rst delete mode 100644 docs/fr/pelican-themes.rst delete mode 100644 docs/fr/themes.rst diff --git a/docs/fr/astuces.rst b/docs/fr/astuces.rst deleted file mode 100644 index 3f9a3987..00000000 --- a/docs/fr/astuces.rst +++ /dev/null @@ -1,20 +0,0 @@ -Trucs et astuces pour Pelican -############################# - -Personnaliser l'url d'un article pour Pelican -============================================= - -Par défaut, quand vous créez un article ayant pour titre *Mon article pour Pelican*, -l'url par défaut devient *mon-article-pour-pelican.html*. Cependant, il est possible -de modifier cela en utilisant la technique utilisée pour les traductions d'article, -c'est à dire le paramètre *:slug:* :: - - Mon article pour Pelican - ######################## - - :date: 2011-01-31 11:05 - :slug: super-article-pour-pelican - - bla, bla, bla … - -En prenant cet exemple ci dessus, votre url deviendra *super-article-pour-pelican.html* diff --git a/docs/fr/bases.rst b/docs/fr/bases.rst deleted file mode 100644 index 7a6fd118..00000000 --- a/docs/fr/bases.rst +++ /dev/null @@ -1,58 +0,0 @@ -Les bases de Pelican -#################### - -Créer son premier article -========================= - -Pour créer notre premier article, nous allons éditer un fichier, par exemple premier_article.rst :: - - Premier article pour Pelican - ############################ - :author: Guillaume - :date: 2011-01-08 10:20 - :category: GNU-Linux - :tags: tutoriel, git - Ceci est un tutoriel pour configurer git. - Bla, bla, bla .... - -Maintenant que ce fichier est créé, on va lancer la création du blog :: - - pelican . - -Vous aller obtenir une sortie comme celle ci — $PATH représente le dossier où vous -avez créé votre article :: - - [ok] writing $PATH/output/feeds/all.atom.xml - [ok] writing $PATH/output/feeds/GNU/Linux.atom.xml - [ok] writing $PATH/output/feeds/all-en.atom.xml - [ok] writing $PATH/output/premier-article-pour-pelican.html - [ok] writing $PATH/output/index.html - [ok] writing $PATH/output/tags.html - [ok] writing $PATH/output/categories.html - [ok] writing $PATH/output/archives.html - [ok] writing $PATH/output/tag/tutoriel.html - [ok] writing $PATH/output/tag/git.html - [ok] writing $PATH/output/category/GNU-Linux.html - - -Première analyse -================ - -Nous allons décortiquer un peu tout ça ensemble. - -* Un dossier output/ a été créé pour y mettre le fichiers xml et html du blog. -* Dans le dossier feeds/, nous retrouvons les différents flux de syndication. -* Le fichier de l’article et la page principale du blog a été généré. -* Le répertoire tag/ propose une page par tag. -* La page correspondant à la catégorie est générée dans le répertoire category/ - -Si vous ouvrez le fichier index.html — ou un autre — avec votre navigateur, vous -remarquerez que : - -* Le thème utilisé par défaut est notmyidea -* Le nom du blog est A Pelican Blog. - -Bien évidemment, il y a des paramètres de base que l’on peut modifier pour mettre -un peu tout ça à sa sauce. C’est ce que nous allons voir au travers du fichier de configuration. - - diff --git a/docs/fr/configuration.rst b/docs/fr/configuration.rst deleted file mode 100644 index 5388dae3..00000000 --- a/docs/fr/configuration.rst +++ /dev/null @@ -1,165 +0,0 @@ -Fichier de configuration -************************ - -On va créer un fichier de configuration que l’on va appeler **settings.py**. On peut -utiliser Pelican sans faire ce fichier, mais il faudrait à chaque fois passer les paramètres -en ligne de commande. Et comme il va nous servir à faire d’autres choses bien utile, -autant l’appréhender de suite. Cependant, nous n’allons voir que la base pour l’instant. - -Paramètres de base -================== - -AUTHOR : - Désigne l’auteur par défaut ; - -DEFAULT_CATEGORY : - La catégorie par défaut des articles. Si ce paramètre n’est - pas documenté, il prendra la valeur misc — pour miscellaneous (divers en français) ; - -SITENAME : - Le nom de votre site ; - -OUTPUT_PATH : - Le répertoire de sortie du blog. - -Quand je dis qu’on va faire simple, on fait simple ! -Passons donc à ce quoi doit ressembler le fichier de configuration :: - - # -*- coding: utf-8 -*- - AUTHOR = "Guillaume" - DEFAULT_CATEGORY = "GNU-Linux" - SITENAME = "Free Culture" - - -Si vous avez un serveur comme Apache de configuré pour votre machine, vous -pouvez paramétrer le répertoire de sortie vers **/var/www/blog** par exemple :: - - OUTPUT_PATH = "/var/www/blog" - -Une remarque importante. Si vous avez besoin de passer un caractère accentué, il -faut le préciser que la chaine est en unicode en faisant par exemple -*AUTHOR = u"Guillaume LAMÉ"* - -Pour bien vérifier que les paramètres sont bien pris en compte, nous allons enlever les lignes *:author: Guillaume* et *:category: GNU-Linux* de notre fichier -**premier_article.rst** et regénérer le blog. - -Rafraichissez votre page, ce devrait être bon. - -Nous allons maintenant passer en revue les différents paramètres de Pelican. Je les -ai regroupé par thème. Cependant, c’est surtout un listing avant de rentrer dans les -détails au prochain chapitre. - -Flux de syndication -=================== - -CATEGORY_FEED_ATOM : - Chemin d’écriture des flux Atom liés aux catégories ; - -CATEGORY_FEED_RSS : - Idem pour les flux rss (Optionnel); - -FEED_ATOM : - Chemin du flux Atom global; - -FEED_RSS : - Chemin du flux Rss global (Optionnel); - -FEED_ALL_ATOM : - Chemin du flux Atom global qui inclut la totalité des posts, indépendamment de la langue; - -FEED_ALL_RSS : - Chemin du flux Rss global qui inclut la totalité des posts, indépendamment de la langue (Optionnel); - -TAG_FEED_ATOM : - Chemin des flux Atom pour les tags (Optionnel); - -TAG_FEED_RSS : - Chemin des flux Rss pour les tags (Optionnel). - - -Traductions -=========== - -DEFAULT_LANG : - Le langage par défaut à utiliser. «*en*» par défaut ; - -TRANSLATION_FEED_ATOM : - Chemin du flux Atom pour les traductions. - -TRANSLATION_FEED_RSS : - Chemin du flux RSS pour les traductions. - - -Thèmes -====== - -CSS_FILE : - Fichier css à utiliser si celui-ci est différent du fichier par défaut (*main.css*) ; - -DISPLAY_PAGES_ON_MENU : - Affiche ou non les pages statiques sur le menu du thème ; - -DISQUS_SITENAME : - Indiquer le nom du site spécifié sur Disqus ; - -GITHUB_URL : - Indiquez votre url Github ; - -GOOGLE_ANALYTICS : - 'UA-XXXX-YYYY' pour activer Google analytics ; - -GOSQUARED_SITENAME : - 'XXX-YYYYYY-X' pour activer GoSquared ; - -JINJA_EXTENSIONS : - Liste d'extension Jinja2 que vous souhaitez utiliser ; - -LINKS : - Une liste de tuples (Titre, url) pour afficher la liste de lien ; - -PDF_PROCESSOR : - Génère ou non les articles et pages au format pdf ; - -NEWEST_FIRST_ARCHIVES : - Met les articles plus récent en tête de l'archive ; - -SOCIAL : - Une liste de tuples (Titre, url) pour afficher la liste de lien dans la section "Social" ; - -STATIC_THEME_PATHS : - Répertoire du thème que vous souhaitez importer dans l'arborescence finale ; - -THEME : - Thème à utiliser: - -TWITTER_USERNAME : - Permet d'afficher un bouton permettant le tweet des articles. - -Pelican est fournit avec :doc:`pelican-themes`, un script permettant de gérer les thèmes - - - -Paramètres divers -================= - -DEFAULT_DATE: - Date par défaut à utiliser si l'information de date n'est pas spécifiée - dans les metadonnées de l'article. - Si 'fs', Pelican se basera sur le *mtime* du fichier. - Si c'est un tuple, il sera passé au constructeur datetime.datetime pour - générer l'objet datetime utilisé par défaut. - -KEEP_OUTPUT DIRECTORY : - Ne génère que les fichiers modifiés et n'efface pas le repertoire de sortie ; - -MARKUP : - Langage de balisage à utiliser ; - -PATH : - Répertoire à suivre pour les fichiers inclus ; - -SITEURL : - URL de base de votre site ; - -STATIC_PATHS : - Les chemins statiques que vous voulez avoir accès sur le chemin de sortie "statique" ; diff --git a/docs/fr/conventions.rst b/docs/fr/conventions.rst deleted file mode 100644 index bf88c07e..00000000 --- a/docs/fr/conventions.rst +++ /dev/null @@ -1,18 +0,0 @@ -Conventions -########### - -Environnement de test -===================== - -Les exemples sont basées sur une distribution Debian. Pour les autres distributions, -il y aura des ajustements à faire, notamment pour l’installation de Pelican. Les -noms des paquets peuvent changer. - -Conventions typographiques -========================== - -Un petit rappel concernant les codes sources. - - * $ correspond à une ligne à exécuter en tant qu’utilisateur courant du systême ; - * # correspond à une ligne à exécuter en tant que root ; - * **settings.py** : Les noms des répertoires et fichiers sont en gras. diff --git a/docs/fr/faq.rst b/docs/fr/faq.rst deleted file mode 100644 index d945f447..00000000 --- a/docs/fr/faq.rst +++ /dev/null @@ -1,40 +0,0 @@ -Foire aux questions (FAQ) -######################### - -Voici un résumé des questions fréquemment posées pour pelican. - -Est-il obligatoire d'avoir un fichier de configuration ? -======================================================== - -Non. Les fichiers de configuration sont juste un moyen facile de configurer -pelican. Pour les opérations de base, il est possible de spécifier des -options -en invoquant pelican avec la ligne de commande (voir pelican --help pour -plus -d'informations à ce sujet) - -Je crée mon propre thème, comment utiliser pygments? -==================================================== - -Pygment ajoute quelques classes au contenu généré, de sorte qua colorisation -de votre thème se fait grâce à un fichier css. Vous pouvez jeter un oeil à -celui proposé par`sur le site du projet `_ - -Comment puis-je créer mon propre thèm -===================================== - -Vueillez vous référer à :ref:`theming-pelican-fr`. - -Comment puis-je aider? -====================== - -Vous avez plusieurs options pour aider. Tout d'abord, vous pouvez utiliser -le -pélican, et signaler toute idée ou problème que vous avez sur le bugtracker -. - -Si vous voulez contribuer, jeter un oeil au dépôt git , ajoutez vos -modifications et faites une demande, je les regarderai dès que possible - -Vous pouvez aussi contribuer en créant des thèmes, et/ou compléter la -documentation. diff --git a/docs/fr/index.rst b/docs/fr/index.rst deleted file mode 100644 index 2deb5050..00000000 --- a/docs/fr/index.rst +++ /dev/null @@ -1,57 +0,0 @@ -Pelican -####### - -Pelican est un generateur de blog simple codé en python - -* Écrivez vos articles directement dans votre éditeur favori (vim !) et - directement en syntaxe reStructuredText ou Markdown ; -* Un outil simple en ligne de conmmande pour (re)générer le blog ; -* Sortie complètement statique, facile pour l'héberger n'importe où ; - -Fonctionnalités -=============== - -Pelican supporte actuellement : - -* des articles de blog ; -* des pages statiques ; -* les commentaires via un service externe (`disqus `_) - Notez qu'étant bien un service externe assez pratique, vous ne gérez pas - vous même les commentaires. Ce qui pourrait occasionner une perte de vos données; -* support de template (les templates sont crées avec `jinja2 `_) ; -* génération optionnelle de vos pages et articles en pdf. - -Pourquoi le nom "Pelican" ? -============================ - -Vous n'avez pas remarqué ? "Pelican" est un anagramme pour "Calepin" ;) - -Code source -=========== - -Vous pouvez accéder au code source via git à l'adresse -http://github.com/getpelican/pelican/ - -Feedback ! -========== - -Si vous voulez de nouvelles fonctionnalitées pour Pelican, n'hésitez pas à nous le dire, -à cloner le dépôt, etc … C'est open source !!! - -Contactez Alexis à "alexis at notmyidea dot org" pour quelques requêtes ou retour d'expérience que ce soi ! - -Documentation -============= - -.. toctree:: - :maxdepth: 2 - - conventions - installation - bases - configuration - themes - parametres_article - astuces - faq - pelican-themes diff --git a/docs/fr/installation.rst b/docs/fr/installation.rst deleted file mode 100644 index da327725..00000000 --- a/docs/fr/installation.rst +++ /dev/null @@ -1,67 +0,0 @@ -Installation et mise à jour de Pelican -###################################### - -Installation -============ - -Il y a deux façons d’installer Pelican sur son système. La première est via l’utilitaire -pip, l’autre façon est de télécharger Pelican via Github. Ici nous allons voir les deux -façons de procéder. - -Via pip -------- - -Pour installer Pelican via pip, vous aurez besoin du paquet python-pip. puis installez Pelican :: - - # apt-get install python-pip - # pip install pelican - - -Via Github ----------- - -Pour installer Pelican en reprenant le code via Github, nous aurons besoin du paquet -git-core pour récupérez les sources de Pelican. Puis nous procédons à l’installation :: - - # apt-get install git-core - $ git clone https://github.com/getpelican/pelican.git - $ cd pelican - # python setup.py install - -Mises à jour -============ - -Via pip -------- - -Rien de bien compliqué pour mettre à jour via pip :: - - $ cd votreRepertoireSource - $ pip install --upgrade pelican - - -Via Github ----------- - -C'est un peu plus long avec Github par contre :: - - $ cd votreRepertoireSource - $ git pull origin master - $ cd pelican - # python setup.py install - -Vous aurez un message d’erreur si le module setuptools de python n’est pas installé. -La manipulation est la suivante :: - - # apt-get install python-setuptools - -Alors, quelle méthode choisir ? -=============================== - -Vous avez le choix entre deux méthodes, mais aussi entre deux concepts. La méthode -de Github est la version de développement, où les modifications arrivent assez -fréquemment sans être testées à fond. La version de pip est une version arrêtée avec un -numéro de version dans laquelle vous aurez moins de bug. N’oubliez cependant pas -que le projet est très jeune et manque donc de maturité. Si vous aimez avoir les toutes -dernières versions utilisez Github, sinon penchez vous sur pip. - diff --git a/docs/fr/parametres_article.rst b/docs/fr/parametres_article.rst deleted file mode 100644 index a3d25b55..00000000 --- a/docs/fr/parametres_article.rst +++ /dev/null @@ -1,106 +0,0 @@ -Les paramètres des articles dans Pelican -######################################## - -Les catégories -============== - -Nous avons vu que pour affecter un article à une catégorie, nous avions le paramètre *:category:*. -Il y a cependant plus simple, affecter un répertoire à une catégorie. - -Dans le répertoire ou vous avez vos articles, créez le repertoire **GNU-Linux** et déplacez y le fichier -**premier_article.rst**. Bien évidemment nous ne verront pas la différence, car jusqu'ici *GNU-Linux* -est notre catégorie par défaut. - -Nous allons faire un autre exemple d'article avec la catégorie Pelican. Créez le répertoire **Pelican** -et collez cette exemple d'article :: - - Préparation de la documentation - ############################### - - :date: 2011-01-27 15:28 - :tags: documentation - - Il y a quand même pas mal de boulot pour faire une documentation ! - -Et lancez la compilation du blog. Vous voyez que la catégorie est affectée automatiquement. - -Les tags -======== - -Pour les tags, il n'y a rien de compliqué. il suffit de mettre le(s) tags séparés si besoin d'une virgule. :: - - Préparation de la documentation - ############################### - - :date: 2011-01-27 15:28 - :tags: documentation, pelican - -Par contre, par soucis de clarté au niveau des url je vous conseille de mettre les expression de plusieurs -mots séparées par des tirets :: - - :tags: mise-a-jour - -et non :: - - :tags: mise a jour - - -Les auteurs -=========== - -Par défaut, vous pouvez indiqué votre nom en tant qu'auteur dans le fichier de configuration. -S'il y a plusieurs auteurs pour le site, vous pouvez le définir manuellement dans -l'article avec la méta-donnée :: - - :author: Guillaume - -La date -======= - -La date se met au format anglophone : **YYYY-MM-DD hh:mm** :: - - :date: 2011-01-31 14:12 - - -Les traductions -=============== - -Pelican permet de générer un blog multilingue assez facilement. Pour cela nous devons : - -* Définir la langue de base du blog ; -* Donner une référence à l'article initial ; -* Définir la langue du fichier traduit et y reporter la référence. - -Pour définir la langue de base nous allons modifier le fichier **settings.py** et y rajouter la ligne suivante :: - - DEFAULT_LANG = "fr" - -Puis ajouter la référence dans notre article d'origine qui deviendra :: - - Préparation de la documentation - ############################### - - :date: 2011-01-27 15:28 - :tags: documentation - :slug: preparation-de-la-documentation - - Il y a quand même pas mal de boulot pour faire une documentation ! - -Nous n'avons plus qu'à créer l'article en anglais :: - - Start of documentation - ###################### - - :slug: preparation-de-la-documention - :lang: en - - There are still a lot of work to documentation ! - -**Il est important de comprendre que la valeur de :slug: deviendra votre url. Ne mettez donc pas un diminutif pour -identifier l'article** - -Rien de plus à savoir pour traduire efficacement des articles. - - -Maintenant que vous avez toutes les clés en main pour créer un article, nous allons passer à la personnalisation -du fichier de configuration. diff --git a/docs/fr/pelican-themes.rst b/docs/fr/pelican-themes.rst deleted file mode 100644 index 810fa785..00000000 --- a/docs/fr/pelican-themes.rst +++ /dev/null @@ -1,172 +0,0 @@ -pelican-themes -############## - - - -Description -=========== - -``pelican-themes`` est un outil en lignes de commandes pour gérer les thèmes de Pelican. - - -Utilisation: -"""""""""""" - -| pelican-themes [-h] [-l] [-i *chemin d'un thème* [*chemin d'un thème* ...]] -| [-r *nom d'un thème* [*nom d'un thème* ...]] -| [-s *chemin d'un thème* [*chemin d'un thème* ...]] [-v] [--version] - -Arguments: -"""""""""" - - --h, --help Afficher l'aide et quitter - --l, --list Montrer les thèmes installés - --i chemin, --install chemin Chemin(s) d'accès d'un ou plusieurs thème à installer - --r nom, --remove nom Noms d'un ou plusieurs thèmes à installer - --s chemin, --symlink chemin Fonctionne de la même façon que l'option ``--install``, mais crée un lien symbolique au lieu d'effectuer une copie du thème vers le répertoire des thèmes. - Utile pour le développement de thèmes. - --v, --verbose Sortie détaillée - ---version Affiche la version du script et quitte - - - -Exemples -======== - - -Lister les thèmes installés -""""""""""""""""""""""""""" - -``pelican-themes`` peut afficher les thèmes disponibles. - -Pour cela, vous pouvez utiliser l'option ``-l`` ou ``--list``, comme ceci: - -.. code-block:: console - - $ pelican-themes -l - notmyidea - two-column@ - simple - $ pelican-themes --list - notmyidea - two-column@ - simple - -Dans cet exemple, nous voyons qu'il y a trois thèmes d'installés: ``notmyidea``, ``simple`` and ``two-column``. - -``two-column`` est suivi d'un ``@`` par ce que c'est un lien symbolique (voir `Créer des liens symboliques`_). - -Notez que vous pouvez combiner l'option ``--list`` avec l'option ``--verbose``, pour afficher plus de détails: - -.. code-block:: console - - $ pelican-themes -v -l - /usr/local/lib/python2.6/dist-packages/pelican-2.6.0-py2.6.egg/pelican/themes/notmyidea - /usr/local/lib/python2.6/dist-packages/pelican-2.6.0-py2.6.egg/pelican/themes/two-column (symbolic link to `/home/skami/Dev/Python/pelican-themes/two-column') - /usr/local/lib/python2.6/dist-packages/pelican-2.6.0-py2.6.egg/pelican/themes/simple - - -Installer des thèmes -"""""""""""""""""""" - -Vous pouvez installer un ou plusieurs thèmes en utilisant l'option ``-i`` ou ``--install``. - -Cette option prends en argument le(s) chemin(s) d'accès du ou des thème(s) que vous voulez installer, et peut se combiner avec l'option ``--verbose``: - -.. code-block:: console - - # pelican-themes --install ~/Dev/Python/pelican-themes/notmyidea-cms --verbose - -.. code-block:: console - - # pelican-themes --install ~/Dev/Python/pelican-themes/notmyidea-cms\ - ~/Dev/Python/pelican-themes/martyalchin \ - --verbose - -.. code-block:: console - - # pelican-themes -vi ~/Dev/Python/pelican-themes/two-column - - -Supprimer des thèmes -"""""""""""""""""""" - -``pelican-themes`` peut aussi supprimer des thèmes précédemment installés grâce à l'option ``-r`` ou ``--remove``. - -Cette option prends en argument le ou les nom(s) des thèmes que vous voulez installer, et peux se combiner avec l'option ``--verbose``: - -.. code-block:: console - - # pelican-themes --remove two-column - -.. code-block:: console - - # pelican-themes -r martyachin notmyidea-cmd -v - - - - - -Créer des liens symboliques -""""""""""""""""""""""""""" - - -L'option ``-s`` ou ``--symlink`` de ``pelican-themes`` permet de lier symboliquement un thème. - -Cette option s'utilise exactement comme l'option ``--install``: - -.. code-block:: console - - # pelican-themes --symlink ~/Dev/Python/pelican-themes/two-column - -Dans l'exemple ci dessus, un lien symbolique pointant vers le thème ``two-column`` a été installé dans le répertoire des thèmes de Pelican, toute modification sur le thème ``two-column`` prendra donc effet immédiatement. - -Cela peut être pratique pour le développement de thèmes - -.. code-block:: console - - $ sudo pelican-themes -s ~/Dev/Python/pelican-themes/two-column - $ pelican ~/Blog/content -o /tmp/out -t two-column - $ firefox /tmp/out/index.html - $ vim ~/Dev/Pelican/pelican-themes/two-coumn/static/css/main.css - $ pelican ~/Blog/content -o /tmp/out -t two-column - $ cp /tmp/bg.png ~/Dev/Pelican/pelican-themes/two-coumn/static/img/bg.png - $ pelican ~/Blog/content -o /tmp/out -t two-column - $ vim ~/Dev/Pelican/pelican-themes/two-coumn/templates/index.html - $ pelican ~/Blog/content -o /tmp/out -t two-column - - -Notez que cette fonctionnalité nécessite d'avoir un système d'exploitation et un système de fichiers supportant les liens symboliques, elle n'est donc pas disponible sous Micro$oft®©™ Fenêtre®©™. - -Faire plusieurs choses à la fois -"""""""""""""""""""""""""""""""" - - -Les options ``--install``, ``--remove`` et ``--symlink`` peuvent être employées en même temps, ce qui permets de réaliser plusieurs opérations en même temps: - -.. code-block:: console - - # pelican-themes --remove notmyidea-cms two-column \ - --install ~/Dev/Python/pelican-themes/notmyidea-cms-fr \ - --symlink ~/Dev/Python/pelican-themes/two-column \ - --verbose - -Dans cette exemple, le thème ``notmyidea-cms`` sera remplacé par le thème ``notmyidea-cms-fr`` et le thème ``two-column`` sera lié symboliquement... - - - -À voir également -================ - -- http://docs.notmyidea.org/alexis/pelican/ -- ``/usr/share/doc/pelican/`` si vous avez installé Pelican par le `dépôt APT `_ - - - diff --git a/docs/fr/themes.rst b/docs/fr/themes.rst deleted file mode 100644 index 20d9d41f..00000000 --- a/docs/fr/themes.rst +++ /dev/null @@ -1,171 +0,0 @@ -.. _theming-pelican: - -Cette page est une traduction de la documentation originale, en anglais et -disponible `ici <../themes.html>`_. - -Comment créer des thèmes pour Pelican -##################################### - -Pelican utlise le très bon moteur de template `jinja2 `_ -pour produire de l'HTML. La syntaxe de jinja2 est vraiment très simple. Si vous -voulez créer votre propre thème, soyez libre de prendre inspiration sur le theme -"simple" qui est disponible `ici -`_ - -Structure -========= - -Pour réaliser votre propre thème vous devez respecter la structure suivante :: - - ├── static - │   ├── css - │   └── images - └── templates - ├── archives.html // pour afficher les archives - ├── article.html // généré pour chaque article - ├── categories.html // doit lister toutes les catégories - ├── category.html // généré pour chaque catégorie - ├── index.html // la page d'index, affiche tous les articles - ├── page.html // généré pour chaque page - ├── tag.html // généré pour chaque tag - └── tags.html // doit lister tous les tags. Peut être un nuage de tag. - - -* `static` contient tout le contenu statique. Il sera copié dans le dossier - `theme/static`. J'ai mis un dossier css et un image, mais ce sont juste des - exemples. Mettez ce dont vous avez besoin ici. - -* `templates` contient tous les templates qui vont être utiliser pour générer les - pages. J'ai juste mis les templates obligatoires ici, vous pouvez définir les - vôtres si cela vous aide à vous organiser pendant que vous réaliser le thème. - Vous pouvez par exemple utiliser les directives {% include %} et {% extends %} - de jinja2. - -Templates et variables -====================== - -Cela utilise une syntaxe simple, que vous pouvez insérer dans vos pages HTML. -Ce document décrit les templates qui doivent exister dans un thème, et quelles -variables seront passées à chaque template, au moment de le générer. - -Tous les templates recevront les variables définies dans votre fichier de -configuration, si elles sont en capitales. Vous pouvez y accéder directement. - -Variables communes ------------------- - -Toutes ces variables seront passées à chaque template. - -============= =================================================== -Variable Description -============= =================================================== -articles C'est la liste des articles, ordonnée décroissante - par date. Tous les éléments de la liste sont des - objets `Article`, vous pouvez donc accéder à leurs - propriétés (exemple : title, summary, author, etc). -dates La même liste d'articles, ordonnée croissante par - date. -tags Un dictionnaire contenant tous les tags (clés), et - la liste des articles correspondants à chacun - d'entre eux (valeur). -categories Un dictionnaire contenant toutes les catégories - (clés), et la liste des articles correspondants à - chacune d'entre elles (valeur). -pages La liste des pages. -============= =================================================== - -index.html ----------- - -La page d'accueil de votre blog, sera générée dans output/index.html. - -Si la pagination est activée, les pages suivantes seront à l'adresse -output/index`n`.html. - -=================== =================================================== -Variable Description -=================== =================================================== -articles_paginator Un objet paginator de la liste d'articles. -articles_page La page actuelle d'articles. -dates_paginator Un objet paginator de la liste d'articles, ordonné - par date croissante. -dates_pages La page actuelle d'articles, ordonnée par date - croissante. -page_name 'index'. -=================== =================================================== - -category.html -------------- - -Ce template sera généré pour chaque catégorie existante, et se retrouvera -finalement à output/category/`nom de la catégorie`.html. - -Si la pagination est activée, les pages suivantes seront disponibles à -l'adresse output/category/`nom de la catégorie``n`.html. - -=================== =================================================== -Variable Description -=================== =================================================== -category La catégorie qui est en train d'être générée. -articles Les articles dans cette catégorie. -dates Les articles dans cette catégorie, ordonnés par - date croissante. -articles_paginator Un objet paginator de la liste d'articles. -articles_page La page actuelle d'articles. -dates_paginator Un objet paginator de la liste d'articles, ordonné - par date croissante. -dates_pages La page actuelle d'articles, ordonnée par date - croissante. -page_name 'category/`nom de la catégorie`'. -=================== =================================================== - -article.html -------------- - -Ce template sera généré pour chaque article. Les fichiers .html seront -disponibles à output/`nom de l'article`.html. - -============= =================================================== -Variable Description -============= =================================================== -article L'objet article à afficher. -category Le nom de la catégorie de l'article actuel. -============= =================================================== - -page.html ---------- - -Pour chaque page ce template sera généré à l'adresse -output/`nom de la page`.html - -============= =================================================== -Variable Description -============= =================================================== -page L'objet page à afficher. Vous pouvez accéder à son - titre (title), slug, et son contenu (content). -============= =================================================== - -tag.html --------- - -Ce template sera généré pour chaque tag. Cela créera des fichiers .html à -l'adresse output/tag/`nom du tag`.html. - -Si la pagination est activée, les pages suivantes seront disponibles à -l'adresse output/tag/`nom du tag``n`.html - -=================== =================================================== -Variable Description -=================== =================================================== -tag Nom du tag à afficher. -articles Une liste des articles contenant ce tag. -dates Une liste des articles contenant ce tag, ordonnée - par date croissante. -articles_paginator Un objet paginator de la liste d'articles. -articles_page La page actuelle d'articles. -dates_paginator Un objet paginator de la liste d'articles, ordonné - par date croissante. -dates_pages La page actuelle d'articles, ordonnée par date - croissante. -page_name 'tag/`nom du tag`'. -=================== =================================================== diff --git a/docs/index.rst b/docs/index.rst index c2deb6de..aa30b1f0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -63,8 +63,6 @@ someone will almost always respond to your inquiry. Documentation ------------- -A French version of the documentation is available at :doc:`fr/index`. - .. toctree:: :maxdepth: 2 From 9a753f4fa97c6b086ba8129043b00d1fba55290e Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Fri, 9 May 2014 18:14:24 +0200 Subject: [PATCH 0257/1427] Fix intrasite links substitions in content The Content.__eq__ method would indirectly call _update_content too soon, resulting in failed intrasite links substitution This effectively reverts fd779267000ac539ee0a9ba5856d103fbbc7cd7c for pelican/contents.py, it was unnecessary anyways. Thanks to Strom for finding this. --- pelican/contents.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index c02047b8..615a7fd8 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -325,13 +325,6 @@ class Content(object): os.path.abspath(self.settings['PATH'])) ) - def __eq__(self, other): - """Compare with metadata and content of other Content object""" - return other and self.metadata == other.metadata and self.content == other.content - - # keep basic hashing functionality for caching to work - __hash__ = object.__hash__ - class Page(Content): mandatory_properties = ('title',) From 7cbf750329b6b6d42cfcca12449d6e6419fbf2fa Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Sat, 10 May 2014 17:38:58 +0200 Subject: [PATCH 0258/1427] Fix typo in command-line option description. --- pelican/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index d6417391..e9fef163 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -265,7 +265,7 @@ def parse_arguments(): parser.add_argument('-D', '--debug', action='store_const', const=logging.DEBUG, dest='verbosity', - help='Show all message, including debug messages.') + help='Show all messages, including debug messages.') parser.add_argument('--version', action='version', version=__version__, help='Print the pelican version and exit.') From 7313d327fb21fbdaa4c39819db6297f4cd635b2c Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Sun, 11 May 2014 18:14:58 -0700 Subject: [PATCH 0259/1427] Prepare for splitting up Getting Started docs --- docs/{getting_started.rst => content.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{getting_started.rst => content.rst} (100%) diff --git a/docs/getting_started.rst b/docs/content.rst similarity index 100% rename from docs/getting_started.rst rename to docs/content.rst From 86e11c619d0208d9bc8418821c2e2a31e6f991ea Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 12 May 2014 07:48:37 -0700 Subject: [PATCH 0260/1427] Split Getting Started docs into separate sections The "Getting Started" docs became overly long and unwieldy over time. This splits it into separate sections, including: * Quickstart * Installation * Writing content * Publish your site --- docs/content.rst | 307 ++-------------------------------------- docs/faq.rst | 12 +- docs/importer.rst | 6 +- docs/index.rst | 22 +-- docs/install.rst | 122 ++++++++++++++++ docs/pelican-themes.rst | 9 -- docs/publish.rst | 174 +++++++++++++++++++++++ docs/quickstart.rst | 73 ++++++++++ docs/settings.rst | 10 +- docs/themes.rst | 18 ++- 10 files changed, 420 insertions(+), 333 deletions(-) create mode 100644 docs/install.rst create mode 100644 docs/publish.rst create mode 100644 docs/quickstart.rst diff --git a/docs/content.rst b/docs/content.rst index 8ee37162..24fc6e9b 100644 --- a/docs/content.rst +++ b/docs/content.rst @@ -1,289 +1,8 @@ -Getting started +Writing content ############### -Installing Pelican -================== - -Pelican currently runs best on Python 2.7.x; earlier versions of Python are -not supported. There is provisional support for Python 3.3, although there may -be rough edges, particularly with regards to optional 3rd-party components. - -You can install Pelican via several different methods. The simplest is via -`pip `_:: - - $ pip install pelican - -If you don't have ``pip`` installed, an alternative method is -``easy_install``:: - - $ easy_install pelican - -(Keep in mind that operating systems will often require you to prefix the above -commands with ``sudo`` in order to install Pelican system-wide.) - -While the above is the simplest method, the recommended approach is to create -a virtual environment for Pelican via virtualenv_ before installing Pelican. -Assuming you have virtualenv_ installed, you can then open a new terminal -session and create a new virtual environment for Pelican:: - - $ virtualenv ~/virtualenvs/pelican - $ cd ~/virtualenvs/pelican - $ . bin/activate - -Once the virtual environment has been created and activated, Pelican can be -be installed via ``pip install pelican`` as noted above. Alternatively, if -you have the project source, you can install Pelican using the distutils -method:: - - $ cd path-to-Pelican-source - $ python setup.py install - -If you have Git installed and prefer to install the latest bleeding-edge -version of Pelican rather than a stable release, use the following command:: - - $ pip install -e git+https://github.com/getpelican/pelican.git#egg=pelican - -If you plan on using Markdown as a markup format, you'll need to install the -Markdown library as well:: - - $ pip install Markdown - -If you want to use AsciiDoc_ you need to install it from `source -`_ or use your operating -system's package manager. - -Basic usage ------------ - -Once Pelican is installed, you can use it to convert your Markdown or reST -content into HTML via the ``pelican`` command, specifying the path to your -content and (optionally) the path to your settings file:: - -$ pelican /path/to/your/content/ [-s path/to/your/settings.py] - -The above command will generate your site and save it in the ``output/`` -folder, using the default theme to produce a simple site. The default theme -consists of very simple HTML without styling and is provided so folks may use -it as a basis for creating their own themes. - -You can also tell Pelican to watch for your modifications, instead of -manually re-running it every time you want to see your changes. To enable this, -run the ``pelican`` command with the ``-r`` or ``--autoreload`` option. - -Pelican has other command-line switches available. Have a look at the help to -see all the options you can use:: - - $ pelican --help - -Continue reading below for more detail, and check out the Pelican wiki's -`Tutorials `_ page for -links to community-published tutorials. - -Viewing the generated files ---------------------------- - -The files generated by Pelican are static files, so you don't actually need -anything special to view them. You can use your browser to open the generated -HTML files directly:: - - firefox output/index.html - -Because the above method may have trouble locating your CSS and other linked -assets, running a simple web server using Python will often provide a more -reliable previewing experience:: - - cd output && python -m SimpleHTTPServer - -Once the ``SimpleHTTPServer`` has been started, you can preview your site at -http://localhost:8000/ - -Upgrading ---------- - -If you installed a stable Pelican release via ``pip`` or ``easy_install`` and -wish to upgrade to the latest stable release, you can do so by adding -``--upgrade`` to the relevant command. For pip, that would be:: - - $ pip install --upgrade pelican - -If you installed Pelican via distutils or the bleeding-edge method, simply -perform the same step to install the most recent version. - -Dependencies ------------- - -When Pelican is installed, the following dependent Python packages should be -automatically installed without any action on your part: - -* `feedgenerator `_, to generate the - Atom feeds -* `jinja2 `_, for templating support -* `pygments `_, for syntax highlighting -* `docutils `_, for supporting - reStructuredText as an input format -* `pytz `_, for timezone definitions -* `blinker `_, an object-to-object and - broadcast signaling system -* `unidecode `_, for ASCII - transliterations of Unicode text -* `six `_, for Python 2 and 3 compatibility - utilities -* `MarkupSafe `_, for a markup safe - string implementation -* `python-dateutil `_, to read - the date metadata - -If you want the following optional packages, you will need to install them -manually via ``pip``: - -* `markdown `_, for supporting Markdown as - an input format -* `typogrify `_, for - typographical enhancements - -Kickstart your site -=================== - -Once Pelican has been installed, you can create a skeleton project via the -``pelican-quickstart`` command, which begins by asking some questions about -your site:: - - $ pelican-quickstart - -Once you finish answering all the questions, your project will consist of the -following hierarchy (except for "pages", which you can optionally add yourself -if you plan to create non-chronological content):: - - yourproject/ - ├── content - │   └── (pages) - ├── output - ├── develop_server.sh - ├── fabfile.py - ├── Makefile - ├── pelicanconf.py # Main settings file - └── publishconf.py # Settings to use when ready to publish - -The next step is to begin to adding content to the *content* folder that has -been created for you. (See the **Writing content using Pelican** section below -for more information about how to format your content.) - -Once you have written some content to generate, you can use the ``pelican`` -command to generate your site, which will be placed in the output folder. - -Automation tools -================ - -While the ``pelican`` command is the canonical way to generate your site, -automation tools can be used to streamline the generation and publication -flow. One of the questions asked during the ``pelican-quickstart`` process -described above pertains to whether you want to automate site generation and -publication. If you answered "yes" to that question, a ``fabfile.py`` and -``Makefile`` will be generated in the root of your project. These files, -pre-populated with certain information gleaned from other answers provided -during the ``pelican-quickstart`` process, are meant as a starting point and -should be customized to fit your particular needs and usage patterns. If you -find one or both of these automation tools to be of limited utility, these -files can deleted at any time and will not affect usage of the canonical -``pelican`` command. - -Following are automation tools that "wrap" the ``pelican`` command and can -simplify the process of generating, previewing, and uploading your site. - -Fabric ------- - -The advantage of Fabric_ is that it is written in Python and thus can be used -in a wide range of environments. The downside is that it must be installed -separately. Use the following command to install Fabric, prefixing with -``sudo`` if your environment requires it:: - - $ pip install Fabric - -Take a moment to open the ``fabfile.py`` file that was generated in your -project root. You will see a number of commands, any one of which can be -renamed, removed, and/or customized to your liking. Using the out-of-the-box -configuration, you can generate your site via:: - - $ fab build - -If you'd prefer to have Pelican automatically regenerate your site every time a -change is detected (which is handy when testing locally), use the following -command instead:: - - $ fab regenerate - -To serve the generated site so it can be previewed in your browser at -http://localhost:8000/:: - - $ fab serve - -If during the ``pelican-quickstart`` process you answered "yes" when asked -whether you want to upload your site via SSH, you can use the following command -to publish your site via rsync over SSH:: - - $ fab publish - -These are just a few of the commands available by default, so feel free to -explore ``fabfile.py`` and see what other commands are available. More -importantly, don't hesitate to customize ``fabfile.py`` to suit your specific -needs and preferences. - -Make ----- - -A ``Makefile`` is also automatically created for you when you say "yes" to -the relevant question during the ``pelican-quickstart`` process. The advantage -of this method is that the ``make`` command is built into most POSIX systems -and thus doesn't require installing anything else in order to use it. The -downside is that non-POSIX systems (e.g., Windows) do not include ``make``, -and installing it on those systems can be a non-trivial task. - -If you want to use ``make`` to generate your site, run:: - - $ make html - -If you'd prefer to have Pelican automatically regenerate your site every time a -change is detected (which is handy when testing locally), use the following -command instead:: - - $ make regenerate - -To serve the generated site so it can be previewed in your browser at -http://localhost:8000/:: - - $ make serve - -Normally you would need to run ``make regenerate`` and ``make serve`` in two -separate terminal sessions, but you can run both at once via:: - - $ make devserver - -The above command will simultaneously run Pelican in regeneration mode as well -as serve the output at http://localhost:8000. Once you are done testing your -changes, you should stop the development server via:: - - $ ./develop_server.sh stop - -When you're ready to publish your site, you can upload it via the method(s) you -chose during the ``pelican-quickstart`` questionnaire. For this example, we'll -use rsync over ssh:: - - $ make rsync_upload - -That's it! Your site should now be live. - -(The default ``Makefile`` and ``devserver.sh`` scripts use the ``python`` and -``pelican`` executables to complete its tasks. If you want to use different -executables, such as ``python3``, you can set the ``PY`` and ``PELICAN`` -environment variables, respectively, to override the default executable names.) - - -Writing content using Pelican -============================= - Articles and pages ------------------- +================== Pelican considers "articles" to be chronological content, such as posts on a blog, and thus associated with a date. @@ -295,7 +14,7 @@ pages). .. _internal_metadata: File metadata -------------- +============= Pelican tries to be smart enough to get the information it needs from the file system (for instance, about the category of your articles), but some @@ -400,7 +119,7 @@ Please note that the metadata available inside your files takes precedence over the metadata extracted from the filename. Pages ------ +===== If you create a folder named ``pages`` inside the content folder, all the files in it will be used to generate static pages, such as **About** or @@ -416,7 +135,7 @@ things like making error pages that fit the generated theme of your site. .. _ref-linking-to-internal-content: Linking to internal content ---------------------------- +=========================== From Pelican 3.1 onwards, it is now possible to specify intra-site links to files in the *source content* hierarchy instead of files in the *generated* @@ -494,14 +213,14 @@ curly braces (``{}``). For example: ``|filename|an_article.rst``, ``|tag|tagname``, ``|category|foobar``. The syntax was changed from ``||`` to ``{}`` to avoid collision with Markdown extensions or reST directives. -Importing an existing blog --------------------------- +Importing an existing site +========================== -It is possible to import your blog from Dotclear, WordPress, and RSS feeds using -a simple script. See :ref:`import`. +It is possible to import your site from WordPress, Tumblr, Dotclear, and RSS +feeds using a simple script. See :ref:`import`. Translations ------------- +============ It is possible to translate articles. To do so, you need to add a ``lang`` meta attribute to your articles/pages and set a ``DEFAULT_LANG`` setting (which is @@ -559,7 +278,7 @@ which posts are translations:: .. _internal_pygments_options: Syntax highlighting -------------------- +=================== Pelican is able to provide colorized syntax highlighting for your code blocks. To do so, you have to use the following conventions inside your content files. @@ -641,14 +360,12 @@ If specified, settings for individual code blocks will override the defaults in your settings file. Publishing drafts ------------------ +================= If you want to publish an article as a draft (for friends to review before publishing, for example), you can add a ``Status: draft`` attribute to its metadata. That article will then be output to the ``drafts`` folder and not listed on the index page nor on any category or tag page. -.. _virtualenv: http://www.virtualenv.org/ .. _W3C ISO 8601: http://www.w3.org/TR/NOTE-datetime -.. _Fabric: http://fabfile.org/ .. _AsciiDoc: http://www.methods.co.nz/asciidoc/ diff --git a/docs/faq.rst b/docs/faq.rst index bf468c51..3a5cfec5 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -65,9 +65,11 @@ How do I create my own theme? Please refer to :ref:`theming-pelican`. -I'm using Markdown and getting ``No valid files found in content`` errors. +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``. Markdown is not a hard dependency for Pelican, so if you have content in Markdown format, you will need to explicitly install the Markdown library. You can do so by typing the following command, prepending ``sudo`` if @@ -75,10 +77,6 @@ permissions require it:: pip install markdown -If you don't have ``pip`` installed, consider installing it via:: - - easy_install pip - Can I use arbitrary metadata in my templates? ============================================== @@ -157,8 +155,8 @@ disable all feed generation, you only need to specify the following settings:: CATEGORY_FEED_ATOM = None TRANSLATION_FEED_ATOM = None -Please note that ``None`` and ``''`` are not the same thing. The word ``None`` -should not be surrounded by quotes. +The word ``None`` should not be surrounded by quotes. Please note that ``None`` +and ``''`` are not the same thing. I'm getting a warning about feeds generated without SITEURL being set properly ============================================================================== diff --git a/docs/importer.rst b/docs/importer.rst index b1d1b926..309ca144 100644 --- a/docs/importer.rst +++ b/docs/importer.rst @@ -1,9 +1,7 @@ .. _import: -================================= - Import from other blog software -================================= - +Importing an existing site +########################## Description =========== diff --git a/docs/index.rst b/docs/index.rst index aa30b1f0..36a3282b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,30 +10,32 @@ Pelican |release| Were you looking for version |last_stable| documentation? -Pelican is a static site generator, written in Python_. +Pelican is a static site generator, written in Python_. Highlights include: -* Write your content directly with your editor of choice (vim!) +* Write your content directly with your editor of choice in reStructuredText_, Markdown_, or AsciiDoc_ formats * Includes a simple CLI tool to (re)generate your site * Easy to interface with distributed version control systems and web hooks * Completely static output is easy to host anywhere +Ready to get started? Check out the :doc:`Quickstart` guide. + Features -------- Pelican |version| currently supports: * Articles (e.g., blog posts) and pages (e.g., "About", "Projects", "Contact") -* Comments, via an external service (Disqus). (Please note that while - useful, Disqus is an external service, and thus the comment data will be - somewhat outside of your control and potentially subject to data loss.) +* Comments, via an external service (Disqus). If you prefer to have more + control over your comment data, self-hosted comments are another option. + Check out the `Pelican Plugins`_ repository for more details. * Theming support (themes are created using Jinja2_ templates) * Publication of articles in multiple languages * Atom/RSS feeds * Code syntax highlighting * Import from WordPress, Dotclear, or RSS feeds * Integration with external tools: Twitter, Google Analytics, etc. (optional) -* Fast rebuild times thanks to content caching and selective output writing. +* Fast rebuild times thanks to content caching and selective output writing Why the name "Pelican"? ----------------------- @@ -66,16 +68,19 @@ Documentation .. toctree:: :maxdepth: 2 - getting_started + quickstart + install + content + publish settings themes plugins - internals pelican-themes importer faq tips contribute + internals report changelog @@ -88,5 +93,6 @@ Documentation .. _Jinja2: http://jinja.pocoo.org/ .. _`Pelican documentation`: http://docs.getpelican.com/latest/ .. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html +.. _`Pelican plugins`: https://github.com/getpelican/pelican-plugins .. _`#pelican on Freenode`: irc://irc.freenode.net/pelican .. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4 diff --git a/docs/install.rst b/docs/install.rst new file mode 100644 index 00000000..34cd33ea --- /dev/null +++ b/docs/install.rst @@ -0,0 +1,122 @@ +Installing Pelican +################## + +Pelican currently runs best on Python 2.7.x; earlier versions of Python are +not supported. There is provisional support for Python 3.3+, although there may +be rough edges, particularly with regards to optional 3rd-party components. + +You can install Pelican via several different methods. The simplest is via +`pip `_:: + + pip install pelican + +(Keep in mind that operating systems will often require you to prefix the above +command with ``sudo`` in order to install Pelican system-wide.) + +While the above is the simplest method, the recommended approach is to create +a virtual environment for Pelican via virtualenv_ before installing Pelican. +Assuming you have virtualenv_ installed, you can then open a new terminal +session and create a new virtual environment for Pelican:: + + virtualenv ~/virtualenvs/pelican + cd ~/virtualenvs/pelican + source bin/activate + +Once the virtual environment has been created and activated, Pelican can be +be installed via ``pip install pelican`` as noted above. Alternatively, if +you have the project source, you can install Pelican using the distutils +method:: + + cd path-to-Pelican-source + python setup.py install + +If you have Git installed and prefer to install the latest bleeding-edge +version of Pelican rather than a stable release, use the following command:: + + pip install -e "git+https://github.com/getpelican/pelican.git#egg=pelican" + +Once Pelican is installed, you can run ``pelican --help`` to see basic usage +options. For more detail, refer to the :doc:`Publish` section. + +Optional packages +----------------- + +If you plan on using `Markdown `_ as a +markup format, you'll need to install the Markdown library:: + + pip install Markdown + +Typographical enhancements can be enabled in your settings file, but first the +requisite `Typogrify `_ library must be +installed:: + + pip install typogrify + +If you want to use AsciiDoc_ you need to install it from `source +`_ or use your operating +system's package manager. + +Dependencies +------------ + +When Pelican is installed, the following dependent Python packages should be +automatically installed without any action on your part: + +* `feedgenerator `_, to generate the + Atom feeds +* `jinja2 `_, for templating support +* `pygments `_, for syntax highlighting +* `docutils `_, for supporting + reStructuredText as an input format +* `pytz `_, for timezone definitions +* `blinker `_, an object-to-object and + broadcast signaling system +* `unidecode `_, for ASCII + transliterations of Unicode text +* `six `_, for Python 2 and 3 compatibility + utilities +* `MarkupSafe `_, for a markup safe + string implementation +* `python-dateutil `_, to read + the date metadata + +Upgrading +--------- + +If you installed a stable Pelican release via ``pip`` or ``easy_install`` and +wish to upgrade to the latest stable release, you can do so by adding +``--upgrade`` to the relevant command. For pip, that would be:: + + pip install --upgrade pelican + +If you installed Pelican via distutils or the bleeding-edge method, simply +perform the same step to install the most recent version. + +Kickstart your site +=================== + +Once Pelican has been installed, you can create a skeleton project via the +``pelican-quickstart`` command, which begins by asking some questions about +your site:: + + pelican-quickstart + +Once you finish answering all the questions, your project will consist of the +following hierarchy (except for *pages* — shown in parentheses below — which you +can optionally add yourself if you plan to create non-chronological content):: + + yourproject/ + ├── content + │   └── (pages) + ├── output + ├── develop_server.sh + ├── fabfile.py + ├── Makefile + ├── pelicanconf.py # Main settings file + └── publishconf.py # Settings to use when ready to publish + +The next step is to begin to adding content to the *content* folder that has +been created for you. + +.. _virtualenv: http://www.virtualenv.org/ +.. _AsciiDoc: http://www.methods.co.nz/asciidoc/ diff --git a/docs/pelican-themes.rst b/docs/pelican-themes.rst index 23be8355..7090c648 100644 --- a/docs/pelican-themes.rst +++ b/docs/pelican-themes.rst @@ -153,12 +153,3 @@ The ``--install``, ``--remove`` and ``--symlink`` option are not mutually exclus --verbose In this example, the theme ``notmyidea-cms`` is replaced by the theme ``notmyidea-cms-fr`` - - - - -See also -======== - -- http://docs.notmyidea.org/alexis/pelican/ -- ``/usr/share/doc/pelican/`` if you have installed Pelican using the `APT repository `_ diff --git a/docs/publish.rst b/docs/publish.rst new file mode 100644 index 00000000..266009e4 --- /dev/null +++ b/docs/publish.rst @@ -0,0 +1,174 @@ +Publish your site +################# + +Site generation +=============== + +Once Pelican is installed and you have some content (e.g., in Markdown or reST +format), you can convert your content into HTML via the ``pelican`` command, +specifying the path to your content and (optionally) the path to your +:doc:`settings` file:: + + pelican /path/to/your/content/ [-s path/to/your/settings.py] + +The above command will generate your site and save it in the ``output/`` +folder, using the default theme to produce a simple site. The default theme +consists of very simple HTML without styling and is provided so folks may use +it as a basis for creating their own themes. + +You can also tell Pelican to watch for your modifications, instead of +manually re-running it every time you want to see your changes. To enable this, +run the ``pelican`` command with the ``-r`` or ``--autoreload`` option. + +Pelican has other command-line switches available. Have a look at the help to +see all the options you can use:: + + pelican --help + +Viewing the generated files +--------------------------- + +The files generated by Pelican are static files, so you don't actually need +anything special to view them. You can use your browser to open the generated +HTML files directly:: + + firefox output/index.html + +Because the above method may have trouble locating your CSS and other linked +assets, running a simple web server using Python will often provide a more +reliable previewing experience:: + + cd output + python -m SimpleHTTPServer + +Once the ``SimpleHTTPServer`` has been started, you can preview your site at +http://localhost:8000/ + +Deployment +========== + +After you have generated your site, previewed it in your local development +environment, and are ready to deploy it to production, you might first +re-generate your site with any production-specific settings (e.g., analytics +feeds, etc.) that you may have defined:: + + pelican content -s publishconf.py + +The steps for deploying your site will depend on where it will be hosted. +If you have SSH access to a server running Nginx or Apache, you might use the +``rsync`` tool to transmit your site files:: + + rsync --avc --delete output/ host.example.com:/var/www/your-site/ + +There are many other deployment options, some of which can be configured when +first setting up your site via the ``pelican-quickstart`` command. See the +:doc:`Tips` page for detail on publishing via GitHub Pages. + +Automation +========== + +While the ``pelican`` command is the canonical way to generate your site, +automation tools can be used to streamline the generation and publication +flow. One of the questions asked during the ``pelican-quickstart`` process +pertains to whether you want to automate site generation and publication. +If you answered "yes" to that question, a ``fabfile.py`` and +``Makefile`` will be generated in the root of your project. These files, +pre-populated with certain information gleaned from other answers provided +during the ``pelican-quickstart`` process, are meant as a starting point and +should be customized to fit your particular needs and usage patterns. If you +find one or both of these automation tools to be of limited utility, these +files can deleted at any time and will not affect usage of the canonical +``pelican`` command. + +Following are automation tools that "wrap" the ``pelican`` command and can +simplify the process of generating, previewing, and uploading your site. + +Fabric +------ + +The advantage of Fabric_ is that it is written in Python and thus can be used +in a wide range of environments. The downside is that it must be installed +separately. Use the following command to install Fabric, prefixing with +``sudo`` if your environment requires it:: + + pip install Fabric + +Take a moment to open the ``fabfile.py`` file that was generated in your +project root. You will see a number of commands, any one of which can be +renamed, removed, and/or customized to your liking. Using the out-of-the-box +configuration, you can generate your site via:: + + fab build + +If you'd prefer to have Pelican automatically regenerate your site every time a +change is detected (which is handy when testing locally), use the following +command instead:: + + fab regenerate + +To serve the generated site so it can be previewed in your browser at +http://localhost:8000/:: + + fab serve + +If during the ``pelican-quickstart`` process you answered "yes" when asked +whether you want to upload your site via SSH, you can use the following command +to publish your site via rsync over SSH:: + + fab publish + +These are just a few of the commands available by default, so feel free to +explore ``fabfile.py`` and see what other commands are available. More +importantly, don't hesitate to customize ``fabfile.py`` to suit your specific +needs and preferences. + +Make +---- + +A ``Makefile`` is also automatically created for you when you say "yes" to +the relevant question during the ``pelican-quickstart`` process. The advantage +of this method is that the ``make`` command is built into most POSIX systems +and thus doesn't require installing anything else in order to use it. The +downside is that non-POSIX systems (e.g., Windows) do not include ``make``, +and installing it on those systems can be a non-trivial task. + +If you want to use ``make`` to generate your site, run:: + + make html + +If you'd prefer to have Pelican automatically regenerate your site every time a +change is detected (which is handy when testing locally), use the following +command instead:: + + make regenerate + +To serve the generated site so it can be previewed in your browser at +http://localhost:8000/:: + + make serve + +Normally you would need to run ``make regenerate`` and ``make serve`` in two +separate terminal sessions, but you can run both at once via:: + + make devserver + +The above command will simultaneously run Pelican in regeneration mode as well +as serve the output at http://localhost:8000. Once you are done testing your +changes, you should stop the development server via:: + + ./develop_server.sh stop + +When you're ready to publish your site, you can upload it via the method(s) you +chose during the ``pelican-quickstart`` questionnaire. For this example, we'll +use rsync over ssh:: + + make rsync_upload + +That's it! Your site should now be live. + +(The default ``Makefile`` and ``devserver.sh`` scripts use the ``python`` and +``pelican`` executables to complete its tasks. If you want to use different +executables, such as ``python3``, you can set the ``PY`` and ``PELICAN`` +environment variables, respectively, to override the default executable names.) + +.. _Fabric: http://fabfile.org/ diff --git a/docs/quickstart.rst b/docs/quickstart.rst new file mode 100644 index 00000000..44f99dd2 --- /dev/null +++ b/docs/quickstart.rst @@ -0,0 +1,73 @@ +Quickstart +########## + +Reading through all the documentation is highly recommended, but for the truly +impatient, following are some quick steps to get started. + +Installation +------------ + +Install Pelican on Python 2.7.x or Python 3.3+ by running the following command +in your preferred terminal, prefixing with ``sudo`` if permissions warrant:: + + pip install pelican markdown + +Create a project +---------------- + +First, choose a name for your project, create an appropriately-named directory +for your it, and switch to that directory:: + + mkdir -p ~/projects/yoursite + cd ~/projects/yoursite + +Create a skeleton project via the ``pelican-quickstart`` command, which begins +by asking some questions about your site:: + + pelican-quickstart + +For questions that have default values denoted in brackets, feel free to use +the Return key to accept those default values. When asked for your URL prefix, +enter your domain name as indicated (e.g., ``http://example.com``). + +Create an article +----------------- + +You cannot run Pelican until you have created some content. Use your preferred +text editor to create your first article with the following content:: + + Title: My First Review + Date: 2010-12-03 10:20 + Category: Review + + Following is a review of my favorite mechanical keyboard. + +Given that this example article is in Markdown format, save it as +``~/projects/yoursite/content/keyboard-review.md``. + +Generate your site +------------------ + +From your project directory, run the ``pelican`` command to generate your site:: + + pelican content + +Your site has now been generated inside the ``output`` directory. (You may see a +warning related to feeds, but that is normal when developing locally and can be +ignored for now.) + +Preview your site +----------------- + +Open a new terminal session and run the following commands to switch to your +``output`` directory and launch Python's built-in web server:: + + cd ~/projects/yoursite/output + python -m SimpleHTTPServer + +Preview your site by navigating to http://localhost:8000/ in your browser. + +Continue reading the other documentation sections for more detail, and check out +the Pelican wiki's Tutorials_ page for links to community-published tutorials. + +.. _Tutorials: https://github.com/getpelican/pelican/wiki/Tutorials diff --git a/docs/settings.rst b/docs/settings.rst index 2782977c..7a69784e 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -1,10 +1,10 @@ Settings ######## -Pelican is configurable thanks to a configuration file you can pass to +Pelican is configurable thanks to a settings file you can pass to the command line:: - $ pelican content -s path/to/your/settingsfile.py + pelican content -s path/to/your/settingsfile.py (If you used the ``pelican-quickstart`` command, your primary settings file will be named ``pelicanconf.py`` by default.) @@ -201,8 +201,8 @@ for URL formation: *relative* and *absolute*. Relative URLs are useful when testing locally, and absolute URLs are reliable and most useful when publishing. One method of supporting both is to have one Pelican configuration file for local development and another for publishing. To see an example of this -type of setup, use the ``pelican-quickstart`` script as described at the top of -the :doc:`Getting Started ` page, which will produce two separate +type of setup, use the ``pelican-quickstart`` script as described in the +:doc:`Installation ` section, which will produce two separate configuration files for local development and publishing, respectively. You can customize the URLs and locations where files will be saved. The @@ -603,7 +603,7 @@ For example:: Translations ============ -Pelican offers a way to translate articles. See the :doc:`Getting Started ` section for +Pelican offers a way to translate articles. See the :doc:`Content ` section for more information. ======================================================== ===================================================== diff --git a/docs/themes.rst b/docs/themes.rst index b029e816..7fcba671 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -1,13 +1,21 @@ .. _theming-pelican: -How to create themes for Pelican -################################ +Creating themes +############### -Pelican uses the great `Jinja2 `_ templating engine to -generate its HTML output. Jinja2 syntax is really simple. If you want to -create your own theme, feel free to take inspiration from the `"simple" theme +To generate its HTML output, Pelican uses the `Jinja `_ +templating engine due to its flexibility and straightforward syntax. If you want +to create your own theme, feel free to take inspiration from the `"simple" theme `_. +To generate your site using a theme you have created (or downloaded manually and +then modified), you can specify that theme via the ``-t`` flag:: + + pelican content -s pelicanconf.py -t /projects/your-site/themes/your-theme + +If you'd rather not specify the theme on every invocation, you can define +``THEME`` in your settings to point to the location of your preferred theme. + Structure ========= From 9d2a129832174c8a65c615148cfbc0a650ecb85c Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 13 May 2014 07:18:33 -0700 Subject: [PATCH 0261/1427] If PATH is undefined, Pelican uses PWD as content --- docs/settings.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 7a69784e..4701e92d 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -94,7 +94,7 @@ Setting name (followed by default value, if any) ``READERS = {}`` A dictionary of file extensions / Reader classes for Pelican to process or ignore. For example, to avoid processing .html files, set: ``READERS = {'html': None}``. To add a custom reader for the - `foo` extension, set: ``READERS = {'foo': FooReader}`` + ``foo`` extension, set: ``READERS = {'foo': FooReader}`` ``IGNORE_FILES = ['.#*']`` A list of file globbing patterns to match against the source files to be ignored by the processor. For example, the default ``['.#*']`` will ignore emacs lock files. @@ -108,10 +108,12 @@ Setting name (followed by default value, if any) include them explicitly and enumerate the full list of desired Markdown extensions.) ``OUTPUT_PATH = 'output/'`` Where to output the generated files. -``PATH = None`` Path to content directory to be processed by Pelican. -``PAGE_DIR = 'pages'`` Directory to look at for pages, relative to `PATH`. +``PATH`` Path to content directory to be processed by Pelican. If undefined, + and content path is not specified via an argument to the ``pelican`` + command, Pelican will use the current working directory. +``PAGE_DIR = 'pages'`` Directory to look at for pages, relative to ``PATH``. ``PAGE_EXCLUDES = ()`` A list of directories to exclude when looking for pages. -``ARTICLE_DIR = ''`` Directory to look at for articles, relative to `PATH`. +``ARTICLE_DIR = ''`` Directory to look at for articles, relative to ``PATH``. ``ARTICLE_EXCLUDES = ('pages',)`` A list of directories to exclude when looking for articles. ``OUTPUT_SOURCES = False`` Set to True if you want to copy the articles and pages in their original format (e.g. Markdown or reStructuredText) to the From bf6a4ad74741a954fecefa4536637190ef23fb5e Mon Sep 17 00:00:00 2001 From: Vincent Jousse Date: Wed, 14 May 2014 11:11:58 +0200 Subject: [PATCH 0262/1427] Add missing methods --- pelican/tests/test_generators.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 659383ac..07871cef 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -58,6 +58,12 @@ class TestArticlesGenerator(unittest.TestCase): cls.generator.generate_context() cls.articles = cls.distill_articles(cls.generator.articles) + def setUp(self): + self.temp_cache = mkdtemp(prefix='pelican_cache.') + + def tearDown(self): + rmtree(self.temp_cache) + @staticmethod def distill_articles(articles): return [[article.title, article.status, article.category.name, From d635a347d1a5bb7336edb712356237b141efb4d1 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Mon, 21 Apr 2014 11:36:17 +0200 Subject: [PATCH 0263/1427] move {ARTICLE,PAGE}_DIR -> {ARTICLE,PAGE}_PATHS Instead of one path a list can be given. This is due to popular request. Should help people not wanting to use Pelican for blogging. Maintain backward compatibility though. Thanks to @ingwinlu for pointing out the change in StaticGenerator. --- docs/settings.rst | 4 +-- pelican/generators.py | 60 +++++++++++++++----------------- pelican/settings.py | 26 ++++++++++---- pelican/tests/test_generators.py | 2 +- 4 files changed, 51 insertions(+), 41 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 4701e92d..33117a7f 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -111,9 +111,9 @@ Setting name (followed by default value, if any) ``PATH`` Path to content directory to be processed by Pelican. If undefined, and content path is not specified via an argument to the ``pelican`` command, Pelican will use the current working directory. -``PAGE_DIR = 'pages'`` Directory to look at for pages, relative to ``PATH``. +``PAGE_PATHS = ['pages']`` A list of directories to look at for pages, relative to ``PATH``. ``PAGE_EXCLUDES = ()`` A list of directories to exclude when looking for pages. -``ARTICLE_DIR = ''`` Directory to look at for articles, relative to ``PATH``. +``ARTICLE_PATHS = ['']`` A list of directories to look at for articles, relative to ``PATH``. ``ARTICLE_EXCLUDES = ('pages',)`` A list of directories to exclude when looking for articles. ``OUTPUT_SOURCES = False`` Set to True if you want to copy the articles and pages in their original format (e.g. Markdown or reStructuredText) to the diff --git a/pelican/generators.py b/pelican/generators.py index 7c6ba66b..deb237a2 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -110,29 +110,30 @@ class Generator(object): return True return False - def get_files(self, path, exclude=[], extensions=None): + def get_files(self, paths, exclude=[], extensions=None): """Return a list of files to use, based on rules - :param path: the path to search (relative to self.path) + :param paths: the list pf paths to search (relative to self.path) :param exclude: the list of path to exclude :param extensions: the list of allowed extensions (if False, all extensions are allowed) """ files = [] - root = os.path.join(self.path, path) + for path in paths: + root = os.path.join(self.path, path) - if os.path.isdir(root): - for dirpath, dirs, temp_files in os.walk(root, followlinks=True): - for e in exclude: - if e in dirs: - dirs.remove(e) - reldir = os.path.relpath(dirpath, self.path) - for f in temp_files: - fp = os.path.join(reldir, f) - if self._include_path(fp, extensions): - files.append(fp) - elif os.path.exists(root) and self._include_path(path, extensions): - files.append(path) # can't walk non-directories + if os.path.isdir(root): + for dirpath, dirs, temp_files in os.walk(root, followlinks=True): + for e in exclude: + if e in dirs: + dirs.remove(e) + reldir = os.path.relpath(dirpath, self.path) + for f in temp_files: + fp = os.path.join(reldir, f) + if self._include_path(fp, extensions): + files.append(fp) + elif os.path.exists(root) and self._include_path(path, extensions): + files.append(path) # can't walk non-directories return files def add_source_path(self, content): @@ -462,7 +463,7 @@ class ArticlesGenerator(CachingGenerator): all_articles = [] all_drafts = [] for f in self.get_files( - self.settings['ARTICLE_DIR'], + self.settings['ARTICLE_PATHS'], exclude=self.settings['ARTICLE_EXCLUDES']): article = self.get_cached_data(f, None) if article is None: @@ -586,7 +587,7 @@ class PagesGenerator(CachingGenerator): all_pages = [] hidden_pages = [] for f in self.get_files( - self.settings['PAGE_DIR'], + self.settings['PAGE_PATHS'], exclude=self.settings['PAGE_EXCLUDES']): page = self.get_cached_data(f, None) if page is None: @@ -660,20 +661,17 @@ class StaticGenerator(Generator): def generate_context(self): self.staticfiles = [] - - # walk static paths - for static_path in self.settings['STATIC_PATHS']: - for f in self.get_files( - static_path, extensions=False): - static = self.readers.read_file( - base_path=self.path, path=f, content_class=Static, - fmt='static', context=self.context, - preread_signal=signals.static_generator_preread, - preread_sender=self, - context_signal=signals.static_generator_context, - context_sender=self) - self.staticfiles.append(static) - self.add_source_path(static) + for f in self.get_files(self.settings['STATIC_PATHS'], + extensions=False): + static = self.readers.read_file( + base_path=self.path, path=f, content_class=Static, + fmt='static', context=self.context, + preread_signal=signals.static_generator_preread, + preread_sender=self, + context_signal=signals.static_generator_context, + context_sender=self) + self.staticfiles.append(static) + self.add_source_path(static) self._update_context(('staticfiles',)) signals.static_generator_finalized.send(self) diff --git a/pelican/settings.py b/pelican/settings.py index f759ff9e..219ebbd0 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -29,9 +29,9 @@ DEFAULT_THEME = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'themes', 'notmyidea') DEFAULT_CONFIG = { 'PATH': os.curdir, - 'ARTICLE_DIR': '', + 'ARTICLE_PATHS': [''], 'ARTICLE_EXCLUDES': ('pages',), - 'PAGE_DIR': 'pages', + 'PAGE_PATHS': ['pages'], 'PAGE_EXCLUDES': (), 'THEME': DEFAULT_THEME, 'OUTPUT_PATH': 'output', @@ -311,6 +311,16 @@ def configure_settings(settings): key=lambda r: r[0], ) + # move {ARTICLE,PAGE}_DIR -> {ARTICLE,PAGE}_PATHS + for key in ['ARTICLE', 'PAGE']: + old_key = key + '_DIR' + new_key = key + '_PATHS' + if old_key in settings: + logger.warning('Deprecated setting {}, moving it to {} list'.format( + old_key, new_key)) + settings[new_key] = [settings[old_key]] # also make a list + del settings[old_key] + # Save people from accidentally setting a string rather than a list path_keys = ( 'ARTICLE_EXCLUDES', @@ -324,13 +334,15 @@ def configure_settings(settings): 'PLUGINS', 'STATIC_PATHS', 'THEME_STATIC_PATHS', + 'ARTICLE_PATHS', + 'PAGE_PATHS', ) for PATH_KEY in filter(lambda k: k in settings, path_keys): - if isinstance(settings[PATH_KEY], six.string_types): - logger.warning("Detected misconfiguration with %s setting " - "(must be a list), falling back to the default" - % PATH_KEY) - settings[PATH_KEY] = DEFAULT_CONFIG[PATH_KEY] + if isinstance(settings[PATH_KEY], six.string_types): + logger.warning("Detected misconfiguration with %s setting " + "(must be a list), falling back to the default" + % PATH_KEY) + settings[PATH_KEY] = DEFAULT_CONFIG[PATH_KEY] for old, new, doc in [ ('LESS_GENERATOR', 'the Webassets plugin', None), diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 7b79e8f3..668a0093 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -372,8 +372,8 @@ class TestPageGenerator(unittest.TestCase): def test_generate_context(self): settings = get_settings(filenames={}) - settings['PAGE_DIR'] = 'TestPages' # relative to CUR_DIR settings['CACHE_PATH'] = self.temp_cache + settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR settings['DEFAULT_DATE'] = (1970, 1, 1) generator = PagesGenerator( From 21882fd4a00ffdcee7029d7d5ee5ed355ef34a94 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Wed, 14 May 2014 14:06:58 +0200 Subject: [PATCH 0264/1427] Fix #1344 move PLUGIN_PATH -> PLUGIN_PATHS Pelican uses *_PATHS names for settings that represent a list of paths. --- docs/plugins.rst | 6 +++--- docs/settings.rst | 1 + pelican/__init__.py | 4 ++-- pelican/settings.py | 20 ++++++++++++-------- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index 717019a8..a13d9dce 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -21,10 +21,10 @@ Alternatively, another method is to import them and add them to the list:: PLUGINS = [myplugin,] If your plugins are not in an importable path, you can specify a list of paths -via the ``PLUGIN_PATH`` setting. As shown in the following example, paths in -the ``PLUGIN_PATH`` list can be absolute or relative to the settings file:: +via the ``PLUGIN_PATHS`` setting. As shown in the following example, paths in +the ``PLUGIN_PATHS`` list can be absolute or relative to the settings file:: - PLUGIN_PATH = ["plugins", "/srv/pelican/plugins"] + PLUGIN_PATHS = ["plugins", "/srv/pelican/plugins"] PLUGINS = ["assets", "liquid_tags", "sitemap"] Where to find plugins diff --git a/docs/settings.rst b/docs/settings.rst index 33117a7f..34245ff4 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -125,6 +125,7 @@ Setting name (followed by default value, if any) not. Only set this to ``True`` when developing/testing and only if you fully understand the effect it can have on links/feeds. ``PLUGINS = []`` The list of plugins to load. See :ref:`plugins`. +``PLUGIN_PATHS = []`` A list of directories where to look for plugins. See :ref:`plugins`. ``SITENAME = 'A Pelican Blog'`` Your site name ``SITEURL`` Base URL of your website. Not defined by default, so it is best to specify your SITEURL; if you do not, feeds diff --git a/pelican/__init__.py b/pelican/__init__.py index e9fef163..082e5a58 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -63,9 +63,9 @@ class Pelican(object): def init_plugins(self): self.plugins = [] - logger.debug('Temporarily adding PLUGIN_PATH to system path') + logger.debug('Temporarily adding PLUGIN_PATHS to system path') _sys_path = sys.path[:] - for pluginpath in self.settings['PLUGIN_PATH']: + for pluginpath in self.settings['PLUGIN_PATHS']: sys.path.insert(0, pluginpath) for plugin in self.settings['PLUGINS']: # if it's a string, then import it diff --git a/pelican/settings.py b/pelican/settings.py index 219ebbd0..c93050ad 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -114,7 +114,7 @@ DEFAULT_CONFIG = { 'ARTICLE_PERMALINK_STRUCTURE': '', 'TYPOGRIFY': False, 'SUMMARY_MAX_LENGTH': 50, - 'PLUGIN_PATH': [], + 'PLUGIN_PATHS': [], 'PLUGINS': [], 'PYGMENTS_RST_OPTIONS': {}, 'TEMPLATE_PAGES': {}, @@ -147,13 +147,17 @@ def read_settings(path=None, override=None): if p not in ('THEME') or os.path.exists(absp): local_settings[p] = absp - if isinstance(local_settings['PLUGIN_PATH'], six.string_types): - logger.warning("Defining %s setting as string has been deprecated (should be a list)" % 'PLUGIN_PATH') - local_settings['PLUGIN_PATH'] = [local_settings['PLUGIN_PATH']] - else: - if 'PLUGIN_PATH' in local_settings and local_settings['PLUGIN_PATH'] is not None: - local_settings['PLUGIN_PATH'] = [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_PATH']] + if 'PLUGIN_PATH' in local_settings: + logger.warning('PLUGIN_PATH setting has been replaced by ' + 'PLUGIN_PATHS, moving it to the new setting name.') + local_settings['PLUGIN_PATHS'] = local_settings['PLUGIN_PATH'] + del local_settings['PLUGIN_PATH'] + if isinstance(local_settings['PLUGIN_PATHS'], six.string_types): + logger.warning("Defining %s setting as string has been deprecated (should be a list)" % 'PLUGIN_PATHS') + 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']] else: local_settings = copy.deepcopy(DEFAULT_CONFIG) From 6aa0e4346de498ada234f772163579e677cfd10b Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Wed, 14 May 2014 14:30:21 +0200 Subject: [PATCH 0265/1427] Add {PAGE,ARTICLE}_PATHS to {ARTICLE,PAGE}_EXCLUDES automatically This makes it easier for someone to change PAGE_PATHS without the need to change ARTICLE_EXCLUDES accordingly. --- docs/settings.rst | 6 ++++-- pelican/settings.py | 16 ++++++++++++++-- pelican/tests/test_settings.py | 5 ++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 34245ff4..fd5f488b 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -112,9 +112,11 @@ Setting name (followed by default value, if any) and content path is not specified via an argument to the ``pelican`` command, Pelican will use the current working directory. ``PAGE_PATHS = ['pages']`` A list of directories to look at for pages, relative to ``PATH``. -``PAGE_EXCLUDES = ()`` A list of directories to exclude when looking for pages. +``PAGE_EXCLUDES = []`` A list of directories to exclude when looking for pages in addition + to ``ARTICLE_PATHS``. ``ARTICLE_PATHS = ['']`` A list of directories to look at for articles, relative to ``PATH``. -``ARTICLE_EXCLUDES = ('pages',)`` A list of directories to exclude when looking for articles. +``ARTICLE_EXCLUDES = []`` A list of directories to exclude when looking for articles in addition + to ``PAGE_PATHS``. ``OUTPUT_SOURCES = False`` Set to True if you want to copy the articles and pages in their original format (e.g. Markdown or reStructuredText) to the specified ``OUTPUT_PATH``. diff --git a/pelican/settings.py b/pelican/settings.py index c93050ad..69ade05f 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -30,9 +30,9 @@ DEFAULT_THEME = os.path.join(os.path.dirname(os.path.abspath(__file__)), DEFAULT_CONFIG = { 'PATH': os.curdir, 'ARTICLE_PATHS': [''], - 'ARTICLE_EXCLUDES': ('pages',), + 'ARTICLE_EXCLUDES': [], 'PAGE_PATHS': ['pages'], - 'PAGE_EXCLUDES': (), + 'PAGE_EXCLUDES': [], 'THEME': DEFAULT_THEME, 'OUTPUT_PATH': 'output', 'READERS': {}, @@ -348,6 +348,18 @@ def configure_settings(settings): % PATH_KEY) settings[PATH_KEY] = DEFAULT_CONFIG[PATH_KEY] + # Add {PAGE,ARTICLE}_PATHS to {ARTICLE,PAGE}_EXCLUDES + mutually_exclusive = ('ARTICLE', 'PAGE') + for type_1, type_2 in [mutually_exclusive, mutually_exclusive[::-1]]: + try: + includes = settings[type_1 + '_PATHS'] + excludes = settings[type_2 + '_EXCLUDES'] + for path in includes: + if path not in excludes: + excludes.append(path) + except KeyError: + continue # setting not specified, nothing to do + for old, new, doc in [ ('LESS_GENERATOR', 'the Webassets plugin', None), ('FILES_TO_COPY', 'STATIC_PATHS and EXTRA_PATH_METADATA', diff --git a/pelican/tests/test_settings.py b/pelican/tests/test_settings.py index 930e0fea..260eff05 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -43,7 +43,10 @@ class TestSettingsConfiguration(unittest.TestCase): # Providing no file should return the default values. settings = read_settings(None) expected = copy.deepcopy(DEFAULT_CONFIG) - expected['FEED_DOMAIN'] = '' # Added by configure settings + # Added by configure settings + expected['FEED_DOMAIN'] = '' + expected['ARTICLE_EXCLUDES'] = ['pages'] + expected['PAGE_EXCLUDES'] = [''] self.maxDiff = None self.assertDictEqual(settings, expected) From 144cddaf39ad6577cfa4a11ef003f6fa44c81d58 Mon Sep 17 00:00:00 2001 From: Mark Lee Date: Tue, 20 May 2014 13:53:02 -0700 Subject: [PATCH 0266/1427] Address code review comments from PR getpelican/pelican#1348 The text about the sort-by-key function comes from: https://docs.python.org/2/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange Minor style cleanup as well. --- docs/settings.rst | 36 +++++++++++++++++++++--------------- pelican/utils.py | 14 +++++--------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 55740296..0c804f1c 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -260,16 +260,19 @@ posts for the month at ``posts/2011/Aug/index.html``. arrive at an appropriate archive of posts, without having to specify a page name. -====================================================== ======================================================== +====================================================== ============================================================== Setting name (followed by default value, if any) What does it do? -====================================================== ======================================================== +====================================================== ============================================================== ``ARTICLE_URL = '{slug}.html'`` The URL to refer to an article. ``ARTICLE_SAVE_AS = '{slug}.html'`` The place where we will save an article. -``ARTICLE_ORDER_BY = 'slug'`` The metadata attribute used to sort articles. By default - the articles_page.object_list template variable is - ordered by slug. If you modify this, make sure all - articles contain the attribute you specify. You can - also specify a sorting function. +``ARTICLE_ORDER_BY = 'slug'`` The metadata attribute used to sort articles. By default, + the ``articles_page.object_list`` template variable is + ordered by slug. If you modify this, make sure all + articles contain the attribute you specify. You can also + specify a "sorting" function of one argument that is used + to extract a comparison key from each article. For example, + sorting by title without using the built-in functionality + would use the function ``operator.attrgetter('title')``. ``ARTICLE_LANG_URL = '{slug}-{lang}.html'`` The URL to refer to an article which doesn't use the default language. ``ARTICLE_LANG_SAVE_AS = '{slug}-{lang}.html'`` The place where we will save an article which @@ -285,13 +288,16 @@ Setting name (followed by default value, if any) What does it do? the same as PAGE_URL or you need to use a rewrite in your server config. -``PAGE_ORDER_BY = 'filename'`` The metadata attribute used to sort pages. By default - the PAGES template variable is ordered by filename - (path not included). Note that the option 'filename' - is a special option supported in the source code. If - you modify this settings, make sure all pages contain - the attribute you specify. You can also specify a - sorting function. +``PAGE_ORDER_BY = 'basename'`` The metadata attribute used to sort pages. By default + the ``PAGES`` template variable is ordered by basename + (i.e., path not included). Note that the option ``'basename'`` + is a special option supported in the source code. If + you modify this setting, make sure all pages contain + the attribute you specify. You can also specify a "sorting" + function of one argument that is used to extract a comparison + key from each page. For example, the basename function looks + similar to + ``lambda x: os.path.basename(getattr(x, 'source_path', ''))``. ``PAGE_LANG_URL = 'pages/{slug}-{lang}.html'`` The URL we will use to link to a page which doesn't use the default language. ``PAGE_LANG_SAVE_AS = 'pages/{slug}-{lang}.html'`` The location we will save the page which doesn't @@ -309,7 +315,7 @@ Setting name (followed by default value, if any) What does it do? non-alphanumerics when generating slugs. Specified as a list of 2-tuples of ``(from, to)`` which are applied in order. -====================================================== ======================================================== +====================================================== ============================================================== .. note:: diff --git a/pelican/utils.py b/pelican/utils.py index ecdf5e0d..076c41ea 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -465,17 +465,13 @@ def process_translations(content_list, order_by=None): a.translations = [x for x in items if x != a] if order_by: - if hasattr(order_by, '__call__'): + if callable(order_by): try: index.sort(key=order_by) - except: - if hasattr(order_by, 'func_name'): - logger.error("Error sorting with function %s" % order_by.func_name) - else: - logger.error("Error sorting with function %r" % order_by) - elif order_by == 'filename': - index.sort(key=lambda x:os.path.basename( - x.source_path or '')) + except Exception: + logger.error('Error sorting with function {}'.format(order_by)) + elif order_by == 'basename': + index.sort(key=lambda x: os.path.basename(x.source_path or '')) elif order_by != 'slug': index.sort(key=attrgetter(order_by)) From b8db970455100b3b9393fcb20cc1b7fd9c3730f4 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Sun, 25 May 2014 09:12:35 +0200 Subject: [PATCH 0267/1427] Fix RstReader authors metadata processing The reader would return a list of authors already, but METADATA_PROCESSORS['authors'] expects a string. Added a test case for this (only the HTMLReader had it). --- pelican/readers.py | 1 + pelican/tests/test_generators.py | 1 + pelican/tests/test_readers.py | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/pelican/readers.py b/pelican/readers.py index c63b8981..60df8551 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -145,6 +145,7 @@ class RstReader(BaseReader): elif element.tagname == 'authors': # author list name = element.tagname value = [element.astext() for element in element.children] + value = ','.join(value) # METADATA_PROCESSORS expects a string else: # standard fields (e.g. address) name = element.tagname value = element.astext() diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 7b79e8f3..156f7b50 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -109,6 +109,7 @@ class TestArticlesGenerator(unittest.TestCase): ['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 without category !', 'published', 'Default', 'article'], ['This is an article without category !', 'published', diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index fd30e9b9..3533cd31 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -150,6 +150,15 @@ class RstReaderTest(ReaderTest): except ImportError: return unittest.skip('need the typogrify distribution') + def test_article_with_multiple_authors(self): + page = self.read_file(path='article_with_multiple_authors.rst') + expected = { + 'authors': ['First Author', 'Second Author'] + } + + for key, value in expected.items(): + self.assertEqual(value, page.metadata[key], key) + class MdReaderTest(ReaderTest): From 43701cae7c0c7fc4617374eab13695da8372e104 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Tue, 27 May 2014 12:15:50 -0400 Subject: [PATCH 0268/1427] Docs update for *_{previous|next}_page variables --- docs/themes.rst | 56 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/docs/themes.rst b/docs/themes.rst index 7fcba671..4be9a8e5 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -119,17 +119,25 @@ This is the home page of your blog, generated at output/index.html. If pagination is active, subsequent pages will reside in output/index`n`.html. -=================== =================================================== +====================== =================================================== Variable Description -=================== =================================================== +====================== =================================================== articles_paginator A paginator object for the list of articles articles_page The current page of articles +articles_previous_page The previous page of articles (``None`` if page does + not exist) +articles_next_page The next page of articles (``None`` if page does + not exist) dates_paginator A paginator object for the article list, ordered by date, ascending. dates_page The current page of articles, ordered by date, ascending. +dates_previous_page The previous page of articles, ordered by date, + ascending (``None`` if page does not exist) +dates_next_page The next page of articles, ordered by date, + ascending (``None`` if page does not exist) page_name 'index' -- useful for pagination links -=================== =================================================== +====================== =================================================== author.html ------------- @@ -140,22 +148,30 @@ output generated at output/author/`author_name`.html. If pagination is active, subsequent pages will reside as defined by setting AUTHOR_SAVE_AS (`Default:` output/author/`author_name'n'`.html). -=================== =================================================== +====================== =================================================== Variable Description -=================== =================================================== +====================== =================================================== author The name of the author being processed articles Articles by this author dates Articles by this author, but ordered by date, ascending articles_paginator A paginator object for the list of articles articles_page The current page of articles +articles_previous_page The previous page of articles (``None`` if page does + not exist) +articles_next_page The next page of articles (``None`` if page does + not exist) dates_paginator A paginator object for the article list, ordered by date, ascending. dates_page The current page of articles, ordered by date, ascending. +dates_previous_page The previous page of articles, ordered by date, + ascending (``None`` if page does not exist) +dates_next_page The next page of articles, ordered by date, + ascending (``None`` if page does not exist) page_name AUTHOR_URL where everything after `{slug}` is removed -- useful for pagination links -=================== =================================================== +====================== =================================================== category.html ------------- @@ -166,22 +182,30 @@ output generated at output/category/`category_name`.html. If pagination is active, subsequent pages will reside as defined by setting CATEGORY_SAVE_AS (`Default:` output/category/`category_name'n'`.html). -=================== =================================================== +====================== =================================================== Variable Description -=================== =================================================== +====================== =================================================== category The name of the category being processed articles Articles for this category dates Articles for this category, but ordered by date, ascending articles_paginator A paginator object for the list of articles articles_page The current page of articles +articles_previous_page The previous page of articles (``None`` if page does + not exist) +articles_next_page The next page of articles (``None`` if page does + not exist) dates_paginator A paginator object for the list of articles, ordered by date, ascending dates_page The current page of articles, ordered by date, ascending +dates_previous_page The previous page of articles, ordered by date, + ascending (``None`` if page does not exist) +dates_next_page The next page of articles, ordered by date, + ascending (``None`` if page does not exist) page_name CATEGORY_URL where everything after `{slug}` is removed -- useful for pagination links -=================== =================================================== +====================== =================================================== article.html ------------- @@ -244,22 +268,30 @@ saved as output/tag/`tag_name`.html. If pagination is active, subsequent pages will reside as defined in setting TAG_SAVE_AS (`Default:` output/tag/`tag_name'n'`.html). -=================== =================================================== +====================== =================================================== Variable Description -=================== =================================================== +====================== =================================================== tag The name of the tag being processed articles Articles related to this tag dates Articles related to this tag, but ordered by date, ascending articles_paginator A paginator object for the list of articles articles_page The current page of articles +articles_previous_page The previous page of articles (``None`` if page does + not exist) +articles_next_page The next page of articles (``None`` if page does + not exist) dates_paginator A paginator object for the list of articles, ordered by date, ascending dates_page The current page of articles, ordered by date, ascending +dates_previous_page The previous page of articles, ordered by date, + ascending (``None`` if page does not exist) +dates_next_page The next page of articles, ordered by date, + ascending (``None`` if page does not exist) page_name TAG_URL where everything after `{slug}` is removed -- useful for pagination links -=================== =================================================== +====================== =================================================== period_archives.html -------------------- From b572cbeef15ac2e4fe6107110f52fbdc212cd52a Mon Sep 17 00:00:00 2001 From: Mark Lee Date: Tue, 27 May 2014 11:42:37 -0700 Subject: [PATCH 0269/1427] Addressed comments from @avaris in PR getpelican/pelican#1348 --- pelican/settings.py | 2 +- pelican/utils.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pelican/settings.py b/pelican/settings.py index f49d3bd4..6845b830 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -70,7 +70,7 @@ DEFAULT_CONFIG = { 'DRAFT_LANG_SAVE_AS': os.path.join('drafts', '{slug}-{lang}.html'), 'PAGE_URL': 'pages/{slug}.html', 'PAGE_SAVE_AS': os.path.join('pages', '{slug}.html'), - 'PAGE_ORDER_BY': 'filename', + 'PAGE_ORDER_BY': 'basename', 'PAGE_LANG_URL': 'pages/{slug}-{lang}.html', 'PAGE_LANG_SAVE_AS': os.path.join('pages', '{slug}-{lang}.html'), 'STATIC_URL': '{path}', diff --git a/pelican/utils.py b/pelican/utils.py index 076c41ea..c2d6ca22 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -473,7 +473,12 @@ def process_translations(content_list, order_by=None): elif order_by == 'basename': index.sort(key=lambda x: os.path.basename(x.source_path or '')) elif order_by != 'slug': - index.sort(key=attrgetter(order_by)) + try: + index.sort(key=attrgetter(order_by)) + except AttributeError: + error_msg = ('There is no "{}" attribute in the item metadata.' + 'Defaulting to slug order.') + logger.warning(error_msg.format(order_by)) return index, translations From ef967056778a6610fe7b61c349acec0990073dd7 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Sun, 8 Jun 2014 11:32:10 +0200 Subject: [PATCH 0270/1427] catch arbitrary exceptions during cache unpickling It is hard to forsee what could be raised. --- pelican/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/utils.py b/pelican/utils.py index 2af34ecf..84b3a41e 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -577,7 +577,7 @@ class FileDataCacher(object): 'run). Proceeding with empty cache.\n{}').format( self._cache_path, err)) self._cache = {} - except pickle.UnpicklingError as err: + except Exception as err: logger.warning(('Cannot unpickle cache {}, cache may be using ' 'an incompatible protocol (see pelican caching docs). ' 'Proceeding with empty cache.\n{}').format( From def654434c49a9a17c31ba4b6d970a517e5af320 Mon Sep 17 00:00:00 2001 From: OGINO Masanori Date: Tue, 10 Jun 2014 08:28:10 +0900 Subject: [PATCH 0271/1427] Require six version 1.4.0 or later. six.moves.urllib.parse is available since version 1.4.0. Signed-off-by: OGINO Masanori --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e989d549..a2bcaeaa 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,8 @@ from setuptools import setup requires = ['feedgenerator >= 1.6', 'jinja2 >= 2.7', 'pygments', 'docutils', - 'pytz >= 0a', 'blinker', 'unidecode', 'six', 'python-dateutil'] + 'pytz >= 0a', 'blinker', 'unidecode', 'six >= 1.4', + 'python-dateutil'] entry_points = { 'console_scripts': [ From ca3aa1e75fac0b54feb7170e6a5f1c03c1097145 Mon Sep 17 00:00:00 2001 From: OGINO Masanori Date: Tue, 10 Jun 2014 17:30:17 +0900 Subject: [PATCH 0272/1427] Use six.moves.urllib. Signed-off-by: OGINO Masanori --- pelican/contents.py | 8 +------- pelican/tools/pelican_import.py | 9 +++------ pelican/writers.py | 4 +--- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 615a7fd8..220db611 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals, print_function import six -from six.moves.urllib.parse import unquote +from six.moves.urllib.parse import (unquote, urlparse, urlunparse) import copy import locale @@ -11,14 +11,8 @@ import os import re import sys -try: - from urlparse import urlparse, urlunparse -except ImportError: - from urllib.parse import urlparse, urlunparse - from datetime import datetime - from pelican import signals from pelican.settings import DEFAULT_CONFIG from pelican.utils import (slugify, truncate_html_words, memoized, strftime, diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 27e47754..7c8662c9 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -6,15 +6,9 @@ import argparse try: # py3k import from html.parser import HTMLParser - from urllib.request import urlretrieve - from urllib.parse import urlparse - from urllib.error import URLError except ImportError: # py2 import from HTMLParser import HTMLParser # NOQA - from urllib import urlretrieve - from urlparse import urlparse - from urllib2 import URLError import os import re import subprocess @@ -23,6 +17,9 @@ import time import logging from codecs import open +from six.moves.urllib.error import URLError +from six.moves.urllib.parse import urlparse +from six.moves.urllib.request import urlretrieve from pelican.utils import slugify from pelican.log import init diff --git a/pelican/writers.py b/pelican/writers.py index a92feee4..3e01ee6c 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -8,12 +8,10 @@ import logging if not six.PY3: from codecs import open - from urlparse import urlparse -else: - from urllib.parse import urlparse from feedgenerator import Atom1Feed, Rss201rev2Feed from jinja2 import Markup +from six.moves.urllib.parse import urlparse from pelican.paginator import Paginator from pelican.utils import (get_relative_path, path_to_url, set_date_tzinfo, From bf9316bb7e4a7aa2a6e335c681d4255d1deed23e Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Tue, 10 Jun 2014 18:05:29 -0400 Subject: [PATCH 0273/1427] Remove AsciiDocReader from core. Fixes #1355 --- .travis.yml | 2 - docs/content.rst | 5 ++- docs/index.rst | 3 +- docs/install.rst | 5 --- docs/internals.rst | 4 +- docs/settings.rst | 2 - pelican/readers.py | 39 ------------------- pelican/settings.py | 1 - .../content/article_with_asc_extension.asc | 12 ------ .../content/article_with_asc_options.asc | 9 ----- pelican/tests/test_readers.py | 36 ----------------- 11 files changed, 6 insertions(+), 112 deletions(-) delete mode 100644 pelican/tests/content/article_with_asc_extension.asc delete mode 100644 pelican/tests/content/article_with_asc_options.asc diff --git a/.travis.yml b/.travis.yml index 41ad82b2..54dcf0ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,8 @@ python: - "3.4" before_install: - sudo apt-get update -qq - - sudo apt-get install -qq --no-install-recommends asciidoc - sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8 install: - - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then ln -s /usr/share/asciidoc/asciidocapi.py ~/virtualenv/python2.7/lib/python2.7/site-packages/; fi - pip install . - pip install -r dev_requirements.txt - pip install nose-cov diff --git a/docs/content.rst b/docs/content.rst index 24fc6e9b..ad81bed1 100644 --- a/docs/content.rst +++ b/docs/content.rst @@ -57,8 +57,8 @@ pattern:: This is the content of my super blog post. -Conventions for AsciiDoc_ posts, which should have an ``.asc`` extension, can -be found on the AsciiDoc_ site. +Readers for additional formats (such as AsciiDoc_) are available via plugins. +Refer to `pelican-plugins`_ repository for those. Pelican can also process HTML files ending in ``.html`` and ``.htm``. Pelican interprets the HTML in a very straightforward manner, reading metadata from @@ -369,3 +369,4 @@ listed on the index page nor on any category or tag page. .. _W3C ISO 8601: http://www.w3.org/TR/NOTE-datetime .. _AsciiDoc: http://www.methods.co.nz/asciidoc/ +.. _pelican-plugins: http://github.com/getpelican/pelican-plugins diff --git a/docs/index.rst b/docs/index.rst index 36a3282b..2beb8b20 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,7 +13,7 @@ Pelican |release| Pelican is a static site generator, written in Python_. Highlights include: * Write your content directly with your editor of choice - in reStructuredText_, Markdown_, or AsciiDoc_ formats + in reStructuredText_ or Markdown_ formats * Includes a simple CLI tool to (re)generate your site * Easy to interface with distributed version control systems and web hooks * Completely static output is easy to host anywhere @@ -89,7 +89,6 @@ Documentation .. _Python: http://www.python.org/ .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _Markdown: http://daringfireball.net/projects/markdown/ -.. _AsciiDoc: http://www.methods.co.nz/asciidoc/index.html .. _Jinja2: http://jinja.pocoo.org/ .. _`Pelican documentation`: http://docs.getpelican.com/latest/ .. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html diff --git a/docs/install.rst b/docs/install.rst index 34cd33ea..418c8ca6 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -52,10 +52,6 @@ installed:: pip install typogrify -If you want to use AsciiDoc_ you need to install it from `source -`_ or use your operating -system's package manager. - Dependencies ------------ @@ -119,4 +115,3 @@ The next step is to begin to adding content to the *content* folder that has been created for you. .. _virtualenv: http://www.virtualenv.org/ -.. _AsciiDoc: http://www.methods.co.nz/asciidoc/ diff --git a/docs/internals.rst b/docs/internals.rst index f69a9bb8..303a327f 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -13,7 +13,7 @@ Overall structure ================= What Pelican does is take a list of files and process them into some sort of -output. Usually, the input files are reStructuredText, Markdown and AsciiDoc +output. Usually, the input files are reStructuredText and Markdown files, and the output is a blog, but both input and output can be anything you want. @@ -23,7 +23,7 @@ The logic is separated into different classes and concepts: on. Since those operations are commonly used, the object is created once and then passed to the generators. -* **Readers** are used to read from various formats (AsciiDoc, HTML, Markdown and +* **Readers** are used to read from various formats (HTML, Markdown and reStructuredText for now, but the system is extensible). Given a file, they return metadata (author, tags, category, etc.) and content (HTML-formatted). diff --git a/docs/settings.rst b/docs/settings.rst index 4701e92d..5d9c574f 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -159,8 +159,6 @@ Setting name (followed by default value, if any) Can be used to separate templates from the theme. Example: projects, resume, profile ... These templates need to use ``DIRECT_TEMPLATES`` setting. -``ASCIIDOC_OPTIONS = []`` A list of options to pass to AsciiDoc. See the `manpage - `_. ``WITH_FUTURE_DATES = True`` If disabled, content with dates in the future will get a default status of ``draft``. See :ref:`reading_only_modified_content` for caveats. diff --git a/pelican/readers.py b/pelican/readers.py index 60df8551..431e6937 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -17,11 +17,6 @@ try: from markdown import Markdown except ImportError: Markdown = False # NOQA -try: - from asciidocapi import AsciiDocAPI - asciidoc = True -except ImportError: - asciidoc = False try: from html import escape except ImportError: @@ -349,40 +344,6 @@ class HTMLReader(BaseReader): return parser.body, metadata -class AsciiDocReader(BaseReader): - """Reader for AsciiDoc files""" - - enabled = bool(asciidoc) - file_extensions = ['asc', 'adoc', 'asciidoc'] - default_options = ["--no-header-footer", "-a newline=\\n"] - - def read(self, source_path): - """Parse content and metadata of asciidoc files""" - from cStringIO import StringIO - with pelican_open(source_path) as source: - text = StringIO(source) - content = StringIO() - ad = AsciiDocAPI() - - options = self.settings['ASCIIDOC_OPTIONS'] - if isinstance(options, (str, unicode)): - options = [m.strip() for m in options.split(',')] - options = self.default_options + options - for o in options: - ad.options(*o.split()) - - ad.execute(text, content, backend="html4") - content = content.getvalue() - - metadata = {} - for name, value in ad.asciidoc.document.attributes.items(): - name = name.lower() - metadata[name] = self.process_metadata(name, value) - if 'doctitle' in metadata: - metadata['title'] = metadata['doctitle'] - return content, metadata - - class Readers(FileStampDataCacher): """Interface for all readers. diff --git a/pelican/settings.py b/pelican/settings.py index f759ff9e..a94e4bf2 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -98,7 +98,6 @@ DEFAULT_CONFIG = { 'PELICAN_CLASS': 'pelican.Pelican', 'DEFAULT_DATE_FORMAT': '%a %d %B %Y', 'DATE_FORMATS': {}, - 'ASCIIDOC_OPTIONS': [], 'MD_EXTENSIONS': ['codehilite(css_class=highlight)', 'extra'], 'JINJA_EXTENSIONS': [], 'JINJA_FILTERS': {}, diff --git a/pelican/tests/content/article_with_asc_extension.asc b/pelican/tests/content/article_with_asc_extension.asc deleted file mode 100644 index 9ce2166c..00000000 --- a/pelican/tests/content/article_with_asc_extension.asc +++ /dev/null @@ -1,12 +0,0 @@ -Test AsciiDoc File Header -========================= -:Author: Author O. Article -:Email: -:Date: 2011-09-15 09:05 -:Category: Blog -:Tags: Linux, Python, Pelican - -Used for pelican test ---------------------- - -The quick brown fox jumped over the lazy dog's back. diff --git a/pelican/tests/content/article_with_asc_options.asc b/pelican/tests/content/article_with_asc_options.asc deleted file mode 100644 index bafb3a4a..00000000 --- a/pelican/tests/content/article_with_asc_options.asc +++ /dev/null @@ -1,9 +0,0 @@ -Test AsciiDoc File Header -========================= - -Used for pelican test ---------------------- - -version {revision} - -The quick brown fox jumped over the lazy dog's back. diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index 3533cd31..6228989b 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -333,42 +333,6 @@ class MdReaderTest(ReaderTest): self.assertEqual(value, page.metadata[key], key) -class AdReaderTest(ReaderTest): - - @unittest.skipUnless(readers.asciidoc, "asciidoc isn't installed") - def test_article_with_asc_extension(self): - # Ensure the asc extension is being processed by the correct reader - page = self.read_file( - path='article_with_asc_extension.asc') - expected = ('
    \n

    ' - 'Used for pelican test

    \n' - '

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

    \n') - self.assertEqual(page.content, expected) - expected = { - 'category': 'Blog', - 'author': 'Author O. Article', - 'title': 'Test AsciiDoc File Header', - 'date': datetime.datetime(2011, 9, 15, 9, 5), - 'tags': ['Linux', 'Python', 'Pelican'], - } - - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) - - @unittest.skipUnless(readers.asciidoc, "asciidoc isn't installed") - def test_article_with_asc_options(self): - # test to ensure the ASCIIDOC_OPTIONS is being used - reader = readers.AsciiDocReader( - dict(ASCIIDOC_OPTIONS=["-a revision=1.0.42"])) - content, metadata = reader.read(_path('article_with_asc_options.asc')) - expected = ('
    \n

    Used for' - ' pelican test

    \n

    version 1.0.42

    \n' - '

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

    \n') - self.assertEqual(content, expected) - - class HTMLReaderTest(ReaderTest): def test_article_with_comments(self): page = self.read_file(path='article_with_comments.html') From 24106081b5bbb110eed16d2714f65aa1ea2a9fc2 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Tue, 24 Jun 2014 22:29:36 +0100 Subject: [PATCH 0274/1427] [coveralls] Exclude tests from coverage --- .coveragerc | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..2cb24879 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,3 @@ +[report] +omit = pelican/tests/* + From 3f6b130d6ed427f19e444057958661ee00875ca0 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Sun, 27 Apr 2014 10:25:57 +0200 Subject: [PATCH 0275/1427] Fix #1198, enable custom locale in template rendering, fixes links reverts getpelican/pelican@ddcccfeaa952d2e1e24ceac94e5d66c73b57c01b If one used a locale that made use of unicode characters (like fr_FR.UTF-8) the files on disk would be in correct locale while links would be to C. Uses a SafeDatetime class that works with unicode format strigns by using custom strftime to prevent ascii decoding errors with Python2. Also added unicode decoding for the calendar module to fix period archives. --- docs/contribute.rst | 2 + pelican/contents.py | 7 +- pelican/generators.py | 32 +- pelican/readers.py | 9 +- .../tests/output/custom_locale/archives.html | 100 ++++ .../author/alexis-metaireau.html | 173 +++++++ .../author/alexis-metaireau2.html | 187 ++++++++ .../author/alexis-metaireau3.html | 138 ++++++ .../tests/output/custom_locale/authors.html | 82 ++++ .../output/custom_locale/categories.html | 80 ++++ .../output/custom_locale/category/bar.html | 101 ++++ .../output/custom_locale/category/cat1.html | 170 +++++++ .../output/custom_locale/category/misc.html | 181 +++++++ .../output/custom_locale/category/yeah.html | 109 +++++ .../custom_locale/drafts/a-draft-article.html | 100 ++++ .../feeds/alexis-metaireau.atom.xml | 61 +++ .../feeds/alexis-metaireau.rss.xml | 61 +++ .../custom_locale/feeds/all-en.atom.xml | 61 +++ .../custom_locale/feeds/all-fr.atom.xml | 4 + .../output/custom_locale/feeds/all.atom.xml | 63 +++ .../output/custom_locale/feeds/all.rss.xml | 63 +++ .../output/custom_locale/feeds/bar.atom.xml | 8 + .../output/custom_locale/feeds/bar.rss.xml | 8 + .../output/custom_locale/feeds/cat1.atom.xml | 7 + .../output/custom_locale/feeds/cat1.rss.xml | 7 + .../output/custom_locale/feeds/misc.atom.xml | 38 ++ .../output/custom_locale/feeds/misc.rss.xml | 38 ++ .../output/custom_locale/feeds/yeah.atom.xml | 14 + .../output/custom_locale/feeds/yeah.rss.xml | 14 + pelican/tests/output/custom_locale/index.html | 173 +++++++ .../tests/output/custom_locale/index2.html | 187 ++++++++ .../tests/output/custom_locale/index3.html | 138 ++++++ .../output/custom_locale/jinja2_template.html | 77 +++ .../output/custom_locale/oh-yeah-fr.html | 116 +++++ .../output/custom_locale/override/index.html | 81 ++++ .../pages/this-is-a-test-hidden-page.html | 81 ++++ .../pages/this-is-a-test-page.html | 81 ++++ .../output/custom_locale/pictures/Fat_Cat.jpg | Bin 0 -> 62675 bytes .../output/custom_locale/pictures/Sushi.jpg | Bin 0 -> 28992 bytes .../custom_locale/pictures/Sushi_Macro.jpg | Bin 0 -> 38594 bytes .../02/this-is-a-super-article/index.html | 129 +++++ .../2010/octobre/15/unbelievable/index.html | 146 ++++++ .../posts/2010/octobre/20/oh-yeah/index.html | 121 +++++ .../20/a-markdown-powered-article/index.html | 115 +++++ .../2011/février/17/article-1/index.html | 114 +++++ .../2011/février/17/article-2/index.html | 114 +++++ .../2011/février/17/article-3/index.html | 114 +++++ .../2012/février/29/second-article/index.html | 116 +++++ .../30/filename_metadata-example/index.html | 114 +++++ pelican/tests/output/custom_locale/robots.txt | 2 + .../custom_locale/second-article-fr.html | 116 +++++ .../tests/output/custom_locale/tag/bar.html | 160 +++++++ .../tests/output/custom_locale/tag/baz.html | 114 +++++ .../tests/output/custom_locale/tag/foo.html | 130 +++++ .../output/custom_locale/tag/foobar.html | 109 +++++ .../tests/output/custom_locale/tag/oh.html | 80 ++++ .../tests/output/custom_locale/tag/yeah.html | 101 ++++ pelican/tests/output/custom_locale/tags.html | 87 ++++ .../output/custom_locale/theme/css/main.css | 451 ++++++++++++++++++ .../custom_locale/theme/css/pygment.css | 205 ++++++++ .../output/custom_locale/theme/css/reset.css | 52 ++ .../custom_locale/theme/css/typogrify.css | 3 + .../output/custom_locale/theme/css/wide.css | 48 ++ .../theme/images/icons/aboutme.png | Bin 0 -> 751 bytes .../theme/images/icons/bitbucket.png | Bin 0 -> 3714 bytes .../theme/images/icons/delicious.png | Bin 0 -> 958 bytes .../theme/images/icons/facebook.png | Bin 0 -> 202 bytes .../theme/images/icons/github.png | Bin 0 -> 1714 bytes .../theme/images/icons/gitorious.png | Bin 0 -> 227 bytes .../theme/images/icons/gittip.png | Bin 0 -> 487 bytes .../theme/images/icons/google-groups.png | Bin 0 -> 803 bytes .../theme/images/icons/google-plus.png | Bin 0 -> 527 bytes .../theme/images/icons/hackernews.png | Bin 0 -> 3273 bytes .../theme/images/icons/lastfm.png | Bin 0 -> 975 bytes .../theme/images/icons/linkedin.png | Bin 0 -> 896 bytes .../theme/images/icons/reddit.png | Bin 0 -> 693 bytes .../custom_locale/theme/images/icons/rss.png | Bin 0 -> 879 bytes .../theme/images/icons/slideshare.png | Bin 0 -> 535 bytes .../theme/images/icons/speakerdeck.png | Bin 0 -> 1049 bytes .../theme/images/icons/stackoverflow.png | Bin 0 -> 916 bytes .../theme/images/icons/twitter.png | Bin 0 -> 830 bytes .../theme/images/icons/vimeo.png | Bin 0 -> 544 bytes .../theme/images/icons/youtube.png | Bin 0 -> 458 bytes pelican/tests/test_contents.py | 5 +- pelican/tests/test_pelican.py | 25 +- pelican/tests/test_readers.py | 30 +- pelican/tests/test_utils.py | 35 +- pelican/tools/pelican_import.py | 6 +- pelican/utils.py | 21 +- pelican/writers.py | 7 +- samples/pelican_FR.conf.py | 59 +++ 91 files changed, 5704 insertions(+), 77 deletions(-) create mode 100644 pelican/tests/output/custom_locale/archives.html create mode 100644 pelican/tests/output/custom_locale/author/alexis-metaireau.html create mode 100644 pelican/tests/output/custom_locale/author/alexis-metaireau2.html create mode 100644 pelican/tests/output/custom_locale/author/alexis-metaireau3.html create mode 100644 pelican/tests/output/custom_locale/authors.html create mode 100644 pelican/tests/output/custom_locale/categories.html create mode 100644 pelican/tests/output/custom_locale/category/bar.html create mode 100644 pelican/tests/output/custom_locale/category/cat1.html create mode 100644 pelican/tests/output/custom_locale/category/misc.html create mode 100644 pelican/tests/output/custom_locale/category/yeah.html create mode 100644 pelican/tests/output/custom_locale/drafts/a-draft-article.html create mode 100644 pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml create mode 100644 pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml create mode 100644 pelican/tests/output/custom_locale/feeds/all-en.atom.xml create mode 100644 pelican/tests/output/custom_locale/feeds/all-fr.atom.xml create mode 100644 pelican/tests/output/custom_locale/feeds/all.atom.xml create mode 100644 pelican/tests/output/custom_locale/feeds/all.rss.xml create mode 100644 pelican/tests/output/custom_locale/feeds/bar.atom.xml create mode 100644 pelican/tests/output/custom_locale/feeds/bar.rss.xml create mode 100644 pelican/tests/output/custom_locale/feeds/cat1.atom.xml create mode 100644 pelican/tests/output/custom_locale/feeds/cat1.rss.xml create mode 100644 pelican/tests/output/custom_locale/feeds/misc.atom.xml create mode 100644 pelican/tests/output/custom_locale/feeds/misc.rss.xml create mode 100644 pelican/tests/output/custom_locale/feeds/yeah.atom.xml create mode 100644 pelican/tests/output/custom_locale/feeds/yeah.rss.xml create mode 100644 pelican/tests/output/custom_locale/index.html create mode 100644 pelican/tests/output/custom_locale/index2.html create mode 100644 pelican/tests/output/custom_locale/index3.html create mode 100644 pelican/tests/output/custom_locale/jinja2_template.html create mode 100644 pelican/tests/output/custom_locale/oh-yeah-fr.html create mode 100644 pelican/tests/output/custom_locale/override/index.html create mode 100644 pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html create mode 100644 pelican/tests/output/custom_locale/pages/this-is-a-test-page.html create mode 100644 pelican/tests/output/custom_locale/pictures/Fat_Cat.jpg create mode 100644 pelican/tests/output/custom_locale/pictures/Sushi.jpg create mode 100644 pelican/tests/output/custom_locale/pictures/Sushi_Macro.jpg create mode 100644 pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html create mode 100644 pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html create mode 100644 pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html create mode 100644 pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html create mode 100644 pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html create mode 100644 pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html create mode 100644 pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html create mode 100644 pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html create mode 100644 pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html create mode 100644 pelican/tests/output/custom_locale/robots.txt create mode 100644 pelican/tests/output/custom_locale/second-article-fr.html create mode 100644 pelican/tests/output/custom_locale/tag/bar.html create mode 100644 pelican/tests/output/custom_locale/tag/baz.html create mode 100644 pelican/tests/output/custom_locale/tag/foo.html create mode 100644 pelican/tests/output/custom_locale/tag/foobar.html create mode 100644 pelican/tests/output/custom_locale/tag/oh.html create mode 100644 pelican/tests/output/custom_locale/tag/yeah.html create mode 100644 pelican/tests/output/custom_locale/tags.html create mode 100644 pelican/tests/output/custom_locale/theme/css/main.css create mode 100644 pelican/tests/output/custom_locale/theme/css/pygment.css create mode 100644 pelican/tests/output/custom_locale/theme/css/reset.css create mode 100644 pelican/tests/output/custom_locale/theme/css/typogrify.css create mode 100644 pelican/tests/output/custom_locale/theme/css/wide.css create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/aboutme.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/bitbucket.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/delicious.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/facebook.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/github.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/gitorious.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/gittip.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/google-groups.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/google-plus.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/hackernews.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/lastfm.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/linkedin.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/reddit.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/rss.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/slideshare.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/speakerdeck.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/stackoverflow.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/twitter.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/vimeo.png create mode 100644 pelican/tests/output/custom_locale/theme/images/icons/youtube.png create mode 100644 samples/pelican_FR.conf.py diff --git a/docs/contribute.rst b/docs/contribute.rst index 57349156..044ef924 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -90,6 +90,8 @@ functional tests. To do so, you can use the following two commands:: $ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/custom/ \ -s samples/pelican.conf.py samples/content/ + $ LC_ALL=fr_FR.utf8 pelican -o pelican/tests/output/custom_locale/ \ + -s samples/pelican_FR.conf.py samples/content/ $ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/basic/ \ samples/content/ diff --git a/pelican/contents.py b/pelican/contents.py index 220db611..297a537b 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -11,13 +11,12 @@ import os import re import sys -from datetime import datetime 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) + path_to_url, SafeDatetime) # Import these so that they're avalaible when you import from pelican.contents. from pelican.urlwrappers import (URLWrapper, Author, Category, Tag) # NOQA @@ -127,7 +126,7 @@ class Content(object): if not hasattr(self, 'status'): self.status = settings['DEFAULT_STATUS'] if not settings['WITH_FUTURE_DATES']: - if hasattr(self, 'date') and self.date > datetime.now(): + if hasattr(self, 'date') and self.date > SafeDatetime.now(): self.status = 'draft' # store the summary metadata if it is set @@ -161,7 +160,7 @@ class Content(object): 'path': path_to_url(path), 'slug': getattr(self, 'slug', ''), 'lang': getattr(self, 'lang', 'en'), - 'date': getattr(self, 'date', datetime.now()), + 'date': getattr(self, 'date', SafeDatetime.now()), 'author': slugify( getattr(self, 'author', ''), slug_substitutions diff --git a/pelican/generators.py b/pelican/generators.py index deb237a2..865cc6f8 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals, print_function import os +import six import math import random import logging @@ -348,31 +349,22 @@ class ArticlesGenerator(CachingGenerator): # format string syntax can be used for specifying the # period archive dates date = archive[0].date - # Under python 2, with non-ascii locales, u"{:%b}".format(date) might raise UnicodeDecodeError - # because u"{:%b}".format(date) will call date.__format__(u"%b"), which will return a byte string - # and not a unicode string. - # eg: - # locale.setlocale(locale.LC_ALL, 'ja_JP.utf8') - # date.__format__(u"%b") == '12\xe6\x9c\x88' # True - try: - save_as = save_as_fmt.format(date=date) - except UnicodeDecodeError: - # Python2 only: - # Let date.__format__() work with byte strings instead of characters since it fails to work with characters - bytes_save_as_fmt = save_as_fmt.encode('utf8') - bytes_save_as = bytes_save_as_fmt.format(date=date) - save_as = unicode(bytes_save_as,'utf8') + save_as = save_as_fmt.format(date=date) context = self.context.copy() if key == period_date_key['year']: context["period"] = (_period,) - elif key == period_date_key['month']: - context["period"] = (_period[0], - calendar.month_name[_period[1]]) else: - context["period"] = (_period[0], - calendar.month_name[_period[1]], - _period[2]) + month_name = calendar.month_name[_period[1]] + if not six.PY3: + month_name = month_name.decode('utf-8') + if key == period_date_key['month']: + context["period"] = (_period[0], + month_name) + else: + context["period"] = (_period[0], + month_name, + _period[2]) write(save_as, template, context, dates=archive, blog=True) diff --git a/pelican/readers.py b/pelican/readers.py index 431e6937..e977b349 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals, print_function -import datetime import logging import os import re @@ -28,7 +27,7 @@ except ImportError: from pelican import signals from pelican.contents import Page, Category, Tag, Author -from pelican.utils import get_date, pelican_open, FileStampDataCacher +from pelican.utils import get_date, pelican_open, FileStampDataCacher, SafeDatetime METADATA_PROCESSORS = { @@ -494,7 +493,7 @@ def default_metadata(settings=None, process=None): value = process('category', value) metadata['category'] = value if settings.get('DEFAULT_DATE', None) and settings['DEFAULT_DATE'] != 'fs': - metadata['date'] = datetime.datetime(*settings['DEFAULT_DATE']) + metadata['date'] = SafeDatetime(*settings['DEFAULT_DATE']) return metadata @@ -502,7 +501,7 @@ def path_metadata(full_path, source_path, settings=None): metadata = {} if settings: if settings.get('DEFAULT_DATE', None) == 'fs': - metadata['date'] = datetime.datetime.fromtimestamp( + metadata['date'] = SafeDatetime.fromtimestamp( os.stat(full_path).st_ctime) metadata.update(settings.get('EXTRA_PATH_METADATA', {}).get( source_path, {})) @@ -525,7 +524,7 @@ def parse_path_metadata(source_path, settings=None, process=None): ... process=reader.process_metadata) >>> pprint.pprint(metadata) # doctest: +ELLIPSIS {'category': , - 'date': datetime.datetime(2013, 1, 1, 0, 0), + 'date': SafeDatetime(2013, 1, 1, 0, 0), 'slug': 'my-slug'} """ metadata = {} diff --git a/pelican/tests/output/custom_locale/archives.html b/pelican/tests/output/custom_locale/archives.html new file mode 100644 index 00000000..a7b96336 --- /dev/null +++ b/pelican/tests/output/custom_locale/archives.html @@ -0,0 +1,100 @@ + + + + + Alexis' log + + + + + + + + + +Fork me on GitHub + + +
    +

    Archives for Alexis' log

    + +
    +
    ven. 30 novembre 2012
    +
    FILENAME_METADATA example
    +
    mer. 29 février 2012
    +
    Second article
    +
    mer. 20 avril 2011
    +
    A markdown powered article
    +
    jeu. 17 février 2011
    +
    Article 1
    +
    jeu. 17 février 2011
    +
    Article 2
    +
    jeu. 17 février 2011
    +
    Article 3
    +
    jeu. 02 décembre 2010
    +
    This is a super article !
    +
    mer. 20 octobre 2010
    +
    Oh yeah !
    +
    ven. 15 octobre 2010
    +
    Unbelievable !
    +
    dim. 14 mars 2010
    +
    The baz tag
    +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau.html b/pelican/tests/output/custom_locale/author/alexis-metaireau.html new file mode 100644 index 00000000..b54446c8 --- /dev/null +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau.html @@ -0,0 +1,173 @@ + + + + + Alexis' log - Alexis Métaireau + + + + + + + + + +Fork me on GitHub + + + + +
    +

    Other articles

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

    + Page 1 / 3 + » +

    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html new file mode 100644 index 00000000..07020512 --- /dev/null +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html @@ -0,0 +1,187 @@ + + + + + Alexis' log - Alexis Métaireau + + + + + + + + + +Fork me on GitHub + + + +
    +
      +
    1. + +
    2. + +
    3. + +
    4. +
      +

      Oh yeah !

      +
      + +
      +
      +

      Why not ?

      +

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

      +alternate text +
      + + read more +

      There are comments.

      +
    5. +
    +

    + « + Page 2 / 3 + » +

    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html new file mode 100644 index 00000000..9578e3d6 --- /dev/null +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html @@ -0,0 +1,138 @@ + + + + + Alexis' log - Alexis Métaireau + + + + + + + + + +Fork me on GitHub + + + +
    +
      +
    1. + +
    2. +
    +

    + « + Page 3 / 3 +

    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/authors.html b/pelican/tests/output/custom_locale/authors.html new file mode 100644 index 00000000..2558c4d8 --- /dev/null +++ b/pelican/tests/output/custom_locale/authors.html @@ -0,0 +1,82 @@ + + + + + Alexis' log - Authors + + + + + + + + + +Fork me on GitHub + + + +
    +

    Authors on Alexis' log

    + +
    + +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/categories.html b/pelican/tests/output/custom_locale/categories.html new file mode 100644 index 00000000..17d9de76 --- /dev/null +++ b/pelican/tests/output/custom_locale/categories.html @@ -0,0 +1,80 @@ + + + + + Alexis' log + + + + + + + + + +Fork me on GitHub + + + +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/category/bar.html b/pelican/tests/output/custom_locale/category/bar.html new file mode 100644 index 00000000..d9fc6acb --- /dev/null +++ b/pelican/tests/output/custom_locale/category/bar.html @@ -0,0 +1,101 @@ + + + + + Alexis' log - bar + + + + + + + + + +Fork me on GitHub + + + + +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/category/cat1.html b/pelican/tests/output/custom_locale/category/cat1.html new file mode 100644 index 00000000..1b09acfe --- /dev/null +++ b/pelican/tests/output/custom_locale/category/cat1.html @@ -0,0 +1,170 @@ + + + + + Alexis' log - cat1 + + + + + + + + + +Fork me on GitHub + + + + +
    +

    Other articles

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

    + Page 1 / 1 +

    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/category/misc.html b/pelican/tests/output/custom_locale/category/misc.html new file mode 100644 index 00000000..bcaec248 --- /dev/null +++ b/pelican/tests/output/custom_locale/category/misc.html @@ -0,0 +1,181 @@ + + + + + Alexis' log - misc + + + + + + + + + +Fork me on GitHub + + + + +
    +

    Other articles

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

    + Page 1 / 1 +

    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/category/yeah.html b/pelican/tests/output/custom_locale/category/yeah.html new file mode 100644 index 00000000..7f881612 --- /dev/null +++ b/pelican/tests/output/custom_locale/category/yeah.html @@ -0,0 +1,109 @@ + + + + + Alexis' log - yeah + + + + + + + + + +Fork me on GitHub + + + + +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/drafts/a-draft-article.html b/pelican/tests/output/custom_locale/drafts/a-draft-article.html new file mode 100644 index 00000000..82fb057b --- /dev/null +++ b/pelican/tests/output/custom_locale/drafts/a-draft-article.html @@ -0,0 +1,100 @@ + + + + + A draft article + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + A draft article

    +
    + +
    +

    This is a draft article, it should live under the /drafts/ folder and not be +listed anywhere else.

    + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml new file mode 100644 index 00000000..202b9f71 --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.atom.xml @@ -0,0 +1,61 @@ + +Alexis' loghttp://blog.notmyidea.org/2013-11-17T23:29:00+01:00FILENAME_METADATA example2012-11-30T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/<p>Some cool stuff!</p> +Second article2012-02-29T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/<p>This is some article, in english</p> +A markdown powered article2011-04-20T00:00:00+02:00Alexis Métaireautag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/<p>You're mutually oblivious.</p> +<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a> +<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p>Article 12011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/<p>Article 1</p> +Article 22011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/<p>Article 2</p> +Article 32011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/<p>Article 3</p> +This is a super article !2013-11-17T23:29:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> +Oh yeah !2010-10-20T10:14:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> +Unbelievable !2010-10-15T20:30:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/<p>Or completely awesome. Depends the needs.</p> +<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a> +<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> +The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml new file mode 100644 index 00000000..dfb83630 --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/alexis-metaireau.rss.xml @@ -0,0 +1,61 @@ + +Alexis' loghttp://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100FILENAME_METADATA examplehttp://blog.notmyidea.org/posts/2012/November/30/filename_metadata-example/<p>Some cool stuff!</p> +Alexis MétaireauFri, 30 Nov 2012 00:00:00 +0100tag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/Second articlehttp://blog.notmyidea.org/posts/2012/February/29/second-article/<p>This is some article, in english</p> +Alexis MétaireauWed, 29 Feb 2012 00:00:00 +0100tag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/foobarbazA markdown powered articlehttp://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/<p>You're mutually oblivious.</p> +<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a> +<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p>Alexis MétaireauWed, 20 Apr 2011 00:00:00 +0200tag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/Article 1http://blog.notmyidea.org/posts/2011/February/17/article-1/<p>Article 1</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/Article 2http://blog.notmyidea.org/posts/2011/February/17/article-2/<p>Article 2</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/Article 3http://blog.notmyidea.org/posts/2011/February/17/article-3/<p>Article 3</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/This is a super article !http://blog.notmyidea.org/posts/2010/December/02/this-is-a-super-article/<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> +Alexis MétaireauSun, 17 Nov 2013 23:29:00 +0100tag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/foobarfoobarOh yeah !http://blog.notmyidea.org/posts/2010/October/20/oh-yeah/<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> +Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0200tag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/ohbaryeahUnbelievable !http://blog.notmyidea.org/posts/2010/October/15/unbelievable/<p>Or completely awesome. Depends the needs.</p> +<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a> +<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> +Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/The baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> +Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:tag/baz.html \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/all-en.atom.xml b/pelican/tests/output/custom_locale/feeds/all-en.atom.xml new file mode 100644 index 00000000..3bb10e38 --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/all-en.atom.xml @@ -0,0 +1,61 @@ + +Alexis' loghttp://blog.notmyidea.org/2013-11-17T23:29:00+01:00FILENAME_METADATA example2012-11-30T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/<p>Some cool stuff!</p> +Second article2012-02-29T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/<p>This is some article, in english</p> +A markdown powered article2011-04-20T00:00:00+02:00Alexis Métaireautag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/<p>You're mutually oblivious.</p> +<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a> +<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p>Article 12011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/<p>Article 1</p> +Article 22011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/<p>Article 2</p> +Article 32011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/<p>Article 3</p> +This is a super article !2013-11-17T23:29:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> +Oh yeah !2010-10-20T10:14:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> +Unbelievable !2010-10-15T20:30:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/<p>Or completely awesome. Depends the needs.</p> +<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a> +<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> +The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml b/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml new file mode 100644 index 00000000..5d58742c --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/all-fr.atom.xml @@ -0,0 +1,4 @@ + +Alexis' loghttp://blog.notmyidea.org/2012-03-02T14:01:01+01:00Trop bien !2012-03-02T14:01:01+01:00Alexis Métaireautag:blog.notmyidea.org,2012-03-02:oh-yeah-fr.html<p>Et voila du contenu en français</p> +Deuxième article2012-02-29T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-02-29:second-article-fr.html<p>Ceci est un article, en français.</p> + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/all.atom.xml b/pelican/tests/output/custom_locale/feeds/all.atom.xml new file mode 100644 index 00000000..f709f2b1 --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/all.atom.xml @@ -0,0 +1,63 @@ + +Alexis' loghttp://blog.notmyidea.org/2013-11-17T23:29:00+01:00FILENAME_METADATA example2012-11-30T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/<p>Some cool stuff!</p> +Trop bien !2012-03-02T14:01:01+01:00Alexis Métaireautag:blog.notmyidea.org,2012-03-02:oh-yeah-fr.html<p>Et voila du contenu en français</p> +Second article2012-02-29T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/<p>This is some article, in english</p> +Deuxième article2012-02-29T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-02-29:second-article-fr.html<p>Ceci est un article, en français.</p> +A markdown powered article2011-04-20T00:00:00+02:00Alexis Métaireautag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/<p>You're mutually oblivious.</p> +<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a> +<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p>Article 12011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/<p>Article 1</p> +Article 22011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/<p>Article 2</p> +Article 32011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/<p>Article 3</p> +This is a super article !2013-11-17T23:29:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> +Oh yeah !2010-10-20T10:14:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> +Unbelievable !2010-10-15T20:30:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/<p>Or completely awesome. Depends the needs.</p> +<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a> +<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> +The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/all.rss.xml b/pelican/tests/output/custom_locale/feeds/all.rss.xml new file mode 100644 index 00000000..39fbc240 --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/all.rss.xml @@ -0,0 +1,63 @@ + +Alexis' loghttp://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100FILENAME_METADATA examplehttp://blog.notmyidea.org/posts/2012/November/30/filename_metadata-example/<p>Some cool stuff!</p> +Alexis MétaireauFri, 30 Nov 2012 00:00:00 +0100tag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/Trop bien !http://blog.notmyidea.org/oh-yeah-fr.html<p>Et voila du contenu en français</p> +Alexis MétaireauFri, 02 Mar 2012 14:01:01 +0100tag:blog.notmyidea.org,2012-03-02:oh-yeah-fr.htmlSecond articlehttp://blog.notmyidea.org/posts/2012/February/29/second-article/<p>This is some article, in english</p> +Alexis MétaireauWed, 29 Feb 2012 00:00:00 +0100tag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/foobarbazDeuxième articlehttp://blog.notmyidea.org/second-article-fr.html<p>Ceci est un article, en français.</p> +Alexis MétaireauWed, 29 Feb 2012 00:00:00 +0100tag:blog.notmyidea.org,2012-02-29:second-article-fr.htmlfoobarbazA markdown powered articlehttp://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/<p>You're mutually oblivious.</p> +<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a> +<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p>Alexis MétaireauWed, 20 Apr 2011 00:00:00 +0200tag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/Article 1http://blog.notmyidea.org/posts/2011/February/17/article-1/<p>Article 1</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/Article 2http://blog.notmyidea.org/posts/2011/February/17/article-2/<p>Article 2</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/Article 3http://blog.notmyidea.org/posts/2011/February/17/article-3/<p>Article 3</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/This is a super article !http://blog.notmyidea.org/posts/2010/December/02/this-is-a-super-article/<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> +Alexis MétaireauSun, 17 Nov 2013 23:29:00 +0100tag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/foobarfoobarOh yeah !http://blog.notmyidea.org/posts/2010/October/20/oh-yeah/<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> +Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0200tag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/ohbaryeahUnbelievable !http://blog.notmyidea.org/posts/2010/October/15/unbelievable/<p>Or completely awesome. Depends the needs.</p> +<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a> +<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> +Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/The baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> +Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:tag/baz.html \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/bar.atom.xml b/pelican/tests/output/custom_locale/feeds/bar.atom.xml new file mode 100644 index 00000000..13a5cde2 --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/bar.atom.xml @@ -0,0 +1,8 @@ + +Alexis' loghttp://blog.notmyidea.org/2010-10-20T10:14:00+02:00Oh yeah !2010-10-20T10:14:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/bar.rss.xml b/pelican/tests/output/custom_locale/feeds/bar.rss.xml new file mode 100644 index 00000000..4426eb6a --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/bar.rss.xml @@ -0,0 +1,8 @@ + +Alexis' loghttp://blog.notmyidea.org/Wed, 20 Oct 2010 10:14:00 +0200Oh yeah !http://blog.notmyidea.org/posts/2010/October/20/oh-yeah/<div class="section" id="why-not"> +<h2>Why not ?</h2> +<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! +YEAH !</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +</div> +Alexis MétaireauWed, 20 Oct 2010 10:14:00 +0200tag:blog.notmyidea.org,2010-10-20:posts/2010/October/20/oh-yeah/ohbaryeah \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/cat1.atom.xml b/pelican/tests/output/custom_locale/feeds/cat1.atom.xml new file mode 100644 index 00000000..54d382c4 --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/cat1.atom.xml @@ -0,0 +1,7 @@ + +Alexis' loghttp://blog.notmyidea.org/2011-04-20T00:00:00+02:00A markdown powered article2011-04-20T00:00:00+02:00Alexis Métaireautag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/<p>You're mutually oblivious.</p> +<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a> +<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p>Article 12011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/<p>Article 1</p> +Article 22011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/<p>Article 2</p> +Article 32011-02-17T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/<p>Article 3</p> + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/cat1.rss.xml b/pelican/tests/output/custom_locale/feeds/cat1.rss.xml new file mode 100644 index 00000000..4f3b12f5 --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/cat1.rss.xml @@ -0,0 +1,7 @@ + +Alexis' loghttp://blog.notmyidea.org/Wed, 20 Apr 2011 00:00:00 +0200A markdown powered articlehttp://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/<p>You're mutually oblivious.</p> +<p><a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a root-relative link to unbelievable</a> +<a href="http://blog.notmyidea.org/posts/2010/October/15/unbelievable/">a file-relative link to unbelievable</a></p>Alexis MétaireauWed, 20 Apr 2011 00:00:00 +0200tag:blog.notmyidea.org,2011-04-20:posts/2011/April/20/a-markdown-powered-article/Article 1http://blog.notmyidea.org/posts/2011/February/17/article-1/<p>Article 1</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-1/Article 2http://blog.notmyidea.org/posts/2011/February/17/article-2/<p>Article 2</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-2/Article 3http://blog.notmyidea.org/posts/2011/February/17/article-3/<p>Article 3</p> +Alexis MétaireauThu, 17 Feb 2011 00:00:00 +0100tag:blog.notmyidea.org,2011-02-17:posts/2011/February/17/article-3/ \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/misc.atom.xml b/pelican/tests/output/custom_locale/feeds/misc.atom.xml new file mode 100644 index 00000000..18800485 --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/misc.atom.xml @@ -0,0 +1,38 @@ + +Alexis' loghttp://blog.notmyidea.org/2012-11-30T00:00:00+01:00FILENAME_METADATA example2012-11-30T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/<p>Some cool stuff!</p> +Second article2012-02-29T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/<p>This is some article, in english</p> +Unbelievable !2010-10-15T20:30:00+02:00Alexis Métaireautag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/<p>Or completely awesome. Depends the needs.</p> +<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a> +<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> +The baz tag2010-03-14T00:00:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-03-14:tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/misc.rss.xml b/pelican/tests/output/custom_locale/feeds/misc.rss.xml new file mode 100644 index 00000000..0be49f79 --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/misc.rss.xml @@ -0,0 +1,38 @@ + +Alexis' loghttp://blog.notmyidea.org/Fri, 30 Nov 2012 00:00:00 +0100FILENAME_METADATA examplehttp://blog.notmyidea.org/posts/2012/November/30/filename_metadata-example/<p>Some cool stuff!</p> +Alexis MétaireauFri, 30 Nov 2012 00:00:00 +0100tag:blog.notmyidea.org,2012-11-30:posts/2012/November/30/filename_metadata-example/Second articlehttp://blog.notmyidea.org/posts/2012/February/29/second-article/<p>This is some article, in english</p> +Alexis MétaireauWed, 29 Feb 2012 00:00:00 +0100tag:blog.notmyidea.org,2012-02-29:posts/2012/February/29/second-article/foobarbazUnbelievable !http://blog.notmyidea.org/posts/2010/October/15/unbelievable/<p>Or completely awesome. Depends the needs.</p> +<p><a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a root-relative link to markdown-article</a> +<a class="reference external" href="http://blog.notmyidea.org/posts/2011/April/20/a-markdown-powered-article/">a file-relative link to markdown-article</a></p> +<div class="section" id="testing-sourcecode-directive"> +<h2>Testing sourcecode directive</h2> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table></div> +<div class="section" id="testing-another-case"> +<h2>Testing another case</h2> +<p>This will now have a line number in 'custom' since it's the default in +pelican.conf, it will have nothing in default.</p> +<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +</td></tr></table><p>Lovely.</p> +</div> +<div class="section" id="testing-more-sourcecode-directives"> +<h2>Testing more sourcecode directives</h2> +<div class="highlight"><pre><span id="foo-8"><a name="foo-8"></a><span class="lineno special"> 8</span> <span class="testingk">def</span> <span class="testingnf">run</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingp">):</span><br></span><span id="foo-9"><a name="foo-9"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">assert_has_content</span><span class="testingp">()</span><br></span><span id="foo-10"><a name="foo-10"></a><span class="lineno special">10</span> <span class="testingk">try</span><span class="testingp">:</span><br></span><span id="foo-11"><a name="foo-11"></a><span class="lineno"> </span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">get_lexer_by_name</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">arguments</span><span class="testingp">[</span><span class="testingmi">0</span><span class="testingp">])</span><br></span><span id="foo-12"><a name="foo-12"></a><span class="lineno special">12</span> <span class="testingk">except</span> <span class="testingne">ValueError</span><span class="testingp">:</span><br></span><span id="foo-13"><a name="foo-13"></a><span class="lineno"> </span> <span class="testingc"># no lexer found - use the text one instead of an exception</span><br></span><span id="foo-14"><a name="foo-14"></a><span class="lineno special">14</span> <span class="testingn">lexer</span> <span class="testingo">=</span> <span class="testingn">TextLexer</span><span class="testingp">()</span><br></span><span id="foo-15"><a name="foo-15"></a><span class="lineno"> </span> <br></span><span id="foo-16"><a name="foo-16"></a><span class="lineno special">16</span> <span class="testingk">if</span> <span class="testingp">(</span><span class="testings">&#39;linenos&#39;</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span> <span class="testingow">and</span><br></span><span id="foo-17"><a name="foo-17"></a><span class="lineno"> </span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingow">not</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;table&#39;</span><span class="testingp">,</span> <span class="testings">&#39;inline&#39;</span><span class="testingp">)):</span><br></span><span id="foo-18"><a name="foo-18"></a><span class="lineno special">18</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testings">&#39;linenos&#39;</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testings">&#39;table&#39;</span><br></span><span id="foo-19"><a name="foo-19"></a><span class="lineno"> </span> <br></span><span id="foo-20"><a name="foo-20"></a><span class="lineno special">20</span> <span class="testingk">for</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingp">(</span><span class="testings">&#39;nowrap&#39;</span><span class="testingp">,</span> <span class="testings">&#39;nobackground&#39;</span><span class="testingp">,</span> <span class="testings">&#39;anchorlinenos&#39;</span><span class="testingp">):</span><br></span><span id="foo-21"><a name="foo-21"></a><span class="lineno"> </span> <span class="testingk">if</span> <span class="testingn">flag</span> <span class="testingow">in</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">:</span><br></span><span id="foo-22"><a name="foo-22"></a><span class="lineno special">22</span> <span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">[</span><span class="testingn">flag</span><span class="testingp">]</span> <span class="testingo">=</span> <span class="testingbp">True</span><br></span><span id="foo-23"><a name="foo-23"></a><span class="lineno"> </span> <br></span><span id="foo-24"><a name="foo-24"></a><span class="lineno special">24</span> <span class="testingc"># noclasses should already default to False, but just in case...</span><br></span><span id="foo-25"><a name="foo-25"></a><span class="lineno"> </span> <span class="testingn">formatter</span> <span class="testingo">=</span> <span class="testingn">HtmlFormatter</span><span class="testingp">(</span><span class="testingn">noclasses</span><span class="testingo">=</span><span class="testingbp">False</span><span class="testingp">,</span> <span class="testingo">**</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">options</span><span class="testingp">)</span><br></span><span id="foo-26"><a name="foo-26"></a><span class="lineno special">26</span> <span class="testingn">parsed</span> <span class="testingo">=</span> <span class="testingn">highlight</span><span class="testingp">(</span><span class="testings">&#39;</span><span class="testingse">\n</span><span class="testings">&#39;</span><span class="testingo">.</span><span class="testingn">join</span><span class="testingp">(</span><span class="testingbp">self</span><span class="testingo">.</span><span class="testingn">content</span><span class="testingp">),</span> <span class="testingn">lexer</span><span class="testingp">,</span> <span class="testingn">formatter</span><span class="testingp">)</span><br></span><span id="foo-27"><a name="foo-27"></a><span class="lineno"> </span> <span class="testingk">return</span> <span class="testingp">[</span><span class="testingn">nodes</span><span class="testingo">.</span><span class="testingn">raw</span><span class="testingp">(</span><span class="testings">&#39;&#39;</span><span class="testingp">,</span> <span class="testingn">parsed</span><span class="testingp">,</span> <span class="testingn">format</span><span class="testingo">=</span><span class="testings">&#39;html&#39;</span><span class="testingp">)]</span><br></span></pre></div> +<p>Lovely.</p> +</div> +<div class="section" id="testing-even-more-sourcecode-directives"> +<h2>Testing even more sourcecode directives</h2> +<span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +<p>Lovely.</p> +</div> +<div class="section" id="testing-overriding-config-defaults"> +<h2>Testing overriding config defaults</h2> +<p>Even if the default is line numbers, we can override it here</p> +<div class="highlight"><pre><span class="n">formatter</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">options</span> <span class="ow">and</span> <span class="n">VARIANTS</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">keys</span><span class="p">()[</span><span class="mi">0</span><span class="p">]]</span> +</pre></div> +<p>Lovely.</p> +</div> +Alexis MétaireauFri, 15 Oct 2010 20:30:00 +0200tag:blog.notmyidea.org,2010-10-15:posts/2010/October/15/unbelievable/The baz taghttp://blog.notmyidea.org/tag/baz.html<p>This article overrides the listening of the articles under the <em>baz</em> tag.</p> +Alexis MétaireauSun, 14 Mar 2010 00:00:00 +0100tag:blog.notmyidea.org,2010-03-14:tag/baz.html \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/yeah.atom.xml b/pelican/tests/output/custom_locale/feeds/yeah.atom.xml new file mode 100644 index 00000000..5f7d8c4f --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/yeah.atom.xml @@ -0,0 +1,14 @@ + +Alexis' loghttp://blog.notmyidea.org/2013-11-17T23:29:00+01:00This is a super article !2013-11-17T23:29:00+01:00Alexis Métaireautag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/feeds/yeah.rss.xml b/pelican/tests/output/custom_locale/feeds/yeah.rss.xml new file mode 100644 index 00000000..50c5803c --- /dev/null +++ b/pelican/tests/output/custom_locale/feeds/yeah.rss.xml @@ -0,0 +1,14 @@ + +Alexis' loghttp://blog.notmyidea.org/Sun, 17 Nov 2013 23:29:00 +0100This is a super article !http://blog.notmyidea.org/posts/2010/December/02/this-is-a-super-article/<p>Some content here !</p> +<div class="section" id="this-is-a-simple-title"> +<h2>This is a simple title</h2> +<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> +<img alt="alternate text" src="http://blog.notmyidea.org/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> +<pre class="literal-block"> +&gt;&gt;&gt; from ipdb import set_trace +&gt;&gt;&gt; set_trace() +</pre> +<p>→ And now try with some utf8 hell: ééé</p> +</div> +Alexis MétaireauSun, 17 Nov 2013 23:29:00 +0100tag:blog.notmyidea.org,2010-12-02:posts/2010/December/02/this-is-a-super-article/foobarfoobar \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/index.html b/pelican/tests/output/custom_locale/index.html new file mode 100644 index 00000000..fd9a82b4 --- /dev/null +++ b/pelican/tests/output/custom_locale/index.html @@ -0,0 +1,173 @@ + + + + + Alexis' log + + + + + + + + + +Fork me on GitHub + + + + +
    +

    Other articles

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

    + Page 1 / 3 + » +

    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/index2.html b/pelican/tests/output/custom_locale/index2.html new file mode 100644 index 00000000..f02ba27c --- /dev/null +++ b/pelican/tests/output/custom_locale/index2.html @@ -0,0 +1,187 @@ + + + + + Alexis' log + + + + + + + + + +Fork me on GitHub + + + +
    +
      +
    1. + +
    2. + +
    3. + +
    4. +
      +

      Oh yeah !

      +
      + +
      +
      +

      Why not ?

      +

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

      +alternate text +
      + + read more +

      There are comments.

      +
    5. +
    +

    + « + Page 2 / 3 + » +

    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html new file mode 100644 index 00000000..9c7416b6 --- /dev/null +++ b/pelican/tests/output/custom_locale/index3.html @@ -0,0 +1,138 @@ + + + + + Alexis' log + + + + + + + + + +Fork me on GitHub + + + +
    +
      +
    1. + +
    2. +
    +

    + « + Page 3 / 3 +

    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/jinja2_template.html b/pelican/tests/output/custom_locale/jinja2_template.html new file mode 100644 index 00000000..0eafa913 --- /dev/null +++ b/pelican/tests/output/custom_locale/jinja2_template.html @@ -0,0 +1,77 @@ + + + + + Alexis' log + + + + + + + + + +Fork me on GitHub + + + +Some text + +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/oh-yeah-fr.html b/pelican/tests/output/custom_locale/oh-yeah-fr.html new file mode 100644 index 00000000..cdda855d --- /dev/null +++ b/pelican/tests/output/custom_locale/oh-yeah-fr.html @@ -0,0 +1,116 @@ + + + + + Trop bien ! + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + Trop bien !

    +
    + +
    +

    Et voila du contenu en français

    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/override/index.html b/pelican/tests/output/custom_locale/override/index.html new file mode 100644 index 00000000..e84d79fe --- /dev/null +++ b/pelican/tests/output/custom_locale/override/index.html @@ -0,0 +1,81 @@ + + + + + Override url/save_as + + + + + + + + + +Fork me on GitHub + + +
    +

    Override url/save_as

    + +

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

    + +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html new file mode 100644 index 00000000..dced8107 --- /dev/null +++ b/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html @@ -0,0 +1,81 @@ + + + + + This is a test hidden page + + + + + + + + + +Fork me on GitHub + + +
    +

    This is a test hidden page

    + +

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

    + +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html b/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html new file mode 100644 index 00000000..46ea4fef --- /dev/null +++ b/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html @@ -0,0 +1,81 @@ + + + + + This is a test page + + + + + + + + + +Fork me on GitHub + + +
    +

    This is a test page

    + +

    Just an image.

    +alternate text + +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/pictures/Fat_Cat.jpg b/pelican/tests/output/custom_locale/pictures/Fat_Cat.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d8a96d356be94e782aaf36ba4e47168435b56338 GIT binary patch literal 62675 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

    )n(M0K@w($Mad^U|4b&>Gs6u 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 literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/pictures/Sushi.jpg b/pelican/tests/output/custom_locale/pictures/Sushi.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e49e5f0ab53461187c2cf61ebc1f3befdc937aed GIT binary patch literal 28992 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 V9RVclK)_SR8Zrq#de{_0|JgZd7T*8> literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html new file mode 100644 index 00000000..ec1007f3 --- /dev/null +++ b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html @@ -0,0 +1,129 @@ + + + + + This is a super article ! + + + + + + + + + +Fork me on GitHub + +

    +
    +
    +
    +

    + This is a super article !

    +
    + +
    +

    Some content here !

    +
    +

    This is a simple title

    +

    And here comes the cool stuff.

    +alternate text +alternate text +
    +>>> from ipdb import set_trace
    +>>> set_trace()
    +
    +

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

    +
    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html new file mode 100644 index 00000000..cb8ccef5 --- /dev/null +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html @@ -0,0 +1,146 @@ + + + + + Unbelievable ! + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + Unbelievable !

    +
    + +
    +

    Or completely awesome. Depends the needs.

    +

    a root-relative link to markdown-article +a file-relative link to markdown-article

    +
    +

    Testing sourcecode directive

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

    Testing another case

    +

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

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

    Lovely.

    +
    +
    +

    Testing more sourcecode directives

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

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

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

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

    Lovely.

    +
    +
    +

    Testing even more sourcecode directives

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

    Lovely.

    +
    +
    +

    Testing overriding config defaults

    +

    Even if the default is line numbers, we can override it here

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

    Lovely.

    +
    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html new file mode 100644 index 00000000..c42bce5d --- /dev/null +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html @@ -0,0 +1,121 @@ + + + + + Oh yeah ! + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + Oh yeah !

    +
    + +
    +
    +

    Why not ?

    +

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

    +alternate text +
    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html new file mode 100644 index 00000000..49cc6078 --- /dev/null +++ b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html @@ -0,0 +1,115 @@ + + + + + A markdown powered article + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + A markdown powered article

    +
    + +
    +

    You're mutually oblivious.

    +

    a root-relative link to unbelievable +a file-relative link to unbelievable

    +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html new file mode 100644 index 00000000..8029e585 --- /dev/null +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html @@ -0,0 +1,114 @@ + + + + + Article 1 + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + Article 1

    +
    + +
    +

    Article 1

    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html new file mode 100644 index 00000000..ca6aaaf3 --- /dev/null +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html @@ -0,0 +1,114 @@ + + + + + Article 2 + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + Article 2

    +
    + +
    +

    Article 2

    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html new file mode 100644 index 00000000..4f255f4f --- /dev/null +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html @@ -0,0 +1,114 @@ + + + + + Article 3 + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + Article 3

    +
    + +
    +

    Article 3

    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html new file mode 100644 index 00000000..46b07717 --- /dev/null +++ b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html @@ -0,0 +1,116 @@ + + + + + Second article + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + Second article

    +
    + +
    +

    This is some article, in english

    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html new file mode 100644 index 00000000..0d021cde --- /dev/null +++ b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html @@ -0,0 +1,114 @@ + + + + + FILENAME_METADATA example + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + FILENAME_METADATA example

    +
    + +
    +

    Some cool stuff!

    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/robots.txt b/pelican/tests/output/custom_locale/robots.txt new file mode 100644 index 00000000..19a6e299 --- /dev/null +++ b/pelican/tests/output/custom_locale/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: /pictures diff --git a/pelican/tests/output/custom_locale/second-article-fr.html b/pelican/tests/output/custom_locale/second-article-fr.html new file mode 100644 index 00000000..2798c94b --- /dev/null +++ b/pelican/tests/output/custom_locale/second-article-fr.html @@ -0,0 +1,116 @@ + + + + + Deuxième article + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + Deuxième article

    +
    + +
    +

    Ceci est un article, en français.

    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/tag/bar.html b/pelican/tests/output/custom_locale/tag/bar.html new file mode 100644 index 00000000..8a65a834 --- /dev/null +++ b/pelican/tests/output/custom_locale/tag/bar.html @@ -0,0 +1,160 @@ + + + + + Alexis' log - bar + + + + + + + + + +Fork me on GitHub + + + + +
    +

    Other articles

    +
    +
      + +
    1. + +
    2. +
      +

      Oh yeah !

      +
      + +
      +
      +

      Why not ?

      +

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

      +alternate text +
      + + read more +

      There are comments.

      +
    3. +
    +

    + Page 1 / 1 +

    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/tag/baz.html b/pelican/tests/output/custom_locale/tag/baz.html new file mode 100644 index 00000000..52467abb --- /dev/null +++ b/pelican/tests/output/custom_locale/tag/baz.html @@ -0,0 +1,114 @@ + + + + + The baz tag + + + + + + + + + +Fork me on GitHub + + +
    +
    +
    +

    + The baz tag

    +
    + +
    +

    This article overrides the listening of the articles under the baz tag.

    + +
    +
    +

    Comments !

    +
    + + +
    + +
    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/tag/foo.html b/pelican/tests/output/custom_locale/tag/foo.html new file mode 100644 index 00000000..87cb4ec5 --- /dev/null +++ b/pelican/tests/output/custom_locale/tag/foo.html @@ -0,0 +1,130 @@ + + + + + Alexis' log - foo + + + + + + + + + +Fork me on GitHub + + + + +
    +

    Other articles

    +
    +
      + +
    1. +
    +

    + Page 1 / 1 +

    +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/tag/foobar.html b/pelican/tests/output/custom_locale/tag/foobar.html new file mode 100644 index 00000000..0e5414b0 --- /dev/null +++ b/pelican/tests/output/custom_locale/tag/foobar.html @@ -0,0 +1,109 @@ + + + + + Alexis' log - foobar + + + + + + + + + +Fork me on GitHub + + + + +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/tag/oh.html b/pelican/tests/output/custom_locale/tag/oh.html new file mode 100644 index 00000000..21c8e352 --- /dev/null +++ b/pelican/tests/output/custom_locale/tag/oh.html @@ -0,0 +1,80 @@ + + + + + Oh Oh Oh + + + + + + + + + +Fork me on GitHub + + +
    +

    Oh Oh Oh

    + +

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

    + +
    +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/tag/yeah.html b/pelican/tests/output/custom_locale/tag/yeah.html new file mode 100644 index 00000000..a8fe6f51 --- /dev/null +++ b/pelican/tests/output/custom_locale/tag/yeah.html @@ -0,0 +1,101 @@ + + + + + Alexis' log - yeah + + + + + + + + + +Fork me on GitHub + + + + +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/tags.html b/pelican/tests/output/custom_locale/tags.html new file mode 100644 index 00000000..0da0d291 --- /dev/null +++ b/pelican/tests/output/custom_locale/tags.html @@ -0,0 +1,87 @@ + + + + + Alexis' log - Tags + + + + + + + + + +Fork me on GitHub + + + +
    +

    Tags for Alexis' log

    + +
    + +
    +
    +

    blogroll

    + +
    + +
    + + + + + + \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/theme/css/main.css b/pelican/tests/output/custom_locale/theme/css/main.css new file mode 100644 index 00000000..2efb518d --- /dev/null +++ b/pelican/tests/output/custom_locale/theme/css/main.css @@ -0,0 +1,451 @@ +/* + Name: Smashing HTML5 + Date: July 2009 + Description: Sample layout for HTML5 and CSS3 goodness. + Version: 1.0 + License: MIT + Licensed by: Smashing Media GmbH + Original author: Enrique Ramírez +*/ + +/* Imports */ +@import url("reset.css"); +@import url("pygment.css"); +@import url("typogrify.css"); +@import url(//fonts.googleapis.com/css?family=Yanone+Kaffeesatz&subset=latin); + +/***** Global *****/ +/* Body */ +body { + background: #F5F4EF; + color: #000305; + font-size: 87.5%; /* Base font size: 14px */ + font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; + line-height: 1.429; + margin: 0; + padding: 0; + text-align: left; +} + +/* Headings */ +h1 {font-size: 2em } +h2 {font-size: 1.571em} /* 22px */ +h3 {font-size: 1.429em} /* 20px */ +h4 {font-size: 1.286em} /* 18px */ +h5 {font-size: 1.143em} /* 16px */ +h6 {font-size: 1em} /* 14px */ + +h1, h2, h3, h4, h5, h6 { + font-weight: 400; + line-height: 1.1; + margin-bottom: .8em; + font-family: 'Yanone Kaffeesatz', arial, serif; +} + +h3, h4, h5, h6 { margin-top: .8em; } + +hr { border: 2px solid #EEEEEE; } + +/* Anchors */ +a {outline: 0;} +a img {border: 0px; text-decoration: none;} +a:link, a:visited { + color: #C74350; + padding: 0 1px; + text-decoration: underline; +} +a:hover, a:active { + background-color: #C74350; + color: #fff; + text-decoration: none; + text-shadow: 1px 1px 1px #333; +} + +h1 a:hover { + background-color: inherit +} + +/* Paragraphs */ +div.line-block, +p { margin-top: 1em; + margin-bottom: 1em;} + +strong, b {font-weight: bold;} +em, i {font-style: italic;} + +/* Lists */ +ul { + list-style: outside disc; + margin: 0em 0 0 1.5em; +} + +ol { + list-style: outside decimal; + margin: 0em 0 0 1.5em; +} + +li { margin-top: 0.5em;} + +.post-info { + float:right; + margin:10px; + padding:5px; +} + +.post-info p{ + margin-top: 1px; + margin-bottom: 1px; +} + +.readmore { float: right } + +dl {margin: 0 0 1.5em 0;} +dt {font-weight: bold;} +dd {margin-left: 1.5em;} + +pre{background-color: rgb(238, 238, 238); padding: 10px; margin: 10px; overflow: auto;} + +/* Quotes */ +blockquote { + margin: 20px; + font-style: italic; +} +cite {} + +q {} + +div.note { + float: right; + margin: 5px; + font-size: 85%; + max-width: 300px; +} + +/* Tables */ +table {margin: .5em auto 1.5em auto; width: 98%;} + + /* Thead */ + thead th {padding: .5em .4em; text-align: left;} + thead td {} + + /* Tbody */ + tbody td {padding: .5em .4em;} + tbody th {} + + tbody .alt td {} + tbody .alt th {} + + /* Tfoot */ + tfoot th {} + tfoot td {} + +/* HTML5 tags */ +header, section, footer, +aside, nav, article, figure { + display: block; +} + +/***** Layout *****/ +.body {clear: both; margin: 0 auto; width: 800px;} +img.right, figure.right {float: right; margin: 0 0 2em 2em;} +img.left, figure.left {float: left; margin: 0 2em 2em 0;} + +/* + Header +*****************/ +#banner { + margin: 0 auto; + padding: 2.5em 0 0 0; +} + + /* Banner */ + #banner h1 {font-size: 3.571em; line-height: 0;} + #banner h1 a:link, #banner h1 a:visited { + color: #000305; + display: block; + font-weight: bold; + margin: 0 0 .6em .2em; + text-decoration: none; + } + #banner h1 a:hover, #banner h1 a:active { + background: none; + color: #C74350; + text-shadow: none; + } + + #banner h1 strong {font-size: 0.36em; font-weight: normal;} + + /* Main Nav */ + #banner nav { + background: #000305; + font-size: 1.143em; + height: 40px; + line-height: 30px; + margin: 0 auto 2em auto; + padding: 0; + text-align: center; + width: 800px; + + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + } + + #banner nav ul {list-style: none; margin: 0 auto; width: 800px;} + #banner nav li {float: left; display: inline; margin: 0;} + + #banner nav a:link, #banner nav a:visited { + color: #fff; + display: inline-block; + height: 30px; + padding: 5px 1.5em; + text-decoration: none; + } + #banner nav a:hover, #banner nav a:active, + #banner nav .active a:link, #banner nav .active a:visited { + background: #C74451; + color: #fff; + text-shadow: none !important; + } + + #banner nav li:first-child a { + border-top-left-radius: 5px; + -moz-border-radius-topleft: 5px; + -webkit-border-top-left-radius: 5px; + + border-bottom-left-radius: 5px; + -moz-border-radius-bottomleft: 5px; + -webkit-border-bottom-left-radius: 5px; + } + +/* + Featured +*****************/ +#featured { + background: #fff; + margin-bottom: 2em; + overflow: hidden; + padding: 20px; + width: 760px; + + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} + +#featured figure { + border: 2px solid #eee; + float: right; + margin: 0.786em 2em 0 5em; + width: 248px; +} +#featured figure img {display: block; float: right;} + +#featured h2 {color: #C74451; font-size: 1.714em; margin-bottom: 0.333em;} +#featured h3 {font-size: 1.429em; margin-bottom: .5em;} + +#featured h3 a:link, #featured h3 a:visited {color: #000305; text-decoration: none;} +#featured h3 a:hover, #featured h3 a:active {color: #fff;} + +/* + Body +*****************/ +#content { + background: #fff; + margin-bottom: 2em; + overflow: hidden; + padding: 20px 20px; + width: 760px; + + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} + +/* + Extras +*****************/ +#extras {margin: 0 auto 3em auto; overflow: hidden;} + +#extras ul {list-style: none; margin: 0;} +#extras li {border-bottom: 1px solid #fff;} +#extras h2 { + color: #C74350; + font-size: 1.429em; + margin-bottom: .25em; + padding: 0 3px; +} + +#extras a:link, #extras a:visited { + color: #444; + display: block; + border-bottom: 1px solid #F4E3E3; + text-decoration: none; + padding: .3em .25em; +} + +#extras a:hover, #extras a:active {color: #fff;} + + /* Blogroll */ + #extras .blogroll { + float: left; + width: 615px; + } + + #extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;} + + /* Social */ + #extras .social { + float: right; + width: 175px; + } + + #extras div[class='social'] a { + background-repeat: no-repeat; + background-position: 3px 6px; + padding-left: 25px; + } + + /* Icons */ + .social a[href*='about.me'] {background-image: url('../images/icons/aboutme.png');} + .social a[href*='bitbucket.org'] {background-image: url('../images/icons/bitbucket.png');} + .social a[href*='delicious.com'] {background-image: url('../images/icons/delicious.png');} + .social a[href*='digg.com'] {background-image: url('../images/icons/digg.png');} + .social a[href*='facebook.com'] {background-image: url('../images/icons/facebook.png');} + .social a[href*='gitorious.org'] {background-image: url('../images/icons/gitorious.png');} + .social a[href*='github.com'], + .social a[href*='git.io'] { + background-image: url('../images/icons/github.png'); + background-size: 16px 16px; + } + .social a[href*='gittip.com'] {background-image: url('../images/icons/gittip.png');} + .social a[href*='plus.google.com'] {background-image: url('../images/icons/google-plus.png');} + .social a[href*='groups.google.com'] {background-image: url('../images/icons/google-groups.png');} + .social a[href*='news.ycombinator.com'], + .social a[href*='hackernewsers.com'] {background-image: url('../images/icons/hackernews.png');} + .social a[href*='last.fm'], .social a[href*='lastfm.'] {background-image: url('../images/icons/lastfm.png');} + .social a[href*='linkedin.com'] {background-image: url('../images/icons/linkedin.png');} + .social a[href*='reddit.com'] {background-image: url('../images/icons/reddit.png');} + .social a[type$='atom+xml'], .social a[type$='rss+xml'] {background-image: url('../images/icons/rss.png');} + .social a[href*='slideshare.net'] {background-image: url('../images/icons/slideshare.png');} + .social a[href*='speakerdeck.com'] {background-image: url('../images/icons/speakerdeck.png');} + .social a[href*='stackoverflow.com'] {background-image: url('../images/icons/stackoverflow.png');} + .social a[href*='twitter.com'] {background-image: url('../images/icons/twitter.png');} + .social a[href*='vimeo.com'] {background-image: url('../images/icons/vimeo.png');} + .social a[href*='youtube.com'] {background-image: url('../images/icons/youtube.png');} + +/* + About +*****************/ +#about { + background: #fff; + font-style: normal; + margin-bottom: 2em; + overflow: hidden; + padding: 20px; + text-align: left; + width: 760px; + + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; +} + +#about .primary {float: left; width: 165px;} +#about .primary strong {color: #C64350; display: block; font-size: 1.286em;} +#about .photo {float: left; margin: 5px 20px;} + +#about .url:link, #about .url:visited {text-decoration: none;} + +#about .bio {float: right; width: 500px;} + +/* + Footer +*****************/ +#contentinfo {padding-bottom: 2em; text-align: right;} + +/***** Sections *****/ +/* Blog */ +.hentry { + display: block; + clear: both; + border-bottom: 1px solid #eee; + padding: 1.5em 0; +} +li:last-child .hentry, #content > .hentry {border: 0; margin: 0;} +#content > .hentry {padding: 1em 0;} +.hentry img{display : none ;} +.entry-title {font-size: 3em; margin-bottom: 10px; margin-top: 0;} +.entry-title a:link, .entry-title a:visited {text-decoration: none; color: #333;} +.entry-title a:visited {background-color: #fff;} + +.hentry .post-info * {font-style: normal;} + + /* Content */ + .hentry footer {margin-bottom: 2em;} + .hentry footer address {display: inline;} + #posts-list footer address {display: block;} + + /* Blog Index */ + #posts-list {list-style: none; margin: 0;} + #posts-list .hentry {padding-left: 10px; position: relative;} + + #posts-list footer { + left: 10px; + position: relative; + float: left; + top: 0.5em; + width: 190px; + } + + /* About the Author */ + #about-author { + background: #f9f9f9; + clear: both; + font-style: normal; + margin: 2em 0; + padding: 10px 20px 15px 20px; + + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + } + + #about-author strong { + color: #C64350; + clear: both; + display: block; + font-size: 1.429em; + } + + #about-author .photo {border: 1px solid #ddd; float: left; margin: 5px 1em 0 0;} + + /* Comments */ + #comments-list {list-style: none; margin: 0 1em;} + #comments-list blockquote { + background: #f8f8f8; + clear: both; + font-style: normal; + margin: 0; + padding: 15px 20px; + + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + } + #comments-list footer {color: #888; padding: .5em 1em 0 0; text-align: right;} + + #comments-list li:nth-child(2n) blockquote {background: #F5f5f5;} + + /* Add a Comment */ + #add-comment label {clear: left; float: left; text-align: left; width: 150px;} + #add-comment input[type='text'], + #add-comment input[type='email'], + #add-comment input[type='url'] {float: left; width: 200px;} + + #add-comment textarea {float: left; height: 150px; width: 495px;} + + #add-comment p.req {clear: both; margin: 0 .5em 1em 0; text-align: right;} + + #add-comment input[type='submit'] {float: right; margin: 0 .5em;} + #add-comment * {margin-bottom: .5em;} diff --git a/pelican/tests/output/custom_locale/theme/css/pygment.css b/pelican/tests/output/custom_locale/theme/css/pygment.css new file mode 100644 index 00000000..fdd056f6 --- /dev/null +++ b/pelican/tests/output/custom_locale/theme/css/pygment.css @@ -0,0 +1,205 @@ +.hll { +background-color:#eee; +} +.c { +color:#408090; +font-style:italic; +} +.err { +border:1px solid #FF0000; +} +.k { +color:#007020; +font-weight:bold; +} +.o { +color:#666666; +} +.cm { +color:#408090; +font-style:italic; +} +.cp { +color:#007020; +} +.c1 { +color:#408090; +font-style:italic; +} +.cs { +background-color:#FFF0F0; +color:#408090; +} +.gd { +color:#A00000; +} +.ge { +font-style:italic; +} +.gr { +color:#FF0000; +} +.gh { +color:#000080; +font-weight:bold; +} +.gi { +color:#00A000; +} +.go { +color:#303030; +} +.gp { +color:#C65D09; +font-weight:bold; +} +.gs { +font-weight:bold; +} +.gu { +color:#800080; +font-weight:bold; +} +.gt { +color:#0040D0; +} +.kc { +color:#007020; +font-weight:bold; +} +.kd { +color:#007020; +font-weight:bold; +} +.kn { +color:#007020; +font-weight:bold; +} +.kp { +color:#007020; +} +.kr { +color:#007020; +font-weight:bold; +} +.kt { +color:#902000; +} +.m { +color:#208050; +} +.s { +color:#4070A0; +} +.na { +color:#4070A0; +} +.nb { +color:#007020; +} +.nc { +color:#0E84B5; +font-weight:bold; +} +.no { +color:#60ADD5; +} +.nd { +color:#555555; +font-weight:bold; +} +.ni { +color:#D55537; +font-weight:bold; +} +.ne { +color:#007020; +} +.nf { +color:#06287E; +} +.nl { +color:#002070; +font-weight:bold; +} +.nn { +color:#0E84B5; +font-weight:bold; +} +.nt { +color:#062873; +font-weight:bold; +} +.nv { +color:#BB60D5; +} +.ow { +color:#007020; +font-weight:bold; +} +.w { +color:#BBBBBB; +} +.mf { +color:#208050; +} +.mh { +color:#208050; +} +.mi { +color:#208050; +} +.mo { +color:#208050; +} +.sb { +color:#4070A0; +} +.sc { +color:#4070A0; +} +.sd { +color:#4070A0; +font-style:italic; +} +.s2 { +color:#4070A0; +} +.se { +color:#4070A0; +font-weight:bold; +} +.sh { +color:#4070A0; +} +.si { +color:#70A0D0; +font-style:italic; +} +.sx { +color:#C65D09; +} +.sr { +color:#235388; +} +.s1 { +color:#4070A0; +} +.ss { +color:#517918; +} +.bp { +color:#007020; +} +.vc { +color:#BB60D5; +} +.vg { +color:#BB60D5; +} +.vi { +color:#BB60D5; +} +.il { +color:#208050; +} diff --git a/pelican/tests/output/custom_locale/theme/css/reset.css b/pelican/tests/output/custom_locale/theme/css/reset.css new file mode 100644 index 00000000..1e217566 --- /dev/null +++ b/pelican/tests/output/custom_locale/theme/css/reset.css @@ -0,0 +1,52 @@ +/* + Name: Reset Stylesheet + Description: Resets browser's default CSS + Author: Eric Meyer + Author URI: http://meyerweb.com/eric/tools/css/reset/ +*/ + +/* v1.0 | 20080212 */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + background: transparent; + border: 0; + font-size: 100%; + margin: 0; + outline: 0; + padding: 0; + vertical-align: baseline; +} + +body {line-height: 1;} + +ol, ul {list-style: none;} + +blockquote, q {quotes: none;} + +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +/* remember to define focus styles! */ +:focus { + outline: 0; +} + +/* remember to highlight inserts somehow! */ +ins {text-decoration: none;} +del {text-decoration: line-through;} + +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: collapse; + border-spacing: 0; +} \ No newline at end of file diff --git a/pelican/tests/output/custom_locale/theme/css/typogrify.css b/pelican/tests/output/custom_locale/theme/css/typogrify.css new file mode 100644 index 00000000..c9b34dc8 --- /dev/null +++ b/pelican/tests/output/custom_locale/theme/css/typogrify.css @@ -0,0 +1,3 @@ +.caps {font-size:.92em;} +.amp {color:#666; font-size:1.05em;font-family:"Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua",serif; font-style:italic;} +.dquo {margin-left:-.38em;} diff --git a/pelican/tests/output/custom_locale/theme/css/wide.css b/pelican/tests/output/custom_locale/theme/css/wide.css new file mode 100644 index 00000000..88fd59ce --- /dev/null +++ b/pelican/tests/output/custom_locale/theme/css/wide.css @@ -0,0 +1,48 @@ +@import url("main.css"); + +body { + font:1.3em/1.3 "Hoefler Text","Georgia",Georgia,serif,sans-serif; +} + +.post-info{ + display: none; +} + +#banner nav { + display: none; + -moz-border-radius: 0px; + margin-bottom: 20px; + overflow: hidden; + font-size: 1em; + background: #F5F4EF; +} + +#banner nav ul{ + padding-right: 50px; +} + +#banner nav li{ + float: right; + color: #000; +} + +#banner nav li a { + color: #000; +} + +#banner h1 { + margin-bottom: -18px; +} + +#featured, #extras { + padding: 50px; +} + +#featured { + padding-top: 20px; +} + +#extras { + padding-top: 0px; + padding-bottom: 0px; +} diff --git a/pelican/tests/output/custom_locale/theme/images/icons/aboutme.png b/pelican/tests/output/custom_locale/theme/images/icons/aboutme.png new file mode 100644 index 0000000000000000000000000000000000000000..9609df3bd9d766cd4b827fb0a8339b700c1abf24 GIT binary patch literal 751 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM%9|WRD45bDP46hOx7_4S6Fo+k-*%fF5)Ta>O z6XFU~z-^brZ51PwI9(_Kh^7g}PZf%vA`~}SFm|F~be~{E7r$Q>mqnyt>;%CWAnF&0 z>J^CU7KrHN4{PNQZsheU=5fy8Gz;Yisssv#ckqX`@rAbXg*5X8HSz`4@%dHr`jqpy z=5RZtaF_)12GsKU*YNsP@%UEo0L8sad4MK(6mq-eal2-7IizygC9)g&bNiP6KWohG zS-|Cz#pRUFX_v@p6U%1c!{t%H>ygjnp2z8w&f}cUVI9k18Od!O&8Fwc>5|Q96~*D0 z#%UhPVH?k3;LWPz#^I34reVi!8_%X{#by@DrewmR<-%&@&nzOxqVB+|@6E{0!>ne@ zqU+A2Y{jhQ!lY=;OkH}&m?E%Ja zC$sHaw(BO9N_QK10Mpk${dC;$Iv-Sgs1 z>}GlM0tE(@$Z4WS9~SJov+DT=kEZa@(E0WcEcx0FsV`5Db6NC!V}woNM4#AY#y4|V zcGfJc2;H@B=ic4*7ff{9i&|5ruCcY7__a#1S1tN{P32SRuV!D_tlt0mk+S{xzkezF zn-7Y&EBC3-DcWUz;LL;X@g_ZzKBs5>H{b4a!=6dHduc-B)u;`Jo`r8<+{W~Vq2`0N zaE8$A3qX&kmbgZgq$HN4S|t~y0x1R~14Cn717lr7(-1=oD?>9YBLiInb1MS_`T5Fb hC>nC}Q!>*kF*O*PK{PbaT?tCT44$rjF6*2UngApI;B){0 literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/theme/images/icons/bitbucket.png b/pelican/tests/output/custom_locale/theme/images/icons/bitbucket.png new file mode 100644 index 0000000000000000000000000000000000000000..d05ba1610eab6ec3f9a4dcae689d4d88bda5433f GIT binary patch literal 3714 zcmZWr2T&8t)=eNvZ_=a)7$GPn0YsGE2?3PQlwJa%HzATJhF%1MbZG)oL=h=U6a+(& zE=cHzNRuvIddbI+e*bgc%-fl}d-u+{=bpQ>JF_v!TW}_NUU~okz=Y7%GCo~de;X~; z>71D4DhB}2tGj7xA`zOJLP$R!XE#qY0HDj1^8{;ZGRhTACS51>o&yVsZ1X;#6Y5vg z;tus0tY*%hOnKlC9(O^ey4u8spVsdn$dSujP9Y0bFKpuyIK4Hv(A+q`A9Ba!>*5}x}TK1^UvjUmTfsND7&LH@{ z7S-S}NjWp~rEXi*?iw`2scuZ70SWORJ_$|&U)K~q3!v8?l2ZzTaL-YtnpCy&$ee&a zZ>L?6iISG#%BL%M4W4PK@Z5bdFJ9M~kq(*7=e6kJ`6!!9s6$R5}gB2zAf z4{6XOQ$!YEZtwEI!2VMMfD}ijUH1Yc-!n!=n}cU9<`)%!Hnm0mqaSnJfxK%j09WiX z8w-j-po-Z>GTi1CJDd+Ut4t11(&DMjH>V%r49MF=#>Z0JAr(GPw1FZPUYV{*t7ZJF zs}{rat_KaHlLA2|6y=TwM`oLl6>_dRA=hHkbLBOR#0A(Dw#>dzRi9=CAbOouo11=Z zxGn_vbp}A3c)BGJG(;t}U0To}oev}vH^SL2PelN3Y?dY~*F}G^y zB?RYHwDw%_i(-AP?ruirbg~m=kM(4xQQqxKXS2l9yhiTL$VD@w#Z#snunkpc;x!pR8FCaQCZG?-`fvY}8ZDhYcg{*bG_ z)DuF0h!9;?>L=7tT`D&<&|7ttucuc~`YSLX+}#&282X?WbSq5G_pCMOP9u{q17iqW zjXtVLUkW%8>P8#3!OB^n{wk5Hh$T~Fn3d0x_P(Y%@&*TWt7c3hYqHuwFXZKUvG7;< znn>mCOBmi-TAT30XEeP+vS%qkd<0tt)qn%jSHSy_(Mm;rTKK1~DJ>l5QyT8Zv1z>; zE*0kLPu$ad8vuz&yH6>!Y8|xHeLCi}e4APtiRQvUwu>G~;3BFL?W{!RBEAptx=D9u zsn15|J7k>aYU7!SwjfNSZaWO4^rVI|95ZekaL>y*vtA872z!nidQ)sJ`dT%BPL3@* zUb5x3eS*DJW%?WHuMR7U{LJl*JU{q-gKExO%XnWa2UW9-5b8pfo0+B|o4Ai+6CCSd zEKU9!sD~Hp>4W)$1tt>&+Zsd@=`2AWVJsJ)2nl-%o3irKF3?gU*gnY`g}yYzfTLLN zw^*aDym|ap{Ud@Oaw~-c5hcuREPN%C7;1`e2iriDA*72boeEBHzw;Jqwua@xc|vv~ zEXk_K)XNYKLp^?NjnP~3dLPv8tKHXpZp`%3d(iwmbun# z>MT+fC!b$dzWPG4Po-$QRJqLYWtF*0;jOA3cAI3V)phl1>uM9MhM_*p9A;Nxw4SeG zlvkEl)Bta=3jW;KEA-7iPCdKnWqxdagF~)E;k4tWS4pLPVuhB4ifuO>-`|bhRGG1Y zRL0~Wk`eKpH%-0{PMF%)l8IzSk9k*!Z0WVS_JYdq78b8{hM&ndD|I|9yq|p_&Y&D; z9+$&4&SfXDDq$kIEFoxd#o~;)7x8E1Ve_|^EDsw;e0!ApjAK<#@%a3^vJ%P3fQD7P zVwO$iI9J`RI`S^_F8By?Bu%3njf*BR?|sDtT&|1253wyTPS^Bo5@u*b14Z8qAYM%7 zSoB=!Vd^PlVC3H59+W)q*LQcihTks<>I5a&;e7K4&BoGZESC?vZ;r$-1hjayjDJ-g zrPLgWv*@!V!QHe|yoXwMUw@&Zpc11Jufkp*P+y9N?K@DY zC;^A#TO%6_ha*RZpc|mqbc^&wARW+c8Wjc^x^rjlfts6uO?6>{uzovX>p?CmjbqJ# zNF};!j5eHy{^y|*vaca4_iM~>K{zZ(5~Z!B*;1#Q(9z!kkBjHh=fdiA!dtb6;5W4c zwbQlr)6FF;s%CWvmIR?6fTg>zlT<9`fxbP2-QBHirrPP9`sD;KAurw0jV_(8cr@Rn zckINV(KDmUdVANZ_4zi94;n_(B*`8tX3thnPS4eG=c+RIv2?I)-wvwlN9+$nk?+aN zlNR_p2jktl5v!!3iZ$j2Q>1SZ|YRDSHV<$|BJ4KBfU!CI@jn@MDNB>Z{%r-XY$ii0E9UtUj@`xKwt_Gad8 zyANe*M>CMUbP0L2@WVdn#zpzfAMN{s>$!|dqxWO+ua-n9dAsj>En_Y7hrbWUyf2b- z3p5CvTnZV;xzKY#QZ@VJ;WlA6Gpp*ei#|E2hx2K+d>%IFmquIUW?T$+w89;XN-c4= zKc~F1e4;Bfy~1|$gI~!Q>FC%jz0{>#-bN9hgD0&T;jhtpvF(y<7JpE%{ba zp3Iazu0MlcIJ7x}P3+d<=N_Nr@KMK|JzRs<2cFn|S6;$TQ2J;}SZr0hAKak0QXE%i z3PkA*#d2S%mQjZGN1RJY+bhpT?8#M+ToR&cG+`%c2Nj0RXUHXFaUG)0Pk0%eOgJEn zKX|jh2aCp~i!D(@J0PD71 z{33Ruv|e>Ll#+ksxZR$#v|_k#n3fl>ihW3T{0j&1a-N^ui`Y7nQLqR){ZrEU>RR{% z01WKE4G743dL94(db^pLW6WfYP-rO5%l8xw04U)UPF*iF#z6?@<>~FO zfKvwlK`5O1zr!%F&>slKLm6y-6Dg$W0grn@Mxj^9sS&V zF>XHILce((P(JrC%3$#CLVw4&XGGhim5`Rj3)|?6{!;4Q-+Di-Y1WYwde&nSOfw$g>hB1 zxjQV#rfjf)5{`)50OR_@^G(@G-Asg2<%(J$_p;@dAfw}(*`9sV(pwYPC$snFv-6+& z_w()O)K#LP+#ZpuIzz+|o?2DKf-gmmE^wl&`KBue-8;%|Da-SRZv>1-U-tPd>NUdH zHL`D4AGV8`v9KgO{f^rnK3=LReiSigsCx1$;9i=lc}A8}NteMd=H``}Zk4ckq1k{D}C8i8hSiyD{P?lK(|H8-FJuHAKt!4%PA%j_l{G~9xb*!3cv^W zdVjWOZSkPfA9#D``j^Nq(Sm7A;ZP)y@ytWZ#ydWkU(4@C4J0n-HehKafRT9dRnnI` z{UuZT$Qu*vP9wzOuK0L8|3T!sH6JyTrf^IoB2&GL=rOZUmXDAOOFoQ4rw*`c02 zss50}r?*7VupWuBmbyV}!rMIDaOp8@wRKem`u4_ARY)4g0YF1Gb&y!T6?8>dbVG7wVRUJ4ZXi@?ZDjy5FfTVRFgbe517iRH0~bj| zK~y*q1;M><8+9DO;m`f<=Zk&uOYFLC>!dcMNlRiXOAJIPLZ}-N2r=?6l!XNe7Ld9z z6a-9cEObDHN_0a4!Gc7jsFEAnrb(blQ`d>p#Bb+&^WA-U9s{6QELO(jaU+wlmuj`y zOSM|Pw6u7>?C5v1c7C;edel2@_h-|CVA`%`PmXKp)XB`5jeDpcel^zC-n+HDy!3vp ze$j3;8sxHR%H=c6&oA=Vefai!sFa}BgS}_Mz552E)|xJtFMV+H<`)V8 z0000&I&H=CwkC7|1+Ht5N=8hoX|`Vs_~ouY)RRea08$5l2_gU{hT&pq6FW7 z_P)m??qXUg(&k9=<<+O!vJ60ggg6!eOcNj>n-Mylh$t3LyAecfGI@`Qt8oVzqjIjW zs7s|O7Zgbn0tSWw5CbKQWRLbk7iAT&b4RGW3$c(&DZC)X=0EBkozJI+07|6+0002! z96pEP9Emb;Mn1?1gf?J9LHxOMyT9w>A|jE9 z1R@fPNF*X6Uw!$hG@H#gbvAe5wL~g-eup^rQ51ogp);b<%&}090TY-2A(DWhOb(AU zt=5*KY8U5!ilTtw@DL53MyKF(LtA0!3c*Cpe=q09vQf z+Cmu?U@++SOV_V|XzTs`Ct)sEAP7&e6nI{jI35E>5T)?~0l@biEX!gz^cXoVei)W7 zR4cQ(UcdZcW8)ct>tJdR43DWPg=J>YsWP8^{+%q$SDl^R1C=DY=lj9GdwUzhVljPq zs{nN~l=<`rQ{G z*EMNgZp)TeGt$L@YM4rb{DK+IJaKeh4CFB;dAqwXbg;^L06Clm9+AaB8pQTsa66f8 z2V~fKx;Tb#Tu)ADV02S85J*U9G7veylX_i&!&z12Q~+;~Q|gK~r==k=&T|S@G#cDE lb^5gT;nTtbJUf;eFfcqx6@IxHmKxL%0V&0TRzzznhgyqrIC$F)0{WwLXLrBvd*^wc_uSc%h%m9E z{W5z3f#4_!7RvAyFh6!S_*<8qJ%KOIm?#E|L=rJQq=gB5C6WLG5;c?r%V0>EmEH#X z5eSwPRa6WXBMs#$5H%GtW2go-in9p>zW@UYDNNWc^XOXZQ? z1QjEV00I#$3^1wQUJ8&-2UsjB-G|9y(LDhMNN3PM{APL4eYi{(m*ERcUnJa{R+-3^ z34^A6;U^v`8N*O6ji%S@sd{fJqD`XFIUJ5zgTe5^5nj414F(y!G&=H(f)Lgzv?>%+ zAsWD}2qhpH7>|TU`X&W6IxDNuO_vET7|j5oG&&VDr!)hUO8+0KR?nh!m<)a!?|%yG zqOwq!CWCcIhE{<$E|F|@g>nP6FoYr6C<8>D?ID9%&5J(4oSbR1I^byW*g@__U z4QsF&uJSEcFeleM3~ChjEQGbHOjsGDMbyAl(p=Ttv9RaVo8~I#js@@Y9C^_2U})yn zzSHU%6FxuY?d;&65MyR({^lU*3$z$ZllDb(o&<7d;A_`h2U+3~BJ2Hv`{W}KEU801#cv_B|9Cm!ynR{S`AMsSn z;7E=B;mb!wx$L;S>yGXG^6=&WlQn9$s?&L%Y1D8TI^MlKB1DqsEng$>f4=xYWBoPI z_S1p!sJ#d2?YI4kPA{k}Eby?F=f-J9zIc`YDl^pzjVm~9ebE?Hn?t0Nx+la|D0MB; z9)2xv1G>a1|A9kQ>~DV<=X3-4yC&n!m8-3K#P z{X@0zRuQsy$+N ziSCoLJU{Z$nQy4A4Y5UJ07$5FA~qL2%Q+cLaqDU?Lz3?=BC5;Nk6BbTmmceEaM>-Z zi>O&-dSE=%ex;vcvCOk{*JQ5^_4M z4lW7%l9IqY(z7pV(?I@@8=KPFO82)O{VDI18-*d-k$YmI^XiuPs_LuFw<^ZcD}yP5 c*NrbeloN*74g`U%%F6r~k%+>C^#XapzmV0H-2eap literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/theme/images/icons/gitorious.png b/pelican/tests/output/custom_locale/theme/images/icons/gitorious.png new file mode 100644 index 0000000000000000000000000000000000000000..3eeb3ecec36a73ff505e04ecdecbcc4792ef6786 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!O@L2`D?^o)dRwqYcev@q#DMvQ z*(*AHH`kW0om8=NO8&uVRmbKn*uQ@5(H+Zf?%R0x*ww37FTJ_{>eZ{eAD{pK|9`68 zWgnn9NuDl_ArhBs&l(CfDDW^GWLD*36#n?Xe(OZZj60uZDW6tS-s!tZ;qA;Vf>oNe zoO+M!7w(Q%nqK|iN%H(B6U8~-_(gR#lieQ**`4zb(6ROF5mHl4iMd b=se=)bK`s~y3?}Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyq( z0|^AkZL;YA00C!7L_t(I%VS`Gf&_0HFrDaQqmtllJp)MJ0^(m#dOmnQe`Ts~~BcM&z2u(nCq_gq= z7&lY6AuyMK{funGm2)TmfBF3B|J(^}V8h})E&un_7XJV6?(P5kw{QH<2ysPrkqQGe zaQ=a8*|2Qhe-QZh?;n_+)zkF9r6dC`2Exbo?ff4HG!&|51_Ly-!Oq;YV!?kH04e_c z`xn?|n1LYr#DU#F11w-JxWxdCCa^$xYWRObptr6V*$cl=42W?z`+xTMA)r;58bP){ zesK4Hl#4Nj0k@C?G1=e#|F54v;Lb)4le}mj^k}^W4@6{%>dY9FfCGV^+9H&wMGiz2 zjKtz?1=auqi>CG<2OMfNq9-I62F3rB_Uiwu=1l{q2lUhkPe>?9*&EFzURJO;$Dfob d%1o4IX8>>N|HqM7x0V0^002ovPDHLkV1m@;(I5Z- literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/theme/images/icons/google-groups.png b/pelican/tests/output/custom_locale/theme/images/icons/google-groups.png new file mode 100644 index 0000000000000000000000000000000000000000..5de15e68f4d1e4176b46fe6346d42f53e3296b21 GIT binary patch literal 803 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM%9|WRD45bDP46hOx7_4S6Fo+k-*%fF5)Mpyt z6XFU~@c;jRouXIDNvA)3c>3}ElMnA6zkmDa-J1t*U*CKC>h7DDcV4}?_43)x7td}y ze|qiNlPk|2Uv}&NGS4Ioi}&t+__Wd%$cHF^5*9C6Zh|& zS-fcGqD3=xieDc*uvxqCB`~XH4>TcVYa`q+>QZfU%sx2Z(n*wUnoNHS?EYjGdv*%IzuiFv_(s)h|m2&VRzopr01MEB^#A|> literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/theme/images/icons/google-plus.png b/pelican/tests/output/custom_locale/theme/images/icons/google-plus.png new file mode 100644 index 0000000000000000000000000000000000000000..3c6b74324031611f20c0a3810131aa74fd0a5a9f GIT binary patch literal 527 zcmV+q0`UEbP)ZK z>upIAVz}dFal_O6CT?`r$qOAn;FBaOK-k!D!Jidayc;Y>7GYGxnGBxue+IChM|)V`ar=`j`~3M zC07%M|NqZF^fS8TVssIRJYwIy1j>GXU1E8{UJs6dLU1-v!-3nb2Irih=+?6PKKJ@8lF5#&^v9fcghZg&b90USur#Ch_yBx0YzYJ9ftpZ|9@Mbb;3sH7#Llc zapC{3OLv2zTp&7Rr3KUgB!TRsHrfndzX7?Q-YzOS8|-?va>A!K_nuYT9<U;^giL<|Q^RJR%`Z!y$8yYa>Upa1{=xci{g2`qvgF`RFk`e^@wXB(G2 zShMW$$p`;HLHGV;SLhaf#f_+F1DIs^y0_<$oBBFk`Sl>Ab*Ovm_g^4AcawG2Lj|D- zqK4swiTo;US!A@iaP1FZz%33(6Ney%eY$dMwPn_zpvZ?`|G(*ST!StSLI9gPlb*yp RBR2p5002ovPDHLkV1g3B{|5j7 literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/theme/images/icons/hackernews.png b/pelican/tests/output/custom_locale/theme/images/icons/hackernews.png new file mode 100644 index 0000000000000000000000000000000000000000..fc7a82d4d68068d5fb032885b93e670c385ae1b4 GIT binary patch literal 3273 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 literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/theme/images/icons/lastfm.png b/pelican/tests/output/custom_locale/theme/images/icons/lastfm.png new file mode 100644 index 0000000000000000000000000000000000000000..3a6c6262b644dadbcf6cce5dfe4fed9740a9ec1f GIT binary patch literal 975 zcmV;=12FuFP)6?8>dbVG7wVRUJ4ZXi@?ZDjy5FfTVRFgbe517iRH11L#E zK~y*q1;I~j6jdC+@$Y-{cJ_af?v|Cd&=58%RRW19jgdqNQ4(W7BPZ0vgC6ith#oz2 zwmQaM~wsz^Z-QCXa%DC5DyB?4oI zHSqw7c|gHN7dAskEJO*20zV5*!N7txD5W7|ke~#<+t+u*fj>r ztH?8TXslr8?-TWV6(gOX%;6VdsD%CI2XwQA8l*5f0`DG!`c7!JVEYK^^e^` zcD=D}N`gz%A|jG3Hzk<+OHvW}@9ocI<-{ifWL)N6Op zl_6x*$4|GSoI{~dSr4lfE@RM%BU`HwdyYXEVvL7at4)4lw9&i$@PL;@=v)?SEfh1D zasgHF@wCQe1o1p1(b(1+x;KE@R@B7!CDScsKTn?9J!|Sao$80mhEYbMc$-A=FpjF~ xhgEG<4rh((w99I&de5(oe5r7A)lWa1`5&mym2=&ymqP#m002ovPDHLkV1nRSx}g97 literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/theme/images/icons/linkedin.png b/pelican/tests/output/custom_locale/theme/images/icons/linkedin.png new file mode 100644 index 0000000000000000000000000000000000000000..d29c1201bcb0c278d49f573f9ef95ebfe932fb5b GIT binary patch literal 896 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 KT-G@yGywnx3UT@X literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/theme/images/icons/stackoverflow.png b/pelican/tests/output/custom_locale/theme/images/icons/stackoverflow.png new file mode 100644 index 0000000000000000000000000000000000000000..f5b65e9990dbf423ff652b297f1d0172c8c1cf27 GIT binary patch literal 916 zcmeAS@N?(olHy`uVBq!ia0vp^0w65F1|7sn8f<3~fRJ+vJ~_8MEyP6_>_pwTJhc%)&)Mu9Ed(YG|jmTlZ9 z@KIPeZ^FK^-?9GB|W>{Q7m)EC+@8;a3mYxk}1?tCE&x6kxKjd-nU)D+_Cb1MZtP zf6ioHdF7Ur;v^T%WC`(qYnHY?KFysk()nSxK=;)vJ-w30K5t&s)3ykMV9;LODnQ81ohPJtCw>uw6%3rniLY9y-{G%>&K7poH)Ybf9hcZ zPx7%K&0{68BG<3+JpQ`j393DPNr7J)G`t`kgi;Fv5%&IGQ?s!oY8v6HF>egkKm6gM^6?fU09+hB7{`$2g z!Dr>|-0l5+On2^2_f4p`m-+THD#}N=VM2Fz(!8K!0$xisQhfv_dG-h{GMTvHCdbLN z^XFvvX21P9HE8+fMNXk>uPs~lGBfn^9g#*-%#3?x$4 ze30#20|W~;m8*zxxBvb3Dl3rX#Piv+IUiP)6?8>dbVG7wVRUJ4ZXi@?ZDjy5FfTVRFgbe517iRH0+&fd zK~y*q1;I;eT~!#z;orN~+WXwn#6*y$w(*h*ilq*eIv-e)>{rfz2>o|YjIakM#%UhMb+@bNp@nA+}SPdDo7O9oA zGH1DQ=3VCA#gT5=SO4a&tyI0gXBT8cdC;FG`L+ZpV~z_2(k zy27+~*2T$!9(dBjs?l*Gs@^i^ycrPqX$n7{!nGav4CBKp&|ow~joLoVsusArMnnq)x&OT0jOD0^FU!r)xMV z@T`Lg@WUazJu+?=Xo^@Bc7w?~m$5A3q=>T&2}l_pF5!6#!NjS=iGu;#28{!#P2-AU z0h%HbP#g*{cet?yAML`kY`i-ZA56r3H`W4*h|+12)Ju^?5tk}wyNb*^F%HHT7vW$4 z0AT#-;$9CI5)714(INy>lS^h8jI9{BG64bGL3}rZ+e`R_3xqi;MG7(3u z(Kz(IvnufS+IiH% z|H$#?(3yDKFI$%htO#ZKU{k0-ZZa!IYoMLFnl`;oUNO7#KQyI_l_SgjkN^Mx07*qo IM6N<$g7S@P4*&oF literal 0 HcmV?d00001 diff --git a/pelican/tests/output/custom_locale/theme/images/icons/vimeo.png b/pelican/tests/output/custom_locale/theme/images/icons/vimeo.png new file mode 100644 index 0000000000000000000000000000000000000000..dba472022f0fcf7ecdd8f4847a8a3bde90789bc7 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM%9|WRD45bDP46hOx7_4S6Fo+k-*%fF5l;{oc z32_C|+_Uys@43gj_L}zg+illgm0x@zv+3s4+i(B>|Nr6VAFo3XmfU;0&*Mqkod+xp6y#3gPyN@QHxXzy0-n4wn!XxLor|jgco@-h?@!*{&Jj>5-y7s_z z@+$6G2Ohk5z2Vroh5JukeEmths6S=tPM4Z~$^H#Vee))+-^JCwnxk~8b9t9&bn*5L zvT{HNF_r}R1v5B2yO9RsBze2LaDKeG^bL^H>FMGaB5^si|EAa>1s)gXg^zYT($l&Y zn|<~Fe;e+k=x2)RJNb=;l_qw0c;tC<3C(%IzGPL~DJ!ccy{fL?3~Y=q61VnM+{k& zPwWILICuW)Iw1Sxx96+>e|Uf6_up&l^DbVzD4uS~541_8B*-rqWG?~05TV1%3sir^ z)5S5Q;#N{XT0%k!1BU`zox({D0S6wQ93LMAIc5h2!;S_HIkiHz2^JA8f(i{15>tM# zJZaz(V9+^u@ZiYinline' ' markup and stuff to "typogrify' '"...

    \n', - 'date': datetime.datetime(2010, 12, 2, 10, 14), - 'modified': datetime.datetime(2010, 12, 2, 10, 20), + 'date': SafeDatetime(2010, 12, 2, 10, 14), + 'modified': SafeDatetime(2010, 12, 2, 10, 20), 'tags': ['foo', 'bar', 'foobar'], 'custom_field': 'http://notmyidea.org', } @@ -70,7 +70,7 @@ class RstReaderTest(ReaderTest): 'category': 'yeah', 'author': 'Alexis Métaireau', 'title': 'Rst with filename metadata', - 'date': datetime.datetime(2012, 11, 29), + 'date': SafeDatetime(2012, 11, 29), } for key, value in page.metadata.items(): self.assertEqual(value, expected[key], key) @@ -85,7 +85,7 @@ class RstReaderTest(ReaderTest): 'category': 'yeah', 'author': 'Alexis Métaireau', 'title': 'Rst with filename metadata', - 'date': datetime.datetime(2012, 11, 29), + 'date': SafeDatetime(2012, 11, 29), 'slug': 'article_with_filename_metadata', 'mymeta': 'foo', } @@ -171,8 +171,8 @@ class MdReaderTest(ReaderTest): 'category': 'test', 'title': 'Test md File', 'summary': '

    I have a lot to test

    ', - 'date': datetime.datetime(2010, 12, 2, 10, 14), - 'modified': datetime.datetime(2010, 12, 2, 10, 20), + 'date': SafeDatetime(2010, 12, 2, 10, 14), + 'modified': SafeDatetime(2010, 12, 2, 10, 20), 'tags': ['foo', 'bar', 'foobar'], } for key, value in metadata.items(): @@ -184,8 +184,8 @@ class MdReaderTest(ReaderTest): 'title': 'マックOS X 10.8でパイソンとVirtualenvをインストールと設定', 'summary': '

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

    ', 'category': '指導書', - 'date': datetime.datetime(2012, 12, 20), - 'modified': datetime.datetime(2012, 12, 22), + 'date': SafeDatetime(2012, 12, 20), + 'modified': SafeDatetime(2012, 12, 22), 'tags': ['パイソン', 'マック'], 'slug': 'python-virtualenv-on-mac-osx-mountain-lion-10.8', } @@ -220,8 +220,8 @@ class MdReaderTest(ReaderTest): 'summary': ( '

    Summary with inline markup ' 'should be supported.

    '), - 'date': datetime.datetime(2012, 10, 31), - 'modified': datetime.datetime(2012, 11, 1), + 'date': SafeDatetime(2012, 10, 31), + 'modified': SafeDatetime(2012, 11, 1), 'slug': 'article-with-markdown-containing-footnotes', 'multiline': [ 'Line Metadata should be handle properly.', @@ -311,7 +311,7 @@ class MdReaderTest(ReaderTest): expected = { 'category': 'yeah', 'author': 'Alexis Métaireau', - 'date': datetime.datetime(2012, 11, 30), + 'date': SafeDatetime(2012, 11, 30), } for key, value in expected.items(): self.assertEqual(value, page.metadata[key], key) @@ -325,7 +325,7 @@ class MdReaderTest(ReaderTest): expected = { 'category': 'yeah', 'author': 'Alexis Métaireau', - 'date': datetime.datetime(2012, 11, 30), + 'date': SafeDatetime(2012, 11, 30), 'slug': 'md_w_filename_meta', 'mymeta': 'foo', } @@ -358,7 +358,7 @@ class HTMLReaderTest(ReaderTest): 'author': 'Alexis Métaireau', 'title': 'This is a super article !', 'summary': 'Summary and stuff', - 'date': datetime.datetime(2010, 12, 2, 10, 14), + 'date': SafeDatetime(2010, 12, 2, 10, 14), 'tags': ['foo', 'bar', 'foobar'], 'custom_field': 'http://notmyidea.org', } @@ -382,7 +382,7 @@ class HTMLReaderTest(ReaderTest): 'author': 'Alexis Métaireau', 'title': 'This is a super article !', 'summary': 'Summary and stuff', - 'date': datetime.datetime(2010, 12, 2, 10, 14), + 'date': SafeDatetime(2010, 12, 2, 10, 14), 'tags': ['foo', 'bar', 'foobar'], 'custom_field': 'http://notmyidea.org', } diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 3c12a15b..df1918b5 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals, print_function, absolute_import import logging import shutil import os -import datetime import time import locale from sys import platform, version_info @@ -38,24 +37,24 @@ class TestUtils(LoggedTestCase): def test_get_date(self): # valid ones - date = datetime.datetime(year=2012, month=11, day=22) - date_hour = datetime.datetime( + date = utils.SafeDatetime(year=2012, month=11, day=22) + date_hour = utils.SafeDatetime( year=2012, month=11, day=22, hour=22, minute=11) - date_hour_z = datetime.datetime( + date_hour_z = utils.SafeDatetime( year=2012, month=11, day=22, hour=22, minute=11, tzinfo=pytz.timezone('UTC')) - date_hour_est = datetime.datetime( + date_hour_est = utils.SafeDatetime( year=2012, month=11, day=22, hour=22, minute=11, tzinfo=pytz.timezone('EST')) - date_hour_sec = datetime.datetime( + date_hour_sec = utils.SafeDatetime( year=2012, month=11, day=22, hour=22, minute=11, second=10) - date_hour_sec_z = datetime.datetime( + date_hour_sec_z = utils.SafeDatetime( year=2012, month=11, day=22, hour=22, minute=11, second=10, tzinfo=pytz.timezone('UTC')) - date_hour_sec_est = datetime.datetime( + date_hour_sec_est = utils.SafeDatetime( year=2012, month=11, day=22, hour=22, minute=11, second=10, tzinfo=pytz.timezone('EST')) - date_hour_sec_frac_z = datetime.datetime( + date_hour_sec_frac_z = utils.SafeDatetime( year=2012, month=11, day=22, hour=22, minute=11, second=10, microsecond=123000, tzinfo=pytz.timezone('UTC')) dates = { @@ -76,14 +75,14 @@ class TestUtils(LoggedTestCase): } # examples from http://www.w3.org/TR/NOTE-datetime - iso_8601_date = datetime.datetime(year=1997, month=7, day=16) - iso_8601_date_hour_tz = datetime.datetime( + iso_8601_date = utils.SafeDatetime(year=1997, month=7, day=16) + iso_8601_date_hour_tz = utils.SafeDatetime( year=1997, month=7, day=16, hour=19, minute=20, tzinfo=pytz.timezone('CET')) - iso_8601_date_hour_sec_tz = datetime.datetime( + iso_8601_date_hour_sec_tz = utils.SafeDatetime( year=1997, month=7, day=16, hour=19, minute=20, second=30, tzinfo=pytz.timezone('CET')) - iso_8601_date_hour_sec_ms_tz = datetime.datetime( + iso_8601_date_hour_sec_ms_tz = utils.SafeDatetime( year=1997, month=7, day=16, hour=19, minute=20, second=30, microsecond=450000, tzinfo=pytz.timezone('CET')) iso_8601 = { @@ -258,7 +257,7 @@ class TestUtils(LoggedTestCase): self.assertFalse(os.path.exists(test_directory)) def test_strftime(self): - d = datetime.date(2012, 8, 29) + d = utils.SafeDatetime(2012, 8, 29) # simple formatting self.assertEqual(utils.strftime(d, '%d/%m/%y'), '29/08/12') @@ -296,7 +295,7 @@ class TestUtils(LoggedTestCase): else: locale.setlocale(locale.LC_TIME, str('tr_TR.UTF-8')) - d = datetime.date(2012, 8, 29) + d = utils.SafeDatetime(2012, 8, 29) # simple self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 Ağustos 2012') @@ -329,7 +328,7 @@ class TestUtils(LoggedTestCase): else: locale.setlocale(locale.LC_TIME, str('fr_FR.UTF-8')) - d = datetime.date(2012, 8, 29) + d = utils.SafeDatetime(2012, 8, 29) # simple self.assertEqual(utils.strftime(d, '%d %B %Y'), '29 août 2012') @@ -448,7 +447,7 @@ class TestDateFormatter(unittest.TestCase): os.makedirs(template_dir) with open(template_path, 'w') as template_file: template_file.write('date = {{ date|strftime("%A, %d %B %Y") }}') - self.date = datetime.date(2012, 8, 29) + self.date = utils.SafeDatetime(2012, 8, 29) def tearDown(self): @@ -464,7 +463,7 @@ class TestDateFormatter(unittest.TestCase): def test_french_strftime(self): # This test tries to reproduce an issue that occured with python3.3 under macos10 only locale.setlocale(locale.LC_ALL, str('fr_FR.UTF-8')) - date = datetime.datetime(2014,8,14) + date = utils.SafeDatetime(2014,8,14) # we compare the lower() dates since macos10 returns "Jeudi" for %A whereas linux reports "jeudi" self.assertEqual( u'jeudi, 14 août 2014', utils.strftime(date, date_format="%A, %d %B %Y").lower() ) df = utils.DateFormatter() diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 7c8662c9..b6078201 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -21,7 +21,7 @@ from six.moves.urllib.error import URLError from six.moves.urllib.parse import urlparse from six.moves.urllib.request import urlretrieve -from pelican.utils import slugify +from pelican.utils import slugify, SafeDatetime from pelican.log import init logger = logging.getLogger(__name__) @@ -303,7 +303,7 @@ def dc2fields(file): def posterous2fields(api_token, email, password): """Imports posterous posts""" import base64 - from datetime import datetime, timedelta + from datetime import timedelta try: # py3k import import json @@ -340,7 +340,7 @@ def posterous2fields(api_token, email, password): slug = slugify(post.get('title')) tags = [tag.get('name') for tag in post.get('tags')] raw_date = post.get('display_date') - date_object = datetime.strptime(raw_date[:-6], "%Y/%m/%d %H:%M:%S") + date_object = SafeDatetime.strptime(raw_date[:-6], "%Y/%m/%d %H:%M:%S") offset = int(raw_date[-5:]) delta = timedelta(hours = offset / 100) date_object -= delta diff --git a/pelican/utils.py b/pelican/utils.py index 84b3a41e..586d85ff 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -14,6 +14,7 @@ import shutil import traceback import pickle import hashlib +import datetime from collections import Hashable from contextlib import contextmanager @@ -56,7 +57,10 @@ def strftime(date, date_format): for candidate in candidates: # test for valid C89 directives only if candidate[1] in 'aAbBcdfHIjmMpSUwWxXyYzZ%': - formatted = date.strftime(candidate) + if isinstance(date, SafeDatetime): + formatted = date.strftime(candidate, safe=False) + else: + formatted = date.strftime(candidate) # convert Py2 result to unicode if not six.PY3 and enc is not None: formatted = formatted.decode(enc) @@ -68,6 +72,17 @@ def strftime(date, date_format): return template % tuple(formatted_candidates) +class SafeDatetime(datetime.datetime): + '''Subclass of datetime that works with utf-8 format strings on PY2''' + + def strftime(self, fmt, safe=True): + '''Uses our custom strftime if supposed to be *safe*''' + if safe: + return strftime(self, fmt) + else: + return super(SafeDatetime, self).strftime(fmt) + + class DateFormatter(object): '''A date formatter object used as a jinja filter @@ -183,8 +198,10 @@ def get_date(string): If no format matches the given date, raise a ValueError. """ string = re.sub(' +', ' ', string) + default = SafeDatetime.now().replace(hour=0, minute=0, + second=0, microsecond=0) try: - return dateutil.parser.parse(string) + return dateutil.parser.parse(string, default=default) except (TypeError, ValueError): raise ValueError('{0!r} is not a valid date'.format(string)) diff --git a/pelican/writers.py b/pelican/writers.py index 3e01ee6c..61acdadd 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -151,12 +151,7 @@ class Writer(object): def _write_file(template, localcontext, output_path, name, override): """Render the template write the file.""" - old_locale = locale.setlocale(locale.LC_ALL) - locale.setlocale(locale.LC_ALL, str('C')) - try: - output = template.render(localcontext) - finally: - locale.setlocale(locale.LC_ALL, old_locale) + output = template.render(localcontext) path = os.path.join(output_path, name) try: os.makedirs(os.path.dirname(path)) diff --git a/samples/pelican_FR.conf.py b/samples/pelican_FR.conf.py new file mode 100644 index 00000000..1f6aaaa1 --- /dev/null +++ b/samples/pelican_FR.conf.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +AUTHOR = 'Alexis Métaireau' +SITENAME = "Alexis' log" +SITEURL = 'http://blog.notmyidea.org' +TIMEZONE = "Europe/Paris" + +# can be useful in development, but set to False when you're ready to publish +RELATIVE_URLS = True + +GITHUB_URL = 'http://github.com/ametaireau/' +DISQUS_SITENAME = "blog-notmyidea" +PDF_GENERATOR = False +REVERSE_CATEGORY_ORDER = True +LOCALE = "fr_FR.UTF-8" +DEFAULT_PAGINATION = 4 +DEFAULT_DATE = (2012, 3, 2, 14, 1, 1) + +ARTICLE_URL = 'posts/{date:%Y}/{date:%B}/{date:%d}/{slug}/' +ARTICLE_SAVE_AS = ARTICLE_URL + 'index.html' + +FEED_ALL_RSS = 'feeds/all.rss.xml' +CATEGORY_FEED_RSS = 'feeds/%s.rss.xml' + +LINKS = (('Biologeek', 'http://biologeek.org'), + ('Filyb', "http://filyb.info/"), + ('Libert-fr', "http://www.libert-fr.com"), + ('N1k0', "http://prendreuncafe.com/blog/"), + ('Tarek Ziadé', "http://ziade.org/blog"), + ('Zubin Mithra', "http://zubin71.wordpress.com/"),) + +SOCIAL = (('twitter', 'http://twitter.com/ametaireau'), + ('lastfm', 'http://lastfm.com/user/akounet'), + ('github', 'http://github.com/ametaireau'),) + +# global metadata to all the contents +DEFAULT_METADATA = (('yeah', 'it is'),) + +# 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', + ] + +# custom page generated with a jinja2 template +TEMPLATE_PAGES = {'pages/jinja2_template.html': 'jinja2_template.html'} + +# code blocks with line numbers +PYGMENTS_RST_OPTIONS = {'linenos': 'table'} + +# foobar will not be used, because it's not in caps. All configuration keys +# have to be in caps +foobar = "barbaz" From 009543b7e476d3bcaca3dd8576a47631c06de1e7 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Thu, 26 Jun 2014 00:51:45 -0400 Subject: [PATCH 0276/1427] Fix test errors on OSX On OSX, if LC_TIME and LC_CTYPE differs the output of strftime is not properly decoded in Python 3. This makes sure that the 'utils.DateFormatter' and the related Jinja filter 'strftime' set the same value for LC_TIME and LC_CTYPE while formatting. Also, '%a' is removed from DEFAULT_DATE_FORMAT in 'custom_locale' tests. OSX and *nix have different conversions for '%a' ('Jeu' vs 'jeu.') and there is not a feasible way to handle the difference for tests. --- docs/contribute.rst | 2 +- .../tests/output/custom_locale/archives.html | 20 +++++++++---------- .../author/alexis-metaireau.html | 8 ++++---- .../author/alexis-metaireau2.html | 10 +++++----- .../author/alexis-metaireau3.html | 4 ++-- .../output/custom_locale/category/bar.html | 2 +- .../output/custom_locale/category/cat1.html | 8 ++++---- .../output/custom_locale/category/misc.html | 8 ++++---- .../output/custom_locale/category/yeah.html | 4 ++-- .../custom_locale/drafts/a-draft-article.html | 2 +- pelican/tests/output/custom_locale/index.html | 8 ++++---- .../tests/output/custom_locale/index2.html | 10 +++++----- .../tests/output/custom_locale/index3.html | 4 ++-- .../output/custom_locale/oh-yeah-fr.html | 2 +- .../02/this-is-a-super-article/index.html | 4 ++-- .../2010/octobre/15/unbelievable/index.html | 2 +- .../posts/2010/octobre/20/oh-yeah/index.html | 2 +- .../20/a-markdown-powered-article/index.html | 2 +- .../2011/février/17/article-1/index.html | 2 +- .../2011/février/17/article-2/index.html | 2 +- .../2011/février/17/article-3/index.html | 2 +- .../2012/février/29/second-article/index.html | 2 +- .../30/filename_metadata-example/index.html | 2 +- .../custom_locale/second-article-fr.html | 2 +- .../tests/output/custom_locale/tag/bar.html | 8 ++++---- .../tests/output/custom_locale/tag/baz.html | 2 +- .../tests/output/custom_locale/tag/foo.html | 6 +++--- .../output/custom_locale/tag/foobar.html | 4 ++-- .../tests/output/custom_locale/tag/yeah.html | 2 +- pelican/tests/test_pelican.py | 2 +- pelican/utils.py | 10 ++++++++-- ...{pelican_FR.conf.py => pelican.conf_FR.py} | 1 + 32 files changed, 78 insertions(+), 71 deletions(-) rename samples/{pelican_FR.conf.py => pelican.conf_FR.py} (98%) diff --git a/docs/contribute.rst b/docs/contribute.rst index 044ef924..19604da0 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -91,7 +91,7 @@ functional tests. To do so, you can use the following two commands:: $ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/custom/ \ -s samples/pelican.conf.py samples/content/ $ LC_ALL=fr_FR.utf8 pelican -o pelican/tests/output/custom_locale/ \ - -s samples/pelican_FR.conf.py samples/content/ + -s samples/pelican.conf_FR.py samples/content/ $ LC_ALL=en_US.utf8 pelican -o pelican/tests/output/basic/ \ samples/content/ diff --git a/pelican/tests/output/custom_locale/archives.html b/pelican/tests/output/custom_locale/archives.html index a7b96336..1336b487 100644 --- a/pelican/tests/output/custom_locale/archives.html +++ b/pelican/tests/output/custom_locale/archives.html @@ -32,25 +32,25 @@

    Archives for Alexis' log

    -
    ven. 30 novembre 2012
    +
    30 novembre 2012
    FILENAME_METADATA example
    -
    mer. 29 février 2012
    +
    29 février 2012
    Second article
    -
    mer. 20 avril 2011
    +
    20 avril 2011
    A markdown powered article
    -
    jeu. 17 février 2011
    +
    17 février 2011
    Article 1
    -
    jeu. 17 février 2011
    +
    17 février 2011
    Article 2
    -
    jeu. 17 février 2011
    +
    17 février 2011
    Article 3
    -
    jeu. 02 décembre 2010
    +
    02 décembre 2010
    This is a super article !
    -
    mer. 20 octobre 2010
    +
    20 octobre 2010
    Oh yeah !
    -
    ven. 15 octobre 2010
    +
    15 octobre 2010
    Unbelievable !
    -
    dim. 14 mars 2010
    +
    14 mars 2010
    The baz tag
    diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau.html b/pelican/tests/output/custom_locale/author/alexis-metaireau.html index b54446c8..7c2fa448 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau.html @@ -34,7 +34,7 @@

    FILENAME_METADATA example

    - Published: ven. 30 novembre 2012 + Published: 30 novembre 2012
    @@ -59,7 +59,7 @@
    - Published: mer. 29 février 2012 + Published: 29 février 2012
    @@ -84,7 +84,7 @@
    - Published: mer. 20 avril 2011 + Published: 20 avril 2011
    @@ -108,7 +108,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html index 07020512..9ba313da 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html @@ -40,7 +40,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    @@ -63,7 +63,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    @@ -86,11 +86,11 @@
    - Published: jeu. 02 décembre 2010 + Published: 02 décembre 2010
    - Updated: dim. 17 novembre 2013 + Updated: 17 novembre 2013
    @@ -114,7 +114,7 @@ as well as inline markup.

    - Published: mer. 20 octobre 2010 + Published: 20 octobre 2010
    diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html index 9578e3d6..5104b7b0 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html @@ -40,7 +40,7 @@
    - Published: ven. 15 octobre 2010 + Published: 15 octobre 2010
    @@ -73,7 +73,7 @@ pelican.conf, it ...

    - Published: dim. 14 mars 2010 + Published: 14 mars 2010
    diff --git a/pelican/tests/output/custom_locale/category/bar.html b/pelican/tests/output/custom_locale/category/bar.html index d9fc6acb..c693d3f5 100644 --- a/pelican/tests/output/custom_locale/category/bar.html +++ b/pelican/tests/output/custom_locale/category/bar.html @@ -34,7 +34,7 @@

    Oh yeah !

    - Published: mer. 20 octobre 2010 + Published: 20 octobre 2010
    diff --git a/pelican/tests/output/custom_locale/category/cat1.html b/pelican/tests/output/custom_locale/category/cat1.html index 1b09acfe..c332f817 100644 --- a/pelican/tests/output/custom_locale/category/cat1.html +++ b/pelican/tests/output/custom_locale/category/cat1.html @@ -34,7 +34,7 @@

    A markdown powered article

    - Published: mer. 20 avril 2011 + Published: 20 avril 2011
    @@ -60,7 +60,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    @@ -83,7 +83,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    @@ -106,7 +106,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    diff --git a/pelican/tests/output/custom_locale/category/misc.html b/pelican/tests/output/custom_locale/category/misc.html index bcaec248..02562525 100644 --- a/pelican/tests/output/custom_locale/category/misc.html +++ b/pelican/tests/output/custom_locale/category/misc.html @@ -34,7 +34,7 @@

    FILENAME_METADATA example

    - Published: ven. 30 novembre 2012 + Published: 30 novembre 2012
    @@ -59,7 +59,7 @@
    - Published: mer. 29 février 2012 + Published: 29 février 2012
    @@ -84,7 +84,7 @@
    - Published: ven. 15 octobre 2010 + Published: 15 octobre 2010
    @@ -117,7 +117,7 @@ pelican.conf, it ...

    - Published: dim. 14 mars 2010 + Published: 14 mars 2010
    diff --git a/pelican/tests/output/custom_locale/category/yeah.html b/pelican/tests/output/custom_locale/category/yeah.html index 7f881612..6427095b 100644 --- a/pelican/tests/output/custom_locale/category/yeah.html +++ b/pelican/tests/output/custom_locale/category/yeah.html @@ -34,11 +34,11 @@

    This is a super article !

    - Published: jeu. 02 décembre 2010 + Published: 02 décembre 2010
    - Updated: dim. 17 novembre 2013 + Updated: 17 novembre 2013
    diff --git a/pelican/tests/output/custom_locale/drafts/a-draft-article.html b/pelican/tests/output/custom_locale/drafts/a-draft-article.html index 82fb057b..16ff22cd 100644 --- a/pelican/tests/output/custom_locale/drafts/a-draft-article.html +++ b/pelican/tests/output/custom_locale/drafts/a-draft-article.html @@ -39,7 +39,7 @@
    - Published: ven. 02 mars 2012 + Published: 02 mars 2012
    diff --git a/pelican/tests/output/custom_locale/index.html b/pelican/tests/output/custom_locale/index.html index fd9a82b4..5898c5b5 100644 --- a/pelican/tests/output/custom_locale/index.html +++ b/pelican/tests/output/custom_locale/index.html @@ -34,7 +34,7 @@

    FILENAME_METADATA example

    - Published: ven. 30 novembre 2012 + Published: 30 novembre 2012
    @@ -59,7 +59,7 @@
    - Published: mer. 29 février 2012 + Published: 29 février 2012
    @@ -84,7 +84,7 @@
    - Published: mer. 20 avril 2011 + Published: 20 avril 2011
    @@ -108,7 +108,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    diff --git a/pelican/tests/output/custom_locale/index2.html b/pelican/tests/output/custom_locale/index2.html index f02ba27c..f1ed6b46 100644 --- a/pelican/tests/output/custom_locale/index2.html +++ b/pelican/tests/output/custom_locale/index2.html @@ -40,7 +40,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    @@ -63,7 +63,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    @@ -86,11 +86,11 @@
    - Published: jeu. 02 décembre 2010 + Published: 02 décembre 2010
    - Updated: dim. 17 novembre 2013 + Updated: 17 novembre 2013
    @@ -114,7 +114,7 @@ as well as inline markup.

    - Published: mer. 20 octobre 2010 + Published: 20 octobre 2010
    diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html index 9c7416b6..7f7a905f 100644 --- a/pelican/tests/output/custom_locale/index3.html +++ b/pelican/tests/output/custom_locale/index3.html @@ -40,7 +40,7 @@
    - Published: ven. 15 octobre 2010 + Published: 15 octobre 2010
    @@ -73,7 +73,7 @@ pelican.conf, it ...

    - Published: dim. 14 mars 2010 + Published: 14 mars 2010
    diff --git a/pelican/tests/output/custom_locale/oh-yeah-fr.html b/pelican/tests/output/custom_locale/oh-yeah-fr.html index cdda855d..72c8b62d 100644 --- a/pelican/tests/output/custom_locale/oh-yeah-fr.html +++ b/pelican/tests/output/custom_locale/oh-yeah-fr.html @@ -39,7 +39,7 @@
    - Published: ven. 02 mars 2012 + Published: 02 mars 2012
    diff --git a/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html index ec1007f3..0dd0e3da 100644 --- a/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html @@ -39,11 +39,11 @@
    - Published: jeu. 02 décembre 2010 + Published: 02 décembre 2010
    - Updated: dim. 17 novembre 2013 + Updated: 17 novembre 2013
    diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html index cb8ccef5..6cdb4bb1 100644 --- a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html @@ -39,7 +39,7 @@
    - Published: ven. 15 octobre 2010 + Published: 15 octobre 2010
    diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html index c42bce5d..233df239 100644 --- a/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html @@ -39,7 +39,7 @@
    - Published: mer. 20 octobre 2010 + Published: 20 octobre 2010
    diff --git a/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html index 49cc6078..aa4d254f 100644 --- a/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html @@ -39,7 +39,7 @@
    - Published: mer. 20 avril 2011 + Published: 20 avril 2011
    diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html index 8029e585..e9c86a97 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html @@ -39,7 +39,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html index ca6aaaf3..8724d7c2 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html @@ -39,7 +39,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html index 4f255f4f..c14b537d 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html @@ -39,7 +39,7 @@
    - Published: jeu. 17 février 2011 + Published: 17 février 2011
    diff --git a/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html index 46b07717..598bf797 100644 --- a/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html @@ -39,7 +39,7 @@
    - Published: mer. 29 février 2012 + Published: 29 février 2012
    diff --git a/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html index 0d021cde..66339175 100644 --- a/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html +++ b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html @@ -39,7 +39,7 @@
    - Published: ven. 30 novembre 2012 + Published: 30 novembre 2012
    diff --git a/pelican/tests/output/custom_locale/second-article-fr.html b/pelican/tests/output/custom_locale/second-article-fr.html index 2798c94b..eb2225d9 100644 --- a/pelican/tests/output/custom_locale/second-article-fr.html +++ b/pelican/tests/output/custom_locale/second-article-fr.html @@ -39,7 +39,7 @@
    - Published: mer. 29 février 2012 + Published: 29 février 2012
    diff --git a/pelican/tests/output/custom_locale/tag/bar.html b/pelican/tests/output/custom_locale/tag/bar.html index 8a65a834..a8e3bd7a 100644 --- a/pelican/tests/output/custom_locale/tag/bar.html +++ b/pelican/tests/output/custom_locale/tag/bar.html @@ -34,7 +34,7 @@

    Second article

    - Published: mer. 29 février 2012 + Published: 29 février 2012
    @@ -61,11 +61,11 @@
    - Published: jeu. 02 décembre 2010 + Published: 02 décembre 2010
    - Updated: dim. 17 novembre 2013 + Updated: 17 novembre 2013
    @@ -89,7 +89,7 @@ as well as inline markup.

    - Published: mer. 20 octobre 2010 + Published: 20 octobre 2010
    diff --git a/pelican/tests/output/custom_locale/tag/baz.html b/pelican/tests/output/custom_locale/tag/baz.html index 52467abb..77e09faf 100644 --- a/pelican/tests/output/custom_locale/tag/baz.html +++ b/pelican/tests/output/custom_locale/tag/baz.html @@ -39,7 +39,7 @@
    - Published: dim. 14 mars 2010 + Published: 14 mars 2010
    diff --git a/pelican/tests/output/custom_locale/tag/foo.html b/pelican/tests/output/custom_locale/tag/foo.html index 87cb4ec5..3c238811 100644 --- a/pelican/tests/output/custom_locale/tag/foo.html +++ b/pelican/tests/output/custom_locale/tag/foo.html @@ -34,7 +34,7 @@

    Second article

    - Published: mer. 29 février 2012 + Published: 29 février 2012
    @@ -61,11 +61,11 @@
    - Published: jeu. 02 décembre 2010 + Published: 02 décembre 2010
    - Updated: dim. 17 novembre 2013 + Updated: 17 novembre 2013
    diff --git a/pelican/tests/output/custom_locale/tag/foobar.html b/pelican/tests/output/custom_locale/tag/foobar.html index 0e5414b0..e30c4906 100644 --- a/pelican/tests/output/custom_locale/tag/foobar.html +++ b/pelican/tests/output/custom_locale/tag/foobar.html @@ -34,11 +34,11 @@

    This is a super article !

    - Published: jeu. 02 décembre 2010 + Published: 02 décembre 2010
    - Updated: dim. 17 novembre 2013 + Updated: 17 novembre 2013
    diff --git a/pelican/tests/output/custom_locale/tag/yeah.html b/pelican/tests/output/custom_locale/tag/yeah.html index a8fe6f51..6754ecf6 100644 --- a/pelican/tests/output/custom_locale/tag/yeah.html +++ b/pelican/tests/output/custom_locale/tag/yeah.html @@ -34,7 +34,7 @@

    Oh yeah !

    - Published: mer. 20 octobre 2010 + Published: 20 octobre 2010
    diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index cfae594a..bad73569 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -20,7 +20,7 @@ OUTPUT_PATH = os.path.abspath(os.path.join(CURRENT_DIR, 'output')) INPUT_PATH = os.path.join(SAMPLES_PATH, "content") SAMPLE_CONFIG = os.path.join(SAMPLES_PATH, "pelican.conf.py") -SAMPLE_FR_CONFIG = os.path.join(SAMPLES_PATH, "pelican_FR.conf.py") +SAMPLE_FR_CONFIG = os.path.join(SAMPLES_PATH, "pelican.conf_FR.py") def recursiveDiff(dcmp): diff --git a/pelican/utils.py b/pelican/utils.py index 586d85ff..5e4822ba 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -94,12 +94,18 @@ class DateFormatter(object): self.locale = locale.setlocale(locale.LC_TIME) def __call__(self, date, date_format): - old_locale = locale.setlocale(locale.LC_TIME) + old_lc_time = locale.setlocale(locale.LC_TIME) + old_lc_ctype = locale.setlocale(locale.LC_CTYPE) + locale.setlocale(locale.LC_TIME, self.locale) + # on OSX, encoding from LC_CTYPE determines the unicode output in PY3 + # make sure it's same as LC_TIME + locale.setlocale(locale.LC_CTYPE, self.locale) formatted = strftime(date, date_format) - locale.setlocale(locale.LC_TIME, old_locale) + locale.setlocale(locale.LC_TIME, old_lc_time) + locale.setlocale(locale.LC_CTYPE, old_lc_ctype) return formatted diff --git a/samples/pelican_FR.conf.py b/samples/pelican.conf_FR.py similarity index 98% rename from samples/pelican_FR.conf.py rename to samples/pelican.conf_FR.py index 1f6aaaa1..0c4ec60d 100644 --- a/samples/pelican_FR.conf.py +++ b/samples/pelican.conf_FR.py @@ -16,6 +16,7 @@ REVERSE_CATEGORY_ORDER = True LOCALE = "fr_FR.UTF-8" DEFAULT_PAGINATION = 4 DEFAULT_DATE = (2012, 3, 2, 14, 1, 1) +DEFAULT_DATE_FORMAT = '%d %B %Y' ARTICLE_URL = 'posts/{date:%Y}/{date:%B}/{date:%d}/{slug}/' ARTICLE_SAVE_AS = ARTICLE_URL + 'index.html' From ce8574aff4e527b533557feedbb6256937359ede Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Wed, 11 Jun 2014 15:58:06 -0400 Subject: [PATCH 0277/1427] Fix HTMLParser related deprecation warnings in Py3.4 --- pelican/readers.py | 12 ++++++------ pelican/tools/pelican_import.py | 9 ++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pelican/readers.py b/pelican/readers.py index 431e6937..954ea412 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -21,10 +21,7 @@ try: from html import escape except ImportError: from cgi import escape -try: - from html.parser import HTMLParser -except ImportError: - from HTMLParser import HTMLParser +from six.moves.html_parser import HTMLParser from pelican import signals from pelican.contents import Page, Category, Tag, Author @@ -43,7 +40,6 @@ METADATA_PROCESSORS = { logger = logging.getLogger(__name__) - class BaseReader(object): """Base class to read files. @@ -231,7 +227,11 @@ class HTMLReader(BaseReader): class _HTMLParser(HTMLParser): def __init__(self, settings, filename): - HTMLParser.__init__(self) + try: + # Python 3.4+ + HTMLParser.__init__(self, convert_charrefs=False) + except TypeError: + HTMLParser.__init__(self) self.body = '' self.metadata = {} self.settings = settings diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 7c8662c9..a50aa216 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -4,11 +4,10 @@ from __future__ import unicode_literals, print_function import argparse try: - # py3k import - from html.parser import HTMLParser + from html import unescape # py3.4+ except ImportError: - # py2 import - from HTMLParser import HTMLParser # NOQA + from six.moves.html_parser import HTMLParser + unescape = HTMLParser().unescape import os import re import subprocess @@ -129,7 +128,7 @@ def wp2fields(xml, wp_custpost=False): try: # Use HTMLParser due to issues with BeautifulSoup 3 - title = HTMLParser().unescape(item.title.contents[0]) + title = unescape(item.title.contents[0]) except IndexError: title = 'No title [%s]' % item.find('post_name').string logger.warning('Post "%s" is lacking a proper title' % title) From fc505091c280f2b8d5645c18352dd14c93e13f11 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Wed, 11 Jun 2014 16:11:48 -0400 Subject: [PATCH 0278/1427] Patch docutils.io.FileInput to not use "U" mode in py3 "U" mode is redundant in py3 since "newline" argument replaces it and by default universal newlines is enabled. As of py3.4, "U" mode triggers a deprecation warning. --- pelican/readers.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pelican/readers.py b/pelican/readers.py index 954ea412..101ed8b5 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -10,6 +10,7 @@ import docutils import docutils.core import docutils.io from docutils.writers.html4css1 import HTMLTranslator +import six # import the directives to have pygments support from pelican import rstdirectives # NOQA @@ -118,6 +119,19 @@ class RstReader(BaseReader): enabled = bool(docutils) file_extensions = ['rst'] + class FileInput(docutils.io.FileInput): + """Patch docutils.io.FileInput to remove "U" mode in py3. + + Universal newlines is enabled by default and "U" mode is deprecated + in py3. + + """ + + def __init__(self, *args, **kwargs): + if six.PY3: + kwargs['mode'] = kwargs.get('mode', 'r').replace('U', '') + docutils.io.FileInput.__init__(self, *args, **kwargs) + def __init__(self, *args, **kwargs): super(RstReader, self).__init__(*args, **kwargs) @@ -149,12 +163,14 @@ class RstReader(BaseReader): extra_params = {'initial_header_level': '2', 'syntax_highlight': 'short', 'input_encoding': 'utf-8', - 'exit_status_level': 2} + 'exit_status_level': 2, + 'embed_stylesheet': False} user_params = self.settings.get('DOCUTILS_SETTINGS') if user_params: extra_params.update(user_params) pub = docutils.core.Publisher( + source_class=self.FileInput, destination_class=docutils.io.StringOutput) pub.set_components('standalone', 'restructuredtext', 'html') pub.writer.translator_class = PelicanHTMLTranslator From 40c9406ca4302b5c60fd8d205aae418b450a0844 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Sun, 25 May 2014 10:06:31 +0200 Subject: [PATCH 0279/1427] Fix test_direct_templates_save_as_false test case A non-existent asserts_called_count attr was used which would always succeed, silently hiding the error. --- pelican/tests/test_generators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index dfa9a36b..9305fa6e 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -198,14 +198,14 @@ class TestArticlesGenerator(unittest.TestCase): settings = get_settings() settings['DIRECT_TEMPLATES'] = ['archives'] - settings['ARCHIVES_SAVE_AS'] = 'archives/index.html' + settings['ARCHIVES_SAVE_AS'] = False settings['CACHE_PATH'] = self.temp_cache generator = ArticlesGenerator( context=settings, settings=settings, path=None, theme=settings['THEME'], output_path=None) write = MagicMock() generator.generate_direct_templates(write) - write.assert_called_count == 0 + self.assertEqual(write.call_count, 0) def test_per_article_template(self): """ From 7fabd712a1b91eeb9a71f3f4b10d4c979acfe799 Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Fri, 27 Jun 2014 20:18:17 +0200 Subject: [PATCH 0280/1427] Generator.get_files backwards compatibility for 1 path This should prevent outdated plugins with generators from failing. Also fixes #1382. --- pelican/generators.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pelican/generators.py b/pelican/generators.py index 865cc6f8..7cbce8de 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -119,6 +119,8 @@ class Generator(object): :param extensions: the list of allowed extensions (if False, all extensions are allowed) """ + if isinstance(paths, six.string_types): + paths = [paths] # backward compatibility for older generators files = [] for path in paths: root = os.path.join(self.path, path) From 95e170a15fcb47cdefb3e8e3d72239ce6dc538a7 Mon Sep 17 00:00:00 2001 From: Giulia Vergottini Date: Wed, 11 Jun 2014 22:56:32 +0200 Subject: [PATCH 0281/1427] prettify github make commit message --- pelican/tools/templates/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in index 6bfa7b0c..6c1e321e 100644 --- a/pelican/tools/templates/Makefile.in +++ b/pelican/tools/templates/Makefile.in @@ -103,7 +103,7 @@ cf_upload: publish cd $(OUTPUTDIR) && swift -v -A https://auth.api.rackspacecloud.com/v1.0 -U $(CLOUDFILES_USERNAME) -K $(CLOUDFILES_API_KEY) upload -c $(CLOUDFILES_CONTAINER) . github: publish - ghp-import -b $(GITHUB_PAGES_BRANCH) $$(OUTPUTDIR) + ghp-import -m "generate blog content" -b $(GITHUB_PAGES_BRANCH) $$(OUTPUTDIR) git push origin $(GITHUB_PAGES_BRANCH) .PHONY: html help clean regenerate serve devserver publish ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload cf_upload github From 5eaf2aa2bfe2fcc92e7e56b48b5e1cfa868d4be4 Mon Sep 17 00:00:00 2001 From: Giulia Vergottini Date: Sun, 29 Jun 2014 17:00:33 +0200 Subject: [PATCH 0282/1427] update make github commit message --- pelican/tools/templates/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in index 6c1e321e..a8ffd6ca 100644 --- a/pelican/tools/templates/Makefile.in +++ b/pelican/tools/templates/Makefile.in @@ -103,7 +103,7 @@ cf_upload: publish cd $(OUTPUTDIR) && swift -v -A https://auth.api.rackspacecloud.com/v1.0 -U $(CLOUDFILES_USERNAME) -K $(CLOUDFILES_API_KEY) upload -c $(CLOUDFILES_CONTAINER) . github: publish - ghp-import -m "generate blog content" -b $(GITHUB_PAGES_BRANCH) $$(OUTPUTDIR) + ghp-import -m "Generate Pelican site" -b $(GITHUB_PAGES_BRANCH) $$(OUTPUTDIR) git push origin $(GITHUB_PAGES_BRANCH) .PHONY: html help clean regenerate serve devserver publish ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload cf_upload github From 43523dac4df78604b51339ecbaccfa82313d3281 Mon Sep 17 00:00:00 2001 From: Eli Bendersky Date: Mon, 30 Jun 2014 06:40:19 -0700 Subject: [PATCH 0283/1427] Fix "server didn't start" error message in develop_server.sh It has port 8000 hardcoded into it, which is confusing when the server runs on another port. --- pelican/tools/templates/develop_server.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/templates/develop_server.sh.in b/pelican/tools/templates/develop_server.sh.in index 4259af5b..732069c2 100755 --- a/pelican/tools/templates/develop_server.sh.in +++ b/pelican/tools/templates/develop_server.sh.in @@ -75,7 +75,7 @@ function start_up(){ echo "Pelican didn't start. Is the Pelican package installed?" return 1 elif ! alive $$srv_pid ; then - echo "The HTTP server didn't start. Is there another service using port 8000?" + echo "The HTTP server didn't start. Is there another service using port" $$port "?" return 1 fi echo 'Pelican and HTTP server processes now running in background.' From 2e1b16826b1b933c9f819bca9cbfd5d754bb3c1e Mon Sep 17 00:00:00 2001 From: Ondrej Grover Date: Mon, 26 May 2014 12:32:05 +0200 Subject: [PATCH 0284/1427] Enhance feedback and contributing guidelines Many folks ask for help (via IRC or filing GitHub issues) without sufficient detail and/or without first determining if the answers to their questions might be better sourced elsewhere. These documentation changes encourage users to follow certain guidelines when reaching out for help. --- CONTRIBUTING.rst | 111 +++++++++++++++++++++++++++++++++++++++----- README.rst | 17 ++----- docs/contribute.rst | 18 ++----- docs/faq.rst | 11 +---- docs/index.rst | 18 ++----- 5 files changed, 111 insertions(+), 64 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 6063613b..6b1f3b05 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,20 +1,91 @@ -Contribution submission guidelines -================================== +Filing issues +------------- + +* Before you file an issue, try `asking for help`_ first. +* If determined to file an issue, first check for `existing issues`_, including closed issues. + +.. _`asking for help`: `How to get help`_ +.. _`existing issues`: https://github.com/getpelican/pelican/issues + +How to get help +--------------- + +Before you ask for help, please make sure you do the following: + +1. Read the documentation_ thoroughly. If in a hurry, at least use the search + field that is provided at top-right on the documentation_ pages. Make sure + you read the docs for the Pelican version you are using. +2. Use a search engine (e.g., DuckDuckGo, Google) to search for a solution to + your problem. Someone may have already found a solution, perhaps in the + form of a plugin_ or a specific combination of settings. + +3. Try reproducing the issue in a clean environment, ensuring you are using: + +* latest Pelican release (or an up-to-date git clone of Pelican master) +* latest releases of libraries used by Pelican +* no plugins or only those related to the issue + +If despite the above efforts you still cannot resolve your problem, be sure to +include in your inquiry the following, preferably in the form of links to +content uploaded to a `paste service`_, GitHub repository, or other +publicly-accessible location: + +* Describe what version of Pelican you are running (output of ``pelican --version`` + or the HEAD commit hash if you cloned the repo) and how exactly you installed + it (the full command you used, e.g. ``pip install pelican``). +* If you are looking for a way to get some end result, prepare a detailed + description of what the end result should look like (preferably in the form of + an image or a mock-up page) and explain in detail what you have done so far to achieve it. +* If you are trying to solve some issue, prepare a detailed description of how + to reproduce the problem. If the issue cannot be easily reproduced, it cannot + be debugged by developers or volunteers. Describe only the **minimum steps** + necessary to reproduce it (no extra plugins, etc.). +* Upload your settings file or any other custom code that would enable people to + reproduce the problem or to see what you have already tried to achieve the + desired end result. +* Upload detailed and **complete** output logs and backtraces (remember to add + the ``--debug`` flag: ``pelican --debug content [...]``) + +.. _documentation: http://docs.getpelican.com/ +.. _`paste service`: https://dpaste.de/ + +Once the above preparation is ready, you can contact people willing to help via +(preferably) the ``#pelican`` IRC channel or send a message to ``authors at getpelican dot com``. +Remember to include all the information you prepared. + +The #pelican IRC channel +........................ + +* Because of differing time zones, you may not get an immediate response to your + question, but please be patient and stay logged into IRC — someone will almost + always respond if you wait long enough (it may take a few hours). +* If you don't have an IRC client handy, use the webchat_ for quick feedback. +* You can direct your IRC client to the channel using this `IRC link`_ or you + can manually join the ``#pelican`` IRC channel on the `freenode IRC network`_. + +.. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4 +.. _`IRC link`: irc://irc.freenode.org/pelican +.. _`freenode IRC network`: http://www.freenode.org/ + + +Contributing code +----------------- + +Before you submit a contribution, please ask whether it is desired so that you +don't spend a lot of time working on something that would be rejected for a +known reason. Consider also whether your new feature might be better suited as +a plugin_ — you can `ask for help`_ to make that determination. + +Using Git and GitHub +.................... -* Consider whether your new feature might be better suited as a plugin_. Folks - are usually available in the `#pelican IRC channel`_ if help is needed to - make that determination. * `Create a new git branch`_ specific to your change (as opposed to making your commits in the master branch). -* **Don't put multiple fixes/features in the same branch / pull request.** +* **Don't put multiple unrelated fixes/features in the same branch / pull request.** For example, if you're hacking on a new feature and find a bugfix that doesn't *require* your new feature, **make a new distinct branch and pull request** for the bugfix. -* Adhere to `PEP8 coding standards`_ whenever possible. * Check for unnecessary whitespace via ``git diff --check`` before committing. -* **Add docs and tests for your changes**. -* `Run all the tests`_ **on both Python 2.7 and 3.3** to ensure nothing was - accidentally broken. * First line of your commit message should start with present-tense verb, be 50 characters or less, and include the relevant issue number(s) if applicable. *Example:* ``Ensure proper PLUGIN_PATH behavior. Refs #428.`` If the commit @@ -32,7 +103,22 @@ Contribution submission guidelines `hub pull-request `_ to turn your GitHub issue into a pull request containing your code. -Check out our `Git Tips`_ page or ask on the `#pelican IRC channel`_ if you +Contribution quality standards +.............................. + +* Adhere to `PEP8 coding standards`_ whenever possible. This can be eased via + the `pep8 `_ or `flake8 + `_ tools, the latter of which in + particular will give you some useful hints about ways in which the + code/formatting can be improved. +* Make sure your code is compatible with Python 2.7, 3.3, and 3.4 — see our + `compatibility cheatsheet`_ for more details. +* Add docs and tests for your changes. Undocumented and untested features will + not be accepted. +* `Run all the tests`_ **on all versions of Python supported by Pelican** to + ensure nothing was accidentally broken. + +Check out our `Git Tips`_ page or `ask for help`_ if you need assistance or have any questions about these guidelines. .. _`plugin`: http://docs.getpelican.com/en/latest/plugins.html @@ -42,4 +128,5 @@ need assistance or have any questions about these guidelines. .. _`Run all the tests`: http://docs.getpelican.com/en/latest/contribute.html#running-the-test-suite .. _`Git Tips`: https://github.com/getpelican/pelican/wiki/Git-Tips .. _`PEP8 coding standards`: http://www.python.org/dev/peps/pep-0008/ - +.. _`ask for help`: `How to get help`_ +.. _`compatibility cheatsheet`: http://docs.getpelican.com/en/latest/contribute.html#python-3-development-tips diff --git a/README.rst b/README.rst index bf506c5f..f6b5e4e2 100644 --- a/README.rst +++ b/README.rst @@ -45,16 +45,10 @@ You can access the source code at: https://github.com/getpelican/pelican If you feel hackish, have a look at the explanation of `Pelican's internals`_. -Feedback / Contact us ---------------------- +How to get help, contribute, or provide feedback +------------------------------------------------ -If you want to see new features in Pelican, don't hesitate to offer -suggestions, clone the repository, etc. There are many ways to contribute_. -That's open source, dude! - -Send a message to "authors at getpelican dot com" with any requests/feedback! You -can also join the team at `#pelican on Freenode`_ (or if you don't have an IRC -client handy, use the webchat_ for quick feedback. +See our `contribution submission and feedback guidelines `_. .. Links @@ -62,8 +56,5 @@ client handy, use the webchat_ for quick feedback. .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _Markdown: http://daringfireball.net/projects/markdown/ .. _Jinja2: http://jinja.pocoo.org/ -.. _`Pelican documentation`: http://docs.getpelican.com/latest/ +.. _`Pelican documentation`: http://docs.getpelican.com/ .. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html -.. _`#pelican on Freenode`: irc://irc.freenode.net/pelican -.. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4 -.. _contribute: http://docs.getpelican.com/en/latest/contribute.html diff --git a/docs/contribute.rst b/docs/contribute.rst index 19604da0..72837593 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -1,13 +1,13 @@ -How to contribute -################# +Contributing and Feedback Guidelines +#################################### There are many ways to contribute to Pelican. You can improve the documentation, add missing features, and fix bugs (or just report them). You can also help out by reviewing and commenting on `existing issues `_. -Don't hesitate to fork Pelican and submit a pull request on GitHub. When doing -so, please adhere to the following guidelines. +Don't hesitate to fork Pelican and submit an issue or pull request on GitHub. +When doing so, please adhere to the following guidelines. .. include:: ../CONTRIBUTING.rst @@ -47,16 +47,6 @@ Or using ``pip``:: $ pip install -e . -Coding standards -================ - -Try to respect what is described in the `PEP8 specification -`_ when making contributions. This -can be eased via the `pep8 `_ or `flake8 -`_ tools, the latter of which in -particular will give you some useful hints about ways in which the -code/formatting can be improved. - Building the docs ================= diff --git a/docs/faq.rst b/docs/faq.rst index 3a5cfec5..5a48a107 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -6,16 +6,7 @@ Here are some frequently asked questions about Pelican. What's the best way to communicate a problem, question, or suggestion? ====================================================================== -If you have a problem, question, or suggestion, please start by striking up a -conversation on `#pelican on Freenode `_. -Those who don't have an IRC client handy can jump in immediately via -`IRC webchat `_. Because -of differing time zones, you may not get an immediate response to your -question, but please be patient and stay logged into IRC — someone will almost -always respond if you wait long enough (it may take a few hours). - -If you're unable to resolve your issue or if you have a feature request, please -refer to the `issue tracker `_. +Please read our :doc:`feedback guidelines `. How can I help? ================ diff --git a/docs/index.rst b/docs/index.rst index 2beb8b20..ccfb9982 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -47,20 +47,10 @@ Source code You can access the source code at: https://github.com/getpelican/pelican -Feedback / Contact us ---------------------- +How to get help, contribute, or provide feedback +------------------------------------------------ -If you want to see new features in Pelican, don't hesitate to offer suggestions, -clone the repository, etc. There are many ways to :doc:`contribute`. -That's open source, dude! - -Send a message to "authors at getpelican dot com" with any requests/feedback. -For a more immediate response, you can also join the team via IRC at -`#pelican on Freenode`_ — if you don't have an IRC client handy, use the -webchat_ for quick feedback. If you ask a question via IRC and don't get an -immediate response, don't leave the channel! It may take a few hours because -of time zone differences, but if you are patient and remain in the channel, -someone will almost always respond to your inquiry. +See our :doc:`feedback and contribution submission guidelines `. Documentation ------------- @@ -93,5 +83,3 @@ Documentation .. _`Pelican documentation`: http://docs.getpelican.com/latest/ .. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html .. _`Pelican plugins`: https://github.com/getpelican/pelican-plugins -.. _`#pelican on Freenode`: irc://irc.freenode.net/pelican -.. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4 From b2ccc62582152eda8efeccee9f0e1d2bb78dba5a Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 30 Jun 2014 18:42:27 -0700 Subject: [PATCH 0285/1427] Add troubleshooting info to CONTRIBUTING docs --- CONTRIBUTING.rst | 20 ++++++++++++++++---- docs/contribute.rst | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 6b1f3b05..67357e48 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -2,7 +2,8 @@ Filing issues ------------- * Before you file an issue, try `asking for help`_ first. -* If determined to file an issue, first check for `existing issues`_, including closed issues. +* If determined to file an issue, first check for `existing issues`_, including + closed issues. .. _`asking for help`: `How to get help`_ .. _`existing issues`: https://github.com/getpelican/pelican/issues @@ -25,9 +26,19 @@ Before you ask for help, please make sure you do the following: * latest releases of libraries used by Pelican * no plugins or only those related to the issue +**NOTE:** The most common sources of problems are anomalies in (1) themes, +(2) settings files, and (3) ``make``/``fab`` automation wrappers. If you can't +reproduce your problem when using the following steps to generate your site, +then the problem is almost certainly with your chosen theme and/or settings +file (and not Pelican itself):: + + cd ~/projects/your-site + git clone https://github.com/getpelican/pelican ~/projects/pelican + pelican content -s ~/projects/pelican/samples/pelican.conf.py -t ~/projects/pelican/pelican/themes/notmyidea + If despite the above efforts you still cannot resolve your problem, be sure to -include in your inquiry the following, preferably in the form of links to -content uploaded to a `paste service`_, GitHub repository, or other +include in your inquiry the following information, preferably in the form of +links to content uploaded to a `paste service`_, GitHub repository, or other publicly-accessible location: * Describe what version of Pelican you are running (output of ``pelican --version`` @@ -35,7 +46,8 @@ publicly-accessible location: it (the full command you used, e.g. ``pip install pelican``). * If you are looking for a way to get some end result, prepare a detailed description of what the end result should look like (preferably in the form of - an image or a mock-up page) and explain in detail what you have done so far to achieve it. + an image or a mock-up page) and explain in detail what you have done so far to + achieve it. * If you are trying to solve some issue, prepare a detailed description of how to reproduce the problem. If the issue cannot be easily reproduced, it cannot be debugged by developers or volunteers. Describe only the **minimum steps** diff --git a/docs/contribute.rst b/docs/contribute.rst index 72837593..a3f56e9f 100644 --- a/docs/contribute.rst +++ b/docs/contribute.rst @@ -1,4 +1,4 @@ -Contributing and Feedback Guidelines +Contributing and feedback guidelines #################################### There are many ways to contribute to Pelican. You can improve the From 669bdc92e1a041fa176d59e766ab4c67619dc8c5 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 30 Jun 2014 18:48:46 -0700 Subject: [PATCH 0286/1427] Change IRC webchat provider to Kiwi IRC --- CONTRIBUTING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 67357e48..85d30c97 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -75,7 +75,7 @@ The #pelican IRC channel * You can direct your IRC client to the channel using this `IRC link`_ or you can manually join the ``#pelican`` IRC channel on the `freenode IRC network`_. -.. _webchat: http://webchat.freenode.net/?channels=pelican&uio=d4 +.. _webchat: https://kiwiirc.com/client/irc.freenode.net/?#pelican .. _`IRC link`: irc://irc.freenode.org/pelican .. _`freenode IRC network`: http://www.freenode.org/ From 975140d352b5dd80d2f0a25048afc097b33af96c Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Mon, 30 Jun 2014 18:58:14 -0700 Subject: [PATCH 0287/1427] Promote URL settings docs to top-level heading --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index 86424ec5..5db687dd 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -197,7 +197,7 @@ Setting name (followed by default value, if any) URL settings ------------- +============ The first thing to understand is that there are currently two supported methods for URL formation: *relative* and *absolute*. Relative URLs are useful From 9af21690270cd1959ab38f33e96f7b2a14ee46d1 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 1 Jul 2014 11:55:18 -0700 Subject: [PATCH 0288/1427] Update changelog in preparation for release --- docs/changelog.rst | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 68348da5..4e3b6834 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,10 +4,24 @@ Release history Next release ============ -* Added the `:modified:` metadata field to complement `:date:`. - Used to specify the last date and time an article was updated independently from the date and time it was published. -* Produce inline links instead of reference-style links when importing content. -* Multiple authors support added via new `:authors:` metadata field. +* Speed up content generation via new caching mechanism +* Switch Pelican docs to prettier RtD theme +* Add support for multiple content and plugin paths +* Add ``:modified:`` metadata field to complement ``:date:``. + Used to specify the last date and time an article was updated independently + from the date and time it was published. +* Add support for multiple authors via new ``:authors:`` metadata field +* Watch for changes in static directories when in auto-regeneration mode +* Add filters to limit log output when desired +* Add language support to drafts +* Add ``SLUGIFY_SOURCE`` setting to control how post slugs are generated +* Fix many issues relating to locale and encoding +* Apply Typogrify filter to post summary +* Preserve file metadata (e.g. time stamps) when copying static files to output +* Move AsciiDoc support from Pelican core into separate plugin +* Produce inline links instead of reference-style links when importing content +* Improve handling of ``IGNORE_FILES`` setting behavior +* Properly escape symbol characters in tag names (e.g., ``C++``) 3.3.0 (2013-09-24) ================== From c7052a6404cd485b2240c1f0f97b26a32df03200 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 1 Jul 2014 12:16:45 -0700 Subject: [PATCH 0289/1427] Build binary wheels when publishing to PyPI --- bumpr.rc | 2 +- dev_requirements.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bumpr.rc b/bumpr.rc index 969fdb24..97dbad22 100644 --- a/bumpr.rc +++ b/bumpr.rc @@ -5,7 +5,7 @@ clean = python setup.py clean rm -rf *egg-info build dist tests = python -m unittest discover -publish = python setup.py sdist register upload +publish = python setup.py sdist bdist_wheel register upload files = README.rst [bump] diff --git a/dev_requirements.txt b/dev_requirements.txt index 01fe2507..a7c10719 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -9,6 +9,7 @@ typogrify # To perform release bumpr==0.2.0 +wheel # For docs theme sphinx_rtd_theme From bea40aab8c1e7630d83000f983122c01093c5cd1 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 1 Jul 2014 12:24:37 -0700 Subject: [PATCH 0290/1427] Additions to changelog --- docs/changelog.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 4e3b6834..0b08c428 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,7 +5,8 @@ Next release ============ * Speed up content generation via new caching mechanism -* Switch Pelican docs to prettier RtD theme +* Add selective post generation (instead of always building entire site) +* Many documentation improvements, including switching to prettier RtD theme * Add support for multiple content and plugin paths * Add ``:modified:`` metadata field to complement ``:date:``. Used to specify the last date and time an article was updated independently @@ -22,6 +23,8 @@ Next release * Produce inline links instead of reference-style links when importing content * Improve handling of ``IGNORE_FILES`` setting behavior * Properly escape symbol characters in tag names (e.g., ``C++``) +* Minor tweaks for Python 3.4 compatibility +* Add several new signals 3.3.0 (2013-09-24) ================== From 09c07e2950fe3b52e3520a441578622eb135adbc Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 1 Jul 2014 13:08:55 -0700 Subject: [PATCH 0291/1427] Fix SLUGIFY_SOURCE docs --- docs/settings.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 5db687dd..d887dcc6 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -173,9 +173,9 @@ Setting name (followed by default value, if any) ``PYGMENTS_RST_OPTIONS = []`` A list of default Pygments settings for your reStructuredText code blocks. See :ref:`internal_pygments_options` for a list of supported options. -``SLUGIFY_SOURCE = 'input'`` Specifies where you want the slug to be automatically generated +``SLUGIFY_SOURCE = 'title'`` Specifies where you want the slug to be automatically generated from. Can be set to ``title`` to use the 'Title:' metadata tag or - ``basename`` to use the article's basename when creating the slug. + ``basename`` to use the article's file name when creating the slug. ``CACHE_CONTENT = True`` If ``True``, save content in a cache file. See :ref:`reading_only_modified_content` for details about caching. ``CONTENT_CACHING_LAYER = 'reader'`` If set to ``'reader'``, save only the raw content and metadata From 7e516cb7b0b9840bcf21915da1652f400c963803 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 1 Jul 2014 13:33:24 -0700 Subject: [PATCH 0292/1427] setup.py version should inherit from __init.py__ --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a2bcaeaa..7f95829f 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,6 @@ #!/usr/bin/env python from setuptools import setup +from pelican import __version__ requires = ['feedgenerator >= 1.6', 'jinja2 >= 2.7', 'pygments', 'docutils', 'pytz >= 0a', 'blinker', 'unidecode', 'six >= 1.4', @@ -21,7 +22,7 @@ CHANGELOG = open('docs/changelog.rst').read() setup( name="pelican", - version="3.3", + version=__version__, url='http://getpelican.com/', author='Alexis Metaireau', author_email='authors@getpelican.com', @@ -41,6 +42,7 @@ setup( 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Software Development :: Libraries :: Python Modules', ], From a47c0e26c0260ea0a1322cf79dc838d35ddec400 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 1 Jul 2014 13:34:47 -0700 Subject: [PATCH 0293/1427] Bump version 3.4.0 --- docs/changelog.rst | 4 ++-- pelican/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 0b08c428..d8c33cb5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,8 +1,8 @@ Release history ############### -Next release -============ +3.4.0 (2014-07-01) +================== * Speed up content generation via new caching mechanism * Add selective post generation (instead of always building entire site) diff --git a/pelican/__init__.py b/pelican/__init__.py index 082e5a58..f3b18039 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -25,7 +25,7 @@ from pelican.settings import read_settings from pelican.utils import clean_output_dir, folder_watcher, file_watcher from pelican.writers import Writer -__version__ = "3.3.1.dev" +__version__ = "3.4.0" DEFAULT_CONFIG_NAME = 'pelicanconf.py' From d41331bd69e3f663f4da341599e102ceaf197d77 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 1 Jul 2014 13:48:16 -0700 Subject: [PATCH 0294/1427] Work around setup.py encoding issue in last commit --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7f95829f..f665a020 100755 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ CHANGELOG = open('docs/changelog.rst').read() setup( name="pelican", - version=__version__, + version=str(__version__), url='http://getpelican.com/', author='Alexis Metaireau', author_email='authors@getpelican.com', From 9b5261512d10a955ca431fd54321cbc9915877bd Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 1 Jul 2014 14:33:04 -0700 Subject: [PATCH 0295/1427] Revert setup.py changes. Ensure universal wheels. Attempts at fancy version number handling in setup.py caused more problems than they were worth, including Travis CI build failures. The setup.cfg key for universal binary wheel generation apparently changed at some point, so that was updated as well. --- setup.cfg | 2 +- setup.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 5e409001..2a9acf13 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ -[wheel] +[bdist_wheel] universal = 1 diff --git a/setup.py b/setup.py index f665a020..c1af72bb 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,5 @@ #!/usr/bin/env python from setuptools import setup -from pelican import __version__ requires = ['feedgenerator >= 1.6', 'jinja2 >= 2.7', 'pygments', 'docutils', 'pytz >= 0a', 'blinker', 'unidecode', 'six >= 1.4', @@ -22,7 +21,7 @@ CHANGELOG = open('docs/changelog.rst').read() setup( name="pelican", - version=str(__version__), + version="3.4.0", url='http://getpelican.com/', author='Alexis Metaireau', author_email='authors@getpelican.com', From e7eb3b8ec39fea6c62350215fb55fadf167041a5 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 1 Jul 2014 16:03:02 -0700 Subject: [PATCH 0296/1427] Increment development version to 3.5.dev --- pelican/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index f3b18039..43521ff1 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -25,7 +25,7 @@ from pelican.settings import read_settings from pelican.utils import clean_output_dir, folder_watcher, file_watcher from pelican.writers import Writer -__version__ = "3.4.0" +__version__ = "3.5.dev" DEFAULT_CONFIG_NAME = 'pelicanconf.py' From 2c50ccb764bb67fcaaed641289e9e4c3649f0f91 Mon Sep 17 00:00:00 2001 From: OGINO Masanori Date: Tue, 10 Jun 2014 09:47:14 +0900 Subject: [PATCH 0297/1427] Add timezone to datetime objects. Refs #962. Based on https://github.com/getpelican/pelican/pull/977, but it adds timezone information before formatting. Signed-off-by: OGINO Masanori --- pelican/contents.py | 9 ++++++- .../basic/a-markdown-powered-article.html | 4 ++-- pelican/tests/output/basic/archives.html | 2 +- pelican/tests/output/basic/article-1.html | 4 ++-- pelican/tests/output/basic/article-2.html | 4 ++-- pelican/tests/output/basic/article-3.html | 4 ++-- .../output/basic/author/alexis-metaireau.html | 8 +++---- pelican/tests/output/basic/categories.html | 2 +- pelican/tests/output/basic/category/bar.html | 2 +- pelican/tests/output/basic/category/cat1.html | 10 ++++---- pelican/tests/output/basic/category/misc.html | 10 ++++---- pelican/tests/output/basic/category/yeah.html | 4 ++-- .../basic/feeds/alexis-metaireau.atom.xml | 6 ++--- .../basic/feeds/alexis-metaireau.rss.xml | 6 ++--- .../tests/output/basic/feeds/all-en.atom.xml | 22 ++++++++--------- .../tests/output/basic/feeds/all-fr.atom.xml | 4 ++-- pelican/tests/output/basic/feeds/all.atom.xml | 24 +++++++++---------- pelican/tests/output/basic/feeds/bar.atom.xml | 4 ++-- .../tests/output/basic/feeds/cat1.atom.xml | 10 ++++---- .../tests/output/basic/feeds/misc.atom.xml | 10 ++++---- .../tests/output/basic/feeds/yeah.atom.xml | 4 ++-- .../basic/filename_metadata-example.html | 4 ++-- pelican/tests/output/basic/index.html | 24 +++++++++---------- pelican/tests/output/basic/oh-yeah.html | 4 ++-- .../tests/output/basic/override/index.html | 2 +- .../pages/this-is-a-test-hidden-page.html | 2 +- .../basic/pages/this-is-a-test-page.html | 2 +- .../tests/output/basic/second-article-fr.html | 4 ++-- .../tests/output/basic/second-article.html | 4 ++-- pelican/tests/output/basic/tag/bar.html | 10 ++++---- pelican/tests/output/basic/tag/baz.html | 4 ++-- pelican/tests/output/basic/tag/foo.html | 8 +++---- pelican/tests/output/basic/tag/foobar.html | 4 ++-- pelican/tests/output/basic/tag/yeah.html | 2 +- .../output/basic/this-is-a-super-article.html | 6 ++--- pelican/tests/output/basic/unbelievable.html | 4 ++-- .../custom/a-markdown-powered-article.html | 4 ++-- pelican/tests/output/custom/article-1.html | 4 ++-- pelican/tests/output/custom/article-2.html | 4 ++-- pelican/tests/output/custom/article-3.html | 4 ++-- .../custom/author/alexis-metaireau.html | 10 ++++---- .../custom/author/alexis-metaireau2.html | 12 +++++----- .../custom/author/alexis-metaireau3.html | 4 ++-- pelican/tests/output/custom/category/bar.html | 2 +- .../tests/output/custom/category/cat1.html | 8 +++---- .../tests/output/custom/category/misc.html | 8 +++---- .../tests/output/custom/category/yeah.html | 4 ++-- .../output/custom/drafts/a-draft-article.html | 2 +- .../custom/filename_metadata-example.html | 2 +- pelican/tests/output/custom/index.html | 8 +++---- pelican/tests/output/custom/index2.html | 12 +++++----- pelican/tests/output/custom/index3.html | 4 ++-- pelican/tests/output/custom/oh-yeah-fr.html | 2 +- pelican/tests/output/custom/oh-yeah.html | 2 +- .../output/custom/second-article-fr.html | 2 +- .../tests/output/custom/second-article.html | 2 +- pelican/tests/output/custom/tag/bar.html | 10 ++++---- pelican/tests/output/custom/tag/baz.html | 2 +- pelican/tests/output/custom/tag/foo.html | 8 +++---- pelican/tests/output/custom/tag/foobar.html | 4 ++-- pelican/tests/output/custom/tag/yeah.html | 2 +- .../custom/this-is-a-super-article.html | 6 ++--- pelican/tests/output/custom/unbelievable.html | 2 +- .../author/alexis-metaireau.html | 8 +++---- .../author/alexis-metaireau2.html | 12 +++++----- .../author/alexis-metaireau3.html | 4 ++-- .../output/custom_locale/category/bar.html | 2 +- .../output/custom_locale/category/cat1.html | 8 +++---- .../output/custom_locale/category/misc.html | 8 +++---- .../output/custom_locale/category/yeah.html | 6 ++--- .../custom_locale/drafts/a-draft-article.html | 2 +- pelican/tests/output/custom_locale/index.html | 8 +++---- .../tests/output/custom_locale/index2.html | 12 +++++----- .../tests/output/custom_locale/index3.html | 4 ++-- .../output/custom_locale/oh-yeah-fr.html | 2 +- .../02/this-is-a-super-article/index.html | 6 ++--- .../2010/octobre/15/unbelievable/index.html | 2 +- .../posts/2010/octobre/20/oh-yeah/index.html | 2 +- .../20/a-markdown-powered-article/index.html | 2 +- .../2011/février/17/article-1/index.html | 2 +- .../2011/février/17/article-2/index.html | 2 +- .../2011/février/17/article-3/index.html | 2 +- .../2012/février/29/second-article/index.html | 2 +- .../30/filename_metadata-example/index.html | 2 +- .../custom_locale/second-article-fr.html | 2 +- .../tests/output/custom_locale/tag/bar.html | 10 ++++---- .../tests/output/custom_locale/tag/baz.html | 2 +- .../tests/output/custom_locale/tag/foo.html | 8 +++---- .../output/custom_locale/tag/foobar.html | 6 ++--- .../tests/output/custom_locale/tag/yeah.html | 2 +- pelican/tests/test_utils.py | 3 +++ pelican/utils.py | 4 +++- 92 files changed, 259 insertions(+), 247 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 297a537b..7a9a8bc0 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -16,7 +16,7 @@ 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, SafeDatetime) + path_to_url, set_date_tzinfo, SafeDatetime) # Import these so that they're avalaible when you import from pelican.contents. from pelican.urlwrappers import (URLWrapper, Author, Category, Tag) # NOQA @@ -117,9 +117,16 @@ class Content(object): locale.setlocale(locale.LC_ALL, locale_string) self.date_format = self.date_format[1] + # manage timezone + default_timezone = settings.get('TIMEZONE', 'UTC') + timezone = getattr(self, 'timezone', default_timezone) + if hasattr(self, 'date'): + self.date = set_date_tzinfo(self.date, timezone) self.locale_date = strftime(self.date, self.date_format) + if hasattr(self, 'modified'): + self.modified = set_date_tzinfo(self.modified, timezone) self.locale_modified = strftime(self.modified, self.date_format) # manage status diff --git a/pelican/tests/output/basic/a-markdown-powered-article.html b/pelican/tests/output/basic/a-markdown-powered-article.html index 40c96766..5fcc42a9 100644 --- a/pelican/tests/output/basic/a-markdown-powered-article.html +++ b/pelican/tests/output/basic/a-markdown-powered-article.html @@ -34,7 +34,7 @@
    - + Published: Wed 20 April 2011 @@ -66,4 +66,4 @@
    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/archives.html b/pelican/tests/output/basic/archives.html index e32c92ba..f8f1a67f 100644 --- a/pelican/tests/output/basic/archives.html +++ b/pelican/tests/output/basic/archives.html @@ -69,4 +69,4 @@
    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/article-1.html b/pelican/tests/output/basic/article-1.html index 15a1496d..4ea7f4e3 100644 --- a/pelican/tests/output/basic/article-1.html +++ b/pelican/tests/output/basic/article-1.html @@ -34,7 +34,7 @@
    - + Published: Thu 17 February 2011 @@ -65,4 +65,4 @@
    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/article-2.html b/pelican/tests/output/basic/article-2.html index c4b09e35..45130e55 100644 --- a/pelican/tests/output/basic/article-2.html +++ b/pelican/tests/output/basic/article-2.html @@ -34,7 +34,7 @@
    - + Published: Thu 17 February 2011 @@ -65,4 +65,4 @@
    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/article-3.html b/pelican/tests/output/basic/article-3.html index 1c07e5e2..8603430f 100644 --- a/pelican/tests/output/basic/article-3.html +++ b/pelican/tests/output/basic/article-3.html @@ -34,7 +34,7 @@
    - + Published: Thu 17 February 2011 @@ -65,4 +65,4 @@
    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/author/alexis-metaireau.html b/pelican/tests/output/basic/author/alexis-metaireau.html index d1dc6da6..11d54185 100644 --- a/pelican/tests/output/basic/author/alexis-metaireau.html +++ b/pelican/tests/output/basic/author/alexis-metaireau.html @@ -29,11 +29,11 @@

    This is a super article !

    - + Published: Thu 02 December 2010
    - + Updated: Sun 17 November 2013 @@ -69,7 +69,7 @@
    - + Published: Wed 20 October 2010 @@ -109,4 +109,4 @@ YEAH !

    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/categories.html b/pelican/tests/output/basic/categories.html index fa2fc012..55e955c8 100644 --- a/pelican/tests/output/basic/categories.html +++ b/pelican/tests/output/basic/categories.html @@ -49,4 +49,4 @@
    - \ No newline at end of file + diff --git a/pelican/tests/output/basic/category/bar.html b/pelican/tests/output/basic/category/bar.html index 763cfe77..18e434cb 100644 --- a/pelican/tests/output/basic/category/bar.html +++ b/pelican/tests/output/basic/category/bar.html @@ -29,7 +29,7 @@

    Oh yeah !

    {% endif %} -

    In {{ article.category }}. {% if PDF_PROCESSOR %}get the pdf{% endif %}

    +

    In {{ article.category }}.

    {% include 'taglist.html' %} {% import 'translations.html' as translations with context %} {{ translations.translations_for(article) }} diff --git a/pelican/themes/notmyidea/templates/page.html b/pelican/themes/notmyidea/templates/page.html index 5ac50b66..0d8283fe 100644 --- a/pelican/themes/notmyidea/templates/page.html +++ b/pelican/themes/notmyidea/templates/page.html @@ -5,8 +5,6 @@

    {{ page.title }}

    {% import 'translations.html' as translations with context %} {{ translations.translations_for(page) }} - {% if PDF_PROCESSOR %}get - the pdf{% endif %} {{ page.content }} {% endblock %} diff --git a/pelican/themes/notmyidea/templates/taglist.html b/pelican/themes/notmyidea/templates/taglist.html index 1e0b95a7..58f35576 100644 --- a/pelican/themes/notmyidea/templates/taglist.html +++ b/pelican/themes/notmyidea/templates/taglist.html @@ -1,2 +1 @@ {% if article.tags %}

    tags: {% for tag in article.tags %}{{ tag | escape }} {% endfor %}

    {% endif %} -{% if PDF_PROCESSOR %}

    get the pdf

    {% endif %} From 87d86d724c6ae01adf1488b2f65dd0ff1e48ca46 Mon Sep 17 00:00:00 2001 From: Kevin Yap Date: Sat, 28 Feb 2015 15:33:54 -0800 Subject: [PATCH 0390/1427] Change phrasing and formatting of README Made a few changes to the README to emphasize Pelican's position as a general-purpose static site generator, and not just a blogging tool. See #1645 for more details. --- README.rst | 59 +++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/README.rst b/README.rst index 564cc77c..a5643514 100644 --- a/README.rst +++ b/README.rst @@ -3,57 +3,58 @@ Pelican |build-status| |coverage-status| Pelican is a static site generator, written in Python_. -* Write your weblog entries directly with your editor of choice (vim!) - in reStructuredText_ or Markdown_ -* Includes a simple CLI tool to (re)generate the weblog -* Easy to interface with DVCSes and web hooks -* Completely static output is easy to host anywhere +* Write content in reStructuredText_ or Markdown_ using your editor of choice. +* Includes a simple command line tool to (re)generate site files. +* Easy to interface with version control systems and web hooks. +* Completely static output is simple to host anywhere. + Features -------- Pelican currently supports: -* Blog articles and pages -* Comments, via an external service (Disqus). (Please note that while - useful, Disqus is an external service, and thus the comment data will be - somewhat outside of your control and potentially subject to data loss.) -* Theming support (themes are created using Jinja2_ templates) -* PDF generation of the articles/pages (optional) +* Blog articles and static pages +* Integration with external services (ex. Google Analytics and Disqus) +* Site themes (created using Jinja2_ templates) * Publication of articles in multiple languages -* Atom/RSS feeds -* Code syntax highlighting -* Import from WordPress, Dotclear, or RSS feeds -* Integration with external tools: Twitter, Google Analytics, etc. (optional) -* Fast rebuild times thanks to content caching and selective output writing. +* Generation of Atom and RSS feeds +* Syntax highlighting via Pygments_ +* Importing existing content from WordPress, Dotclear, and more services +* Fast rebuild times due to content caching and selective output writing -Have a look at the `Pelican documentation`_ for more information. +Check out `Pelican's documentation`_ for further information. -Why the name "Pelican"? ------------------------ - -"Pelican" is an anagram for *calepin*, which means "notebook" in French. ;) - -Source code ------------ - -You can access the source code at: https://github.com/getpelican/pelican - -If you feel hackish, have a look at the explanation of `Pelican's internals`_. How to get help, contribute, or provide feedback ------------------------------------------------ See our `contribution submission and feedback guidelines `_. + +Source code +----------- + +Pelican's source code is `hosted on GitHub`_. If you're feeling hackish, +take a look at `Pelican's internals`_. + + +Why the name "Pelican"? +----------------------- + +"Pelican" is an anagram of *calepin*, which means "notebook" in French. + + .. Links .. _Python: http://www.python.org/ .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _Markdown: http://daringfireball.net/projects/markdown/ .. _Jinja2: http://jinja.pocoo.org/ -.. _`Pelican documentation`: http://docs.getpelican.com/ +.. _Pygments: http://pygments.org/ +.. _`Pelican's documentation`: http://docs.getpelican.com/ .. _`Pelican's internals`: http://docs.getpelican.com/en/latest/internals.html +.. _`hosted on GitHub`: https://github.com/getpelican/pelican .. |build-status| image:: https://img.shields.io/travis/getpelican/pelican/master.svg :target: https://travis-ci.org/getpelican/pelican From e35ca1d6ff4fd9a31b6dd60b2bb345c2fee0828e Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Thu, 5 Mar 2015 12:04:39 -0800 Subject: [PATCH 0391/1427] Minor improvements to README --- README.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index a5643514..0bb3bcc8 100644 --- a/README.rst +++ b/README.rst @@ -3,10 +3,10 @@ Pelican |build-status| |coverage-status| Pelican is a static site generator, written in Python_. -* Write content in reStructuredText_ or Markdown_ using your editor of choice. -* Includes a simple command line tool to (re)generate site files. -* Easy to interface with version control systems and web hooks. -* Completely static output is simple to host anywhere. +* Write content in reStructuredText_ or Markdown_ using your editor of choice +* Includes a simple command line tool to (re)generate site files +* Easy to interface with version control systems and web hooks +* Completely static output is simple to host anywhere Features @@ -14,13 +14,13 @@ Features Pelican currently supports: -* Blog articles and static pages -* Integration with external services (ex. Google Analytics and Disqus) +* Chronological content (e.g., articles, blog posts) as well as static pages +* Integration with external services (e.g., Google Analytics and Disqus) * Site themes (created using Jinja2_ templates) * Publication of articles in multiple languages * Generation of Atom and RSS feeds * Syntax highlighting via Pygments_ -* Importing existing content from WordPress, Dotclear, and more services +* Importing existing content from WordPress, Dotclear, and other services * Fast rebuild times due to content caching and selective output writing Check out `Pelican's documentation`_ for further information. @@ -35,7 +35,7 @@ See our `contribution submission and feedback guidelines `_. Source code ----------- -Pelican's source code is `hosted on GitHub`_. If you're feeling hackish, +Pelican's source code is `hosted on GitHub`_. If you feel like hacking, take a look at `Pelican's internals`_. From 3ea45420152a8465db33fd4a67ac88f1c1426df5 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Tue, 17 Feb 2015 20:05:00 -0500 Subject: [PATCH 0392/1427] Make sure Content uses URLWrappers --- pelican/contents.py | 14 +++----------- pelican/readers.py | 4 ++++ pelican/tests/test_contents.py | 11 ++++++----- pelican/tests/test_generators.py | 32 ++++++++++++++++++++++++++++++++ pelican/tests/test_paginator.py | 4 ++-- 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 074c28be..90121316 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -53,7 +53,7 @@ class Content(object): self._context = context self.translations = [] - local_metadata = dict(settings['DEFAULT_METADATA']) + local_metadata = dict() local_metadata.update(metadata) # set metadata as attributes @@ -166,21 +166,13 @@ class Content(object): """Returns the URL, formatted with the proper values""" metadata = copy.copy(self.metadata) path = self.metadata.get('path', self.get_relative_source_path()) - default_category = self.settings['DEFAULT_CATEGORY'] - slug_substitutions = self.settings.get('SLUG_SUBSTITUTIONS', ()) metadata.update({ 'path': path_to_url(path), 'slug': getattr(self, 'slug', ''), 'lang': getattr(self, 'lang', 'en'), 'date': getattr(self, 'date', SafeDatetime.now()), - 'author': slugify( - getattr(self, 'author', ''), - slug_substitutions - ), - 'category': slugify( - getattr(self, 'category', default_category), - slug_substitutions - ) + 'author': self.author.slug if hasattr(self, 'author') else '', + 'category': self.category.slug if hasattr(self, 'category') else '' }) return metadata diff --git a/pelican/readers.py b/pelican/readers.py index 731fb5da..a9b71bed 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -537,6 +537,10 @@ def find_empty_alt(content, path): def default_metadata(settings=None, process=None): metadata = {} if settings: + for name, value in dict(settings.get('DEFAULT_METADATA', {})).items(): + if process: + value = process(name, value) + metadata[name] = value if 'DEFAULT_CATEGORY' in settings: value = settings['DEFAULT_CATEGORY'] if process: diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index 4b692e29..004d512e 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -8,7 +8,7 @@ import os.path from pelican.tests.support import unittest, get_settings -from pelican.contents import Page, Article, Static, URLWrapper +from pelican.contents import Page, Article, Static, URLWrapper, Author, Category from pelican.settings import DEFAULT_CONFIG from pelican.utils import path_to_url, truncate_html_words, SafeDatetime, posix_join from pelican.signals import content_object_init @@ -33,7 +33,7 @@ class TestPage(unittest.TestCase): 'metadata': { 'summary': TEST_SUMMARY, 'title': 'foo bar', - 'author': 'Blogger', + 'author': Author('Blogger', DEFAULT_CONFIG), }, 'source_path': '/path/to/file/foo.ext' } @@ -374,7 +374,8 @@ class TestPage(unittest.TestCase): content = Page(**args) assert content.authors == [content.author] args['metadata'].pop('author') - args['metadata']['authors'] = ['First Author', 'Second Author'] + args['metadata']['authors'] = [Author('First Author', DEFAULT_CONFIG), + Author('Second Author', DEFAULT_CONFIG)] content = Page(**args) assert content.authors assert content.author == content.authors[0] @@ -396,8 +397,8 @@ class TestArticle(TestPage): 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'] = "O'Brien" - article_kwargs['metadata']['category'] = 'C# & stuff' + article_kwargs['metadata']['author'] = Author("O'Brien", settings) + article_kwargs['metadata']['category'] = Category('C# & stuff', settings) article_kwargs['metadata']['title'] = 'fnord' article_kwargs['settings'] = settings article = Article(**article_kwargs) diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 9f38c002..acf767f2 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -413,6 +413,38 @@ class TestArticlesGenerator(unittest.TestCase): generator.generate_context() generator.readers.read_file.assert_called_count == orig_call_count + def test_standard_metadata_in_default_metadata(self): + settings = get_settings(filenames={}) + settings['CACHE_CONTENT'] = False + settings['DEFAULT_CATEGORY'] = 'Default' + settings['DEFAULT_DATE'] = (1970, 1, 1) + settings['DEFAULT_METADATA'] = (('author', 'Blogger'), + # category will be ignored in favor of + # DEFAULT_CATEGORY + ('category', 'Random'), + ('tags', 'general, untagged')) + generator = ArticlesGenerator( + context=settings.copy(), settings=settings, + path=CONTENT_DIR, theme=settings['THEME'], output_path=None) + generator.generate_context() + + authors = sorted([author.name for author, _ in generator.authors]) + authors_expected = sorted(['Alexis Métaireau', 'Blogger', + 'First Author', 'Second Author']) + self.assertEqual(authors, authors_expected) + + categories = sorted([category.name + for category, _ in generator.categories]) + categories_expected = [ + sorted(['Default', 'TestCategory', 'yeah', 'test', '指導書']), + sorted(['Default', 'TestCategory', 'Yeah', 'test', '指導書'])] + self.assertIn(categories, categories_expected) + + tags = sorted([tag.name for tag in generator.tags]) + tags_expected = sorted(['bar', 'foo', 'foobar', 'general', 'untagged', + 'パイソン', 'マック']) + self.assertEqual(tags, tags_expected) + class TestPageGenerator(unittest.TestCase): # Note: Every time you want to test for a new field; Make sure the test diff --git a/pelican/tests/test_paginator.py b/pelican/tests/test_paginator.py index 5494fda8..002d9e07 100644 --- a/pelican/tests/test_paginator.py +++ b/pelican/tests/test_paginator.py @@ -5,7 +5,7 @@ import locale from pelican.tests.support import unittest, get_settings from pelican.paginator import Paginator -from pelican.contents import Article +from pelican.contents import Article, Author from pelican.settings import DEFAULT_CONFIG from jinja2.utils import generate_lorem_ipsum @@ -26,7 +26,6 @@ class TestPage(unittest.TestCase): 'metadata': { 'summary': TEST_SUMMARY, 'title': 'foo bar', - 'author': 'Blogger', }, 'source_path': '/path/to/file/foo.ext' } @@ -49,6 +48,7 @@ class TestPage(unittest.TestCase): key=lambda r: r[0], ) + self.page_kwargs['metadata']['author'] = Author('Blogger', settings) object_list = [Article(**self.page_kwargs), Article(**self.page_kwargs)] paginator = Paginator('foobar.foo', object_list, settings) page = paginator.page(1) From 4e896c427ddef6b9a19088dfc653f7b8c15f5c08 Mon Sep 17 00:00:00 2001 From: Kevin Yap Date: Fri, 6 Mar 2015 23:51:26 -0800 Subject: [PATCH 0393/1427] Standardize formatting of .travis.yml Use 2 spaces for indentation. --- .travis.yml | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index a052252b..f5a7f04f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,25 +1,24 @@ language: python python: - - "2.7" - - "3.3" - - "3.4" + - "2.7" + - "3.3" + - "3.4" addons: apt_packages: - pandoc before_install: - - sudo apt-get update -qq - - sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8 + - sudo apt-get update -qq + - sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8 install: - - pip install . - - pip install -r dev_requirements.txt - - pip install nose-cov + - pip install . + - pip install -r dev_requirements.txt + - pip install nose-cov script: nosetests -sv --with-coverage --cover-package=pelican pelican after_success: - # Report coverage results to coveralls.io - pip install coveralls - coveralls notifications: - irc: - channels: - - "irc.freenode.org#pelican" - on_success: change + irc: + channels: + - "irc.freenode.org#pelican" + on_success: change From ffe71d324d4812925b2eeddbc52b66f5ebbf3801 Mon Sep 17 00:00:00 2001 From: robertlagrant Date: Fri, 13 Mar 2015 13:42:56 +0200 Subject: [PATCH 0394/1427] Change docs wording on cache regen for #1630 --- docs/settings.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 11444d2e..9fb97883 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -847,13 +847,11 @@ can be invoked by passing the ``--archive`` flag). The cache files are Python pickles, so they may not be readable by different versions of Python as the pickle format often changes. If -such an error is encountered, the cache files have to be rebuilt by -removing them and re-running Pelican, or by using the Pelican -command-line option ``--ignore-cache``. The cache files also have to -be rebuilt when changing the ``GZIP_CACHE`` setting for cache file -reading to work properly. +such an error is encountered, it is caught and the cache file is +rebuilt automatically in the new format. The cache files will also be +rebuilt after the ``GZIP_CACHE`` setting has been changed. -The ``--ignore-cache`` command-line option is also useful when the +The ``--ignore-cache`` command-line option is useful when the whole cache needs to be regenerated, such as when making modifications to the settings file that will affect the cached content, or just for debugging purposes. When Pelican runs in autoreload mode, modification From 0f7f328206b4b3eb085335aa86c620150143ee6e Mon Sep 17 00:00:00 2001 From: Kevin Yap Date: Fri, 13 Mar 2015 23:01:31 -0700 Subject: [PATCH 0395/1427] Remove a couple of unused imports As reported by Pyflakes. --- pelican/contents.py | 2 +- pelican/writers.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 074c28be..a680c411 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals, print_function import six -from six.moves.urllib.parse import (unquote, urlparse, urlunparse) +from six.moves.urllib.parse import urlparse, urlunparse import copy import locale diff --git a/pelican/writers.py b/pelican/writers.py index bf32e272..e90a0004 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -3,7 +3,6 @@ from __future__ import with_statement, unicode_literals, print_function import six import os -import locale import logging if not six.PY3: From ef737c22393174571fe17a6175eb98465c6ec246 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Sat, 14 Mar 2015 13:36:51 -0400 Subject: [PATCH 0396/1427] Use `--relative-urls` only if it is specified Otherwise, `RELATIVE_URLS` in the config file is ignored and `RELATIVE_URLS` is set to `False` if `--relative-urls` is not specified. Fixes an issue introduced in #1592 --- pelican/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 3013744d..056c45ef 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -321,7 +321,8 @@ def get_config(args): config['CACHE_PATH'] = args.cache_path if args.selected_paths: config['WRITE_SELECTED'] = args.selected_paths.split(',') - config['RELATIVE_URLS'] = args.relative_paths + if args.relative_paths: + config['RELATIVE_URLS'] = args.relative_paths config['DEBUG'] = args.verbosity == logging.DEBUG # argparse returns bytes in Py2. There is no definite answer as to which From 875c4a5e05d818c776be3019506921b863b13dc0 Mon Sep 17 00:00:00 2001 From: Anton Antonov Date: Tue, 17 Mar 2015 01:23:29 +0200 Subject: [PATCH 0397/1427] Nitpick Content decorators A bit more readable this way. --- pelican/contents.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pelican/contents.py b/pelican/contents.py index 96466a94..005d045c 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -90,7 +90,7 @@ class Content(object): self.in_default_lang = (self.lang == default_lang) - # create the slug if not existing, generate slug according to + # 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'): @@ -308,8 +308,13 @@ class Content(object): """Dummy function""" pass - url = property(functools.partial(get_url_setting, key='url')) - save_as = property(functools.partial(get_url_setting, key='save_as')) + @property + def url(self): + return self.get_url_setting('url') + + @property + def save_as(self): + return self.get_url_setting('save_as') def _get_template(self): if hasattr(self, 'template') and self.template is not None: From db2e5174502787e447d3df32298cf950c6c894ae Mon Sep 17 00:00:00 2001 From: Forest Date: Mon, 29 Sep 2014 22:51:13 -0700 Subject: [PATCH 0398/1427] Ignore empty metadata. Fixes #1469. Fixes #1398. Some metadata values cause problems when empty. For example, a markdown file containing a Slug: line with no additional text causing Pelican to produce a file named ".html" instead of generating a proper file name. Others, like those created by a PATH_METADATA regex, must be preserved even if empty, so things like PAGE_URL="filename{customvalue}.html" will always work. Essentially, we want to discard empty metadata that we know will be useless or problematic. This is better than raising an exception because (a) it allows users to deliberately keep empty metadata in their source files for filling in later, and (b) users shouldn't be forced to fix empty metadata created by blog migration tools (see #1398). The metadata processors are the ideal place to do this, because they know the type of data they are handling and whether an empty value is wanted. Unfortunately, they can't discard items, and neither can process_metadata(), because their return values are always saved by calling code. We can't safely change the calling code, because some of it lives in custom reader classes out in the field, and we don't want to break those working systems. Discarding empty values at the time of use isn't good enough, because that still allows useless empty values in a source file to override configured defaults. My solution: - When processing a list of values, a metadata processor will omit any unwanted empty ones from the list it returns. - When processing an entirely unwanted value, it will return something easily identifiable that will pass through the reader code. - When collecting the processed metadata, read_file() will filter out items identified as unwanted. These metadata are affected by this change: author, authors, category, slug, status, tags. I also removed a bit of now-superfluous code from generators.py that was discarding empty authors at the time of use. --- pelican/generators.py | 4 +--- pelican/readers.py | 48 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index f0a6d264..75bd6b2a 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -544,10 +544,8 @@ class ArticlesGenerator(CachingGenerator): if hasattr(article, 'tags'): for tag in article.tags: self.tags[tag].append(article) - # ignore blank authors as well as undefined for author in getattr(article, 'authors', []): - if author.name != '': - self.authors[author].append(article) + self.authors[author].append(article) # sort the articles by date self.articles.sort(key=attrgetter('date'), reverse=True) self.dates = list(self.articles) diff --git a/pelican/readers.py b/pelican/readers.py index a9b71bed..3656cd96 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -28,16 +28,44 @@ from pelican.contents import Page, Category, Tag, Author from pelican.utils import get_date, pelican_open, FileStampDataCacher, SafeDatetime, posixize_path +def strip_split(text, sep=','): + """Return a list of stripped, non-empty substrings, delimited by sep.""" + items = [x.strip() for x in text.split(sep)] + return [x for x in items if x] + + +# 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. + """ + name = name.strip() + return processor(name, settings) if name else _DISCARD + + METADATA_PROCESSORS = { - 'tags': lambda x, y: [Tag(tag, y) for tag in x.split(',')], + 'tags': lambda x, y: [Tag(tag, y) for tag in strip_split(x)] or _DISCARD, 'date': lambda x, y: get_date(x.replace('_', ' ')), 'modified': lambda x, y: get_date(x), - 'status': lambda x, y: x.strip(), - 'category': Category, - 'author': Author, - 'authors': lambda x, y: [Author(author.strip(), y) for author in x.split(',')], + '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(a, y) for a in strip_split(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): @@ -447,14 +475,14 @@ class Readers(FileStampDataCacher): reader = self.readers[fmt] - metadata = default_metadata( - settings=self.settings, process=reader.process_metadata) + metadata = _filter_discardable_metadata(default_metadata( + settings=self.settings, process=reader.process_metadata)) metadata.update(path_metadata( full_path=path, source_path=source_path, settings=self.settings)) - metadata.update(parse_path_metadata( + metadata.update(_filter_discardable_metadata(parse_path_metadata( source_path=source_path, settings=self.settings, - process=reader.process_metadata)) + process=reader.process_metadata))) reader_name = reader.__class__.__name__ metadata['reader'] = reader_name.replace('Reader', '').lower() @@ -462,7 +490,7 @@ class Readers(FileStampDataCacher): if content is None: content, reader_metadata = reader.read(path) self.cache_data(path, (content, reader_metadata)) - metadata.update(reader_metadata) + metadata.update(_filter_discardable_metadata(reader_metadata)) if content: # find images with empty alt From 7ad649e3b79c20d9cbb783148e659824c46fbf8c Mon Sep 17 00:00:00 2001 From: Forest Date: Fri, 31 Oct 2014 23:05:19 -0700 Subject: [PATCH 0399/1427] Guess mime type with python-magic if available. Fixes #1514. --- pelican/server.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pelican/server.py b/pelican/server.py index 0a8dc1b6..60252e1f 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -12,6 +12,11 @@ try: except ImportError: import socketserver # NOQA +try: + from magic import from_file as magic_from_file +except ImportError: + magic_from_file = None + PORT = len(sys.argv) in (2, 3) and int(sys.argv[1]) or 8000 SERVER = len(sys.argv) == 3 and sys.argv[2] or "" SUFFIXES = ['', '.html', '/index.html'] @@ -39,6 +44,18 @@ class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler): logging.warning("Unable to find `%s` or variations.", self.original_path) + def guess_type(self, path): + """Guess at the mime type for the specified file. + """ + mimetype = srvmod.SimpleHTTPRequestHandler.guess_type(self, path) + + # If the default guess is too generic, try the python-magic library + if mimetype == 'application/octet-stream' and magic_from_file: + mimetype = magic_from_file(path, mime=True) + + return mimetype + + Handler = ComplexHTTPRequestHandler socketserver.TCPServer.allow_reuse_address = True From 7b4ceb29744b3a00f39925047a0dafef0176214f Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Fri, 3 Apr 2015 18:58:52 -0400 Subject: [PATCH 0400/1427] Make `pelican.server` importable and use it in `fab serve` `fab serve` and `make devserver` use different HTTP Handlers and as a result they behave differently. This makes sure `fab serve` also uses the Handler defined in `pelican.server` in order to get rid of the inconsistency. --- pelican/server.py | 46 ++++++++++++--------------- pelican/tools/templates/fabfile.py.in | 5 +-- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/pelican/server.py b/pelican/server.py index 60252e1f..f58ac085 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -2,30 +2,22 @@ from __future__ import print_function import os import sys import logging -try: - import SimpleHTTPServer as srvmod -except ImportError: - import http.server as srvmod # NOQA -try: - import SocketServer as socketserver -except ImportError: - import socketserver # NOQA +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 -PORT = len(sys.argv) in (2, 3) and int(sys.argv[1]) or 8000 -SERVER = len(sys.argv) == 3 and sys.argv[2] or "" -SUFFIXES = ['', '.html', '/index.html'] - class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler): + SUFFIXES = ['', '.html', '/index.html'] + def do_GET(self): # Try to detect file by applying various suffixes - for suffix in SUFFIXES: + for suffix in self.SUFFIXES: if not hasattr(self, 'original_path'): self.original_path = self.path @@ -56,19 +48,21 @@ class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler): return mimetype -Handler = ComplexHTTPRequestHandler +if __name__ == '__main__': + PORT = len(sys.argv) in (2, 3) and int(sys.argv[1]) or 8000 + SERVER = len(sys.argv) == 3 and sys.argv[2] or "" -socketserver.TCPServer.allow_reuse_address = True -try: - httpd = socketserver.TCPServer((SERVER, PORT), Handler) -except OSError as e: - logging.error("Could not listen on port %s, server %s.", PORT, SERVER) - sys.exit(getattr(e, 'exitcode', 1)) + socketserver.TCPServer.allow_reuse_address = True + try: + 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() -except KeyboardInterrupt as e: - logging.info("Shutting down server.") - httpd.socket.close() + logging.info("Serving at port %s, server %s.", PORT, SERVER) + try: + httpd.serve_forever() + except KeyboardInterrupt as e: + logging.info("Shutting down server.") + httpd.socket.close() diff --git a/pelican/tools/templates/fabfile.py.in b/pelican/tools/templates/fabfile.py.in index a8ab586b..bcc66f6a 100644 --- a/pelican/tools/templates/fabfile.py.in +++ b/pelican/tools/templates/fabfile.py.in @@ -3,9 +3,10 @@ import fabric.contrib.project as project import os import shutil import sys -import SimpleHTTPServer import SocketServer +from pelican.server import ComplexHTTPRequestHandler + # Local path configuration (can be absolute or relative to fabfile) env.deploy_path = 'output' DEPLOY_PATH = env.deploy_path @@ -51,7 +52,7 @@ def serve(): class AddressReuseTCPServer(SocketServer.TCPServer): allow_reuse_address = True - server = AddressReuseTCPServer(('', PORT), SimpleHTTPServer.SimpleHTTPRequestHandler) + server = AddressReuseTCPServer(('', PORT), ComplexHTTPRequestHandler) sys.stderr.write('Serving on port {0} ...\n'.format(PORT)) server.serve_forever() From 946e95172fa85e7d5967226f3dbc723f099f94c9 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Sat, 4 Apr 2015 15:17:59 -0400 Subject: [PATCH 0401/1427] Use pelican.server in Quickstart docs --- docs/quickstart.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 122d65b5..c4f5a897 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -61,10 +61,10 @@ Preview your site ----------------- Open a new terminal session and run the following commands to switch to your -``output`` directory and launch Python's built-in web server:: +``output`` directory and launch Pelican's web server:: cd ~/projects/yoursite/output - python -m SimpleHTTPServer # -m http.server if you use python3 + python -m pelican.server Preview your site by navigating to http://localhost:8000/ in your browser. From 5ca808ed598679dfef4574892c79e1d262632051 Mon Sep 17 00:00:00 2001 From: winlu Date: Mon, 6 Apr 2015 09:49:01 +0200 Subject: [PATCH 0402/1427] fix broken internal metadata link --- docs/settings.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 9fb97883..73837181 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -470,14 +470,14 @@ your resume, and a contact page — you could have:: Path metadata ============= -Not all metadata needs to be `embedded in source file itself`__. For -example, blog posts are often named following a ``YYYY-MM-DD-SLUG.rst`` -pattern, or nested into ``YYYY/MM/DD-SLUG`` directories. To extract -metadata from the filename or path, set ``FILENAME_METADATA`` or -``PATH_METADATA`` to regular expressions that use Python's `group name -notation`_ ``(?P…)``. If you want to attach additional metadata -but don't want to encode it in the path, you can set -``EXTRA_PATH_METADATA``: +Not all metadata needs to be :ref:`embedded in source file itself +`. For example, blog posts are often named +following a ``YYYY-MM-DD-SLUG.rst`` pattern, or nested into +``YYYY/MM/DD-SLUG`` directories. To extract metadata from the +filename or path, set ``FILENAME_METADATA`` or ``PATH_METADATA`` to +regular expressions that use Python's `group name notation`_ ``(?P…)``. +If you want to attach additional metadata but don't want to encode +it in the path, you can set ``EXTRA_PATH_METADATA``: .. parsed-literal:: @@ -506,7 +506,6 @@ particular file: 'static/robots.txt': {'path': 'robots.txt'}, } -__ internal_metadata__ .. _group name notation: http://docs.python.org/3/library/re.html#regular-expression-syntax From 2e590d0e866a8679b99978819a75edded3c1bcef Mon Sep 17 00:00:00 2001 From: winlu Date: Mon, 6 Apr 2015 14:59:06 +0200 Subject: [PATCH 0403/1427] maintain test_readers * move all metadata tests to use a single function call (assertDictHasSubset) * add tests for assertDictHasSubset * correct some tests that iterated over metadata instead of expected metadata, resulting in metadata that was expected to be there but was not * correct resulting broken tests * add additional tests for EXTRA_PATH_METADATA * make MdReaderTest fail if Markdown is not available instead of skipping each method individually --- pelican/tests/test_readers.py | 181 ++++++++++++++++++++++++++-------- 1 file changed, 140 insertions(+), 41 deletions(-) diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index cb657673..d390fb48 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -22,6 +22,52 @@ class ReaderTest(unittest.TestCase): r = readers.Readers(settings=get_settings(**kwargs)) return r.read_file(base_path=CONTENT_PATH, path=path) + def assertDictHasSubset(self, dictionary, subset): + for key, value in subset.items(): + if key in dictionary: + real_value = dictionary.get(key) + self.assertEqual( + value, + real_value, + str('Expected %r to have value %r, but was %r') + % (key, value, real_value)) + else: + self.fail( + str('Expected %r to have value %r, but was not in Dict') + % (key, value)) + +class TestAssertDictHasSubset(ReaderTest): + def setUp(self): + self.dictionary = { + 'key-a' : 'val-a', + 'key-b' : 'val-b'} + + def tearDown(self): + self.dictionary = None + + def test_subset(self): + self.assertDictHasSubset(self.dictionary, {'key-a':'val-a'}) + + def test_equal(self): + self.assertDictHasSubset(self.dictionary, self.dictionary) + + def test_fail_not_set(self): + self.assertRaisesRegexp( + AssertionError, + 'Expected.*key-c.*to have value.*val-c.*but was not in Dict', + self.assertDictHasSubset, + self.dictionary, + {'key-c':'val-c'} + ) + + def test_fail_wrong_val(self): + self.assertRaisesRegexp( + AssertionError, + 'Expected .*key-a.* to have value .*val-b.* but was .*val-a.*', + self.assertDictHasSubset, + self.dictionary, + {'key-a':'val-b'} + ) class DefaultReaderTest(ReaderTest): @@ -48,8 +94,7 @@ class RstReaderTest(ReaderTest): 'custom_field': 'http://notmyidea.org', } - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) + self.assertDictHasSubset(page.metadata, expected) def test_article_with_filename_metadata(self): page = self.read_file( @@ -61,8 +106,7 @@ class RstReaderTest(ReaderTest): 'title': 'Rst with filename metadata', 'reader': 'rst', } - for key, value in page.metadata.items(): - self.assertEqual(value, expected[key], key) + self.assertDictHasSubset(page.metadata, expected) page = self.read_file( path='2012-11-29_rst_w_filename_meta#foo-bar.rst', @@ -74,13 +118,12 @@ class RstReaderTest(ReaderTest): 'date': SafeDatetime(2012, 11, 29), 'reader': 'rst', } - for key, value in page.metadata.items(): - self.assertEqual(value, expected[key], key) + self.assertDictHasSubset(page.metadata, expected) page = self.read_file( path='2012-11-29_rst_w_filename_meta#foo-bar.rst', FILENAME_METADATA=( - '(?P\d{4}-\d{2}-\d{2})_' + '(?P\d{4}-\d{2}-\d{2})' '_(?P.*)' '#(?P.*)-(?P.*)')) expected = { @@ -88,12 +131,11 @@ class RstReaderTest(ReaderTest): 'author': 'Alexis Métaireau', 'title': 'Rst with filename metadata', 'date': SafeDatetime(2012, 11, 29), - 'slug': 'article_with_filename_metadata', + 'slug': 'rst_w_filename_meta', 'mymeta': 'foo', 'reader': 'rst', } - for key, value in page.metadata.items(): - self.assertEqual(value, expected[key], key) + self.assertDictHasSubset(page.metadata, expected) def test_article_metadata_key_lowercase(self): # Keys of metadata should be lowercase. @@ -105,6 +147,81 @@ class RstReaderTest(ReaderTest): self.assertEqual('Yeah', metadata.get('category'), 'Value keeps case.') + def test_article_extra_path_metadata(self): + input_with_metadata = '2012-11-29_rst_w_filename_meta#foo-bar.rst' + page_metadata = self.read_file( + path=input_with_metadata, + FILENAME_METADATA=( + '(?P\d{4}-\d{2}-\d{2})' + '_(?P.*)' + '#(?P.*)-(?P.*)' + ), + EXTRA_PATH_METADATA={ + input_with_metadata: { + 'key-1a': 'value-1a', + 'key-1b': 'value-1b' + } + } + ) + expected_metadata = { + 'category': 'yeah', + 'author' : 'Alexis Métaireau', + 'title': 'Rst with filename metadata', + 'date': SafeDatetime(2012, 11, 29), + 'slug': 'rst_w_filename_meta', + 'mymeta': 'foo', + 'reader': 'rst', + 'key-1a': 'value-1a', + 'key-1b': 'value-1b' + } + self.assertDictHasSubset(page_metadata.metadata, expected_metadata) + + input_file_path_without_metadata = 'article.rst' + page_without_metadata = self.read_file( + path=input_file_path_without_metadata, + EXTRA_PATH_METADATA={ + input_file_path_without_metadata: { + 'author': 'Charlès Overwrite'} + } + ) + expected_without_metadata = { + 'category' : 'misc', + 'author' : 'Charlès Overwrite', + 'title' : 'Article title', + 'reader' : 'rst', + } + self.assertDictHasSubset( + page_without_metadata.metadata, + expected_without_metadata) + + def test_article_extra_path_metadata_dont_overwrite(self): + #EXTRA_PATH_METADATA['author'] should get ignored + #since we don't overwrite already set values + input_file_path = '2012-11-29_rst_w_filename_meta#foo-bar.rst' + page = self.read_file( + path=input_file_path, + FILENAME_METADATA=( + '(?P\d{4}-\d{2}-\d{2})' + '_(?P.*)' + '#(?P.*)-(?P.*)'), + EXTRA_PATH_METADATA={ + input_file_path: { + 'author': 'Charlès Overwrite', + 'key-1b': 'value-1b'} + } + ) + expected = { + 'category': 'yeah', + 'author' : 'Alexis Métaireau', + 'title': 'Rst with filename metadata', + 'date': SafeDatetime(2012, 11, 29), + 'slug': 'rst_w_filename_meta', + 'mymeta': 'foo', + 'reader': 'rst', + 'key-1b': 'value-1b' + } + self.assertDictHasSubset(page.metadata, expected) + def test_typogrify(self): # if nothing is specified in the settings, the content should be # unmodified @@ -205,13 +322,11 @@ class RstReaderTest(ReaderTest): 'authors': ['First Author', 'Second Author'] } - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) - + self.assertDictHasSubset(page.metadata, expected) +@unittest.skipUnless(readers.Markdown, "markdown isn't installed") class MdReaderTest(ReaderTest): - @unittest.skipUnless(readers.Markdown, "markdown isn't installed") def test_article_with_metadata(self): reader = readers.MarkdownReader(settings=get_settings()) content, metadata = reader.read( @@ -224,8 +339,7 @@ class MdReaderTest(ReaderTest): 'modified': SafeDatetime(2010, 12, 2, 10, 20), 'tags': ['foo', 'bar', 'foobar'], } - for key, value in metadata.items(): - self.assertEqual(value, expected[key], key) + self.assertDictHasSubset(metadata, expected) content, metadata = reader.read( _path('article_with_markdown_and_nonascii_summary.md')) @@ -238,10 +352,8 @@ class MdReaderTest(ReaderTest): 'tags': ['パイソン', 'マック'], 'slug': 'python-virtualenv-on-mac-osx-mountain-lion-10.8', } - for key, value in metadata.items(): - self.assertEqual(value, expected[key], key) + self.assertDictHasSubset(metadata, expected) - @unittest.skipUnless(readers.Markdown, "markdown isn't installed") def test_article_with_footnote(self): reader = readers.MarkdownReader(settings=get_settings()) content, metadata = reader.read( @@ -271,7 +383,6 @@ class MdReaderTest(ReaderTest): 'should be supported.

    '), 'date': SafeDatetime(2012, 10, 31), 'modified': SafeDatetime(2012, 11, 1), - 'slug': 'article-with-markdown-containing-footnotes', 'multiline': [ 'Line Metadata should be handle properly.', 'See syntax of Meta-Data extension of Python Markdown package:', @@ -282,10 +393,8 @@ class MdReaderTest(ReaderTest): ] } self.assertEqual(content, expected_content) - for key, value in metadata.items(): - self.assertEqual(value, expected_metadata[key], key) + self.assertDictHasSubset(metadata, expected_metadata) - @unittest.skipUnless(readers.Markdown, "markdown isn't installed") def test_article_with_file_extensions(self): reader = readers.MarkdownReader(settings=get_settings()) # test to ensure the md file extension is being processed by the @@ -322,7 +431,6 @@ class MdReaderTest(ReaderTest): " the mdown extension.

    ") self.assertEqual(content, expected) - @unittest.skipUnless(readers.Markdown, "markdown isn't installed") def test_article_with_markdown_markup_extension(self): # test to ensure the markdown markup extension is being processed as # expected @@ -342,7 +450,6 @@ class MdReaderTest(ReaderTest): self.assertEqual(page.content, expected) - @unittest.skipUnless(readers.Markdown, "markdown isn't installed") def test_article_with_filename_metadata(self): page = self.read_file( path='2012-11-30_md_w_filename_meta#foo-bar.md', @@ -351,8 +458,7 @@ class MdReaderTest(ReaderTest): 'category': 'yeah', 'author': 'Alexis Métaireau', } - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) + self.assertDictHasSubset(page.metadata, expected) page = self.read_file( path='2012-11-30_md_w_filename_meta#foo-bar.md', @@ -362,8 +468,7 @@ class MdReaderTest(ReaderTest): 'author': 'Alexis Métaireau', 'date': SafeDatetime(2012, 11, 30), } - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) + self.assertDictHasSubset(page.metadata, expected) page = self.read_file( path='2012-11-30_md_w_filename_meta#foo-bar.md', @@ -378,8 +483,7 @@ class MdReaderTest(ReaderTest): 'slug': 'md_w_filename_meta', 'mymeta': 'foo', } - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) + self.assertDictHasSubset(page.metadata, expected) class HTMLReaderTest(ReaderTest): @@ -397,8 +501,7 @@ class HTMLReaderTest(ReaderTest): 'tags': ['foo', 'bar', 'foobar'], } - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) + self.assertDictHasSubset(page.metadata, expected) def test_article_with_metadata(self): page = self.read_file(path='article_with_metadata.html') @@ -412,8 +515,7 @@ class HTMLReaderTest(ReaderTest): 'custom_field': 'http://notmyidea.org', } - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) + self.assertDictHasSubset(page.metadata, expected) def test_article_with_multiple_authors(self): page = self.read_file(path='article_with_multiple_authors.html') @@ -421,8 +523,7 @@ class HTMLReaderTest(ReaderTest): 'authors': ['First Author', 'Second Author'] } - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) + self.assertDictHasSubset(page.metadata, expected) def test_article_with_metadata_and_contents_attrib(self): page = self.read_file(path='article_with_metadata_and_contents.html') @@ -435,8 +536,7 @@ class HTMLReaderTest(ReaderTest): 'tags': ['foo', 'bar', 'foobar'], 'custom_field': 'http://notmyidea.org', } - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) + self.assertDictHasSubset(page.metadata, expected) def test_article_with_null_attributes(self): page = self.read_file(path='article_with_null_attributes.html') @@ -460,5 +560,4 @@ class HTMLReaderTest(ReaderTest): 'title': 'Article with Nonconformant HTML meta tags', } - for key, value in expected.items(): - self.assertEqual(value, page.metadata[key], key) + self.assertDictHasSubset(page.metadata, expected) From 0f7938ccb71e34cd020026095fcb086b99c79856 Mon Sep 17 00:00:00 2001 From: Alberto Scotto Date: Wed, 8 Apr 2015 03:32:48 +0200 Subject: [PATCH 0404/1427] fix the meta tags added in #1028 The attribute 'contents' should be 'content'. See http://www.w3schools.com/tags/att_meta_content.asp --- 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 e81261c5..d558183d 100644 --- a/pelican/themes/simple/templates/article.html +++ b/pelican/themes/simple/templates/article.html @@ -2,15 +2,15 @@ {% block head %} {{ super() }} {% for keyword in article.keywords %} - + {% endfor %} {% for description in article.description %} - + {% endfor %} {% for tag in article.tags %} - + {% endfor %} {% endblock %} From adc5c4b572195d12cea1df4d0f8daa49ec2e168f Mon Sep 17 00:00:00 2001 From: winlu Date: Fri, 10 Apr 2015 09:02:12 +0200 Subject: [PATCH 0405/1427] fix broken refs --- docs/themes.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/themes.rst b/docs/themes.rst index fd4ec8f9..7f2693ff 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -367,7 +367,7 @@ source_path Full system path of the article source file. status The article status, can be any of 'published' or 'draft'. summary Rendered summary content. -tags List of :ref:`Tag ` +tags List of :ref:`Tag ` objects. template Template name to use for rendering. title Title of the article. @@ -422,7 +422,7 @@ source_path Full system path of the page source file. status The page status, can be any of 'published' or 'draft'. summary Rendered summary content. -tags List of :ref:`Tag ` +tags List of :ref:`Tag ` objects. template Template name to use for rendering. title Title of the page. From bf7d113caaa9d9fcc860e6687a63589ea00aea10 Mon Sep 17 00:00:00 2001 From: winlu Date: Sat, 11 Apr 2015 22:45:31 +0200 Subject: [PATCH 0406/1427] add skips for tests relying on dev_requirements modules --- pelican/tests/test_generators.py | 16 +++++++++++++++- pelican/tests/test_importer.py | 7 +++++++ pelican/tests/test_rstdirectives.py | 10 ++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index acf767f2..4fb70826 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -6,7 +6,10 @@ from codecs import open try: from unittest.mock import MagicMock except ImportError: - from mock import MagicMock + try: + from mock import MagicMock + except ImportError: + MagicMock = False from shutil import rmtree from tempfile import mkdtemp @@ -112,6 +115,7 @@ class TestArticlesGenerator(unittest.TestCase): return [[article.title, article.status, article.category.name, article.template] for article in articles] + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_generate_feeds(self): settings = get_settings() settings['CACHE_PATH'] = self.temp_cache @@ -215,6 +219,7 @@ class TestArticlesGenerator(unittest.TestCase): categories_expected = ['default', 'yeah', 'test', 'zhi-dao-shu'] self.assertEqual(sorted(categories), sorted(categories_expected)) + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_direct_templates_save_as_default(self): settings = get_settings(filenames={}) @@ -228,6 +233,7 @@ class TestArticlesGenerator(unittest.TestCase): generator.get_template("archives"), settings, blog=True, paginated={}, page_name='archives') + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_direct_templates_save_as_modified(self): settings = get_settings() @@ -244,6 +250,7 @@ class TestArticlesGenerator(unittest.TestCase): blog=True, paginated={}, page_name='archives/index') + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_direct_templates_save_as_false(self): settings = get_settings() @@ -268,6 +275,7 @@ class TestArticlesGenerator(unittest.TestCase): self.assertIn(custom_template, self.articles) self.assertIn(standard_template, self.articles) + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_period_in_timeperiod_archive(self): """ Test that the context of a generated period_archive is passed @@ -347,6 +355,7 @@ class TestArticlesGenerator(unittest.TestCase): authors_expected = ['alexis-metaireau', 'first-author', 'second-author'] self.assertEqual(sorted(authors), sorted(authors_expected)) + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_article_object_caching(self): """Test Article objects caching at the generator level""" settings = get_settings(filenames={}) @@ -367,6 +376,7 @@ class TestArticlesGenerator(unittest.TestCase): generator.generate_context() generator.readers.read_file.assert_called_count == 0 + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_reader_content_caching(self): """Test raw content caching at the reader level""" settings = get_settings(filenames={}) @@ -389,6 +399,7 @@ class TestArticlesGenerator(unittest.TestCase): for reader in readers.values(): reader.read.assert_called_count == 0 + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_ignore_cache(self): """Test that all the articles are read again when not loading cache @@ -492,6 +503,7 @@ class TestPageGenerator(unittest.TestCase): self.assertEqual(sorted(pages_expected), sorted(pages)) self.assertEqual(sorted(hidden_pages_expected), sorted(hidden_pages)) + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_page_object_caching(self): """Test Page objects caching at the generator level""" settings = get_settings(filenames={}) @@ -512,6 +524,7 @@ class TestPageGenerator(unittest.TestCase): generator.generate_context() generator.readers.read_file.assert_called_count == 0 + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_reader_content_caching(self): """Test raw content caching at the reader level""" settings = get_settings(filenames={}) @@ -534,6 +547,7 @@ class TestPageGenerator(unittest.TestCase): for reader in readers.values(): reader.read.assert_called_count == 0 + @unittest.skipUnless(MagicMock, 'Needs Mock module') def test_ignore_cache(self): """Test that all the pages are read again when not loading cache diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index c108bc52..4ace5ccc 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -26,6 +26,12 @@ try: except ImportError: BeautifulSoup = False # NOQA +try: + import bs4.builder._lxml as LXML +except ImportError: + LXML = False + + @skipIfNoExecutable(['pandoc', '--version']) @unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module') @@ -302,6 +308,7 @@ class TestBuildHeader(unittest.TestCase): @unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module') +@unittest.skipUnless(LXML, 'Needs lxml module') class TestWordpressXMLAttachements(unittest.TestCase): def setUp(self): self.old_locale = locale.setlocale(locale.LC_ALL) diff --git a/pelican/tests/test_rstdirectives.py b/pelican/tests/test_rstdirectives.py index ae863b30..7c5f8adf 100644 --- a/pelican/tests/test_rstdirectives.py +++ b/pelican/tests/test_rstdirectives.py @@ -1,9 +1,15 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals, print_function - -from mock import Mock +try: + from unittest.mock import Mock +except ImportError: + try: + from mock import Mock + except ImportError: + Mock = False from pelican.tests.support import unittest +@unittest.skipUnless(Mock, 'Needs Mock module') class Test_abbr_role(unittest.TestCase): def call_it(self, text): from pelican.rstdirectives import abbr_role From f864418b9e1af0a980052dc4c7d51519c57e3649 Mon Sep 17 00:00:00 2001 From: "Daniel M. Drucker" Date: Mon, 13 Apr 2015 11:12:52 -0400 Subject: [PATCH 0407/1427] make the quickstart work The quickstart was worded confusingly - it said "from your project directory", which implied doing a `cd ..` down to the `projects` dir, which would cause `pelican content` to fail. In fact, you need to be in the `yoursite` directory, which is the directory that has the `content` directory in it. --- docs/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index c4f5a897..ef5256c2 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -49,7 +49,7 @@ Given that this example article is in Markdown format, save it as Generate your site ------------------ -From your project directory, run the ``pelican`` command to generate your site:: +From your site directory, run the ``pelican`` command to generate your site:: pelican content From a45a917766089c9e733cf417f942a4b9c651a154 Mon Sep 17 00:00:00 2001 From: winlu Date: Sat, 11 Apr 2015 15:02:53 +0200 Subject: [PATCH 0408/1427] test docs via travis * move build environment into tox * add new environment installing sphinx and testing for doc errors * reorganize dependency installs for easier management --- .travis.yml | 16 +++++++--------- dev_requirements.txt | 3 --- tox.ini | 29 +++++++++++++++++++++++------ 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index f5a7f04f..cc124ea4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,11 @@ language: python python: - "2.7" - - "3.3" - - "3.4" +env: + - TOX_ENV=docs + - TOX_ENV=py27 + - TOX_ENV=py33 + - TOX_ENV=py34 addons: apt_packages: - pandoc @@ -10,13 +13,8 @@ before_install: - sudo apt-get update -qq - sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8 install: - - pip install . - - pip install -r dev_requirements.txt - - pip install nose-cov -script: nosetests -sv --with-coverage --cover-package=pelican pelican -after_success: - - pip install coveralls - - coveralls + - pip install tox +script: tox -e $TOX_ENV notifications: irc: channels: diff --git a/dev_requirements.txt b/dev_requirements.txt index a7c10719..028cbebd 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -10,6 +10,3 @@ typogrify # To perform release bumpr==0.2.0 wheel - -# For docs theme -sphinx_rtd_theme diff --git a/tox.ini b/tox.ini index 5dd36c36..11b3cae8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,28 @@ -# This tests the unified codebase (py27, py33) of Pelican. -# depends on some external libraries that aren't released yet. - [tox] -envlist = py27,py33,py34 +envlist = py27,py33,py34,docs [testenv] -commands = - python -m unittest discover +basepython = + py27: python2.7 + py33: python3.3 + py34: python3.4 +usedevelop=True deps = -rdev_requirements.txt + nose + nose-cov + coveralls + +commands = + {envpython} --version + nosetests -sv --with-coverage --cover-package=pelican pelican + coveralls + +[testenv:docs] +basepython = python2.7 +deps = + sphinx + sphinx_rtd_theme +changedir = docs +commands = + sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html From 2e84e30ad38175819ef97103f9720693c0218fc7 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Tue, 14 Apr 2015 15:19:16 -0400 Subject: [PATCH 0409/1427] Fixes #1695: replace with list comprehension for py3 compatibility --- pelican/tools/pelican_quickstart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 74633630..2f1129dc 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -162,7 +162,7 @@ def ask(question, answer=str_compat, default=None, l=None): def ask_timezone(question, default, tzurl): """Prompt for time zone and validate input""" - lower_tz = map(str.lower, pytz.all_timezones) + lower_tz = [tz.lower() for tz in pytz.all_timezones] while True: r = ask(question, str_compat, default) r = r.strip().replace(' ', '_').lower() From 90a283bc44da299e0860dcc27516bfefe327fd56 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Thu, 16 Apr 2015 19:05:15 -0400 Subject: [PATCH 0410/1427] Fixes #1686: posixize paths in context['filenames'] to fix windows issues --- pelican/generators.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pelican/generators.py b/pelican/generators.py index 75bd6b2a..0ce6439d 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -22,7 +22,8 @@ from jinja2 import (Environment, FileSystemLoader, PrefixLoader, ChoiceLoader, 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, - FileStampDataCacher, python_2_unicode_compatible) + FileStampDataCacher, python_2_unicode_compatible, + posixize_path) from pelican import signals @@ -160,7 +161,7 @@ class Generator(object): (For example, one that was missing mandatory metadata.) The path argument is expected to be relative to self.path. """ - self.context['filenames'][os.path.normpath(path)] = None + self.context['filenames'][posixize_path(os.path.normpath(path))] = None def _is_potential_source_path(self, path): """Return True if path was supposed to be used as a source file. @@ -168,7 +169,7 @@ 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 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 From dceea3aabe36e206d014e2540f86572dcb7c8859 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Thu, 16 Apr 2015 19:16:23 -0400 Subject: [PATCH 0411/1427] Change URLWrapper.slug to a property Main goal is to delay `slugify` call until `slug` is needed. `slugify` can be expensive depending on the input string (see #1172). Changing it to a property gives plugins time to place custom `slug`s before `name` is unnecessarily slugified. With this change, default behavior is same except the time slugification happens. But if you set custom slug, `slugify` won't be used at all. So, this is a partial solution to #1172. The rest, setting a custom slug, would best be handled by a plugin. --- pelican/urlwrappers.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index 06cfa2ab..60bc6a3a 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -13,12 +13,10 @@ logger = logging.getLogger(__name__) @functools.total_ordering class URLWrapper(object): def __init__(self, name, settings): - # next 2 lines are redundant with the setter of the name property - # but are here for clarity self.settings = settings self._name = name - self.slug = slugify(name, self.settings.get('SLUG_SUBSTITUTIONS', ())) - self.name = name + self._slug = None + self._slug_from_name = True @property def name(self): @@ -27,11 +25,28 @@ class URLWrapper(object): @name.setter def name(self, name): self._name = name - self.slug = slugify(name, self.settings.get('SLUG_SUBSTITUTIONS', ())) + # if slug wasn't explicitly set, it needs to be regenerated from name + # so, changing name should reset slug for slugification + if self._slug_from_name: + self._slug = None + + @property + def slug(self): + if self._slug is None: + self._slug = slugify(self.name, + self.settings.get('SLUG_SUBSTITUTIONS', ())) + return self._slug + + @slug.setter + def slug(self, slug): + # if slug is expliticly set, changing name won't alter slug + self._slug_from_name = False + self._slug = slug def as_dict(self): d = self.__dict__ d['name'] = self.name + d['slug'] = self.slug return d def __hash__(self): From 9dd4080fe6162849aec0946cc8406b04da764157 Mon Sep 17 00:00:00 2001 From: winlu Date: Mon, 20 Apr 2015 12:16:05 +0200 Subject: [PATCH 0412/1427] remove tag_cloud from core --- docs/settings.rst | 47 ------------------------------------------- pelican/generators.py | 32 ++--------------------------- pelican/settings.py | 2 -- 3 files changed, 2 insertions(+), 79 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 73837181..2eec1e50 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -620,53 +620,6 @@ This would cause the first page to be written to ``page/{number}`` directories. -Tag cloud -========= - -If you want to generate a tag cloud with all your tags, you can do so using the -following settings. - -================================================ ===================================================== -Setting name (followed by default value) What does it do? -================================================ ===================================================== -``TAG_CLOUD_STEPS = 4`` Count of different font sizes in the tag - cloud. -``TAG_CLOUD_MAX_ITEMS = 100`` Maximum number of tags in the cloud. -================================================ ===================================================== - -The default theme does not include a tag cloud, but it is pretty easy to add one:: - -
      - {% for tag in tag_cloud %} -
    • {{ tag.0 }}
    • - {% endfor %} -
    - -You should then also define CSS styles with appropriate classes (tag-1 to tag-N, -where N matches ``TAG_CLOUD_STEPS``), tag-1 being the most frequent, and -define a ``ul.tagcloud`` class with appropriate list-style to create the cloud. -For example:: - - ul.tagcloud { - list-style: none; - padding: 0; - } - - ul.tagcloud li { - display: inline-block; - } - - li.tag-1 { - font-size: 150%; - } - - li.tag-2 { - font-size: 120%; - } - - ... - - Translations ============ diff --git a/pelican/generators.py b/pelican/generators.py index 75bd6b2a..672850d3 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -3,8 +3,6 @@ from __future__ import unicode_literals, print_function import os import six -import math -import random import logging import shutil import fnmatch @@ -14,7 +12,7 @@ from codecs import open from collections import defaultdict from functools import partial from itertools import chain, groupby -from operator import attrgetter, itemgetter +from operator import attrgetter from jinja2 import (Environment, FileSystemLoader, PrefixLoader, ChoiceLoader, BaseLoader, TemplateNotFound) @@ -552,32 +550,6 @@ class ArticlesGenerator(CachingGenerator): self.dates.sort(key=attrgetter('date'), reverse=self.context['NEWEST_FIRST_ARCHIVES']) - # create tag cloud - tag_cloud = defaultdict(int) - for article in self.articles: - for tag in getattr(article, 'tags', []): - tag_cloud[tag] += 1 - - tag_cloud = sorted(tag_cloud.items(), key=itemgetter(1), reverse=True) - tag_cloud = tag_cloud[:self.settings.get('TAG_CLOUD_MAX_ITEMS')] - - tags = list(map(itemgetter(1), tag_cloud)) - if tags: - max_count = max(tags) - steps = self.settings.get('TAG_CLOUD_STEPS') - - # calculate word sizes - self.tag_cloud = [ - ( - tag, - int(math.floor(steps - (steps - 1) * math.log(count) - / (math.log(max_count)or 1))) - ) - for tag, count in tag_cloud - ] - # put words in chaos - random.shuffle(self.tag_cloud) - # and generate the output :) # order the categories per name @@ -589,7 +561,7 @@ class ArticlesGenerator(CachingGenerator): self.authors.sort() self._update_context(('articles', 'dates', 'tags', 'categories', - 'tag_cloud', 'authors', 'related_posts')) + 'authors', 'related_posts')) self.save_cache() self.readers.save_cache() signals.article_generator_finalized.send(self) diff --git a/pelican/settings.py b/pelican/settings.py index 0d69c08e..0c54e89b 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -93,8 +93,6 @@ DEFAULT_CONFIG = { 'DAY_ARCHIVE_SAVE_AS': '', 'RELATIVE_URLS': False, 'DEFAULT_LANG': 'en', - 'TAG_CLOUD_STEPS': 4, - 'TAG_CLOUD_MAX_ITEMS': 100, 'DIRECT_TEMPLATES': ['index', 'tags', 'categories', 'authors', 'archives'], 'EXTRA_TEMPLATES_PATHS': [], 'PAGINATED_DIRECT_TEMPLATES': ['index'], From 3effe464c6a893c61d8a399f77531e14d348afd1 Mon Sep 17 00:00:00 2001 From: winlu Date: Mon, 20 Apr 2015 16:36:18 +0200 Subject: [PATCH 0413/1427] add faq entry about tag-cloud outsourcing --- docs/faq.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/faq.rst b/docs/faq.rst index 86f12462..ff473624 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -241,3 +241,12 @@ purposes. This can be achieved by explicitly specifying only the filenames of those articles in ``ARTICLE_PATHS``. A list of such filenames could be found using a command similar to ``cd content; find -name '*.md' | head -n 10``. + +My tag-cloud is missing/broken since I upgraded Pelican +======================================================= + +In an ongoing effort to steamline Pelican, `tag_cloud` generation has been +moved out of the pelican core and into a separate `plugin +`_. +See the :ref:`plugins` documentation further information about the +Pelican plugin system. From d6ebf772e3fa90910714e06cd9f5529cc2956df0 Mon Sep 17 00:00:00 2001 From: Matthew Scott Date: Thu, 23 Apr 2015 13:30:24 -0500 Subject: [PATCH 0414/1427] Allow `--path` even when using a virtualenv project --- pelican/tools/pelican_quickstart.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 2f1129dc..58da4649 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -59,6 +59,13 @@ if six.PY3: else: str_compat = unicode +# Create a 'marked' default path, to determine if someone has supplied +# a path on the command-line. +class _DEFAULT_PATH_TYPE(str_compat): + is_default_path = True + +_DEFAULT_PATH = _DEFAULT_PATH_TYPE(os.curdir) + def decoding_strings(f): def wrapper(*args, **kwargs): out = f(*args, **kwargs) @@ -178,7 +185,7 @@ def main(): parser = argparse.ArgumentParser( description="A kickstarter for Pelican", formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('-p', '--path', default=os.curdir, + parser.add_argument('-p', '--path', default=_DEFAULT_PATH, help="The path to generate the blog into") parser.add_argument('-t', '--title', metavar="title", help='Set the title of the website') @@ -200,7 +207,8 @@ needed by Pelican. project = os.path.join( os.environ.get('VIRTUAL_ENV', os.curdir), '.project') - if os.path.isfile(project): + no_path_was_specified = hasattr(args.path, 'is_default_path') + if os.path.isfile(project) and no_path_was_specified: CONF['basedir'] = open(project, 'r').read().rstrip("\n") print('Using project associated with current virtual environment.' 'Will save to:\n%s\n' % CONF['basedir']) From df5495303257d0809139f418db0e9b2607917830 Mon Sep 17 00:00:00 2001 From: Ingmar Steen Date: Wed, 29 Apr 2015 15:08:46 +0200 Subject: [PATCH 0415/1427] Fix intra-site links to drafts. Overwrite an article's source path registration when it is 'upgraded' to a draft. Fixes #865 --- pelican/generators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pelican/generators.py b/pelican/generators.py index 75bd6b2a..e95ec3ab 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -525,6 +525,7 @@ class ArticlesGenerator(CachingGenerator): preread_sender=self, context_signal=signals.article_generator_context, context_sender=self) + self.add_source_path(draft) all_drafts.append(draft) else: logger.error("Unknown status '%s' for file %s, skipping it.", From 264d75b9e04c4aa517a0bfd14473dfe78ee06d2f Mon Sep 17 00:00:00 2001 From: Forest Date: Fri, 8 May 2015 13:39:59 -0700 Subject: [PATCH 0416/1427] Clarify STATIC_EXCLUDE_SOURCES documentatoin. --- docs/settings.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index 73837181..a18e24bf 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -159,7 +159,12 @@ Setting name (followed by default value, if any) Pelican's default settings include the "images" directory here. ``STATIC_EXCLUDES = []`` A list of directories to exclude when looking for static files. ``STATIC_EXCLUDE_SOURCES = True`` If set to False, content source files will not be skipped when - copying files found in ``STATIC_PATHS``. + copying files found in ``STATIC_PATHS``. This setting is for + backward compatibility with Pelican releases before version 3.5. + It has no effect unless ``STATIC_PATHS`` contains a directory that + is also in ``ARTICLE_PATHS`` or ``PAGE_PATHS``. If you are trying + to publish your site's source files, consider using the + ``OUTPUT_SOURCES` setting instead. ``TIMEZONE`` The timezone used in the date information, to generate Atom and RSS feeds. See the *Timezone* section below for more info. From 2f048d365222edd98119dfc9366e202008e5b57f Mon Sep 17 00:00:00 2001 From: Forest Date: Sat, 9 May 2015 21:20:07 -0700 Subject: [PATCH 0417/1427] Add backtick missing from my last docs commit. --- docs/settings.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings.rst b/docs/settings.rst index a18e24bf..330ec2a6 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -164,7 +164,7 @@ Setting name (followed by default value, if any) It has no effect unless ``STATIC_PATHS`` contains a directory that is also in ``ARTICLE_PATHS`` or ``PAGE_PATHS``. If you are trying to publish your site's source files, consider using the - ``OUTPUT_SOURCES` setting instead. + ``OUTPUT_SOURCES`` setting instead. ``TIMEZONE`` The timezone used in the date information, to generate Atom and RSS feeds. See the *Timezone* section below for more info. From ab2dc71d34f0b3387afafb9251b0f6c0cae3998e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 12 May 2015 02:55:45 +0300 Subject: [PATCH 0418/1427] Improve internal error reporting. Fixes #1717. --- pelican/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 056c45ef..12da111a 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -444,7 +444,7 @@ def main(): except Exception as e: if (args.verbosity == logging.DEBUG): - logger.critical(e.args) + logger.critical('Internal failure: %r', e, exc_info=True) raise logger.warning( 'Caught exception "%s". Reloading.', e) From e10ae8a187876b7954d38d1968c0c8f8b6606e22 Mon Sep 17 00:00:00 2001 From: derwinlu Date: Wed, 13 May 2015 21:08:39 +0200 Subject: [PATCH 0419/1427] make tox 2 compatible --- .travis.yml | 2 +- tox.ini | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index cc124ea4..5d7d4a5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ before_install: - sudo apt-get update -qq - sudo locale-gen fr_FR.UTF-8 tr_TR.UTF-8 install: - - pip install tox + - pip install tox==2.0.1 script: tox -e $TOX_ENV notifications: irc: diff --git a/tox.ini b/tox.ini index 11b3cae8..775241b8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,12 @@ [tox] -envlist = py27,py33,py34,docs +envlist = py{27,33,34},docs [testenv] basepython = py27: python2.7 py33: python3.3 py34: python3.4 +passenv = * usedevelop=True deps = -rdev_requirements.txt From f8f89a84761ede361fd97b15d96f0a7cdbef2c31 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Sun, 24 May 2015 12:58:00 +0100 Subject: [PATCH 0420/1427] Fix spelling mistakes in docs --- docs/pelican-themes.rst | 6 +++--- docs/themes.rst | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/pelican-themes.rst b/docs/pelican-themes.rst index 7090c648..e18ebadf 100644 --- a/docs/pelican-themes.rst +++ b/docs/pelican-themes.rst @@ -130,11 +130,11 @@ This is useful for theme development: $ sudo pelican-themes -s ~/Dev/Python/pelican-themes/two-column $ pelican ~/Blog/content -o /tmp/out -t two-column $ firefox /tmp/out/index.html - $ vim ~/Dev/Pelican/pelican-themes/two-coumn/static/css/main.css + $ vim ~/Dev/Pelican/pelican-themes/two-column/static/css/main.css $ pelican ~/Blog/content -o /tmp/out -t two-column - $ cp /tmp/bg.png ~/Dev/Pelican/pelican-themes/two-coumn/static/img/bg.png + $ cp /tmp/bg.png ~/Dev/Pelican/pelican-themes/two-column/static/img/bg.png $ pelican ~/Blog/content -o /tmp/out -t two-column - $ vim ~/Dev/Pelican/pelican-themes/two-coumn/templates/index.html + $ vim ~/Dev/Pelican/pelican-themes/two-column/templates/index.html $ pelican ~/Blog/content -o /tmp/out -t two-column diff --git a/docs/themes.rst b/docs/themes.rst index 7f2693ff..d64683bb 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -359,7 +359,7 @@ default_template Default template name. in_default_lang Boolean representing if the article is written in the default language. lang Language of the article. -locale_date Date formated by the `date_format`. +locale_date Date formatted by the `date_format`. metadata Article header metadata `dict`. save_as Location to save the article page. slug Page slug. @@ -414,7 +414,7 @@ default_template Default template name. in_default_lang Boolean representing if the article is written in the default language. lang Language of the article. -locale_date Date formated by the `date_format`. +locale_date Date formatted by the `date_format`. metadata Page header metadata `dict`. save_as Location to save the page. slug Page slug. From 641b3ffa71801eb5e4f5b818e925021f2287f699 Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Sun, 24 May 2015 13:04:35 +0100 Subject: [PATCH 0421/1427] Fix capitalisation of WordPress --- docs/changelog.rst | 2 +- pelican/tools/pelican_import.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 52367cec..4e297562 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -205,7 +205,7 @@ Release history 2.5 (2010-11-20) ================== -* Import from Wordpress +* Import from WordPress * Added some new themes (martyalchin / wide-notmyidea) * First bug report! * Linkedin support diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index a6424ace..92e8c919 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -101,12 +101,12 @@ def decode_wp_content(content, br=True): return content def get_items(xml): - """Opens a wordpress xml file and returns a list of items""" + """Opens a WordPress xml file and returns a list of items""" try: from bs4 import BeautifulSoup except ImportError: error = ('Missing dependency ' - '"BeautifulSoup4" and "lxml" required to import Wordpress XML files.') + '"BeautifulSoup4" and "lxml" required to import WordPress XML files.') sys.exit(error) with open(xml, encoding='utf-8') as infile: xmlfile = infile.read() @@ -586,7 +586,7 @@ def get_attachments(xml): return attachedposts def download_attachments(output_path, urls): - """Downloads wordpress attachments and returns a list of paths to + """Downloads WordPress attachments and returns a list of paths to attachments that can be associated with a post (relative path to output directory). Files that fail to download, will not be added to posts""" locations = [] From 77ebd05fce1176f0f4c9cd2e48ffa47fb30d1901 Mon Sep 17 00:00:00 2001 From: Jonathan Sundqvist Date: Sun, 24 May 2015 16:59:23 +0100 Subject: [PATCH 0422/1427] Actually stopping server When running `make devserver` and then running `make stopserver` it doesn't stop the server. This patch fixes that. --- pelican/tools/templates/Makefile.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pelican/tools/templates/Makefile.in b/pelican/tools/templates/Makefile.in index 5e3635c3..b97fbe43 100644 --- a/pelican/tools/templates/Makefile.in +++ b/pelican/tools/templates/Makefile.in @@ -92,8 +92,7 @@ else endif stopserver: - kill -9 `cat pelican.pid` - kill -9 `cat srv.pid` + $(BASEDIR)/develop_server.sh stop @echo 'Stopped Pelican and SimpleHTTPServer processes running in background.' publish: From 940eb76b7f70b1c9c7f833d5328d44cb19bde406 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 2 Jun 2015 08:35:12 -0700 Subject: [PATCH 0423/1427] Load external resources via HTTPS when available This replaces all `http://` and `//:` links with `https:`. The protocol- relative URL scheme is now deemed to be an anti-pattern. There are security advantages to using HTTPS, and there are no significant performance concerns. In short, if the asset we need is available via HTTPS, then that asset should always be loaded via HTTPS. Fixes #1736 --- pelican/tests/output/basic/a-markdown-powered-article.html | 2 +- pelican/tests/output/basic/archives.html | 2 +- pelican/tests/output/basic/article-1.html | 2 +- pelican/tests/output/basic/article-2.html | 2 +- pelican/tests/output/basic/article-3.html | 2 +- pelican/tests/output/basic/author/alexis-metaireau.html | 2 +- pelican/tests/output/basic/authors.html | 2 +- pelican/tests/output/basic/categories.html | 2 +- pelican/tests/output/basic/category/bar.html | 2 +- pelican/tests/output/basic/category/cat1.html | 2 +- pelican/tests/output/basic/category/misc.html | 2 +- pelican/tests/output/basic/category/yeah.html | 2 +- pelican/tests/output/basic/filename_metadata-example.html | 2 +- pelican/tests/output/basic/index.html | 2 +- pelican/tests/output/basic/oh-yeah.html | 2 +- pelican/tests/output/basic/override/index.html | 2 +- .../output/basic/pages/this-is-a-test-hidden-page.html | 2 +- pelican/tests/output/basic/pages/this-is-a-test-page.html | 2 +- pelican/tests/output/basic/second-article-fr.html | 2 +- pelican/tests/output/basic/second-article.html | 2 +- pelican/tests/output/basic/tag/bar.html | 2 +- pelican/tests/output/basic/tag/baz.html | 2 +- pelican/tests/output/basic/tag/foo.html | 2 +- pelican/tests/output/basic/tag/foobar.html | 2 +- pelican/tests/output/basic/tag/oh.html | 2 +- pelican/tests/output/basic/tag/yeah.html | 2 +- pelican/tests/output/basic/tags.html | 2 +- pelican/tests/output/basic/theme/css/main.css | 2 +- pelican/tests/output/basic/this-is-a-super-article.html | 2 +- pelican/tests/output/basic/unbelievable.html | 2 +- pelican/tests/output/custom/a-markdown-powered-article.html | 6 +++--- pelican/tests/output/custom/archives.html | 6 +++--- pelican/tests/output/custom/article-1.html | 6 +++--- pelican/tests/output/custom/article-2.html | 6 +++--- pelican/tests/output/custom/article-3.html | 6 +++--- pelican/tests/output/custom/author/alexis-metaireau.html | 6 +++--- pelican/tests/output/custom/author/alexis-metaireau2.html | 6 +++--- pelican/tests/output/custom/author/alexis-metaireau3.html | 6 +++--- pelican/tests/output/custom/authors.html | 6 +++--- pelican/tests/output/custom/categories.html | 6 +++--- pelican/tests/output/custom/category/bar.html | 6 +++--- pelican/tests/output/custom/category/cat1.html | 6 +++--- pelican/tests/output/custom/category/misc.html | 6 +++--- pelican/tests/output/custom/category/yeah.html | 6 +++--- pelican/tests/output/custom/drafts/a-draft-article.html | 6 +++--- pelican/tests/output/custom/filename_metadata-example.html | 6 +++--- pelican/tests/output/custom/index.html | 6 +++--- pelican/tests/output/custom/index2.html | 6 +++--- pelican/tests/output/custom/index3.html | 6 +++--- pelican/tests/output/custom/jinja2_template.html | 6 +++--- pelican/tests/output/custom/oh-yeah-fr.html | 6 +++--- pelican/tests/output/custom/oh-yeah.html | 6 +++--- pelican/tests/output/custom/override/index.html | 6 +++--- .../output/custom/pages/this-is-a-test-hidden-page.html | 6 +++--- pelican/tests/output/custom/pages/this-is-a-test-page.html | 6 +++--- pelican/tests/output/custom/second-article-fr.html | 6 +++--- pelican/tests/output/custom/second-article.html | 6 +++--- pelican/tests/output/custom/tag/bar.html | 6 +++--- pelican/tests/output/custom/tag/baz.html | 6 +++--- pelican/tests/output/custom/tag/foo.html | 6 +++--- pelican/tests/output/custom/tag/foobar.html | 6 +++--- pelican/tests/output/custom/tag/oh.html | 6 +++--- pelican/tests/output/custom/tag/yeah.html | 6 +++--- pelican/tests/output/custom/tags.html | 6 +++--- pelican/tests/output/custom/theme/css/main.css | 2 +- pelican/tests/output/custom/this-is-a-super-article.html | 6 +++--- pelican/tests/output/custom/unbelievable.html | 6 +++--- pelican/tests/output/custom_locale/archives.html | 6 +++--- .../tests/output/custom_locale/author/alexis-metaireau.html | 6 +++--- .../output/custom_locale/author/alexis-metaireau2.html | 6 +++--- .../output/custom_locale/author/alexis-metaireau3.html | 6 +++--- pelican/tests/output/custom_locale/authors.html | 6 +++--- pelican/tests/output/custom_locale/categories.html | 6 +++--- pelican/tests/output/custom_locale/category/bar.html | 6 +++--- pelican/tests/output/custom_locale/category/cat1.html | 6 +++--- pelican/tests/output/custom_locale/category/misc.html | 6 +++--- pelican/tests/output/custom_locale/category/yeah.html | 6 +++--- .../tests/output/custom_locale/drafts/a-draft-article.html | 6 +++--- pelican/tests/output/custom_locale/index.html | 6 +++--- pelican/tests/output/custom_locale/index2.html | 6 +++--- pelican/tests/output/custom_locale/index3.html | 6 +++--- pelican/tests/output/custom_locale/jinja2_template.html | 6 +++--- pelican/tests/output/custom_locale/oh-yeah-fr.html | 6 +++--- pelican/tests/output/custom_locale/override/index.html | 6 +++--- .../custom_locale/pages/this-is-a-test-hidden-page.html | 6 +++--- .../output/custom_locale/pages/this-is-a-test-page.html | 6 +++--- .../2010/décembre/02/this-is-a-super-article/index.html | 6 +++--- .../posts/2010/octobre/15/unbelievable/index.html | 6 +++--- .../custom_locale/posts/2010/octobre/20/oh-yeah/index.html | 6 +++--- .../2011/avril/20/a-markdown-powered-article/index.html | 6 +++--- .../posts/2011/février/17/article-1/index.html | 6 +++--- .../posts/2011/février/17/article-2/index.html | 6 +++--- .../posts/2011/février/17/article-3/index.html | 6 +++--- .../posts/2012/février/29/second-article/index.html | 6 +++--- .../2012/novembre/30/filename_metadata-example/index.html | 6 +++--- pelican/tests/output/custom_locale/second-article-fr.html | 6 +++--- pelican/tests/output/custom_locale/tag/bar.html | 6 +++--- pelican/tests/output/custom_locale/tag/baz.html | 6 +++--- pelican/tests/output/custom_locale/tag/foo.html | 6 +++--- pelican/tests/output/custom_locale/tag/foobar.html | 6 +++--- pelican/tests/output/custom_locale/tag/oh.html | 6 +++--- pelican/tests/output/custom_locale/tag/yeah.html | 6 +++--- pelican/tests/output/custom_locale/tags.html | 6 +++--- pelican/tests/output/custom_locale/theme/css/main.css | 2 +- pelican/themes/notmyidea/static/css/main.css | 2 +- pelican/themes/notmyidea/templates/analytics.html | 2 +- pelican/themes/notmyidea/templates/base.html | 2 +- pelican/themes/notmyidea/templates/disqus_script.html | 2 +- pelican/themes/notmyidea/templates/github.html | 4 ++-- pelican/themes/notmyidea/templates/twitter.html | 2 +- pelican/themes/simple/templates/gosquared.html | 2 +- 111 files changed, 256 insertions(+), 256 deletions(-) diff --git a/pelican/tests/output/basic/a-markdown-powered-article.html b/pelican/tests/output/basic/a-markdown-powered-article.html index 5fcc42a9..dd92d691 100644 --- a/pelican/tests/output/basic/a-markdown-powered-article.html +++ b/pelican/tests/output/basic/a-markdown-powered-article.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/archives.html b/pelican/tests/output/basic/archives.html index f8f1a67f..27b7c862 100644 --- a/pelican/tests/output/basic/archives.html +++ b/pelican/tests/output/basic/archives.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/article-1.html b/pelican/tests/output/basic/article-1.html index 4ea7f4e3..b09eef79 100644 --- a/pelican/tests/output/basic/article-1.html +++ b/pelican/tests/output/basic/article-1.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/article-2.html b/pelican/tests/output/basic/article-2.html index 45130e55..e340a4f6 100644 --- a/pelican/tests/output/basic/article-2.html +++ b/pelican/tests/output/basic/article-2.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/article-3.html b/pelican/tests/output/basic/article-3.html index 8603430f..08bf4fc1 100644 --- a/pelican/tests/output/basic/article-3.html +++ b/pelican/tests/output/basic/article-3.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/author/alexis-metaireau.html b/pelican/tests/output/basic/author/alexis-metaireau.html index 11d54185..eeca537a 100644 --- a/pelican/tests/output/basic/author/alexis-metaireau.html +++ b/pelican/tests/output/basic/author/alexis-metaireau.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/authors.html b/pelican/tests/output/basic/authors.html index 20df01d2..288543b5 100644 --- a/pelican/tests/output/basic/authors.html +++ b/pelican/tests/output/basic/authors.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/categories.html b/pelican/tests/output/basic/categories.html index 55e955c8..9a6682c0 100644 --- a/pelican/tests/output/basic/categories.html +++ b/pelican/tests/output/basic/categories.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/category/bar.html b/pelican/tests/output/basic/category/bar.html index 18e434cb..d3eb38da 100644 --- a/pelican/tests/output/basic/category/bar.html +++ b/pelican/tests/output/basic/category/bar.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/category/cat1.html b/pelican/tests/output/basic/category/cat1.html index f99eb497..f21bc9ab 100644 --- a/pelican/tests/output/basic/category/cat1.html +++ b/pelican/tests/output/basic/category/cat1.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/category/misc.html b/pelican/tests/output/basic/category/misc.html index fc724edb..0368793e 100644 --- a/pelican/tests/output/basic/category/misc.html +++ b/pelican/tests/output/basic/category/misc.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/category/yeah.html b/pelican/tests/output/basic/category/yeah.html index 7fe75a86..09db53bc 100644 --- a/pelican/tests/output/basic/category/yeah.html +++ b/pelican/tests/output/basic/category/yeah.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/filename_metadata-example.html b/pelican/tests/output/basic/filename_metadata-example.html index 638c65dd..9f492fc2 100644 --- a/pelican/tests/output/basic/filename_metadata-example.html +++ b/pelican/tests/output/basic/filename_metadata-example.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/index.html b/pelican/tests/output/basic/index.html index f3814b00..3066172d 100644 --- a/pelican/tests/output/basic/index.html +++ b/pelican/tests/output/basic/index.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/oh-yeah.html b/pelican/tests/output/basic/oh-yeah.html index 76be69fe..caeb8ddb 100644 --- a/pelican/tests/output/basic/oh-yeah.html +++ b/pelican/tests/output/basic/oh-yeah.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/override/index.html b/pelican/tests/output/basic/override/index.html index ed9fa92a..d98009a2 100644 --- a/pelican/tests/output/basic/override/index.html +++ b/pelican/tests/output/basic/override/index.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html index ac31987a..b98f0008 100644 --- a/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/basic/pages/this-is-a-test-hidden-page.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/pages/this-is-a-test-page.html b/pelican/tests/output/basic/pages/this-is-a-test-page.html index 43e5f72e..282fe965 100644 --- a/pelican/tests/output/basic/pages/this-is-a-test-page.html +++ b/pelican/tests/output/basic/pages/this-is-a-test-page.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/second-article-fr.html b/pelican/tests/output/basic/second-article-fr.html index 551027da..c13acce6 100644 --- a/pelican/tests/output/basic/second-article-fr.html +++ b/pelican/tests/output/basic/second-article-fr.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/second-article.html b/pelican/tests/output/basic/second-article.html index ed350752..e9a5b14c 100644 --- a/pelican/tests/output/basic/second-article.html +++ b/pelican/tests/output/basic/second-article.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/tag/bar.html b/pelican/tests/output/basic/tag/bar.html index 5331767b..c461cac5 100644 --- a/pelican/tests/output/basic/tag/bar.html +++ b/pelican/tests/output/basic/tag/bar.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/tag/baz.html b/pelican/tests/output/basic/tag/baz.html index dc26d8e1..7961b19f 100644 --- a/pelican/tests/output/basic/tag/baz.html +++ b/pelican/tests/output/basic/tag/baz.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/tag/foo.html b/pelican/tests/output/basic/tag/foo.html index aed3ad17..1a97fd4a 100644 --- a/pelican/tests/output/basic/tag/foo.html +++ b/pelican/tests/output/basic/tag/foo.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/tag/foobar.html b/pelican/tests/output/basic/tag/foobar.html index 540cde25..891b6866 100644 --- a/pelican/tests/output/basic/tag/foobar.html +++ b/pelican/tests/output/basic/tag/foobar.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/tag/oh.html b/pelican/tests/output/basic/tag/oh.html index ef876b8d..61148527 100644 --- a/pelican/tests/output/basic/tag/oh.html +++ b/pelican/tests/output/basic/tag/oh.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/tag/yeah.html b/pelican/tests/output/basic/tag/yeah.html index b8da2bc6..bd5ff204 100644 --- a/pelican/tests/output/basic/tag/yeah.html +++ b/pelican/tests/output/basic/tag/yeah.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/tags.html b/pelican/tests/output/basic/tags.html index 0eda47d7..44b45591 100644 --- a/pelican/tests/output/basic/tags.html +++ b/pelican/tests/output/basic/tags.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/theme/css/main.css b/pelican/tests/output/basic/theme/css/main.css index 9d7221a2..03a77e69 100644 --- a/pelican/tests/output/basic/theme/css/main.css +++ b/pelican/tests/output/basic/theme/css/main.css @@ -12,7 +12,7 @@ @import url("reset.css"); @import url("pygment.css"); @import url("typogrify.css"); -@import url(//fonts.googleapis.com/css?family=Yanone+Kaffeesatz&subset=latin); +@import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz&subset=latin); /***** Global *****/ /* Body */ diff --git a/pelican/tests/output/basic/this-is-a-super-article.html b/pelican/tests/output/basic/this-is-a-super-article.html index cf957ebf..1fe944eb 100644 --- a/pelican/tests/output/basic/this-is-a-super-article.html +++ b/pelican/tests/output/basic/this-is-a-super-article.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/basic/unbelievable.html b/pelican/tests/output/basic/unbelievable.html index b9b52031..dfb0c54e 100644 --- a/pelican/tests/output/basic/unbelievable.html +++ b/pelican/tests/output/basic/unbelievable.html @@ -7,7 +7,7 @@ diff --git a/pelican/tests/output/custom/a-markdown-powered-article.html b/pelican/tests/output/custom/a-markdown-powered-article.html index 577d61b8..59ffa4d1 100644 --- a/pelican/tests/output/custom/a-markdown-powered-article.html +++ b/pelican/tests/output/custom/a-markdown-powered-article.html @@ -8,13 +8,13 @@ -Fork me on GitHub +Fork me on GitHub
    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = '//' + disqus_shortname + '.disqus.com/count.js'; + s.src = 'https://' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/authors.html b/pelican/tests/output/custom/authors.html index d9aaef34..f157de26 100644 --- a/pelican/tests/output/custom/authors.html +++ b/pelican/tests/output/custom/authors.html @@ -8,13 +8,13 @@ -Fork me on GitHub +Fork me on GitHub
    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = '//' + disqus_shortname + '.disqus.com/count.js'; + s.src = 'https://' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/category/yeah.html b/pelican/tests/output/custom/category/yeah.html index a632664f..127eac13 100644 --- a/pelican/tests/output/custom/category/yeah.html +++ b/pelican/tests/output/custom/category/yeah.html @@ -8,13 +8,13 @@ -Fork me on GitHub +Fork me on GitHub
    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = '//' + disqus_shortname + '.disqus.com/count.js'; + s.src = 'https://' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom/jinja2_template.html b/pelican/tests/output/custom/jinja2_template.html index 0eafa913..21f678f9 100644 --- a/pelican/tests/output/custom/jinja2_template.html +++ b/pelican/tests/output/custom/jinja2_template.html @@ -8,13 +8,13 @@ -Fork me on GitHub +Fork me on GitHub
    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = '//' + disqus_shortname + '.disqus.com/count.js'; + s.src = 'https://' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom_locale/authors.html b/pelican/tests/output/custom_locale/authors.html index 2558c4d8..91ea479d 100644 --- a/pelican/tests/output/custom_locale/authors.html +++ b/pelican/tests/output/custom_locale/authors.html @@ -8,13 +8,13 @@ -Fork me on GitHub +Fork me on GitHub
    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = '//' + disqus_shortname + '.disqus.com/count.js'; + s.src = 'https://' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom_locale/category/yeah.html b/pelican/tests/output/custom_locale/category/yeah.html index 7ee2bbe9..d36c1608 100644 --- a/pelican/tests/output/custom_locale/category/yeah.html +++ b/pelican/tests/output/custom_locale/category/yeah.html @@ -8,13 +8,13 @@ -Fork me on GitHub +Fork me on GitHub
    (function () { var s = document.createElement('script'); s.async = true; s.type = 'text/javascript'; - s.src = '//' + disqus_shortname + '.disqus.com/count.js'; + s.src = 'https://' + disqus_shortname + '.disqus.com/count.js'; (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s); }()); diff --git a/pelican/tests/output/custom_locale/jinja2_template.html b/pelican/tests/output/custom_locale/jinja2_template.html index 0eafa913..21f678f9 100644 --- a/pelican/tests/output/custom_locale/jinja2_template.html +++ b/pelican/tests/output/custom_locale/jinja2_template.html @@ -8,13 +8,13 @@ -Fork me on GitHub +Fork me on GitHub

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom/category/cat1.html b/pelican/tests/output/custom/category/cat1.html index 237943d7..d9132d10 100644 --- a/pelican/tests/output/custom/category/cat1.html +++ b/pelican/tests/output/custom/category/cat1.html @@ -120,9 +120,6 @@

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html index 5fcde24b..fa71085d 100644 --- a/pelican/tests/output/custom/category/misc.html +++ b/pelican/tests/output/custom/category/misc.html @@ -131,9 +131,6 @@ pelican.conf, it ...

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom/category/yeah.html b/pelican/tests/output/custom/category/yeah.html index 127eac13..6d94c9ad 100644 --- a/pelican/tests/output/custom/category/yeah.html +++ b/pelican/tests/output/custom/category/yeah.html @@ -59,9 +59,6 @@

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

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom/tag/bar.html b/pelican/tests/output/custom/tag/bar.html index 2e0d047c..f462ffbd 100644 --- a/pelican/tests/output/custom/tag/bar.html +++ b/pelican/tests/output/custom/tag/bar.html @@ -110,9 +110,6 @@ YEAH !

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom/tag/foo.html b/pelican/tests/output/custom/tag/foo.html index 7c38c24c..9c58956b 100644 --- a/pelican/tests/output/custom/tag/foo.html +++ b/pelican/tests/output/custom/tag/foo.html @@ -80,9 +80,6 @@ as well as inline markup.

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom/tag/foobar.html b/pelican/tests/output/custom/tag/foobar.html index 42705281..7d17d1fb 100644 --- a/pelican/tests/output/custom/tag/foobar.html +++ b/pelican/tests/output/custom/tag/foobar.html @@ -59,9 +59,6 @@

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

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom/tag/yeah.html b/pelican/tests/output/custom/tag/yeah.html index 4d7b506b..e3c765fa 100644 --- a/pelican/tests/output/custom/tag/yeah.html +++ b/pelican/tests/output/custom/tag/yeah.html @@ -51,9 +51,6 @@ YEAH !

    alternate text

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom_locale/category/bar.html b/pelican/tests/output/custom_locale/category/bar.html index 7c8f947d..c416b358 100644 --- a/pelican/tests/output/custom_locale/category/bar.html +++ b/pelican/tests/output/custom_locale/category/bar.html @@ -51,9 +51,6 @@ YEAH !

    alternate text

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom_locale/category/cat1.html b/pelican/tests/output/custom_locale/category/cat1.html index 25bc6179..871b2e3f 100644 --- a/pelican/tests/output/custom_locale/category/cat1.html +++ b/pelican/tests/output/custom_locale/category/cat1.html @@ -120,9 +120,6 @@

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom_locale/category/misc.html b/pelican/tests/output/custom_locale/category/misc.html index 8062c0d6..bb78a8cc 100644 --- a/pelican/tests/output/custom_locale/category/misc.html +++ b/pelican/tests/output/custom_locale/category/misc.html @@ -131,9 +131,6 @@ pelican.conf, it ...

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom_locale/category/yeah.html b/pelican/tests/output/custom_locale/category/yeah.html index d36c1608..c5e6c7f0 100644 --- a/pelican/tests/output/custom_locale/category/yeah.html +++ b/pelican/tests/output/custom_locale/category/yeah.html @@ -59,9 +59,6 @@

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

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom_locale/tag/bar.html b/pelican/tests/output/custom_locale/tag/bar.html index 8ba1aed2..c1f33e64 100644 --- a/pelican/tests/output/custom_locale/tag/bar.html +++ b/pelican/tests/output/custom_locale/tag/bar.html @@ -110,9 +110,6 @@ YEAH !

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom_locale/tag/foo.html b/pelican/tests/output/custom_locale/tag/foo.html index ed67ede0..288f1768 100644 --- a/pelican/tests/output/custom_locale/tag/foo.html +++ b/pelican/tests/output/custom_locale/tag/foo.html @@ -80,9 +80,6 @@ as well as inline markup.

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom_locale/tag/foobar.html b/pelican/tests/output/custom_locale/tag/foobar.html index a569b526..59dcede1 100644 --- a/pelican/tests/output/custom_locale/tag/foobar.html +++ b/pelican/tests/output/custom_locale/tag/foobar.html @@ -59,9 +59,6 @@

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

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/tests/output/custom_locale/tag/yeah.html b/pelican/tests/output/custom_locale/tag/yeah.html index ba767748..4dc36ce5 100644 --- a/pelican/tests/output/custom_locale/tag/yeah.html +++ b/pelican/tests/output/custom_locale/tag/yeah.html @@ -51,9 +51,6 @@ YEAH !

    alternate text

    There are comments.

    -

    - Page 1 / 1 -

    diff --git a/pelican/themes/notmyidea/templates/index.html b/pelican/themes/notmyidea/templates/index.html index 3eac8a3a..6019987b 100644 --- a/pelican/themes/notmyidea/templates/index.html +++ b/pelican/themes/notmyidea/templates/index.html @@ -11,9 +11,6 @@

    {{ article.title }}

    {% include 'article_infos.html' %}{{ article.content }}{% include 'comments.html' %} - {% if loop.length == 1 %} - {% include 'pagination.html' %} - {% endif %} {% if loop.length > 1 %}
    @@ -42,13 +39,11 @@ {% endif %} {% if loop.last %} - {% if loop.length > 1 %} + {% if loop.length > 1 or articles_page.has_other_pages() %} - {% endif %} - {% if articles_page.has_previous() or loop.length > 1 %} - {% include 'pagination.html' %} - {% endif %} - {% if loop.length > 1 %} + {% if articles_page.has_other_pages() %} + {% include 'pagination.html' %} + {% endif %}
    {% endif %} {% endif %} From 21544a404cf51b60d407b0fd4eeadfc1d8339a9c Mon Sep 17 00:00:00 2001 From: Elana Hashman Date: Sun, 21 Jun 2015 13:35:41 -0600 Subject: [PATCH 0447/1427] Update simple theme to fix #1068 --- pelican/themes/simple/templates/index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pelican/themes/simple/templates/index.html b/pelican/themes/simple/templates/index.html index 20104db9..4bd81985 100644 --- a/pelican/themes/simple/templates/index.html +++ b/pelican/themes/simple/templates/index.html @@ -21,6 +21,8 @@ {% endfor %} -{% include 'pagination.html' %} +{% if articles_page.has_other_pages() %} + {% include 'pagination.html' %} +{% endif %}
    {% endblock content %} From ff88c4bb34779d979b8ce4ba042c07621204ea1a Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Fri, 26 Jun 2015 09:44:45 -0700 Subject: [PATCH 0448/1427] Document enabling per-block Markdown line numbers Since users frequently ask how to enable line numbers in Markdown-formatted code blocks, adding instructions to the docs will allow us to point users there. Fixes #1773 --- docs/content.rst | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/docs/content.rst b/docs/content.rst index 0e3310f1..1e6ba099 100644 --- a/docs/content.rst +++ b/docs/content.rst @@ -420,22 +420,29 @@ which posts are translations:: Syntax highlighting =================== -Pelican is able to provide colorized syntax highlighting for your code blocks. -To do so, you have to use the following conventions inside your content files. +Pelican can provide colorized syntax highlighting for your code blocks. +To do so, you must use the following conventions inside your content files. -For reStructuredText, use the code-block directive:: +For reStructuredText, use the ``code-block`` directive to specify the type +of code to be highlighted (in these examples, we'll use ``python``):: - .. code-block:: identifier + .. code-block:: python - + print("Pelican is a static site generator.") -For Markdown, include the language identifier just above the code block, -indenting both the identifier and code:: +For Markdown, which utilizes the `CodeHilite extension`_ to provide syntax +highlighting, include the language identifier just above the code block, +indenting both the identifier and the code:: - A block of text. + There are two ways to specify the identifier: - :::identifier - + :::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 0449/1427] 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 0450/1427] 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 0451/1427] 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 0452/1427] 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 0453/1427] 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 0454/1427] 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 += '' % 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 0455/1427] 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 0456/1427] 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 0457/1427] 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 0458/1427] 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 0459/1427] 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 0460/1427] 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 0461/1427] 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 0462/1427] 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 0463/1427] 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 0464/1427] 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 0465/1427] 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 0466/1427] 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 0467/1427] 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 0468/1427] 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(' entities in the" - " title. You can't miss them.") + self.assertTrue(title, "A normal post with some entities in " + "the title. You can't miss them.") self.assertNotIn('&', title) def test_decode_wp_content_returns_empty(self): @@ -216,14 +233,18 @@ class TestWordpressXmlImporter(unittest.TestCase): encoded_content = encoded_file.read() with open(WORDPRESS_DECODED_CONTENT_SAMPLE, 'r') as decoded_file: decoded_content = decoded_file.read() - self.assertEqual(decode_wp_content(encoded_content, br=False), decoded_content) + self.assertEqual( + decode_wp_content(encoded_content, br=False), + decoded_content) def test_preserve_verbatim_formatting(self): def r(f): with open(f, encoding='utf-8') as infile: return infile.read() silent_f2p = mute(True)(fields2pelican) - test_post = filter(lambda p: p[0].startswith("Code in List"), self.posts) + test_post = filter( + lambda p: p[0].startswith("Code in List"), + self.posts) with temporary_folder() as temp: md = [r(f) for f in silent_f2p(test_post, 'markdown', temp)][0] self.assertTrue(re.search(r'\s+a = \[1, 2, 3\]', md)) @@ -231,14 +252,17 @@ class TestWordpressXmlImporter(unittest.TestCase): for_line = re.search(r'\s+for i in zip\(a, b\):', md).group(0) print_line = re.search(r'\s+print i', md).group(0) - self.assertTrue(for_line.rindex('for') < print_line.rindex('print')) + self.assertTrue( + for_line.rindex('for') < print_line.rindex('print')) def test_code_in_list(self): def r(f): with open(f, encoding='utf-8') as infile: return infile.read() silent_f2p = mute(True)(fields2pelican) - test_post = filter(lambda p: p[0].startswith("Code in List"), self.posts) + test_post = filter( + lambda p: p[0].startswith("Code in List"), + self.posts) with temporary_folder() as temp: md = [r(f) for f in silent_f2p(test_post, 'markdown', temp)][0] sample_line = re.search(r'- This is a code sample', md).group(0) @@ -285,26 +309,29 @@ class TestBuildHeader(unittest.TestCase): self.assertEqual(build_header(*header_data), expected_docutils) self.assertEqual(build_markdown_header(*header_data), expected_md) - def test_build_header_with_east_asian_characters(self): header = build_header('これは広い幅の文字だけで構成されたタイトルです', - None, None, None, None, None) + None, None, None, None, None) self.assertEqual(header, - 'これは広い幅の文字だけで構成されたタイトルです\n' + - '##############################################\n\n') + ('これは広い幅の文字だけで構成されたタイトルです\n' + '##############################################' + '\n\n')) def test_galleries_added_to_header(self): - header = build_header('test', None, None, None, None, - None, attachments=['output/test1', 'output/test2']) - self.assertEqual(header, 'test\n####\n' + ':attachments: output/test1, ' - + 'output/test2\n\n') + header = build_header('test', None, None, None, None, None, + attachments=['output/test1', 'output/test2']) + self.assertEqual(header, ('test\n####\n' + ':attachments: output/test1, ' + 'output/test2\n\n')) def test_galleries_added_to_markdown_header(self): header = build_markdown_header('test', None, None, None, None, None, - attachments=['output/test1', 'output/test2']) - self.assertEqual(header, 'Title: test\n' + 'Attachments: output/test1, ' - + 'output/test2\n\n') + attachments=['output/test1', + 'output/test2']) + self.assertEqual( + header, + 'Title: test\nAttachments: output/test1, output/test2\n\n') @unittest.skipUnless(BeautifulSoup, 'Needs BeautifulSoup module') @@ -326,14 +353,24 @@ class TestWordpressXMLAttachements(unittest.TestCase): self.assertTrue(self.attachments) for post in self.attachments.keys(): if post is None: - self.assertTrue(self.attachments[post][0] == 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Pelican_lakes_entrance02.jpg/240px-Pelican_lakes_entrance02.jpg') + expected = ('https://upload.wikimedia.org/wikipedia/commons/' + 'thumb/2/2c/Pelican_lakes_entrance02.jpg/' + '240px-Pelican_lakes_entrance02.jpg') + self.assertEqual(self.attachments[post][0], expected) elif post == 'with-excerpt': - self.assertTrue(self.attachments[post][0] == 'http://thisurlisinvalid.notarealdomain/not_an_image.jpg') - self.assertTrue(self.attachments[post][1] == 'http://en.wikipedia.org/wiki/File:Pelikan_Walvis_Bay.jpg') + expected_invalid = ('http://thisurlisinvalid.notarealdomain/' + 'not_an_image.jpg') + expected_pelikan = ('http://en.wikipedia.org/wiki/' + 'File:Pelikan_Walvis_Bay.jpg') + self.assertEqual(self.attachments[post][0], expected_invalid) + self.assertEqual(self.attachments[post][1], expected_pelikan) elif post == 'with-tags': - self.assertTrue(self.attachments[post][0] == 'http://thisurlisinvalid.notarealdomain') + expected_invalid = ('http://thisurlisinvalid.notarealdomain') + self.assertEqual(self.attachments[post][0], expected_invalid) else: - self.fail('all attachments should match to a filename or None, {}'.format(post)) + self.fail('all attachments should match to a ' + 'filename or None, {}' + .format(post)) def test_download_attachments(self): real_file = os.path.join(CUR_DIR, 'content/article.rst') @@ -344,4 +381,6 @@ class TestWordpressXMLAttachements(unittest.TestCase): locations = list(silent_da(temp, [good_url, bad_url])) self.assertEqual(1, len(locations)) directory = locations[0] - self.assertTrue(directory.endswith(os.path.join('content', 'article.rst')), directory) + self.assertTrue( + directory.endswith(os.path.join('content', 'article.rst')), + directory) diff --git a/pelican/tests/test_paginator.py b/pelican/tests/test_paginator.py index 002d9e07..903a0305 100644 --- a/pelican/tests/test_paginator.py +++ b/pelican/tests/test_paginator.py @@ -1,18 +1,21 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, absolute_import +from __future__ import absolute_import, unicode_literals + import locale -from pelican.tests.support import unittest, get_settings - -from pelican.paginator import Paginator -from pelican.contents import Article, Author -from pelican.settings import DEFAULT_CONFIG from jinja2.utils import generate_lorem_ipsum +from pelican.contents import Article, Author +from pelican.paginator import Paginator +from pelican.settings import DEFAULT_CONFIG +from pelican.tests.support import get_settings, unittest + + # generate one paragraph, enclosed with

    TEST_CONTENT = str(generate_lorem_ipsum(n=1)) TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False) + class TestPage(unittest.TestCase): def setUp(self): super(TestPage, self).setUp() @@ -49,7 +52,8 @@ class TestPage(unittest.TestCase): ) self.page_kwargs['metadata']['author'] = Author('Blogger', settings) - object_list = [Article(**self.page_kwargs), Article(**self.page_kwargs)] + object_list = [Article(**self.page_kwargs), + Article(**self.page_kwargs)] paginator = Paginator('foobar.foo', object_list, settings) page = paginator.page(1) self.assertEqual(page.save_as, 'foobar.foo') diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index c6332487..b88ad287 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -1,23 +1,25 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function +from __future__ import print_function, unicode_literals import collections -import os -import sys -from tempfile import mkdtemp -from shutil import rmtree import locale import logging +import os import subprocess +import sys + +from shutil import rmtree +from tempfile import mkdtemp from pelican import Pelican from pelican.generators import StaticGenerator from pelican.settings import read_settings -from pelican.tests.support import LoggedTestCase, mute, locale_available, unittest +from pelican.tests.support import (LoggedTestCase, locale_available, + mute, unittest) CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) SAMPLES_PATH = os.path.abspath(os.path.join( - CURRENT_DIR, os.pardir, os.pardir, 'samples')) + CURRENT_DIR, os.pardir, os.pardir, 'samples')) OUTPUT_PATH = os.path.abspath(os.path.join(CURRENT_DIR, 'output')) INPUT_PATH = os.path.join(SAMPLES_PATH, "content") @@ -27,13 +29,10 @@ SAMPLE_FR_CONFIG = os.path.join(SAMPLES_PATH, "pelican.conf_FR.py") def recursiveDiff(dcmp): diff = { - 'diff_files': [os.path.join(dcmp.right, f) - for f in dcmp.diff_files], - 'left_only': [os.path.join(dcmp.right, f) - for f in dcmp.left_only], - 'right_only': [os.path.join(dcmp.right, f) - for f in dcmp.right_only], - } + 'diff_files': [os.path.join(dcmp.right, f) for f in dcmp.diff_files], + 'left_only': [os.path.join(dcmp.right, f) for f in dcmp.left_only], + 'right_only': [os.path.join(dcmp.right, f) for f in dcmp.right_only], + } for sub_dcmp in dcmp.subdirs.values(): for k, v in recursiveDiff(sub_dcmp).items(): diff[k] += v @@ -60,9 +59,13 @@ class TestPelican(LoggedTestCase): def assertDirsEqual(self, left_path, right_path): out, err = subprocess.Popen( - ['git', 'diff', '--no-ext-diff', '--exit-code', '-w', left_path, right_path], - env={str('PAGER'): str('')}, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ['git', 'diff', '--no-ext-diff', '--exit-code', + '-w', left_path, right_path], + env={str('PAGER'): str('')}, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE ).communicate() + def ignorable_git_crlf_errors(line): # Work around for running tests on Windows for msg in [ @@ -85,9 +88,11 @@ class TestPelican(LoggedTestCase): pelican = Pelican(settings=read_settings(path=None)) generator_classes = pelican.get_generator_classes() - self.assertTrue(generator_classes[-1] is StaticGenerator, + self.assertTrue( + generator_classes[-1] is StaticGenerator, "StaticGenerator must be the last generator, but it isn't!") - self.assertIsInstance(generator_classes, collections.Sequence, + self.assertIsInstance( + generator_classes, collections.Sequence, "get_generator_classes() must return a Sequence to preserve order") def test_basic_generation_works(self): @@ -98,10 +103,11 @@ class TestPelican(LoggedTestCase): 'OUTPUT_PATH': self.temp_path, 'CACHE_PATH': self.temp_cache, 'LOCALE': locale.normalize('en_US'), - }) + }) pelican = Pelican(settings=settings) mute(True)(pelican.run)() - self.assertDirsEqual(self.temp_path, os.path.join(OUTPUT_PATH, 'basic')) + self.assertDirsEqual( + self.temp_path, os.path.join(OUTPUT_PATH, 'basic')) self.assertLogCountEqual( count=3, msg="Unable to find.*skipping url replacement", @@ -114,10 +120,11 @@ class TestPelican(LoggedTestCase): 'OUTPUT_PATH': self.temp_path, 'CACHE_PATH': self.temp_cache, 'LOCALE': locale.normalize('en_US'), - }) + }) pelican = Pelican(settings=settings) mute(True)(pelican.run)() - self.assertDirsEqual(self.temp_path, os.path.join(OUTPUT_PATH, 'custom')) + self.assertDirsEqual( + self.temp_path, os.path.join(OUTPUT_PATH, 'custom')) @unittest.skipUnless(locale_available('fr_FR.UTF-8') or locale_available('French'), 'French locale needed') @@ -133,10 +140,11 @@ class TestPelican(LoggedTestCase): 'OUTPUT_PATH': self.temp_path, 'CACHE_PATH': self.temp_cache, 'LOCALE': our_locale, - }) + }) pelican = Pelican(settings=settings) mute(True)(pelican.run)() - self.assertDirsEqual(self.temp_path, os.path.join(OUTPUT_PATH, 'custom_locale')) + self.assertDirsEqual( + self.temp_path, os.path.join(OUTPUT_PATH, 'custom_locale')) def test_theme_static_paths_copy(self): # the same thing with a specified set of settings should work @@ -146,8 +154,9 @@ class TestPelican(LoggedTestCase): 'CACHE_PATH': self.temp_cache, 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, 'very'), os.path.join(SAMPLES_PATH, 'kinda'), - os.path.join(SAMPLES_PATH, 'theme_standard')] - }) + os.path.join(SAMPLES_PATH, + 'theme_standard')] + }) pelican = Pelican(settings=settings) mute(True)(pelican.run)() theme_output = os.path.join(self.temp_path, 'theme') @@ -165,8 +174,9 @@ class TestPelican(LoggedTestCase): 'PATH': INPUT_PATH, 'OUTPUT_PATH': self.temp_path, 'CACHE_PATH': self.temp_cache, - 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, 'theme_standard')] - }) + 'THEME_STATIC_PATHS': [os.path.join(SAMPLES_PATH, + 'theme_standard')] + }) pelican = Pelican(settings=settings) mute(True)(pelican.run)() @@ -184,9 +194,9 @@ class TestPelican(LoggedTestCase): 'WRITE_SELECTED': [ os.path.join(self.temp_path, 'oh-yeah.html'), os.path.join(self.temp_path, 'categories.html'), - ], + ], 'LOCALE': locale.normalize('en_US'), - }) + }) pelican = Pelican(settings=settings) logger = logging.getLogger() orig_level = logger.getEffectiveLevel() diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index dd7e5fc2..71394ee4 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function +from __future__ import print_function, unicode_literals import os from pelican import readers +from pelican.tests.support import get_settings, unittest from pelican.utils import SafeDatetime -from pelican.tests.support import unittest, get_settings + CUR_DIR = os.path.dirname(__file__) CONTENT_PATH = os.path.join(CUR_DIR, 'content') @@ -29,22 +30,26 @@ class ReaderTest(unittest.TestCase): self.assertEqual( value, real_value, - 'Expected %s to have value %s, but was %s' % (key, value, real_value)) + 'Expected %s to have value %s, but was %s' % + (key, value, real_value)) else: self.fail( - 'Expected %s to have value %s, but was not in Dict' % (key, value)) + 'Expected %s to have value %s, but was not in Dict' % + (key, value)) + class TestAssertDictHasSubset(ReaderTest): def setUp(self): self.dictionary = { - 'key-a' : 'val-a', - 'key-b' : 'val-b'} + 'key-a': 'val-a', + 'key-b': 'val-b' + } def tearDown(self): self.dictionary = None def test_subset(self): - self.assertDictHasSubset(self.dictionary, {'key-a':'val-a'}) + self.assertDictHasSubset(self.dictionary, {'key-a': 'val-a'}) def test_equal(self): self.assertDictHasSubset(self.dictionary, self.dictionary) @@ -54,18 +59,17 @@ class TestAssertDictHasSubset(ReaderTest): AssertionError, 'Expected.*key-c.*to have value.*val-c.*but was not in Dict', self.assertDictHasSubset, - self.dictionary, - {'key-c':'val-c'} - ) + self.dictionary, + {'key-c': 'val-c'}) def test_fail_wrong_val(self): self.assertRaisesRegexp( AssertionError, 'Expected .*key-a.* to have value .*val-b.* but was .*val-a.*', self.assertDictHasSubset, - self.dictionary, - {'key-a':'val-b'} - ) + self.dictionary, + {'key-a': 'val-b'}) + class DefaultReaderTest(ReaderTest): @@ -153,17 +157,17 @@ class RstReaderTest(ReaderTest): '(?P\d{4}-\d{2}-\d{2})' '_(?P.*)' '#(?P.*)-(?P.*)' - ), + ), EXTRA_PATH_METADATA={ input_with_metadata: { 'key-1a': 'value-1a', 'key-1b': 'value-1b' - } } - ) + } + ) expected_metadata = { 'category': 'yeah', - 'author' : 'Alexis Métaireau', + 'author': 'Alexis Métaireau', 'title': 'Rst with filename metadata', 'date': SafeDatetime(2012, 11, 29), 'slug': 'rst_w_filename_meta', @@ -179,38 +183,41 @@ class RstReaderTest(ReaderTest): path=input_file_path_without_metadata, EXTRA_PATH_METADATA={ input_file_path_without_metadata: { - 'author': 'Charlès Overwrite'} + 'author': 'Charlès Overwrite' } - ) + } + ) expected_without_metadata = { - 'category' : 'misc', - 'author' : 'Charlès Overwrite', - 'title' : 'Article title', - 'reader' : 'rst', + 'category': 'misc', + 'author': 'Charlès Overwrite', + 'title': 'Article title', + 'reader': 'rst', } self.assertDictHasSubset( page_without_metadata.metadata, expected_without_metadata) def test_article_extra_path_metadata_dont_overwrite(self): - #EXTRA_PATH_METADATA['author'] should get ignored - #since we don't overwrite already set values + # EXTRA_PATH_METADATA['author'] should get ignored + # since we don't overwrite already set values input_file_path = '2012-11-29_rst_w_filename_meta#foo-bar.rst' page = self.read_file( path=input_file_path, FILENAME_METADATA=( '(?P\d{4}-\d{2}-\d{2})' '_(?P.*)' - '#(?P.*)-(?P.*)'), + '#(?P.*)-(?P.*)' + ), EXTRA_PATH_METADATA={ input_file_path: { 'author': 'Charlès Overwrite', - 'key-1b': 'value-1b'} + 'key-1b': 'value-1b' } - ) + } + ) expected = { 'category': 'yeah', - 'author' : 'Alexis Métaireau', + 'author': 'Alexis Métaireau', 'title': 'Rst with filename metadata', 'date': SafeDatetime(2012, 11, 29), 'slug': 'rst_w_filename_meta', @@ -273,7 +280,7 @@ class RstReaderTest(ReaderTest): # typogrify should be able to ignore user specified tags, # but tries to be clever with widont extension page = self.read_file(path='article.rst', TYPOGRIFY=True, - TYPOGRIFY_IGNORE_TAGS = ['p']) + TYPOGRIFY_IGNORE_TAGS=['p']) expected = ('

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

    \n

    Now with added ' 'support for ' @@ -284,7 +291,7 @@ class RstReaderTest(ReaderTest): # typogrify should ignore code blocks by default because # code blocks are composed inside the pre tag page = self.read_file(path='article_with_code_block.rst', - TYPOGRIFY=True) + TYPOGRIFY=True) expected = ('

    An article with some code

    \n' '
    x'
    @@ -292,13 +299,17 @@ class RstReaderTest(ReaderTest):
                             ' y\n
    \n' '

    A block quote:

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

    Normal:\nx & y

    \n') + '

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

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

    An article with some code

    \n' '
    x'
    @@ -306,7 +317,10 @@ class RstReaderTest(ReaderTest):
                             ' y\n
    \n' '

    A block quote:

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

    Normal:\nx & y

    \n') + '

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

    \n') self.assertEqual(page.content, expected) except ImportError: @@ -339,6 +353,7 @@ class RstReaderTest(ReaderTest): self.assertDictHasSubset(page.metadata, expected) + @unittest.skipUnless(readers.Markdown, "markdown isn't installed") class MdReaderTest(ReaderTest): @@ -400,7 +415,8 @@ class MdReaderTest(ReaderTest): 'modified': SafeDatetime(2012, 11, 1), 'multiline': [ 'Line Metadata should be handle properly.', - 'See syntax of Meta-Data extension of Python Markdown package:', + 'See syntax of Meta-Data extension of ' + 'Python Markdown package:', 'If a line is indented by 4 or more spaces,', 'that line is assumed to be an additional line of the value', 'for the previous keyword.', diff --git a/pelican/tests/test_rstdirectives.py b/pelican/tests/test_rstdirectives.py index 7c5f8adf..f6a7221f 100644 --- a/pelican/tests/test_rstdirectives.py +++ b/pelican/tests/test_rstdirectives.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function +from __future__ import print_function, unicode_literals + +from pelican.tests.support import unittest + try: from unittest.mock import Mock except ImportError: @@ -7,7 +10,7 @@ except ImportError: from mock import Mock except ImportError: Mock = False -from pelican.tests.support import unittest + @unittest.skipUnless(Mock, 'Needs Mock module') class Test_abbr_role(unittest.TestCase): diff --git a/pelican/tests/test_settings.py b/pelican/tests/test_settings.py index 5653d07a..7b1e36df 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -1,13 +1,15 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function -import copy -import os -import locale -from sys import platform -from os.path import dirname, abspath, join +from __future__ import print_function, unicode_literals -from pelican.settings import (read_settings, configure_settings, - DEFAULT_CONFIG, DEFAULT_THEME) +import copy +import locale +import os +from os.path import abspath, dirname, join +from sys import platform + + +from pelican.settings import (DEFAULT_CONFIG, DEFAULT_THEME, + configure_settings, read_settings) from pelican.tests.support import unittest @@ -28,12 +30,14 @@ class TestSettingsConfiguration(unittest.TestCase): def test_overwrite_existing_settings(self): self.assertEqual(self.settings.get('SITENAME'), "Alexis' log") - self.assertEqual(self.settings.get('SITEURL'), - 'http://blog.notmyidea.org') + self.assertEqual( + self.settings.get('SITEURL'), + 'http://blog.notmyidea.org') def test_keep_default_settings(self): # Keep default settings if not defined. - self.assertEqual(self.settings.get('DEFAULT_CATEGORY'), + self.assertEqual( + self.settings.get('DEFAULT_CATEGORY'), DEFAULT_CONFIG['DEFAULT_CATEGORY']) def test_dont_copy_small_keys(self): @@ -69,28 +73,31 @@ class TestSettingsConfiguration(unittest.TestCase): def test_static_path_settings_safety(self): # Disallow static paths from being strings - settings = {'STATIC_PATHS': 'foo/bar', - 'THEME_STATIC_PATHS': 'bar/baz', - # These 4 settings are required to run configure_settings - 'PATH': '.', - 'THEME': DEFAULT_THEME, - 'SITEURL': 'http://blog.notmyidea.org/', - 'LOCALE': '', - } + settings = { + 'STATIC_PATHS': 'foo/bar', + 'THEME_STATIC_PATHS': 'bar/baz', + # These 4 settings are required to run configure_settings + 'PATH': '.', + 'THEME': DEFAULT_THEME, + 'SITEURL': 'http://blog.notmyidea.org/', + 'LOCALE': '', + } configure_settings(settings) - self.assertEqual(settings['STATIC_PATHS'], - DEFAULT_CONFIG['STATIC_PATHS']) - self.assertEqual(settings['THEME_STATIC_PATHS'], - DEFAULT_CONFIG['THEME_STATIC_PATHS']) + self.assertEqual( + settings['STATIC_PATHS'], + DEFAULT_CONFIG['STATIC_PATHS']) + self.assertEqual( + settings['THEME_STATIC_PATHS'], + DEFAULT_CONFIG['THEME_STATIC_PATHS']) def test_configure_settings(self): # Manipulations to settings should be applied correctly. settings = { - 'SITEURL': 'http://blog.notmyidea.org/', - 'LOCALE': '', - 'PATH': os.curdir, - 'THEME': DEFAULT_THEME, - } + 'SITEURL': 'http://blog.notmyidea.org/', + 'LOCALE': '', + 'PATH': os.curdir, + 'THEME': DEFAULT_THEME, + } configure_settings(settings) # SITEURL should not have a trailing slash @@ -154,7 +161,7 @@ class TestSettingsConfiguration(unittest.TestCase): settings['PATH'] = '' self.assertRaises(Exception, configure_settings, settings) - # Test nonexistent THEME + # Test nonexistent THEME settings['PATH'] = os.curdir settings['THEME'] = 'foo' diff --git a/pelican/tests/test_urlwrappers.py b/pelican/tests/test_urlwrappers.py index 20a87114..ae6eaaec 100644 --- a/pelican/tests/test_urlwrappers.py +++ b/pelican/tests/test_urlwrappers.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from pelican.urlwrappers import URLWrapper, Tag, Category from pelican.tests.support import unittest +from pelican.urlwrappers import Category, Tag, URLWrapper + class TestURLWrapper(unittest.TestCase): def test_ordering(self): diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 0f8878af..d6fdf70e 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -1,20 +1,22 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function, absolute_import -import logging -import shutil -import os -import time +from __future__ import absolute_import, print_function, unicode_literals + import locale +import logging +import os +import shutil +import time from sys import platform from tempfile import mkdtemp import pytz -from pelican.generators import TemplatePagesGenerator -from pelican.writers import Writer -from pelican.settings import read_settings from pelican import utils -from pelican.tests.support import get_article, LoggedTestCase, locale_available, unittest +from pelican.generators import TemplatePagesGenerator +from pelican.settings import read_settings +from pelican.tests.support import (LoggedTestCase, get_article, + locale_available, unittest) +from pelican.writers import Writer class TestUtils(LoggedTestCase): @@ -72,7 +74,7 @@ class TestUtils(LoggedTestCase): '2012-11-22T22:11:10Z': date_hour_sec_z, '2012-11-22T22:11:10-0500': date_hour_sec_est, '2012-11-22T22:11:10.123Z': date_hour_sec_frac_z, - } + } # examples from http://www.w3.org/TR/NOTE-datetime iso_8601_date = utils.SafeDatetime(year=1997, month=7, day=16) @@ -95,7 +97,6 @@ class TestUtils(LoggedTestCase): # invalid ones invalid_dates = ['2010-110-12', 'yay'] - for value, expected in dates.items(): self.assertEqual(utils.get_date(value), expected, value) @@ -290,7 +291,9 @@ class TestUtils(LoggedTestCase): self.assertEqual(utils.strftime(d, '%d/%m/%Y'), '29/08/2012') # RFC 3339 - self.assertEqual(utils.strftime(d, '%Y-%m-%dT%H:%M:%SZ'),'2012-08-29T00:00:00Z') + self.assertEqual( + utils.strftime(d, '%Y-%m-%dT%H:%M:%SZ'), + '2012-08-29T00:00:00Z') # % escaped self.assertEqual(utils.strftime(d, '%d%%%m%%%y'), '29%08%12') @@ -306,8 +309,9 @@ class TestUtils(LoggedTestCase): 'Published in 29-08-2012') # with non-ascii text - self.assertEqual(utils.strftime(d, '%d/%m/%Y Øl trinken beim Besäufnis'), - '29/08/2012 Øl trinken beim Besäufnis') + self.assertEqual( + utils.strftime(d, '%d/%m/%Y Øl trinken beim Besäufnis'), + '29/08/2012 Øl trinken beim Besäufnis') # alternative formatting options self.assertEqual(utils.strftime(d, '%-d/%-m/%y'), '29/8/12') @@ -316,7 +320,6 @@ class TestUtils(LoggedTestCase): d = utils.SafeDatetime(2012, 8, 9) self.assertEqual(utils.strftime(d, '%-d/%-m/%y'), '9/8/12') - # test the output of utils.strftime in a different locale # Turkish locale @unittest.skipUnless(locale_available('tr_TR.UTF-8') or @@ -339,17 +342,18 @@ class TestUtils(LoggedTestCase): 'Çarşamba, 29 Ağustos 2012') # with text - self.assertEqual(utils.strftime(d, 'Yayınlanma tarihi: %A, %d %B %Y'), + self.assertEqual( + utils.strftime(d, 'Yayınlanma tarihi: %A, %d %B %Y'), 'Yayınlanma tarihi: Çarşamba, 29 Ağustos 2012') # non-ascii format candidate (someone might pass it... for some reason) - self.assertEqual(utils.strftime(d, '%Y yılında %üretim artışı'), + self.assertEqual( + utils.strftime(d, '%Y yılında %üretim artışı'), '2012 yılında %üretim artışı') # restore locale back locale.setlocale(locale.LC_ALL, old_locale) - # test the output of utils.strftime in a different locale # French locale @unittest.skipUnless(locale_available('fr_FR.UTF-8') or @@ -373,21 +377,28 @@ class TestUtils(LoggedTestCase): self.assertTrue(utils.strftime(d, '%A') in ('mercredi', 'Mercredi')) # with text - self.assertEqual(utils.strftime(d, 'Écrit le %d %B %Y'), + self.assertEqual( + utils.strftime(d, 'Écrit le %d %B %Y'), 'Écrit le 29 août 2012') # non-ascii format candidate (someone might pass it... for some reason) - self.assertEqual(utils.strftime(d, '%écrits en %Y'), + self.assertEqual( + utils.strftime(d, '%écrits en %Y'), '%écrits en 2012') # restore locale back locale.setlocale(locale.LC_ALL, old_locale) - def test_maybe_pluralize(self): - self.assertEqual(utils.maybe_pluralize(0, 'Article', 'Articles'), '0 Articles') - self.assertEqual(utils.maybe_pluralize(1, 'Article', 'Articles'), '1 Article') - self.assertEqual(utils.maybe_pluralize(2, 'Article', 'Articles'), '2 Articles') + self.assertEqual( + utils.maybe_pluralize(0, 'Article', 'Articles'), + '0 Articles') + self.assertEqual( + utils.maybe_pluralize(1, 'Article', 'Articles'), + '1 Article') + self.assertEqual( + utils.maybe_pluralize(2, 'Article', 'Articles'), + '2 Articles') class TestCopy(unittest.TestCase): @@ -435,8 +446,9 @@ class TestCopy(unittest.TestCase): def test_copy_file_create_dirs(self): self._create_file('a.txt') - utils.copy(os.path.join(self.root_dir, 'a.txt'), - os.path.join(self.root_dir, 'b0', 'b1', 'b2', 'b3', 'b.txt')) + utils.copy( + os.path.join(self.root_dir, 'a.txt'), + os.path.join(self.root_dir, 'b0', 'b1', 'b2', 'b3', 'b.txt')) self._exist_dir('b0') self._exist_dir('b0', 'b1') self._exist_dir('b0', 'b1', 'b2') @@ -491,35 +503,39 @@ class TestDateFormatter(unittest.TestCase): template_file.write('date = {{ date|strftime("%A, %d %B %Y") }}') self.date = utils.SafeDatetime(2012, 8, 29) - def tearDown(self): shutil.rmtree(self.temp_content) shutil.rmtree(self.temp_output) # reset locale to default locale.setlocale(locale.LC_ALL, '') - @unittest.skipUnless(locale_available('fr_FR.UTF-8') or locale_available('French'), 'French locale needed') def test_french_strftime(self): - # This test tries to reproduce an issue that occurred with python3.3 under macos10 only + # This test tries to reproduce an issue that + # occurred with python3.3 under macos10 only if platform == 'win32': locale.setlocale(locale.LC_ALL, str('French')) else: locale.setlocale(locale.LC_ALL, str('fr_FR.UTF-8')) - date = utils.SafeDatetime(2014,8,14) - # we compare the lower() dates since macos10 returns "Jeudi" for %A whereas linux reports "jeudi" - self.assertEqual( u'jeudi, 14 août 2014', utils.strftime(date, date_format="%A, %d %B %Y").lower() ) + date = utils.SafeDatetime(2014, 8, 14) + # we compare the lower() dates since macos10 returns + # "Jeudi" for %A whereas linux reports "jeudi" + self.assertEqual( + u'jeudi, 14 août 2014', + utils.strftime(date, date_format="%A, %d %B %Y").lower()) df = utils.DateFormatter() - self.assertEqual( u'jeudi, 14 août 2014', df(date, date_format="%A, %d %B %Y").lower() ) + self.assertEqual( + u'jeudi, 14 août 2014', + df(date, date_format="%A, %d %B %Y").lower()) # Let us now set the global locale to C: locale.setlocale(locale.LC_ALL, str('C')) - # DateFormatter should still work as expected since it is the whole point of DateFormatter + # DateFormatter should still work as expected + # since it is the whole point of DateFormatter # (This is where pre-2014/4/15 code fails on macos10) df_date = df(date, date_format="%A, %d %B %Y").lower() - self.assertEqual( u'jeudi, 14 août 2014', df_date ) - + self.assertEqual(u'jeudi, 14 août 2014', df_date) @unittest.skipUnless(locale_available('fr_FR.UTF-8') or locale_available('French'), @@ -530,9 +546,12 @@ class TestDateFormatter(unittest.TestCase): else: locale_string = 'fr_FR.UTF-8' settings = read_settings( - override = {'LOCALE': locale_string, - 'TEMPLATE_PAGES': {'template/source.html': - 'generated/file.html'}}) + override={ + 'LOCALE': locale_string, + 'TEMPLATE_PAGES': { + 'template/source.html': 'generated/file.html' + } + }) generator = TemplatePagesGenerator( {'date': self.date}, settings, @@ -543,7 +562,7 @@ class TestDateFormatter(unittest.TestCase): generator.generate_output(writer) output_path = os.path.join( - self.temp_output, 'generated', 'file.html') + self.temp_output, 'generated', 'file.html') # output file has been generated self.assertTrue(os.path.exists(output_path)) @@ -553,7 +572,6 @@ class TestDateFormatter(unittest.TestCase): self.assertEqual(output_file, utils.strftime(self.date, 'date = %A, %d %B %Y')) - @unittest.skipUnless(locale_available('tr_TR.UTF-8') or locale_available('Turkish'), 'Turkish locale needed') @@ -563,9 +581,12 @@ class TestDateFormatter(unittest.TestCase): else: locale_string = 'tr_TR.UTF-8' settings = read_settings( - override = {'LOCALE': locale_string, - 'TEMPLATE_PAGES': {'template/source.html': - 'generated/file.html'}}) + override={ + 'LOCALE': locale_string, + 'TEMPLATE_PAGES': { + 'template/source.html': 'generated/file.html' + } + }) generator = TemplatePagesGenerator( {'date': self.date}, settings, @@ -576,7 +597,7 @@ class TestDateFormatter(unittest.TestCase): generator.generate_output(writer) output_path = os.path.join( - self.temp_output, 'generated', 'file.html') + self.temp_output, 'generated', 'file.html') # output file has been generated self.assertTrue(os.path.exists(output_path)) diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 92e8c919..f8abbd7a 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -1,29 +1,30 @@ #!/usr/bin/env python - # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function +from __future__ import print_function, unicode_literals + import argparse -try: - from html import unescape # py3.4+ -except ImportError: - from six.moves.html_parser import HTMLParser - unescape = HTMLParser().unescape +import logging import os import re import subprocess import sys import time -import logging from codecs import open + from six.moves.urllib.error import URLError from six.moves.urllib.parse import urlparse from six.moves.urllib.request import urlretrieve -# 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.utils import slugify, SafeDatetime +from pelican.utils import SafeDatetime, slugify + +try: + from html import unescape # py3.4+ +except ImportError: + from six.moves.html_parser import HTMLParser + unescape = HTMLParser().unescape logger = logging.getLogger(__name__) @@ -70,12 +71,19 @@ def decode_wp_content(content, br=True): content = "" for p in pgraphs: content = content + "

    " + p.strip() + "

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

    \s*

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

    ([^<]+)', "

    \\1

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

    ([^<]+)', + "

    \\1

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

    \s*(]*>)\s*

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

    \s*(]*>)\s*

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

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

    ]*)>', "

    ", content) content = content.replace('

    ', '

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

    ', "

    ", content) if pre_tags: @@ -100,13 +116,14 @@ def decode_wp_content(content, br=True): return content + def get_items(xml): """Opens a WordPress xml file and returns a list of items""" try: from bs4 import BeautifulSoup except ImportError: - error = ('Missing dependency ' - '"BeautifulSoup4" and "lxml" required to import WordPress XML files.') + error = ('Missing dependency "BeautifulSoup4" and "lxml" required to ' + 'import WordPress XML files.') sys.exit(error) with open(xml, encoding='utf-8') as infile: xmlfile = infile.read() @@ -114,12 +131,14 @@ def get_items(xml): items = soup.rss.channel.findAll('item') return items + def get_filename(filename, post_id): if filename is not None: return filename else: return post_id + def wp2fields(xml, wp_custpost=False): """Opens a wordpress XML file, and yield Pelican fields""" @@ -141,16 +160,18 @@ def wp2fields(xml, wp_custpost=False): content = item.find('encoded').string raw_date = item.find('post_date').string - date_object = time.strptime(raw_date, "%Y-%m-%d %H:%M:%S") - date = time.strftime("%Y-%m-%d %H:%M", date_object) + date_object = time.strptime(raw_date, '%Y-%m-%d %H:%M:%S') + date = time.strftime('%Y-%m-%d %H:%M', date_object) author = item.find('creator').string - categories = [cat.string for cat in item.findAll('category', {'domain' : 'category'})] - # caturl = [cat['nicename'] for cat in item.find(domain='category')] + categories = [cat.string for cat + in item.findAll('category', {'domain': 'category'})] - tags = [tag.string for tag in item.findAll('category', {'domain' : 'post_tag'})] + tags = [tag.string for tag + in item.findAll('category', {'domain': 'post_tag'})] # To publish a post the status should be 'published' - status = 'published' if item.find('status').string == "publish" else item.find('status').string + status = 'published' if item.find('status').string == "publish" \ + else item.find('status').string kind = 'article' post_type = item.find('post_type').string @@ -159,16 +180,17 @@ def wp2fields(xml, wp_custpost=False): elif wp_custpost: if post_type == 'post': pass - # Old behaviour was to name everything not a page as an article. - # Theoretically all attachments have status == inherit so - # no attachments should be here. But this statement is to + # Old behaviour was to name everything not a page as an + # article.Theoretically all attachments have status == inherit + # so no attachments should be here. But this statement is to # maintain existing behaviour in case that doesn't hold true. elif post_type == 'attachment': pass else: kind = post_type - yield (title, content, filename, date, author, categories, tags, status, - kind, "wp-html") + yield (title, content, filename, date, author, categories, + tags, status, kind, 'wp-html') + def dc2fields(file): """Opens a Dotclear export file, and yield pelican fields""" @@ -176,10 +198,10 @@ def dc2fields(file): from bs4 import BeautifulSoup except ImportError: error = ('Missing dependency ' - '"BeautifulSoup4" and "lxml" required to import Dotclear files.') + '"BeautifulSoup4" and "lxml" required ' + 'to import Dotclear files.') sys.exit(error) - in_cat = False in_post = False category_list = {} @@ -203,7 +225,7 @@ def dc2fields(file): # remove 1st and last "" fields[0] = fields[0][1:] # fields[-1] = fields[-1][:-1] - category_list[fields[0]]=fields[2] + category_list[fields[0]] = fields[2] elif in_post: if not line: in_post = False @@ -249,45 +271,50 @@ def dc2fields(file): # remove seconds post_creadt = ':'.join(post_creadt.split(':')[0:2]) - author = "" + author = '' categories = [] tags = [] if cat_id: - categories = [category_list[id].strip() for id in cat_id.split(',')] + categories = [category_list[id].strip() for id + in cat_id.split(',')] # Get tags related to a post - tag = post_meta.replace('{', '').replace('}', '').replace('a:1:s:3:\\"tag\\";a:', '').replace('a:0:', '') + tag = (post_meta.replace('{', '') + .replace('}', '') + .replace('a:1:s:3:\\"tag\\";a:', '') + .replace('a:0:', '')) if len(tag) > 1: if int(tag[:1]) == 1: newtag = tag.split('"')[1] tags.append( BeautifulSoup( - newtag - , "xml" + newtag, + 'xml' ) # bs4 always outputs UTF-8 .decode('utf-8') ) else: - i=1 - j=1 + i = 1 + j = 1 while(i <= int(tag[:1])): - newtag = tag.split('"')[j].replace('\\','') + newtag = tag.split('"')[j].replace('\\', '') tags.append( BeautifulSoup( - newtag - , "xml" + newtag, + 'xml' ) # bs4 always outputs UTF-8 .decode('utf-8') ) - i=i+1 - if j < int(tag[:1])*2: - j=j+2 + i = i + 1 + if j < int(tag[:1]) * 2: + j = j + 2 """ - dotclear2 does not use markdown by default unless you use the markdown plugin + dotclear2 does not use markdown by default unless + you use the markdown plugin Ref: http://plugins.dotaddict.org/dc2/details/formatting-markdown """ if post_format == "markdown": @@ -322,12 +349,13 @@ def posterous2fields(api_token, email, password): # py2 import import urllib2 as urllib_request - - def get_posterous_posts(api_token, email, password, page = 1): - base64string = base64.encodestring(("%s:%s" % (email, password)).encode('utf-8')).replace(b'\n', b'') - url = "http://posterous.com/api/v2/users/me/sites/primary/posts?api_token=%s&page=%d" % (api_token, page) + def get_posterous_posts(api_token, email, password, page=1): + base64string = base64.encodestring( + ("%s:%s" % (email, password)).encode('utf-8')).replace('\n', '') + url = ("http://posterous.com/api/v2/users/me/sites/primary/" + "posts?api_token=%s&page=%d") % (api_token, page) request = urllib_request.Request(url) - request.add_header("Authorization", "Basic %s" % base64string.decode()) + request.add_header('Authorization', 'Basic %s' % base64string.decode()) handle = urllib_request.urlopen(request) posts = json.loads(handle.read().decode('utf-8')) return posts @@ -344,16 +372,18 @@ def posterous2fields(api_token, email, password): slug = slugify(post.get('title')) tags = [tag.get('name') for tag in post.get('tags')] raw_date = post.get('display_date') - date_object = SafeDatetime.strptime(raw_date[:-6], "%Y/%m/%d %H:%M:%S") + date_object = SafeDatetime.strptime( + raw_date[:-6], '%Y/%m/%d %H:%M:%S') offset = int(raw_date[-5:]) - delta = timedelta(hours = offset / 100) + delta = timedelta(hours=(offset / 100)) date_object -= delta - date = date_object.strftime("%Y-%m-%d %H:%M") - kind = 'article' # TODO: Recognise pages + date = date_object.strftime('%Y-%m-%d %H:%M') + kind = 'article' # TODO: Recognise pages status = 'published' # TODO: Find a way for draft posts - yield (post.get('title'), post.get('body_cleaned'), slug, date, - post.get('user').get('display_name'), [], tags, status, kind, "html") + yield (post.get('title'), post.get('body_cleaned'), + slug, date, post.get('user').get('display_name'), + [], tags, status, kind, 'html') def tumblr2fields(api_key, blogname): @@ -374,7 +404,9 @@ def tumblr2fields(api_key, blogname): import urllib2 as urllib_request def get_tumblr_posts(api_key, blogname, offset=0): - url = "http://api.tumblr.com/v2/blog/%s.tumblr.com/posts?api_key=%s&offset=%d&filter=raw" % (blogname, api_key, offset) + url = ("http://api.tumblr.com/v2/blog/%s.tumblr.com/" + "posts?api_key=%s&offset=%d&filter=raw") % ( + blogname, api_key, offset) request = urllib_request.Request(url) handle = urllib_request.urlopen(request) posts = json.loads(handle.read().decode('utf-8')) @@ -384,7 +416,10 @@ def tumblr2fields(api_key, blogname): posts = get_tumblr_posts(api_key, blogname, offset) while len(posts) > 0: for post in posts: - title = post.get('title') or post.get('source_title') or post.get('type').capitalize() + title = \ + post.get('title') or \ + post.get('source_title') or \ + post.get('type').capitalize() slug = post.get('slug') or slugify(title) tags = post.get('tags') timestamp = post.get('timestamp') @@ -398,7 +433,11 @@ def tumblr2fields(api_key, blogname): fmtstr = '![%s](%s)' else: fmtstr = '%s' - content = '\n'.join(fmtstr % (photo.get('caption'), photo.get('original_size').get('url')) for photo in post.get('photos')) + content = '' + for photo in post.get('photos'): + content += '\n'.join( + fmtstr % (photo.get('caption'), + photo.get('original_size').get('url'))) content += '\n\n' + post.get('caption') elif type == 'quote': if format == 'markdown': @@ -417,16 +456,29 @@ def tumblr2fields(api_key, blogname): fmtstr = '[via](%s)\n\n' else: fmtstr = '

    via

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

    via

    \n' - content = fmtstr % post.get('source_url') + post.get('caption') + '\n'.join(player.get('embed_code') for player in post.get('player')) + source = fmtstr % post.get('source_url') + caption = post.get('caption') + players = '\n'.join(player.get('embed_code') + for player in post.get('player')) + content = source + caption + players elif type == 'answer': title = post.get('question') - content = '

    %s: %s

    \n%s' % (post.get('asking_name'), post.get('asking_url'), post.get('question'), post.get('answer')) + content = ('

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

    \n' + ' %s' % (post.get('asking_name'), + post.get('asking_url'), + post.get('question'), + post.get('answer'))) content = content.rstrip() + '\n' kind = 'article' @@ -438,25 +490,30 @@ def tumblr2fields(api_key, blogname): offset += len(posts) posts = get_tumblr_posts(api_key, blogname, offset) + def feed2fields(file): """Read a feed and yield pelican fields""" import feedparser d = feedparser.parse(file) for entry in d.entries: - date = (time.strftime("%Y-%m-%d %H:%M", entry.updated_parsed) - if hasattr(entry, "updated_parsed") else None) - author = entry.author if hasattr(entry, "author") else None - tags = [e['term'] for e in entry.tags] if hasattr(entry, "tags") else None + date = (time.strftime('%Y-%m-%d %H:%M', entry.updated_parsed) + if hasattr(entry, 'updated_parsed') else None) + author = entry.author if hasattr(entry, 'author') else None + tags = ([e['term'] for e in entry.tags] + if hasattr(entry, 'tags') else None) slug = slugify(entry.title) kind = 'article' - yield (entry.title, entry.description, slug, date, author, [], tags, None, - kind, "html") + yield (entry.title, entry.description, slug, date, + author, [], tags, None, kind, 'html') + + +def build_header(title, date, author, categories, tags, slug, + status=None, attachments=None): + """Build a header from a list of fields""" -def build_header(title, date, author, categories, tags, slug, status=None, attachments=None): from docutils.utils import column_width - """Build a header from a list of fields""" header = '%s\n%s\n' % (title, '#' * column_width(title)) if date: header += ':date: %s\n' % date @@ -475,8 +532,9 @@ def build_header(title, date, author, categories, tags, slug, status=None, attac header += '\n' return header -def build_markdown_header(title, date, author, categories, tags, slug, status=None, - attachments=None): + +def build_markdown_header(title, date, author, categories, tags, + slug, status=None, attachments=None): """Build a header from a list of fields""" header = 'Title: %s\n' % title if date: @@ -496,6 +554,7 @@ def build_markdown_header(title, date, author, categories, tags, slug, status=No header += '\n' return header + def get_ext(out_markup, in_markup='html'): if in_markup == 'markdown' or out_markup == 'markdown': ext = '.md' @@ -503,26 +562,27 @@ def get_ext(out_markup, in_markup='html'): ext = '.rst' return ext + def get_out_filename(output_path, filename, ext, kind, - dirpage, dircat, categories, wp_custpost): + dirpage, dircat, categories, wp_custpost): filename = os.path.basename(filename) # Enforce filename restrictions for various filesystems at once; see # http://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words # we do not need to filter words because an extension will be appended - filename = re.sub(r'[<>:"/\\|?*^% ]', '-', filename) # invalid chars - filename = filename.lstrip('.') # should not start with a dot + filename = re.sub(r'[<>:"/\\|?*^% ]', '-', filename) # invalid chars + filename = filename.lstrip('.') # should not start with a dot if not filename: filename = '_' - filename = filename[:249] # allow for 5 extra characters + filename = filename[:249] # allow for 5 extra characters - out_filename = os.path.join(output_path, filename+ext) + out_filename = os.path.join(output_path, filename + ext) # option to put page posts in pages/ subdirectory if dirpage and kind == 'page': pages_dir = os.path.join(output_path, 'pages') if not os.path.isdir(pages_dir): os.mkdir(pages_dir) - out_filename = os.path.join(pages_dir, filename+ext) + out_filename = os.path.join(pages_dir, filename + ext) elif not dirpage and kind == 'page': pass # option to put wp custom post types in directories with post type @@ -539,18 +599,19 @@ def get_out_filename(output_path, filename, ext, kind, else: catname = '' out_filename = os.path.join(output_path, typename, - catname, filename+ext) + catname, filename + ext) if not os.path.isdir(os.path.join(output_path, typename, catname)): os.makedirs(os.path.join(output_path, typename, catname)) # option to put files in directories with categories names elif dircat and (len(categories) > 0): catname = slugify(categories[0]) - out_filename = os.path.join(output_path, catname, filename+ext) + out_filename = os.path.join(output_path, catname, filename + ext) if not os.path.isdir(os.path.join(output_path, catname)): os.mkdir(os.path.join(output_path, catname)) return out_filename + def get_attachments(xml): """returns a dictionary of posts that have attachments with a list of the attachment_urls @@ -566,7 +627,7 @@ def get_attachments(xml): if kind == 'attachment': attachments.append((item.find('post_parent').string, - item.find('attachment_url').string)) + item.find('attachment_url').string)) else: filename = get_filename(filename, post_id) names[post_id] = filename @@ -575,7 +636,7 @@ def get_attachments(xml): try: parent_name = names[parent] except KeyError: - #attachment's parent is not a valid post + # attachment's parent is not a valid post parent_name = None try: @@ -585,6 +646,7 @@ def get_attachments(xml): attachedposts[parent_name].append(url) return attachedposts + def download_attachments(output_path, urls): """Downloads WordPress attachments and returns a list of paths to attachments that can be associated with a post (relative path to output @@ -592,8 +654,8 @@ def download_attachments(output_path, urls): locations = [] for url in urls: path = urlparse(url).path - #teardown path and rebuild to negate any errors with - #os.path.join and leading /'s + # teardown path and rebuild to negate any errors with + # os.path.join and leading /'s path = path.split('/') filename = path.pop(-1) localpath = '' @@ -608,12 +670,13 @@ def download_attachments(output_path, urls): urlretrieve(url, os.path.join(full_path, filename)) locations.append(os.path.join(localpath, filename)) except (URLError, IOError) as e: - #Python 2.7 throws an IOError rather Than URLError + # Python 2.7 throws an IOError rather Than URLError logger.warning("No file could be downloaded from %s\n%s", url, e) return locations -def fields2pelican(fields, out_markup, output_path, +def fields2pelican( + fields, out_markup, output_path, dircat=False, strip_raw=False, disable_slugs=False, dirpage=False, filename_template=None, filter_author=None, wp_custpost=False, wp_attach=False, attachments=None): @@ -634,24 +697,26 @@ def fields2pelican(fields, out_markup, output_path, ext = get_ext(out_markup, in_markup) if ext == '.md': - header = build_markdown_header(title, date, author, categories, - tags, slug, status, attached_files) + header = build_markdown_header( + title, date, author, categories, tags, slug, + status, attached_files) else: - out_markup = "rst" + out_markup = 'rst' header = build_header(title, date, author, categories, - tags, slug, status, attached_files) + tags, slug, status, attached_files) - out_filename = get_out_filename(output_path, filename, ext, - kind, dirpage, dircat, categories, wp_custpost) + out_filename = get_out_filename( + output_path, filename, ext, kind, dirpage, dircat, + categories, wp_custpost) print(out_filename) - if in_markup in ("html", "wp-html"): - html_filename = os.path.join(output_path, filename+'.html') + if in_markup in ('html', 'wp-html'): + html_filename = os.path.join(output_path, filename + '.html') with open(html_filename, 'w', encoding='utf-8') as fp: # Replace newlines with paragraphs wrapped with

    so # HTML is valid before conversion - if in_markup == "wp-html": + if in_markup == 'wp-html': new_content = decode_wp_content(content) else: paragraphs = content.splitlines() @@ -660,79 +725,95 @@ def fields2pelican(fields, out_markup, output_path, fp.write(new_content) - parse_raw = '--parse-raw' if not strip_raw else '' cmd = ('pandoc --normalize {0} --from=html' - ' --to={1} -o "{2}" "{3}"').format( - parse_raw, out_markup, out_filename, html_filename) + ' --to={1} -o "{2}" "{3}"') + cmd = cmd.format(parse_raw, out_markup, + out_filename, html_filename) try: rc = subprocess.call(cmd, shell=True) if rc < 0: - error = "Child was terminated by signal %d" % -rc + error = 'Child was terminated by signal %d' % -rc exit(error) elif rc > 0: - error = "Please, check your Pandoc installation." + error = 'Please, check your Pandoc installation.' exit(error) except OSError as e: - error = "Pandoc execution failed: %s" % e + error = 'Pandoc execution failed: %s' % e exit(error) os.remove(html_filename) with open(out_filename, 'r', encoding='utf-8') as fs: content = fs.read() - if out_markup == "markdown": - # In markdown, to insert a
    , end a line with two or more spaces & then a end-of-line - content = content.replace("\\\n ", " \n") - content = content.replace("\\\n", " \n") + if out_markup == 'markdown': + # In markdown, to insert a
    , end a line with two + # or more spaces & then a end-of-line + content = content.replace('\\\n ', ' \n') + content = content.replace('\\\n', ' \n') with open(out_filename, 'w', encoding='utf-8') as fs: fs.write(header + content) if wp_attach and attachments and None in attachments: print("downloading attachments that don't have a parent post") urls = attachments[None] - orphan_galleries = download_attachments(output_path, urls) + download_attachments(output_path, urls) + def main(): parser = argparse.ArgumentParser( - description="Transform feed, WordPress, Tumblr, Dotclear, or Posterous " - "files into reST (rst) or Markdown (md) files. Be sure to " - "have pandoc installed.", + description="Transform feed, WordPress, Tumblr, Dotclear, or " + "Posterous files into reST (rst) or Markdown (md) files. " + "Be sure to have pandoc installed.", formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument(dest='input', help='The input file to read') - parser.add_argument('--wpfile', action='store_true', dest='wpfile', + parser.add_argument( + dest='input', help='The input file to read') + parser.add_argument( + '--wpfile', action='store_true', dest='wpfile', help='Wordpress XML export') - parser.add_argument('--dotclear', action='store_true', dest='dotclear', + parser.add_argument( + '--dotclear', action='store_true', dest='dotclear', help='Dotclear export') - parser.add_argument('--posterous', action='store_true', dest='posterous', + parser.add_argument( + '--posterous', action='store_true', dest='posterous', help='Posterous export') - parser.add_argument('--tumblr', action='store_true', dest='tumblr', + parser.add_argument( + '--tumblr', action='store_true', dest='tumblr', help='Tumblr export') - parser.add_argument('--feed', action='store_true', dest='feed', + parser.add_argument( + '--feed', action='store_true', dest='feed', help='Feed to parse') - parser.add_argument('-o', '--output', dest='output', default='output', + parser.add_argument( + '-o', '--output', dest='output', default='output', help='Output path') - parser.add_argument('-m', '--markup', dest='markup', default='rst', + parser.add_argument( + '-m', '--markup', dest='markup', default='rst', help='Output markup format (supports rst & markdown)') - parser.add_argument('--dir-cat', action='store_true', dest='dircat', + parser.add_argument( + '--dir-cat', action='store_true', dest='dircat', help='Put files in directories with categories name') - parser.add_argument('--dir-page', action='store_true', dest='dirpage', + parser.add_argument( + '--dir-page', action='store_true', dest='dirpage', help=('Put files recognised as pages in "pages/" sub-directory' ' (wordpress import only)')) - parser.add_argument('--filter-author', dest='author', + parser.add_argument( + '--filter-author', dest='author', help='Import only post from the specified author') - parser.add_argument('--strip-raw', action='store_true', dest='strip_raw', + parser.add_argument( + '--strip-raw', action='store_true', dest='strip_raw', help="Strip raw HTML code that can't be converted to " "markup such as flash embeds or iframes (wordpress import only)") - parser.add_argument('--wp-custpost', action='store_true', + parser.add_argument( + '--wp-custpost', action='store_true', dest='wp_custpost', help='Put wordpress custom post types in directories. If used with ' '--dir-cat option directories will be created as ' '/post_type/category/ (wordpress import only)') - parser.add_argument('--wp-attach', action='store_true', dest='wp_attach', + parser.add_argument( + '--wp-attach', action='store_true', dest='wp_attach', help='(wordpress import only) Download files uploaded to wordpress as ' 'attachments. Files will be added to posts as a list in the post ' 'header. All files will be downloaded, even if ' @@ -740,16 +821,20 @@ def main(): 'with their original path inside the output directory. ' 'e.g. output/wp-uploads/date/postname/file.jpg ' '-- Requires an internet connection --') - parser.add_argument('--disable-slugs', action='store_true', + parser.add_argument( + '--disable-slugs', action='store_true', dest='disable_slugs', help='Disable storing slugs from imported posts within output. ' 'With this disabled, your Pelican URLs may not be consistent ' 'with your original posts.') - parser.add_argument('-e', '--email', dest='email', + parser.add_argument( + '-e', '--email', dest='email', help="Email address (posterous import only)") - parser.add_argument('-p', '--password', dest='password', + parser.add_argument( + '-p', '--password', dest='password', help="Password (posterous import only)") - parser.add_argument('-b', '--blogname', dest='blogname', + parser.add_argument( + '-b', '--blogname', dest='blogname', help="Blog name (Tumblr import only)") args = parser.parse_args() @@ -766,18 +851,20 @@ def main(): elif args.feed: input_type = 'feed' else: - error = "You must provide either --wpfile, --dotclear, --posterous, --tumblr or --feed options" + error = ('You must provide either --wpfile, --dotclear, ' + '--posterous, --tumblr or --feed options') exit(error) if not os.path.exists(args.output): try: os.mkdir(args.output) except OSError: - error = "Unable to create the output folder: " + args.output + error = 'Unable to create the output folder: ' + args.output exit(error) if args.wp_attach and input_type != 'wordpress': - error = "You must be importing a wordpress xml to use the --wp-attach option" + error = ('You must be importing a wordpress xml ' + 'to use the --wp-attach option') exit(error) if input_type == 'wordpress': @@ -796,14 +883,14 @@ def main(): else: attachments = None - init() # init logging - + # init logging + init() fields2pelican(fields, args.markup, args.output, dircat=args.dircat or False, dirpage=args.dirpage or False, strip_raw=args.strip_raw or False, disable_slugs=args.disable_slugs or False, filter_author=args.author, - wp_custpost = args.wp_custpost or False, - wp_attach = args.wp_attach or False, - attachments = attachments or None) + wp_custpost=args.wp_custpost or False, + wp_attach=args.wp_attach or False, + attachments=attachments or None) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 58da4649..e6ccf2a8 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -1,18 +1,20 @@ #!/usr/bin/env python - # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function -import six +from __future__ import print_function, unicode_literals +import argparse +import codecs import os import string -import argparse import sys -import codecs + import pytz +import six + from pelican import __version__ + _TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates") @@ -44,9 +46,10 @@ CONF = { 'timezone': 'Europe/Paris' } -#url for list of valid timezones +# url for list of valid timezones _TZ_URL = 'http://en.wikipedia.org/wiki/List_of_tz_database_time_zones' + def _input_compat(prompt): if six.PY3: r = input(prompt) @@ -59,6 +62,7 @@ if six.PY3: else: str_compat = unicode + # Create a 'marked' default path, to determine if someone has supplied # a path on the command-line. class _DEFAULT_PATH_TYPE(str_compat): @@ -66,6 +70,7 @@ class _DEFAULT_PATH_TYPE(str_compat): _DEFAULT_PATH = _DEFAULT_PATH_TYPE(os.curdir) + def decoding_strings(f): def wrapper(*args, **kwargs): out = f(*args, **kwargs) @@ -164,7 +169,8 @@ def ask(question, answer=str_compat, default=None, l=None): print('You must enter an integer') return r else: - raise NotImplemented('Argument `answer` must be str_compat, bool, or integer') + raise NotImplemented( + 'Argument `answer` must be str_compat, bool, or integer') def ask_timezone(question, default, tzurl): @@ -177,7 +183,8 @@ def ask_timezone(question, default, tzurl): r = pytz.all_timezones[lower_tz.index(r)] break else: - print('Please enter a valid time zone:\n (check [{0}])'.format(tzurl)) + print('Please enter a valid time zone:\n' + ' (check [{0}])'.format(tzurl)) return r @@ -186,13 +193,13 @@ def main(): description="A kickstarter for Pelican", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('-p', '--path', default=_DEFAULT_PATH, - help="The path to generate the blog into") + help="The path to generate the blog into") parser.add_argument('-t', '--title', metavar="title", - help='Set the title of the website') + help='Set the title of the website') parser.add_argument('-a', '--author', metavar="author", - help='Set the author name of the website') + help='Set the author name of the website') parser.add_argument('-l', '--lang', metavar="lang", - help='Set the default web site language') + help='Set the default web site language') args = parser.parse_args() @@ -214,50 +221,94 @@ needed by Pelican. 'Will save to:\n%s\n' % CONF['basedir']) else: CONF['basedir'] = os.path.abspath(os.path.expanduser( - ask('Where do you want to create your new web site?', answer=str_compat, default=args.path))) + ask('Where do you want to create your new web site?', + answer=str_compat, default=args.path))) - CONF['sitename'] = ask('What will be the title of this web site?', answer=str_compat, default=args.title) - CONF['author'] = ask('Who will be the author of this web site?', answer=str_compat, default=args.author) - CONF['lang'] = ask('What will be the default language of this web site?', str_compat, args.lang or CONF['lang'], 2) + CONF['sitename'] = ask('What will be the title of this web site?', + answer=str_compat, default=args.title) + CONF['author'] = ask('Who will be the author of this web site?', + answer=str_compat, default=args.author) + CONF['lang'] = ask('What will be the default language of this web site?', + str_compat, args.lang or CONF['lang'], 2) - if ask('Do you want to specify a URL prefix? e.g., http://example.com ', answer=bool, default=True): - CONF['siteurl'] = ask('What is your URL prefix? (see above example; no trailing slash)', str_compat, CONF['siteurl']) + if ask('Do you want to specify a URL prefix? e.g., http://example.com ', + answer=bool, default=True): + CONF['siteurl'] = ask('What is your URL prefix? (see ' + 'above example; no trailing slash)', + str_compat, CONF['siteurl']) - CONF['with_pagination'] = ask('Do you want to enable article pagination?', bool, bool(CONF['default_pagination'])) + CONF['with_pagination'] = ask('Do you want to enable article pagination?', + bool, bool(CONF['default_pagination'])) if CONF['with_pagination']: - CONF['default_pagination'] = ask('How many articles per page do you want?', int, CONF['default_pagination']) + CONF['default_pagination'] = ask('How many articles per page ' + 'do you want?', + int, CONF['default_pagination']) else: CONF['default_pagination'] = False - CONF['timezone'] = ask_timezone('What is your time zone?', CONF['timezone'], _TZ_URL) + CONF['timezone'] = ask_timezone('What is your time zone?', + CONF['timezone'], _TZ_URL) - automation = ask('Do you want to generate a Fabfile/Makefile to automate generation and publishing?', bool, True) - develop = ask('Do you want an auto-reload & simpleHTTP script to assist with theme and site development?', bool, True) + automation = ask('Do you want to generate a Fabfile/Makefile ' + 'to automate generation and publishing?', bool, True) + develop = ask('Do you want an auto-reload & simpleHTTP script ' + 'to assist with theme and site development?', bool, True) if automation: - if ask('Do you want to upload your website using FTP?', answer=bool, default=False): - CONF['ftp_host'] = ask('What is the hostname of your FTP server?', str_compat, CONF['ftp_host']) - CONF['ftp_user'] = ask('What is your username on that server?', str_compat, CONF['ftp_user']) - CONF['ftp_target_dir'] = ask('Where do you want to put your web site on that server?', str_compat, CONF['ftp_target_dir']) - if ask('Do you want to upload your website using SSH?', answer=bool, default=False): - CONF['ssh_host'] = ask('What is the hostname of your SSH server?', str_compat, CONF['ssh_host']) - CONF['ssh_port'] = ask('What is the port of your SSH server?', int, CONF['ssh_port']) - CONF['ssh_user'] = ask('What is your username on that server?', str_compat, CONF['ssh_user']) - CONF['ssh_target_dir'] = ask('Where do you want to put your web site on that server?', str_compat, CONF['ssh_target_dir']) - if ask('Do you want to upload your website using Dropbox?', answer=bool, default=False): - CONF['dropbox_dir'] = ask('Where is your Dropbox directory?', str_compat, CONF['dropbox_dir']) - if ask('Do you want to upload your website using S3?', answer=bool, default=False): - CONF['s3_bucket'] = ask('What is the name of your S3 bucket?', str_compat, CONF['s3_bucket']) - if ask('Do you want to upload your website using Rackspace Cloud Files?', answer=bool, default=False): - CONF['cloudfiles_username'] = ask('What is your Rackspace Cloud username?', str_compat, CONF['cloudfiles_username']) - CONF['cloudfiles_api_key'] = ask('What is your Rackspace Cloud API key?', str_compat, CONF['cloudfiles_api_key']) - CONF['cloudfiles_container'] = ask('What is the name of your Cloud Files container?', str_compat, CONF['cloudfiles_container']) - if ask('Do you want to upload your website using GitHub Pages?', answer=bool, default=False): - if ask('Is this your personal page (username.github.io)?', answer=bool, default=False): - CONF['github_pages_branch'] = _GITHUB_PAGES_BRANCHES['personal'] + if ask('Do you want to upload your website using FTP?', + answer=bool, default=False): + CONF['ftp_host'] = ask('What is the hostname of your FTP server?', + str_compat, CONF['ftp_host']) + CONF['ftp_user'] = ask('What is your username on that server?', + str_compat, CONF['ftp_user']) + CONF['ftp_target_dir'] = ask('Where do you want to put your ' + 'web site on that server?', + str_compat, CONF['ftp_target_dir']) + if ask('Do you want to upload your website using SSH?', + answer=bool, default=False): + CONF['ssh_host'] = ask('What is the hostname of your SSH server?', + str_compat, CONF['ssh_host']) + CONF['ssh_port'] = ask('What is the port of your SSH server?', + int, CONF['ssh_port']) + CONF['ssh_user'] = ask('What is your username on that server?', + str_compat, CONF['ssh_user']) + CONF['ssh_target_dir'] = ask('Where do you want to put your ' + 'web site on that server?', + str_compat, CONF['ssh_target_dir']) + + if ask('Do you want to upload your website using Dropbox?', + answer=bool, default=False): + CONF['dropbox_dir'] = ask('Where is your Dropbox directory?', + str_compat, CONF['dropbox_dir']) + + if ask('Do you want to upload your website using S3?', + answer=bool, default=False): + CONF['s3_bucket'] = ask('What is the name of your S3 bucket?', + str_compat, CONF['s3_bucket']) + + if ask('Do you want to upload your website using ' + 'Rackspace Cloud Files?', answer=bool, default=False): + CONF['cloudfiles_username'] = ask('What is your Rackspace ' + 'Cloud username?', str_compat, + CONF['cloudfiles_username']) + CONF['cloudfiles_api_key'] = ask('What is your Rackspace ' + 'Cloud API key?', str_compat, + CONF['cloudfiles_api_key']) + CONF['cloudfiles_container'] = ask('What is the name of your ' + 'Cloud Files container?', + str_compat, + CONF['cloudfiles_container']) + + if ask('Do you want to upload your website using GitHub Pages?', + answer=bool, default=False): + if ask('Is this your personal page (username.github.io)?', + answer=bool, default=False): + CONF['github_pages_branch'] = \ + _GITHUB_PAGES_BRANCHES['personal'] else: - CONF['github_pages_branch'] = _GITHUB_PAGES_BRANCHES['project'] + CONF['github_pages_branch'] = \ + _GITHUB_PAGES_BRANCHES['project'] try: os.makedirs(os.path.join(CONF['basedir'], 'content')) @@ -270,7 +321,8 @@ needed by Pelican. print('Error: {0}'.format(e)) try: - with codecs.open(os.path.join(CONF['basedir'], 'pelicanconf.py'), 'w', 'utf-8') as fd: + with codecs.open(os.path.join(CONF['basedir'], 'pelicanconf.py'), + 'w', 'utf-8') as fd: conf_python = dict() for key, value in CONF.items(): conf_python[key] = repr(value) @@ -283,7 +335,8 @@ needed by Pelican. print('Error: {0}'.format(e)) try: - with codecs.open(os.path.join(CONF['basedir'], 'publishconf.py'), 'w', 'utf-8') as fd: + with codecs.open(os.path.join(CONF['basedir'], 'publishconf.py'), + 'w', 'utf-8') as fd: for line in get_template('publishconf.py'): template = string.Template(line) fd.write(template.safe_substitute(CONF)) @@ -293,7 +346,8 @@ needed by Pelican. if automation: try: - with codecs.open(os.path.join(CONF['basedir'], 'fabfile.py'), 'w', 'utf-8') as fd: + with codecs.open(os.path.join(CONF['basedir'], 'fabfile.py'), + 'w', 'utf-8') as fd: for line in get_template('fabfile.py'): template = string.Template(line) fd.write(template.safe_substitute(CONF)) @@ -301,7 +355,8 @@ needed by Pelican. except OSError as e: print('Error: {0}'.format(e)) try: - with codecs.open(os.path.join(CONF['basedir'], 'Makefile'), 'w', 'utf-8') as fd: + with codecs.open(os.path.join(CONF['basedir'], 'Makefile'), + 'w', 'utf-8') as fd: mkfile_template_name = 'Makefile' py_v = 'PY?=python' if six.PY3: @@ -323,7 +378,9 @@ needed by Pelican. value = '"' + value.replace('"', '\\"') + '"' conf_shell[key] = value try: - with codecs.open(os.path.join(CONF['basedir'], 'develop_server.sh'), 'w', 'utf-8') as fd: + with codecs.open(os.path.join(CONF['basedir'], + 'develop_server.sh'), + 'w', 'utf-8') as fd: lines = list(get_template('develop_server.sh')) py_v = 'PY=${PY:-python}\n' if six.PY3: @@ -333,7 +390,10 @@ needed by Pelican. template = string.Template(line) fd.write(template.safe_substitute(conf_shell)) fd.close() - os.chmod((os.path.join(CONF['basedir'], 'develop_server.sh')), 493) # mode 0o755 + + # mode 0o755 + os.chmod((os.path.join(CONF['basedir'], + 'develop_server.sh')), 493) except OSError as e: print('Error: {0}'.format(e)) diff --git a/pelican/tools/pelican_themes.py b/pelican/tools/pelican_themes.py index 8d71535d..e4bcb7c9 100755 --- a/pelican/tools/pelican_themes.py +++ b/pelican/tools/pelican_themes.py @@ -1,33 +1,12 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function - -import six +from __future__ import print_function, unicode_literals import argparse import os import shutil import sys -try: - import pelican -except: - err('Cannot import pelican.\nYou must install Pelican in order to run this script.', -1) - - -global _THEMES_PATH -_THEMES_PATH = os.path.join( - os.path.dirname( - os.path.abspath( - pelican.__file__ - ) - ), - 'themes' -) - -__version__ = '0.2' -_BUILTIN_THEMES = ['simple', 'notmyidea'] - def err(msg, die=None): """Print an error message and exits if an exit code is given""" @@ -35,43 +14,71 @@ def err(msg, die=None): if die: sys.exit((die if type(die) is int else 1)) +try: + import pelican +except: + err('Cannot import pelican.\nYou must ' + 'install Pelican in order to run this script.', + -1) + + +global _THEMES_PATH +_THEMES_PATH = os.path.join( + os.path.dirname( + os.path.abspath(pelican.__file__) + ), + 'themes' +) + +__version__ = '0.2' +_BUILTIN_THEMES = ['simple', 'notmyidea'] + def main(): """Main function""" - parser = argparse.ArgumentParser(description="""Install themes for Pelican""") + parser = argparse.ArgumentParser( + description="""Install themes for Pelican""") - excl= parser.add_mutually_exclusive_group() - excl.add_argument('-l', '--list', dest='action', action="store_const", const='list', + excl = parser.add_mutually_exclusive_group() + excl.add_argument( + '-l', '--list', dest='action', action="store_const", const='list', help="Show the themes already installed and exit") - excl.add_argument('-p', '--path', dest='action', action="store_const", const='path', + excl.add_argument( + '-p', '--path', dest='action', action="store_const", const='path', help="Show the themes path and exit") - excl.add_argument('-V', '--version', action='version', version='pelican-themes v{0}'.format(__version__), + excl.add_argument( + '-V', '--version', action='version', + version='pelican-themes v{0}'.format(__version__), help='Print the version of this script') - - parser.add_argument('-i', '--install', dest='to_install', nargs='+', metavar="theme path", + parser.add_argument( + '-i', '--install', dest='to_install', nargs='+', metavar="theme path", help='The themes to install') - parser.add_argument('-r', '--remove', dest='to_remove', nargs='+', metavar="theme name", + parser.add_argument( + '-r', '--remove', dest='to_remove', nargs='+', metavar="theme name", help='The themes to remove') - parser.add_argument('-U', '--upgrade', dest='to_upgrade', nargs='+', - metavar="theme path", help='The themes to upgrade') - parser.add_argument('-s', '--symlink', dest='to_symlink', nargs='+', metavar="theme path", - help="Same as `--install', but create a symbolic link instead of copying the theme. Useful for theme development") - parser.add_argument('-c', '--clean', dest='clean', action="store_true", + parser.add_argument( + '-U', '--upgrade', dest='to_upgrade', nargs='+', + metavar="theme path", help='The themes to upgrade') + parser.add_argument( + '-s', '--symlink', dest='to_symlink', nargs='+', metavar="theme path", + help="Same as `--install', but create a symbolic link instead of " + "copying the theme. Useful for theme development") + parser.add_argument( + '-c', '--clean', dest='clean', action="store_true", help="Remove the broken symbolic links of the theme path") - - parser.add_argument('-v', '--verbose', dest='verbose', action="store_true", + parser.add_argument( + '-v', '--verbose', dest='verbose', + action="store_true", help="Verbose output") - args = parser.parse_args() - + to_install = args.to_install or args.to_upgrade to_sym = args.to_symlink or args.clean - if args.action: if args.action is 'list': list_themes(args.verbose) @@ -95,7 +102,7 @@ def main(): if args.to_upgrade: if args.verbose: print('Upgrading themes...') - + for i in args.to_upgrade: install(i, v=args.verbose, u=True) @@ -144,11 +151,13 @@ def list_themes(v=False): def remove(theme_name, v=False): """Removes a theme""" - theme_name = theme_name.replace('/','') + theme_name = theme_name.replace('/', '') target = os.path.join(_THEMES_PATH, theme_name) if theme_name in _BUILTIN_THEMES: - err(theme_name + ' is a builtin theme.\nYou cannot remove a builtin theme with this script, remove it by hand if you want.') + err(theme_name + ' is a builtin theme.\n' + 'You cannot remove a builtin theme with this script, ' + 'remove it by hand if you want.') elif os.path.islink(target): if v: print('Removing link `' + target + "'") @@ -180,7 +189,8 @@ def install(path, v=False, u=False): install(path, v) else: if v: - print("Copying `{p}' to `{t}' ...".format(p=path, t=theme_path)) + print("Copying '{p}' to '{t}' ...".format(p=path, + t=theme_path)) try: shutil.copytree(path, theme_path) @@ -189,14 +199,18 @@ def install(path, v=False, u=False): for root, dirs, files in os.walk(theme_path): for d in dirs: dname = os.path.join(root, d) - os.chmod(dname, 493) # 0o755 + os.chmod(dname, 493) # 0o755 for f in files: fname = os.path.join(root, f) - os.chmod(fname, 420) # 0o644 + os.chmod(fname, 420) # 0o644 except OSError as e: - err("Cannot change permissions of files or directory in `{r}':\n{e}".format(r=theme_path, e=str(e)), die=False) + err("Cannot change permissions of files " + "or directory in `{r}':\n{e}".format(r=theme_path, + e=str(e)), + die=False) except Exception as e: - err("Cannot copy `{p}' to `{t}':\n{e}".format(p=path, t=theme_path, e=str(e))) + err("Cannot copy `{p}' to `{t}':\n{e}".format( + p=path, t=theme_path, e=str(e))) def symlink(path, v=False): @@ -212,11 +226,13 @@ def symlink(path, v=False): err(path + ' : already exists') else: if v: - print("Linking `{p}' to `{t}' ...".format(p=path, t=theme_path)) + print("Linking `{p}' to `{t}' ...".format( + p=path, t=theme_path)) try: os.symlink(path, theme_path) except Exception as e: - err("Cannot link `{p}' to `{t}':\n{e}".format(p=path, t=theme_path, e=str(e))) + err("Cannot link `{p}' to `{t}':\n{e}".format( + p=path, t=theme_path, e=str(e))) def is_broken_link(path): @@ -227,7 +243,7 @@ def is_broken_link(path): def clean(v=False): """Removes the broken symbolic links""" - c=0 + c = 0 for path in os.listdir(_THEMES_PATH): path = os.path.join(_THEMES_PATH, path) if os.path.islink(path): @@ -236,9 +252,9 @@ def clean(v=False): print('Removing {0}'.format(path)) try: os.remove(path) - except OSError as e: + except OSError: print('Error: cannot remove {0}'.format(path)) else: - c+=1 + c += 1 print("\nRemoved {0} broken links".format(c)) diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index 65dee23b..bf1199a8 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -4,9 +4,10 @@ from __future__ import unicode_literals import functools import logging import os + import six -from pelican.utils import (slugify, python_2_unicode_compatible) +from pelican.utils import python_2_unicode_compatible, slugify logger = logging.getLogger(__name__) diff --git a/pelican/utils.py b/pelican/utils.py index 43dca212..786a9425 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -1,29 +1,30 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function -import six +from __future__ import print_function, unicode_literals import codecs +import datetime import errno import fnmatch import locale import logging import os -import pytz import re import shutil import sys import traceback -import pickle -import datetime - from collections import Hashable from contextlib import contextmanager -import dateutil.parser from functools import partial from itertools import groupby -from jinja2 import Markup from operator import attrgetter -from posixpath import join as posix_join + +import dateutil.parser + +from jinja2 import Markup + +import pytz + +import six from six.moves.html_parser import HTMLParser logger = logging.getLogger(__name__) @@ -43,9 +44,9 @@ def strftime(date, date_format): formatting them with the date, (if necessary) decoding the output and replacing formatted output back. ''' - + def strip_zeros(x): + return x.lstrip('0') or '0' c89_directives = 'aAbBcdfHIjmMpSUwWxXyYzZ%' - strip_zeros = lambda x: x.lstrip('0') or '0' # grab candidate format options format_options = '%[-]?.' @@ -200,8 +201,8 @@ def deprecated_attribute(old, new, since=None, remove=None, doc=None): ' and will be removed by version {}'.format(version)) message.append('. Use {} instead.'.format(new)) logger.warning(''.join(message)) - logger.debug(''.join( - six.text_type(x) for x in traceback.format_stack())) + logger.debug(''.join(six.text_type(x) for x + in traceback.format_stack())) def fget(self): _warn() @@ -224,7 +225,7 @@ def get_date(string): """ string = re.sub(' +', ' ', string) default = SafeDatetime.now().replace(hour=0, minute=0, - second=0, microsecond=0) + second=0, microsecond=0) try: return dateutil.parser.parse(string, default=default) except (TypeError, ValueError): @@ -319,12 +320,12 @@ def copy(source, destination, ignores=None): for src_dir, subdirs, others in os.walk(source_): dst_dir = os.path.join(destination_, - os.path.relpath(src_dir, source_)) + os.path.relpath(src_dir, source_)) subdirs[:] = (s for s in subdirs if not any(fnmatch.fnmatch(s, i) for i in ignores)) - others[:] = (o for o in others if not any(fnmatch.fnmatch(o, i) - for i in ignores)) + others[:] = (o for o in others if not any(fnmatch.fnmatch(o, i) + for i in ignores)) if not os.path.isdir(dst_dir): logger.info('Creating directory %s', dst_dir) @@ -338,9 +339,11 @@ def copy(source, destination, ignores=None): logger.info('Copying %s to %s', src_path, dst_path) shutil.copy2(src_path, dst_path) else: - logger.warning('Skipped copy %s (not a file or directory) to %s', + logger.warning('Skipped copy %s (not a file or ' + 'directory) to %s', src_path, dst_path) + def clean_output_dir(path, retention): """Remove all files from output directory except those in retention list""" @@ -366,8 +369,8 @@ def clean_output_dir(path, retention): shutil.rmtree(file) logger.debug("Deleted directory %s", file) except Exception as e: - logger.error("Unable to delete directory %s; %s", - file, e) + logger.error("Unable to delete directory %s; %s", + file, e) elif os.path.isfile(file) or os.path.islink(file): try: os.remove(file) @@ -507,12 +510,12 @@ def process_translations(content_list, order_by=None): for slug, items in grouped_by_slugs: items = list(items) - # items with `translation` metadata will be used as translations… + # items with `translation` metadata will be used as translations... default_lang_items = list(filter( - lambda i: i.metadata.get('translation', 'false').lower() - == 'false', - items)) - # …unless all items with that slug are translations + lambda i: + i.metadata.get('translation', 'false').lower() == 'false', + items)) + # ...unless all items with that slug are translations if not default_lang_items: default_lang_items = items @@ -522,13 +525,14 @@ def process_translations(content_list, order_by=None): len_ = len(lang_items) if len_ > 1: logger.warning('There are %s variants of "%s" with lang %s', - len_, slug, lang) + len_, slug, lang) for x in lang_items: logger.warning('\t%s', x.source_path) # find items with default language - default_lang_items = list(filter(attrgetter('in_default_lang'), - default_lang_items)) + default_lang_items = list(filter( + attrgetter('in_default_lang'), + default_lang_items)) # if there is no article with default language, take an other one if not default_lang_items: @@ -536,10 +540,9 @@ def process_translations(content_list, order_by=None): if not slug: logger.warning( - 'empty slug for %s. ' - 'You can fix this by adding a title or a slug to your ' - 'content', - default_lang_items[0].source_path) + 'Empty slug for %s. You can fix this by ' + 'adding a title or a slug to your content', + default_lang_items[0].source_path) index.extend(default_lang_items) translations.extend([x for x in items if x not in default_lang_items]) for a in items: @@ -567,10 +570,12 @@ def process_translations(content_list, order_by=None): index.sort(key=attrgetter(order_by), reverse=order_reversed) except AttributeError: - logger.warning('There is no "%s" attribute in the item ' + logger.warning( + 'There is no "%s" attribute in the item ' 'metadata. Defaulting to slug order.', order_by) else: - logger.warning('Invalid *_ORDER_BY setting (%s).' + logger.warning( + 'Invalid *_ORDER_BY setting (%s).' 'Valid options are strings and functions.', order_by) return index, translations @@ -589,12 +594,12 @@ def folder_watcher(path, extensions, ignores=[]): dirs[:] = [x for x in dirs if not x.startswith(os.curdir)] for f in files: - if (f.endswith(tuple(extensions)) and - not any(fnmatch.fnmatch(f, ignore) for ignore in ignores)): - try: - yield os.stat(os.path.join(root, f)).st_mtime - except OSError as e: - logger.warning('Caught Exception: %s', e) + if f.endswith(tuple(extensions)) and \ + not any(fnmatch.fnmatch(f, ignore) for ignore in ignores): + try: + yield os.stat(os.path.join(root, f)).st_mtime + except OSError as e: + logger.warning('Caught Exception: %s', e) LAST_MTIME = 0 while True: diff --git a/pelican/writers.py b/pelican/writers.py index e90a0004..4df7b859 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -1,22 +1,24 @@ # -*- coding: utf-8 -*- -from __future__ import with_statement, unicode_literals, print_function -import six +from __future__ import print_function, unicode_literals, with_statement -import os import logging +import os + +from feedgenerator import Atom1Feed, Rss201rev2Feed + +from jinja2 import Markup + +import six +from six.moves.urllib.parse import urlparse + +from pelican import signals +from pelican.paginator import Paginator +from pelican.utils import (get_relative_path, is_selected_for_writing, + path_to_url, set_date_tzinfo) if not six.PY3: from codecs import open -from feedgenerator import Atom1Feed, Rss201rev2Feed -from jinja2 import Markup -from six.moves.urllib.parse import urlparse - -from pelican.paginator import Paginator -from pelican.utils import (get_relative_path, path_to_url, set_date_tzinfo, - is_selected_for_writing) -from pelican import signals - logger = logging.getLogger(__name__) @@ -119,10 +121,10 @@ class Writer(object): feed.write(fp, 'utf-8') logger.info('Writing %s', complete_path) - signals.feed_written.send(complete_path, context=context, feed=feed) + signals.feed_written.send( + complete_path, context=context, feed=feed) return feed - def write_file(self, name, template, context, relative_urls=False, paginated=None, override_output=False, **kwargs): """Render the template and write the file. @@ -139,9 +141,10 @@ class Writer(object): :param **kwargs: additional variables to pass to the templates """ - if name is False or name == "" or\ - not is_selected_for_writing(self.settings,\ - os.path.join(self.output_path, name)): + if name is False or \ + name == "" or \ + not is_selected_for_writing(self.settings, + os.path.join(self.output_path, name)): return elif not name: # other stuff, just return for now @@ -169,7 +172,8 @@ class Writer(object): def _get_localcontext(context, name, kwargs, relative_urls): localcontext = context.copy() - localcontext['localsiteurl'] = localcontext.get('localsiteurl', None) + localcontext['localsiteurl'] = localcontext.get( + 'localsiteurl', None) if relative_urls: relative_url = path_to_url(get_relative_path(name)) localcontext['SITEURL'] = relative_url @@ -201,11 +205,13 @@ class Writer(object): '%s_previous_page' % key: previous_page, '%s_next_page' % key: next_page}) - localcontext = _get_localcontext(context, page.save_as, paginated_kwargs, relative_urls) + localcontext = _get_localcontext( + context, page.save_as, paginated_kwargs, relative_urls) _write_file(template, localcontext, self.output_path, page.save_as, override_output) else: # no pagination - localcontext = _get_localcontext(context, name, kwargs, relative_urls) + localcontext = _get_localcontext( + context, name, kwargs, relative_urls) _write_file(template, localcontext, self.output_path, name, override_output) diff --git a/tox.ini b/tox.ini index 34335b82..56ad0c14 100644 --- a/tox.ini +++ b/tox.ini @@ -38,4 +38,5 @@ deps = flake8 <= 2.4.1 git+https://github.com/public/flake8-import-order@2ac7052a4e02b4a8a0125a106d87465a3b9fd688 commands = + flake8 --version flake8 pelican From 9d57dcf020c11abfb0efc16d6d4387a269964b97 Mon Sep 17 00:00:00 2001 From: Julien Vehent Date: Wed, 19 Aug 2015 12:28:14 -0400 Subject: [PATCH 0469/1427] Fix calculation of tag count in dotclear import Upon import of a dotclear backup, `pelican-import` returned this stacktrace: ``` File "/usr/bin/pelican-import", line 11, in sys.exit(main()) File "/usr/lib/python3.4/site-packages/pelican/tools/pelican_import.py", line 809, in main attachments = attachments or None) File "/usr/lib/python3.4/site-packages/pelican/tools/pelican_import.py", line 621, in fields2pelican kind, in_markup) in fields: File "/usr/lib/python3.4/site-packages/pelican/tools/pelican_import.py", line 262, in dc2fields if int(tag[:1]) == 1: ValueError: invalid literal for int() with base 10: 'a' ``` --- pelican/tools/pelican_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 92e8c919..a6547f63 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -259,7 +259,7 @@ def dc2fields(file): # Get tags related to a post tag = post_meta.replace('{', '').replace('}', '').replace('a:1:s:3:\\"tag\\";a:', '').replace('a:0:', '') if len(tag) > 1: - if int(tag[:1]) == 1: + if int(len(tag[:1])) == 1: newtag = tag.split('"')[1] tags.append( BeautifulSoup( From 170a429c52ed57c45826fa31c31d5bf692698cac Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 25 Aug 2015 10:05:15 -0700 Subject: [PATCH 0470/1427] Add flake8, sphinx, and tox to dev requirements --- dev_requirements.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev_requirements.txt b/dev_requirements.txt index 028cbebd..d11b5c1f 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,5 +1,9 @@ # Tests +flake8 +-e git+https://github.com/public/flake8-import-order#egg=flake8-import-order mock +sphinx +tox # Optional Packages Markdown From ad72287b4cff8c46f35f693b487bfe39d00f0500 Mon Sep 17 00:00:00 2001 From: Justin Mayer Date: Tue, 25 Aug 2015 11:25:57 -0700 Subject: [PATCH 0471/1427] Revert dev requirement additions Tox uses dev_requirements.txt to install things. That wasn't the original purpose of the dev_requirements.txt file, but for now these additions will have to remain a documentation issue until this can be sorted out properly. --- dev_requirements.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index d11b5c1f..028cbebd 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,9 +1,5 @@ # Tests -flake8 --e git+https://github.com/public/flake8-import-order#egg=flake8-import-order mock -sphinx -tox # Optional Packages Markdown From 7f795ed558f7eb5adabf1c2777db9b430ce121ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Fern=C3=A1ndez?= Date: Wed, 26 Aug 2015 11:23:28 +0200 Subject: [PATCH 0472/1427] Remove duplicate tags and authors in metadata --- pelican/readers.py | 5 ++++- .../article_with_duplicate_tags_authors.md | 15 +++++++++++++++ pelican/tests/test_generators.py | 2 ++ pelican/tests/test_readers.py | 10 ++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 pelican/tests/content/article_with_duplicate_tags_authors.md diff --git a/pelican/readers.py b/pelican/readers.py index bc4515e7..2e51c4ff 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -4,6 +4,7 @@ from __future__ import print_function, unicode_literals import logging import os import re +from collections import OrderedDict import docutils import docutils.core @@ -72,7 +73,9 @@ def ensure_metadata_list(text): else: text = text.split(',') - return [v for v in (w.strip() for w in text) if v] + return list(OrderedDict.fromkeys( + [v for v in (w.strip() for w in text) if v] + )) def _process_if_nonempty(processor, name, settings): diff --git a/pelican/tests/content/article_with_duplicate_tags_authors.md b/pelican/tests/content/article_with_duplicate_tags_authors.md new file mode 100644 index 00000000..7ab046f9 --- /dev/null +++ b/pelican/tests/content/article_with_duplicate_tags_authors.md @@ -0,0 +1,15 @@ +Title: Test metadata duplicates +Category: test +Tags: foo, bar, foobar, foo, bar +Authors: Author, First; Author, Second; Author, First +Date: 2010-12-02 10:14 +Modified: 2010-12-02 10:20 +Summary: I have a lot to test + +Test Markdown File Header +========================= + +Used for pelican test +--------------------- + +The quick brown fox jumped over the lazy dog's back. diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 2cfca04f..c9aa1cff 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -160,6 +160,7 @@ class TestArticlesGenerator(unittest.TestCase): ['Test markdown File', 'published', 'test', 'article'], ['Test md File', 'published', 'test', 'article'], ['Test mdown File', 'published', 'test', 'article'], + ['Test metadata duplicates', 'published', 'test', 'article'], ['Test mkd File', 'published', 'test', 'article'], ['This is a super article !', 'published', 'Yeah', 'article'], ['This is a super article !', 'published', 'Yeah', 'article'], @@ -435,6 +436,7 @@ class TestArticlesGenerator(unittest.TestCase): 'Test markdown File', 'Test md File', 'Test mdown File', + 'Test metadata duplicates', 'Test mkd File', 'This is a super article !', 'This is a super article !', diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index 71394ee4..5fabc470 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -516,6 +516,16 @@ class MdReaderTest(ReaderTest): } self.assertDictHasSubset(page.metadata, expected) + def test_duplicate_tags_or_authors_are_removed(self): + reader = readers.MarkdownReader(settings=get_settings()) + content, metadata = reader.read( + _path('article_with_duplicate_tags_authors.md')) + expected = { + 'tags': ['foo', 'bar', 'foobar'], + 'authors': ['Author, First', 'Author, Second'], + } + self.assertDictHasSubset(metadata, expected) + class HTMLReaderTest(ReaderTest): def test_article_with_comments(self): From 9d0804de7af858880e5ef74f0c1c5d8f5ad6419b Mon Sep 17 00:00:00 2001 From: Andrea Corbellini Date: Wed, 19 Aug 2015 16:43:59 +0200 Subject: [PATCH 0473/1427] When truncating, consider hypens, apostrophes and HTML entities. --- pelican/tests/output/basic/category/misc.html | 2 +- pelican/tests/output/basic/index.html | 2 +- .../custom/author/alexis-metaireau3.html | 2 +- .../tests/output/custom/category/misc.html | 2 +- pelican/tests/output/custom/index3.html | 2 +- .../author/alexis-metaireau3.html | 4 +- .../output/custom_locale/category/misc.html | 4 +- .../tests/output/custom_locale/index3.html | 4 +- pelican/tests/test_utils.py | 28 ++++++-- pelican/utils.py | 69 ++++++++++++++++--- 10 files changed, 95 insertions(+), 24 deletions(-) diff --git a/pelican/tests/output/basic/category/misc.html b/pelican/tests/output/basic/category/misc.html index 0368793e..f491a464 100644 --- a/pelican/tests/output/basic/category/misc.html +++ b/pelican/tests/output/basic/category/misc.html @@ -90,7 +90,7 @@

    Testing another case

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

    +pelican.conf, it will ...

    read more diff --git a/pelican/tests/output/basic/index.html b/pelican/tests/output/basic/index.html index 3066172d..4c74500d 100644 --- a/pelican/tests/output/basic/index.html +++ b/pelican/tests/output/basic/index.html @@ -227,7 +227,7 @@ YEAH !

    Testing another case

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

    +pelican.conf, it will ...

    read more diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index 54c768ac..3ca4dd0d 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -59,7 +59,7 @@

    Testing another case

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

    +pelican.conf, it will ...

    read more

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

    Testing another case

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

    +pelican.conf, it will ...

    read more

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

    Testing another case

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

    +pelican.conf, it will ...

    read more

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

    Testing another case

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

    +pelican.conf, it will ...

    read more

    There are comments.

    @@ -135,4 +135,4 @@ pelican.conf, it ...

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/category/misc.html b/pelican/tests/output/custom_locale/category/misc.html index bb78a8cc..f44f725d 100644 --- a/pelican/tests/output/custom_locale/category/misc.html +++ b/pelican/tests/output/custom_locale/category/misc.html @@ -103,7 +103,7 @@

    Testing another case

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

    +pelican.conf, it will ...

    read more

    There are comments.

    @@ -175,4 +175,4 @@ pelican.conf, it ...

    }()); - \ No newline at end of file + diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html index 49f70ba2..926bc25e 100644 --- a/pelican/tests/output/custom_locale/index3.html +++ b/pelican/tests/output/custom_locale/index3.html @@ -59,7 +59,7 @@

    Testing another case

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

    +pelican.conf, it will ...

    read more

    There are comments.

    @@ -135,4 +135,4 @@ pelican.conf, it ...

    }()); - \ No newline at end of file + diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index d6fdf70e..a076a2c7 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -146,31 +146,51 @@ class TestUtils(LoggedTestCase): self.assertEqual(utils.get_relative_path(value), expected) def test_truncate_html_words(self): + # Plain text. self.assertEqual( utils.truncate_html_words('short string', 20), 'short string') - self.assertEqual( utils.truncate_html_words('word ' * 100, 20), 'word ' * 20 + '...') + # Words enclosed or intervaled by HTML tags. 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 + '...') + # Words with hypens and apostrophes. + self.assertEqual( + utils.truncate_html_words("a-b " * 100, 20), + "a-b " * 20 + '...') + self.assertEqual( + utils.truncate_html_words("it's " * 100, 20), + "it's " * 20 + '...') + + # Words with HTML entity references. + self.assertEqual( + utils.truncate_html_words("é " * 100, 20), + "é " * 20 + '...') + self.assertEqual( + utils.truncate_html_words("café " * 100, 20), + "café " * 20 + '...') + self.assertEqual( + utils.truncate_html_words("èlite " * 100, 20), + "èlite " * 20 + '...') + self.assertEqual( + utils.truncate_html_words("cafetiére " * 100, 20), + "cafetiére " * 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 786a9425..7ad0914c 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -25,6 +25,7 @@ from jinja2 import Markup import pytz import six +from six.moves import html_entities from six.moves.html_parser import HTMLParser logger = logging.getLogger(__name__) @@ -408,7 +409,8 @@ def posixize_path(rel_path): class _HTMLWordTruncator(HTMLParser): - _word_regex = re.compile(r'\w[\w-]*', re.U) + _word_regex = re.compile(r"\w[\w'-]*", re.U) + _word_prefix_regex = re.compile(r'\w', re.U) _singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area', 'hr', 'input') @@ -420,17 +422,37 @@ class _HTMLWordTruncator(HTMLParser): self.max_words = max_words self.words_found = 0 self.open_tags = [] + self.last_word_end = None self.truncate_at = None + def getoffset(self): + line_start = 0 + lineno, line_offset = self.getpos() + for i in range(lineno - 1): + line_start = self.rawdata.index('\n', line_start) + 1 + return line_start + line_offset + + def add_word(self, word_end): + self.words_found += 1 + self.last_word_end = None + if self.words_found == self.max_words: + self.truncate_at = word_end + + def add_last_word(self): + if self.last_word_end is not None: + self.add_word(self.last_word_end) + def handle_starttag(self, tag, attrs): if self.truncate_at is not None: return + self.add_last_word() 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 + self.add_last_word() try: i = self.open_tags.index(tag) except ValueError: @@ -442,20 +464,49 @@ class _HTMLWordTruncator(HTMLParser): def handle_data(self, data): word_end = 0 + offset = self.getoffset() 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 + if match.start(0) > 0: + self.add_last_word() + + word_end = match.end(0) + self.last_word_end = offset + word_end + + if word_end < len(data): + self.add_last_word() + + def handle_ref(self, char): + offset = self.getoffset() + ref_end = self.rawdata.index(';', offset) + 1 + + if self.last_word_end is None: + if self._word_prefix_regex.match(char): + self.last_word_end = ref_end + else: + if self._word_regex.match(char): + self.last_word_end = ref_end + else: + self.add_last_word() + + def handle_entityref(self, name): + try: + codepoint = html_entities.name2codepoint[name] + except KeyError: + self.handle_ref('') + else: + self.handle_ref(chr(codepoint)) + + def handle_charref(self, name): + if name.startswith('x'): + codepoint = int(name[1:], 16) + else: + codepoint = int(name) + self.handle_ref(chr(codepoint)) def truncate_html_words(s, num, end_text='...'): From b0d41f081b83cb8b1534d907b95ea5f198f9ea67 Mon Sep 17 00:00:00 2001 From: derwinlu Date: Sat, 5 Sep 2015 10:53:44 +0200 Subject: [PATCH 0474/1427] remove article.keywords from simple theme We don't process 'keywords' metadata specially, so it never gets processed into a list. This prevents issues like #1733. --- pelican/themes/simple/templates/article.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pelican/themes/simple/templates/article.html b/pelican/themes/simple/templates/article.html index 8ddda4d0..48464ce4 100644 --- a/pelican/themes/simple/templates/article.html +++ b/pelican/themes/simple/templates/article.html @@ -1,10 +1,6 @@ {% extends "base.html" %} {% block head %} {{ super() }} - {% for keyword in article.keywords %} - - {% endfor %} - {% if article.description %} {% endif %} From 6afa7704b149c60384e5598afd9ceb11e064d373 Mon Sep 17 00:00:00 2001 From: derwinlu Date: Fri, 11 Sep 2015 22:06:51 +0200 Subject: [PATCH 0475/1427] Add versioning information to debug output --- pelican/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pelican/__init__.py b/pelican/__init__.py index 7fb8dfe4..48cb5980 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -379,6 +379,10 @@ def get_instance(args): def main(): args = parse_arguments() init(args.verbosity) + + logger.debug('Pelican version: %s', __version__) + logger.debug('Python version: %s', sys.version.split()[0]) + pelican, settings = get_instance(args) readers = Readers(settings) From a6c258eb7fda71e7421ae6b41180f2103c166bf3 Mon Sep 17 00:00:00 2001 From: Onur Aslan Date: Tue, 15 Sep 2015 02:24:21 +0300 Subject: [PATCH 0476/1427] Add index and author replacement indicators --- docs/content.rst | 8 ++++---- pelican/contents.py | 6 +++++- pelican/settings.py | 1 + pelican/tests/test_contents.py | 34 +++++++++++++++++++++++++++++++ pelican/tests/test_urlwrappers.py | 8 +++++++- 5 files changed, 51 insertions(+), 6 deletions(-) diff --git a/docs/content.rst b/docs/content.rst index 1e6ba099..0fa89921 100644 --- a/docs/content.rst +++ b/docs/content.rst @@ -329,11 +329,11 @@ of ``{attach}``, and letting the file's location be determined by the project's ``STATIC_SAVE_AS`` and ``STATIC_URL`` settings. (Per-file ``save_as`` and ``url`` overrides can still be set in ``EXTRA_PATH_METADATA``.) -Linking to tags and categories ------------------------------- +Linking to authors, categories, index and tags +---------------------------------------------- -You can link to tags and categories using the ``{tag}tagname`` and -``{category}foobar`` syntax. +You can link to authors, categories, index and tags using the ``{author}name``, +``{category}foobar``, ``{index}`` and ``{tag}tagname`` syntax. Deprecated internal link syntax ------------------------------- diff --git a/pelican/contents.py b/pelican/contents.py index 16d1f074..4d313ab8 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -21,7 +21,7 @@ from pelican.utils import (SafeDatetime, deprecated_attribute, memoized, 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 +from pelican.urlwrappers import (Author, Category, Tag, URLWrapper) # NOQA logger = logging.getLogger(__name__) @@ -253,6 +253,10 @@ class Content(object): origin = '/'.join((siteurl, Category(path, self.settings).url)) elif what == 'tag': origin = '/'.join((siteurl, Tag(path, self.settings).url)) + elif what == 'index': + origin = '/'.join((siteurl, self.settings['INDEX_SAVE_AS'])) + elif what == 'author': + origin = '/'.join((siteurl, Author(path, self.settings).url)) else: logger.warning( "Replacement Indicator '%s' not recognized, " diff --git a/pelican/settings.py b/pelican/settings.py index 4d75333a..d1b1648b 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -64,6 +64,7 @@ DEFAULT_CONFIG = { 'REVERSE_CATEGORY_ORDER': False, 'DELETE_OUTPUT_DIRECTORY': False, 'OUTPUT_RETENTION': [], + 'INDEX_SAVE_AS': 'index.html', 'ARTICLE_URL': '{slug}.html', 'ARTICLE_SAVE_AS': '{slug}.html', 'ARTICLE_ORDER_BY': 'reversed-date', diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index a3664383..59cff844 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -606,6 +606,40 @@ class TestStatic(LoggedTestCase): self.assertNotEqual(content, html) + def test_author_link_syntax(self): + "{author} link syntax triggers url replacement." + + 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.assertNotEqual(content, html) + + def test_index_link_syntax(self): + "{index} link syntax triggers url replacement." + + 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.assertNotEqual(content, html) + + expected_html = ('link') + self.assertEqual(content, expected_html) + def test_unknown_link_syntax(self): "{unknown} link syntax should trigger warning." diff --git a/pelican/tests/test_urlwrappers.py b/pelican/tests/test_urlwrappers.py index ae6eaaec..f3dc8198 100644 --- a/pelican/tests/test_urlwrappers.py +++ b/pelican/tests/test_urlwrappers.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from pelican.tests.support import unittest -from pelican.urlwrappers import Category, Tag, URLWrapper +from pelican.urlwrappers import Author, Category, Tag, URLWrapper class TestURLWrapper(unittest.TestCase): @@ -34,9 +34,11 @@ class TestURLWrapper(unittest.TestCase): def test_equality(self): tag = Tag('test', settings={}) cat = Category('test', settings={}) + author = Author('test', settings={}) # same name, but different class self.assertNotEqual(tag, cat) + self.assertNotEqual(tag, author) # should be equal vs text representing the same name self.assertEqual(tag, u'test') @@ -48,5 +50,9 @@ class TestURLWrapper(unittest.TestCase): tag_equal = Tag('Test', settings={}) self.assertEqual(tag, tag_equal) + # Author describing the same should be equal + author_equal = Author('Test', settings={}) + self.assertEqual(author, author_equal) + cat_ascii = Category('指導書', settings={}) self.assertEqual(cat_ascii, u'zhi-dao-shu') From d583efb8616cf19401865b04c4270b1a41a96d7f Mon Sep 17 00:00:00 2001 From: Andrea Corbellini Date: Fri, 4 Sep 2015 16:49:41 +0200 Subject: [PATCH 0477/1427] When truncating, stop parsing the document immediately after finding the last word. --- pelican/utils.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/pelican/utils.py b/pelican/utils.py index 7ad0914c..97768f53 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -414,6 +414,13 @@ class _HTMLWordTruncator(HTMLParser): _singlets = ('br', 'col', 'link', 'base', 'img', 'param', 'area', 'hr', 'input') + class TruncationCompleted(Exception): + + def __init__(self, truncate_at): + super(_HTMLWordTruncator.TruncationCompleted, self).__init__( + truncate_at) + self.truncate_at = truncate_at + def __init__(self, max_words): # In Python 2, HTMLParser is not a new-style class, # hence super() cannot be used. @@ -425,6 +432,16 @@ class _HTMLWordTruncator(HTMLParser): self.last_word_end = None self.truncate_at = None + def feed(self, *args, **kwargs): + try: + # With Python 2, super() cannot be used. + # See the comment for __init__(). + HTMLParser.feed(self, *args, **kwargs) + except self.TruncationCompleted as exc: + self.truncate_at = exc.truncate_at + else: + self.truncate_at = None + def getoffset(self): line_start = 0 lineno, line_offset = self.getpos() @@ -436,22 +453,18 @@ class _HTMLWordTruncator(HTMLParser): self.words_found += 1 self.last_word_end = None if self.words_found == self.max_words: - self.truncate_at = word_end + raise self.TruncationCompleted(word_end) def add_last_word(self): if self.last_word_end is not None: self.add_word(self.last_word_end) def handle_starttag(self, tag, attrs): - if self.truncate_at is not None: - return self.add_last_word() 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 self.add_last_word() try: i = self.open_tags.index(tag) From c255a35800911f74a3d07689cc6f1296cb6660a9 Mon Sep 17 00:00:00 2001 From: Andrea Corbellini Date: Tue, 22 Sep 2015 20:52:30 +0200 Subject: [PATCH 0478/1427] Use unichr() instead of chr() with Python 2. --- pelican/tests/test_utils.py | 12 ++++++++++++ pelican/utils.py | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index a076a2c7..d967b247 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -190,6 +190,18 @@ class TestUtils(LoggedTestCase): self.assertEqual( utils.truncate_html_words("cafetiére " * 100, 20), "cafetiére " * 20 + '...') + self.assertEqual( + utils.truncate_html_words("∫dx " * 100, 20), + "∫dx " * 20 + '...') + + # Words with HTML character references inside and outside + # the ASCII range. + self.assertEqual( + utils.truncate_html_words("é " * 100, 20), + "é " * 20 + '...') + self.assertEqual( + utils.truncate_html_words("∫dx " * 100, 20), + "∫dx " * 20 + '...') def test_process_translations(self): # create a bunch of articles diff --git a/pelican/utils.py b/pelican/utils.py index 7ad0914c..697a182b 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -499,14 +499,14 @@ class _HTMLWordTruncator(HTMLParser): except KeyError: self.handle_ref('') else: - self.handle_ref(chr(codepoint)) + self.handle_ref(six.unichr(codepoint)) def handle_charref(self, name): if name.startswith('x'): codepoint = int(name[1:], 16) else: codepoint = int(name) - self.handle_ref(chr(codepoint)) + self.handle_ref(six.unichr(codepoint)) def truncate_html_words(s, num, end_text='...'): From a670a3e0409719779d6dbe5939e6c39836e6162b Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Mon, 21 Sep 2015 00:15:29 -0400 Subject: [PATCH 0479/1427] Made name of blogroll/social widgets configurable. The BLOGROLL_WIDGET_NAME and SOCIAL_WIDGET_NAME settings are now respected by notmyidea if they are specified in your config file. They override the default names of "blogroll" and "links" in the notmyidea theme. Used default() in template to simplify template code. Renaming BLOGROLL setting to LINKS, changed default also. Updated tests to check 'links' instead of 'blogroll'. Whoops; links, not link. --- THANKS | 1 + docs/settings.rst | 4 ++++ pelican/tests/output/custom/a-markdown-powered-article.html | 2 +- pelican/tests/output/custom/archives.html | 2 +- pelican/tests/output/custom/article-1.html | 2 +- pelican/tests/output/custom/article-2.html | 2 +- pelican/tests/output/custom/article-3.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau2.html | 2 +- pelican/tests/output/custom/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom/authors.html | 2 +- pelican/tests/output/custom/categories.html | 2 +- pelican/tests/output/custom/category/bar.html | 2 +- pelican/tests/output/custom/category/cat1.html | 2 +- pelican/tests/output/custom/category/misc.html | 2 +- pelican/tests/output/custom/category/yeah.html | 2 +- pelican/tests/output/custom/drafts/a-draft-article.html | 2 +- pelican/tests/output/custom/filename_metadata-example.html | 2 +- pelican/tests/output/custom/index.html | 2 +- pelican/tests/output/custom/index2.html | 2 +- pelican/tests/output/custom/index3.html | 2 +- pelican/tests/output/custom/jinja2_template.html | 2 +- pelican/tests/output/custom/oh-yeah-fr.html | 2 +- pelican/tests/output/custom/oh-yeah.html | 2 +- pelican/tests/output/custom/override/index.html | 2 +- .../tests/output/custom/pages/this-is-a-test-hidden-page.html | 2 +- pelican/tests/output/custom/pages/this-is-a-test-page.html | 2 +- pelican/tests/output/custom/second-article-fr.html | 2 +- pelican/tests/output/custom/second-article.html | 2 +- pelican/tests/output/custom/tag/bar.html | 2 +- pelican/tests/output/custom/tag/baz.html | 2 +- pelican/tests/output/custom/tag/foo.html | 2 +- pelican/tests/output/custom/tag/foobar.html | 2 +- pelican/tests/output/custom/tag/oh.html | 2 +- pelican/tests/output/custom/tag/yeah.html | 2 +- pelican/tests/output/custom/tags.html | 2 +- pelican/tests/output/custom/this-is-a-super-article.html | 2 +- pelican/tests/output/custom/unbelievable.html | 2 +- pelican/tests/output/custom_locale/archives.html | 2 +- .../tests/output/custom_locale/author/alexis-metaireau.html | 2 +- .../tests/output/custom_locale/author/alexis-metaireau2.html | 2 +- .../tests/output/custom_locale/author/alexis-metaireau3.html | 2 +- pelican/tests/output/custom_locale/authors.html | 2 +- pelican/tests/output/custom_locale/categories.html | 2 +- pelican/tests/output/custom_locale/category/bar.html | 2 +- pelican/tests/output/custom_locale/category/cat1.html | 2 +- pelican/tests/output/custom_locale/category/misc.html | 2 +- pelican/tests/output/custom_locale/category/yeah.html | 2 +- .../tests/output/custom_locale/drafts/a-draft-article.html | 2 +- pelican/tests/output/custom_locale/index.html | 2 +- pelican/tests/output/custom_locale/index2.html | 2 +- pelican/tests/output/custom_locale/index3.html | 2 +- pelican/tests/output/custom_locale/jinja2_template.html | 2 +- pelican/tests/output/custom_locale/oh-yeah-fr.html | 2 +- pelican/tests/output/custom_locale/override/index.html | 2 +- .../custom_locale/pages/this-is-a-test-hidden-page.html | 2 +- .../tests/output/custom_locale/pages/this-is-a-test-page.html | 2 +- .../posts/2010/décembre/02/this-is-a-super-article/index.html | 2 +- .../posts/2010/octobre/15/unbelievable/index.html | 2 +- .../custom_locale/posts/2010/octobre/20/oh-yeah/index.html | 2 +- .../posts/2011/avril/20/a-markdown-powered-article/index.html | 2 +- .../custom_locale/posts/2011/février/17/article-1/index.html | 2 +- .../custom_locale/posts/2011/février/17/article-2/index.html | 2 +- .../custom_locale/posts/2011/février/17/article-3/index.html | 2 +- .../posts/2012/février/29/second-article/index.html | 2 +- .../2012/novembre/30/filename_metadata-example/index.html | 2 +- pelican/tests/output/custom_locale/second-article-fr.html | 2 +- pelican/tests/output/custom_locale/tag/bar.html | 2 +- pelican/tests/output/custom_locale/tag/baz.html | 2 +- pelican/tests/output/custom_locale/tag/foo.html | 2 +- pelican/tests/output/custom_locale/tag/foobar.html | 2 +- pelican/tests/output/custom_locale/tag/oh.html | 2 +- pelican/tests/output/custom_locale/tag/yeah.html | 2 +- pelican/tests/output/custom_locale/tags.html | 2 +- pelican/themes/notmyidea/templates/base.html | 4 ++-- 75 files changed, 79 insertions(+), 74 deletions(-) diff --git a/THANKS b/THANKS index 15503473..a3af2426 100644 --- a/THANKS +++ b/THANKS @@ -24,6 +24,7 @@ Andrew Spiers Arnaud BOS asselinpaul Axel Haustant +Ben Rosser (TC01) Benoît HERVIER Borgar Brandon W Maister diff --git a/docs/settings.rst b/docs/settings.rst index 202fc45f..61fd8521 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -724,6 +724,10 @@ Setting name What does it do? ``TWITTER_USERNAME`` Allows for adding a button to articles to encourage others to tweet about them. Add your Twitter username if you want this button to appear. +``LINKS_WIDGET_NAME`` Allows override of the name of the links widget. + If not specified, defaults to "links". +``SOCIAL_WIDGET_NAME`` Allows override of the name of the "social" widget. + If not specified, defaults to "social". ======================= ======================================================= In addition, you can use the "wide" version of the ``notmyidea`` theme by diff --git a/pelican/tests/output/custom/a-markdown-powered-article.html b/pelican/tests/output/custom/a-markdown-powered-article.html index 59ffa4d1..6ccc6c42 100644 --- a/pelican/tests/output/custom/a-markdown-powered-article.html +++ b/pelican/tests/output/custom/a-markdown-powered-article.html @@ -71,7 +71,7 @@
    -

    blogroll

    +

    links

    • Biologeek
    • Filyb
    • diff --git a/pelican/tests/output/custom/archives.html b/pelican/tests/output/custom/archives.html index e238b73d..c256b7de 100644 --- a/pelican/tests/output/custom/archives.html +++ b/pelican/tests/output/custom/archives.html @@ -56,7 +56,7 @@
    -

    blogroll

    +

    links

    • Biologeek
    • Filyb
    • diff --git a/pelican/tests/output/custom/article-1.html b/pelican/tests/output/custom/article-1.html index 1fbd6719..33cf3a5f 100644 --- a/pelican/tests/output/custom/article-1.html +++ b/pelican/tests/output/custom/article-1.html @@ -70,7 +70,7 @@
    -

    blogroll

    +

    links

    • Biologeek
    • Filyb
    • diff --git a/pelican/tests/output/custom/article-2.html b/pelican/tests/output/custom/article-2.html index d62ddd14..8c8e47eb 100644 --- a/pelican/tests/output/custom/article-2.html +++ b/pelican/tests/output/custom/article-2.html @@ -70,7 +70,7 @@
    -

    blogroll

    +

    links

    • Biologeek
    • Filyb
    • diff --git a/pelican/tests/output/custom/article-3.html b/pelican/tests/output/custom/article-3.html index c862a0c2..773b89e0 100644 --- a/pelican/tests/output/custom/article-3.html +++ b/pelican/tests/output/custom/article-3.html @@ -70,7 +70,7 @@
    -

    blogroll

    +

    links

    • Biologeek
    • Filyb
    • diff --git a/pelican/tests/output/custom/author/alexis-metaireau.html b/pelican/tests/output/custom/author/alexis-metaireau.html index cbeb0555..d6521a5e 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau.html +++ b/pelican/tests/output/custom/author/alexis-metaireau.html @@ -129,7 +129,7 @@
    -

    blogroll

    +

    links

    • Biologeek
    • Filyb
    • diff --git a/pelican/tests/output/custom/author/alexis-metaireau2.html b/pelican/tests/output/custom/author/alexis-metaireau2.html index 6357bfb3..2101719b 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom/author/alexis-metaireau2.html @@ -143,7 +143,7 @@ YEAH !

    -

    blogroll

    +

    links

    • Biologeek
    • Filyb
    • diff --git a/pelican/tests/output/custom/author/alexis-metaireau3.html b/pelican/tests/output/custom/author/alexis-metaireau3.html index 3ca4dd0d..d531fd1e 100644 --- a/pelican/tests/output/custom/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom/author/alexis-metaireau3.html @@ -94,7 +94,7 @@ pelican.conf, it will ...

    -

    blogroll

    +

    links

    • Biologeek
    • Filyb
    • diff --git a/pelican/tests/output/custom/authors.html b/pelican/tests/output/custom/authors.html index f157de26..6539b3e9 100644 --- a/pelican/tests/output/custom/authors.html +++ b/pelican/tests/output/custom/authors.html @@ -38,7 +38,7 @@
      -

      blogroll

      +

      links

      • Biologeek
      • Filyb
      • diff --git a/pelican/tests/output/custom/categories.html b/pelican/tests/output/custom/categories.html index ddb1377b..17f44be7 100644 --- a/pelican/tests/output/custom/categories.html +++ b/pelican/tests/output/custom/categories.html @@ -36,7 +36,7 @@
      -

      blogroll

      +

      links

      • Biologeek
      • Filyb
      • diff --git a/pelican/tests/output/custom/category/bar.html b/pelican/tests/output/custom/category/bar.html index 9666d2e2..79803b1a 100644 --- a/pelican/tests/output/custom/category/bar.html +++ b/pelican/tests/output/custom/category/bar.html @@ -54,7 +54,7 @@ YEAH !

        -

        blogroll

        +

        links

        • Biologeek
        • Filyb
        • diff --git a/pelican/tests/output/custom/category/cat1.html b/pelican/tests/output/custom/category/cat1.html index d9132d10..0230ac2f 100644 --- a/pelican/tests/output/custom/category/cat1.html +++ b/pelican/tests/output/custom/category/cat1.html @@ -123,7 +123,7 @@
        -

        blogroll

        +

        links

        • Biologeek
        • Filyb
        • diff --git a/pelican/tests/output/custom/category/misc.html b/pelican/tests/output/custom/category/misc.html index b705a552..abe32c8d 100644 --- a/pelican/tests/output/custom/category/misc.html +++ b/pelican/tests/output/custom/category/misc.html @@ -134,7 +134,7 @@ pelican.conf, it will ...

        -

        blogroll

        +

        links

        • Biologeek
        • Filyb
        • diff --git a/pelican/tests/output/custom/category/yeah.html b/pelican/tests/output/custom/category/yeah.html index 6d94c9ad..d22bb9f8 100644 --- a/pelican/tests/output/custom/category/yeah.html +++ b/pelican/tests/output/custom/category/yeah.html @@ -62,7 +62,7 @@
          -

          blogroll

          +

          links

          • Biologeek
          • Filyb
          • diff --git a/pelican/tests/output/custom/drafts/a-draft-article.html b/pelican/tests/output/custom/drafts/a-draft-article.html index 963722f6..ba424259 100644 --- a/pelican/tests/output/custom/drafts/a-draft-article.html +++ b/pelican/tests/output/custom/drafts/a-draft-article.html @@ -56,7 +56,7 @@ listed anywhere else.

          -

          blogroll

          +

          links

          • Biologeek
          • Filyb
          • diff --git a/pelican/tests/output/custom/filename_metadata-example.html b/pelican/tests/output/custom/filename_metadata-example.html index cf4fc782..c4977f63 100644 --- a/pelican/tests/output/custom/filename_metadata-example.html +++ b/pelican/tests/output/custom/filename_metadata-example.html @@ -70,7 +70,7 @@
          -

          blogroll

          +

          links

          • Biologeek
          • Filyb
          • diff --git a/pelican/tests/output/custom/index.html b/pelican/tests/output/custom/index.html index 284bd0f4..87af6a06 100644 --- a/pelican/tests/output/custom/index.html +++ b/pelican/tests/output/custom/index.html @@ -129,7 +129,7 @@
          -

          blogroll

          +

          links

          • Biologeek
          • Filyb
          • diff --git a/pelican/tests/output/custom/index2.html b/pelican/tests/output/custom/index2.html index 7e5b7230..0c457a65 100644 --- a/pelican/tests/output/custom/index2.html +++ b/pelican/tests/output/custom/index2.html @@ -143,7 +143,7 @@ YEAH !

          -

          blogroll

          +

          links

          • Biologeek
          • Filyb
          • diff --git a/pelican/tests/output/custom/index3.html b/pelican/tests/output/custom/index3.html index b968b7e8..523288c5 100644 --- a/pelican/tests/output/custom/index3.html +++ b/pelican/tests/output/custom/index3.html @@ -94,7 +94,7 @@ pelican.conf, it will ...

          -

          blogroll

          +

          links

          • Biologeek
          • Filyb
          • diff --git a/pelican/tests/output/custom/jinja2_template.html b/pelican/tests/output/custom/jinja2_template.html index 21f678f9..a098ed49 100644 --- a/pelican/tests/output/custom/jinja2_template.html +++ b/pelican/tests/output/custom/jinja2_template.html @@ -33,7 +33,7 @@ Some text
            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/oh-yeah-fr.html b/pelican/tests/output/custom/oh-yeah-fr.html index d457b1bf..9f56f27b 100644 --- a/pelican/tests/output/custom/oh-yeah-fr.html +++ b/pelican/tests/output/custom/oh-yeah-fr.html @@ -72,7 +72,7 @@ Translations:
            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/oh-yeah.html b/pelican/tests/output/custom/oh-yeah.html index 235a4903..caddf53c 100644 --- a/pelican/tests/output/custom/oh-yeah.html +++ b/pelican/tests/output/custom/oh-yeah.html @@ -77,7 +77,7 @@ YEAH !

            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/override/index.html b/pelican/tests/output/custom/override/index.html index 3753ba84..8ab5f7eb 100644 --- a/pelican/tests/output/custom/override/index.html +++ b/pelican/tests/output/custom/override/index.html @@ -37,7 +37,7 @@ at a custom location.

            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html index f8c06b1f..41beb5a3 100644 --- a/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/custom/pages/this-is-a-test-hidden-page.html @@ -37,7 +37,7 @@ Anyone can see this page but it's not linked to anywhere!

            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/pages/this-is-a-test-page.html b/pelican/tests/output/custom/pages/this-is-a-test-page.html index a4b171c9..b548b52c 100644 --- a/pelican/tests/output/custom/pages/this-is-a-test-page.html +++ b/pelican/tests/output/custom/pages/this-is-a-test-page.html @@ -37,7 +37,7 @@
            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/second-article-fr.html b/pelican/tests/output/custom/second-article-fr.html index 42289377..9ada2475 100644 --- a/pelican/tests/output/custom/second-article-fr.html +++ b/pelican/tests/output/custom/second-article-fr.html @@ -72,7 +72,7 @@
            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/second-article.html b/pelican/tests/output/custom/second-article.html index 689b1203..f2d9ffa4 100644 --- a/pelican/tests/output/custom/second-article.html +++ b/pelican/tests/output/custom/second-article.html @@ -72,7 +72,7 @@
            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/tag/bar.html b/pelican/tests/output/custom/tag/bar.html index f462ffbd..b15860f1 100644 --- a/pelican/tests/output/custom/tag/bar.html +++ b/pelican/tests/output/custom/tag/bar.html @@ -113,7 +113,7 @@ YEAH !

            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/tag/baz.html b/pelican/tests/output/custom/tag/baz.html index 2c9b1d08..a581e319 100644 --- a/pelican/tests/output/custom/tag/baz.html +++ b/pelican/tests/output/custom/tag/baz.html @@ -70,7 +70,7 @@
            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/tag/foo.html b/pelican/tests/output/custom/tag/foo.html index 9c58956b..36282dd7 100644 --- a/pelican/tests/output/custom/tag/foo.html +++ b/pelican/tests/output/custom/tag/foo.html @@ -83,7 +83,7 @@ as well as inline markup.

            -

            blogroll

            +

            links

            • Biologeek
            • Filyb
            • diff --git a/pelican/tests/output/custom/tag/foobar.html b/pelican/tests/output/custom/tag/foobar.html index 7d17d1fb..ab84124c 100644 --- a/pelican/tests/output/custom/tag/foobar.html +++ b/pelican/tests/output/custom/tag/foobar.html @@ -62,7 +62,7 @@
              -

              blogroll

              +

              links

              • Biologeek
              • Filyb
              • diff --git a/pelican/tests/output/custom/tag/oh.html b/pelican/tests/output/custom/tag/oh.html index 855d1f8b..c7f9701d 100644 --- a/pelican/tests/output/custom/tag/oh.html +++ b/pelican/tests/output/custom/tag/oh.html @@ -36,7 +36,7 @@
              -

              blogroll

              +

              links

              • Biologeek
              • Filyb
              • diff --git a/pelican/tests/output/custom/tag/yeah.html b/pelican/tests/output/custom/tag/yeah.html index e3c765fa..c14f1f47 100644 --- a/pelican/tests/output/custom/tag/yeah.html +++ b/pelican/tests/output/custom/tag/yeah.html @@ -54,7 +54,7 @@ YEAH !

                -

                blogroll

                +

                links

                • Biologeek
                • Filyb
                • diff --git a/pelican/tests/output/custom/tags.html b/pelican/tests/output/custom/tags.html index 23f70c0f..9d3e2bdf 100644 --- a/pelican/tests/output/custom/tags.html +++ b/pelican/tests/output/custom/tags.html @@ -43,7 +43,7 @@
                  -

                  blogroll

                  +

                  links

                  • Biologeek
                  • Filyb
                  • diff --git a/pelican/tests/output/custom/this-is-a-super-article.html b/pelican/tests/output/custom/this-is-a-super-article.html index e370769c..a36bf412 100644 --- a/pelican/tests/output/custom/this-is-a-super-article.html +++ b/pelican/tests/output/custom/this-is-a-super-article.html @@ -85,7 +85,7 @@
                  -

                  blogroll

                  +

                  links

                  • Biologeek
                  • Filyb
                  • diff --git a/pelican/tests/output/custom/unbelievable.html b/pelican/tests/output/custom/unbelievable.html index 2e51cf07..77a16a8d 100644 --- a/pelican/tests/output/custom/unbelievable.html +++ b/pelican/tests/output/custom/unbelievable.html @@ -102,7 +102,7 @@ pelican.conf, it will have nothing in default.

                  -

                  blogroll

                  +

                  links

                  • Biologeek
                  • Filyb
                  • diff --git a/pelican/tests/output/custom_locale/archives.html b/pelican/tests/output/custom_locale/archives.html index 68b46b14..ed375051 100644 --- a/pelican/tests/output/custom_locale/archives.html +++ b/pelican/tests/output/custom_locale/archives.html @@ -56,7 +56,7 @@
                  -

                  blogroll

                  +

                  links

                  • Biologeek
                  • Filyb
                  • diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau.html b/pelican/tests/output/custom_locale/author/alexis-metaireau.html index f8b36655..299578cf 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau.html @@ -129,7 +129,7 @@
                  -

                  blogroll

                  +

                  links

                  • Biologeek
                  • Filyb
                  • diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html index 5d9e6c3c..6f3a0b71 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau2.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau2.html @@ -143,7 +143,7 @@ YEAH !

                  -

                  blogroll

                  +

                  links

                  • Biologeek
                  • Filyb
                  • diff --git a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html index 2fea24c3..6eb5645e 100644 --- a/pelican/tests/output/custom_locale/author/alexis-metaireau3.html +++ b/pelican/tests/output/custom_locale/author/alexis-metaireau3.html @@ -94,7 +94,7 @@ pelican.conf, it will ...

                  -

                  blogroll

                  +

                  links

                  • Biologeek
                  • Filyb
                  • diff --git a/pelican/tests/output/custom_locale/authors.html b/pelican/tests/output/custom_locale/authors.html index 91ea479d..5cd227d9 100644 --- a/pelican/tests/output/custom_locale/authors.html +++ b/pelican/tests/output/custom_locale/authors.html @@ -38,7 +38,7 @@
                    -

                    blogroll

                    +

                    links

                    • Biologeek
                    • Filyb
                    • diff --git a/pelican/tests/output/custom_locale/categories.html b/pelican/tests/output/custom_locale/categories.html index ddb1377b..17f44be7 100644 --- a/pelican/tests/output/custom_locale/categories.html +++ b/pelican/tests/output/custom_locale/categories.html @@ -36,7 +36,7 @@
                    -

                    blogroll

                    +

                    links

                    • Biologeek
                    • Filyb
                    • diff --git a/pelican/tests/output/custom_locale/category/bar.html b/pelican/tests/output/custom_locale/category/bar.html index c416b358..2aeef95c 100644 --- a/pelican/tests/output/custom_locale/category/bar.html +++ b/pelican/tests/output/custom_locale/category/bar.html @@ -54,7 +54,7 @@ YEAH !

                      -

                      blogroll

                      +

                      links

                      • Biologeek
                      • Filyb
                      • diff --git a/pelican/tests/output/custom_locale/category/cat1.html b/pelican/tests/output/custom_locale/category/cat1.html index 871b2e3f..48c3ba18 100644 --- a/pelican/tests/output/custom_locale/category/cat1.html +++ b/pelican/tests/output/custom_locale/category/cat1.html @@ -123,7 +123,7 @@
                      -

                      blogroll

                      +

                      links

                      • Biologeek
                      • Filyb
                      • diff --git a/pelican/tests/output/custom_locale/category/misc.html b/pelican/tests/output/custom_locale/category/misc.html index f44f725d..d977a4cc 100644 --- a/pelican/tests/output/custom_locale/category/misc.html +++ b/pelican/tests/output/custom_locale/category/misc.html @@ -134,7 +134,7 @@ pelican.conf, it will ...

                      -

                      blogroll

                      +

                      links

                      • Biologeek
                      • Filyb
                      • diff --git a/pelican/tests/output/custom_locale/category/yeah.html b/pelican/tests/output/custom_locale/category/yeah.html index c5e6c7f0..a00e2028 100644 --- a/pelican/tests/output/custom_locale/category/yeah.html +++ b/pelican/tests/output/custom_locale/category/yeah.html @@ -62,7 +62,7 @@
                        -

                        blogroll

                        +

                        links

                        • Biologeek
                        • Filyb
                        • diff --git a/pelican/tests/output/custom_locale/drafts/a-draft-article.html b/pelican/tests/output/custom_locale/drafts/a-draft-article.html index dfd4f00e..c1817e25 100644 --- a/pelican/tests/output/custom_locale/drafts/a-draft-article.html +++ b/pelican/tests/output/custom_locale/drafts/a-draft-article.html @@ -56,7 +56,7 @@ listed anywhere else.

                        -

                        blogroll

                        +

                        links

                        • Biologeek
                        • Filyb
                        • diff --git a/pelican/tests/output/custom_locale/index.html b/pelican/tests/output/custom_locale/index.html index 3ad201de..f1174c31 100644 --- a/pelican/tests/output/custom_locale/index.html +++ b/pelican/tests/output/custom_locale/index.html @@ -129,7 +129,7 @@
                        -

                        blogroll

                        +

                        links

                        • Biologeek
                        • Filyb
                        • diff --git a/pelican/tests/output/custom_locale/index2.html b/pelican/tests/output/custom_locale/index2.html index bbd1efd0..286ff9c3 100644 --- a/pelican/tests/output/custom_locale/index2.html +++ b/pelican/tests/output/custom_locale/index2.html @@ -143,7 +143,7 @@ YEAH !

                        -

                        blogroll

                        +

                        links

                        • Biologeek
                        • Filyb
                        • diff --git a/pelican/tests/output/custom_locale/index3.html b/pelican/tests/output/custom_locale/index3.html index 926bc25e..c30b7bb3 100644 --- a/pelican/tests/output/custom_locale/index3.html +++ b/pelican/tests/output/custom_locale/index3.html @@ -94,7 +94,7 @@ pelican.conf, it will ...

                        -

                        blogroll

                        +

                        links

                        • Biologeek
                        • Filyb
                        • diff --git a/pelican/tests/output/custom_locale/jinja2_template.html b/pelican/tests/output/custom_locale/jinja2_template.html index 21f678f9..a098ed49 100644 --- a/pelican/tests/output/custom_locale/jinja2_template.html +++ b/pelican/tests/output/custom_locale/jinja2_template.html @@ -33,7 +33,7 @@ Some text
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/oh-yeah-fr.html b/pelican/tests/output/custom_locale/oh-yeah-fr.html index f5cae7cf..33e05c3b 100644 --- a/pelican/tests/output/custom_locale/oh-yeah-fr.html +++ b/pelican/tests/output/custom_locale/oh-yeah-fr.html @@ -72,7 +72,7 @@ Translations:
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/override/index.html b/pelican/tests/output/custom_locale/override/index.html index 3753ba84..8ab5f7eb 100644 --- a/pelican/tests/output/custom_locale/override/index.html +++ b/pelican/tests/output/custom_locale/override/index.html @@ -37,7 +37,7 @@ at a custom location.

                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html b/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html index f8c06b1f..41beb5a3 100644 --- a/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html +++ b/pelican/tests/output/custom_locale/pages/this-is-a-test-hidden-page.html @@ -37,7 +37,7 @@ Anyone can see this page but it's not linked to anywhere!

                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html b/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html index a4b171c9..b548b52c 100644 --- a/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html +++ b/pelican/tests/output/custom_locale/pages/this-is-a-test-page.html @@ -37,7 +37,7 @@
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html index def80358..b7018b64 100644 --- a/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/décembre/02/this-is-a-super-article/index.html @@ -85,7 +85,7 @@
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html index a6ea8699..3c364ffc 100644 --- a/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/15/unbelievable/index.html @@ -102,7 +102,7 @@ pelican.conf, it will have nothing in default.

                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html index 59fd6edd..3ec06207 100644 --- a/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html +++ b/pelican/tests/output/custom_locale/posts/2010/octobre/20/oh-yeah/index.html @@ -77,7 +77,7 @@ YEAH !

                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html index e6d585ff..81421b37 100644 --- a/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/avril/20/a-markdown-powered-article/index.html @@ -71,7 +71,7 @@
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html index d768e15f..64bab330 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-1/index.html @@ -70,7 +70,7 @@
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html index b223473a..60de5faa 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-2/index.html @@ -70,7 +70,7 @@
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html index 3bd59fa5..13aa5797 100644 --- a/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html +++ b/pelican/tests/output/custom_locale/posts/2011/février/17/article-3/index.html @@ -70,7 +70,7 @@
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html index aff4d61f..9608314f 100644 --- a/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html +++ b/pelican/tests/output/custom_locale/posts/2012/février/29/second-article/index.html @@ -72,7 +72,7 @@
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html index 659004bd..9b3df468 100644 --- a/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html +++ b/pelican/tests/output/custom_locale/posts/2012/novembre/30/filename_metadata-example/index.html @@ -70,7 +70,7 @@
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/second-article-fr.html b/pelican/tests/output/custom_locale/second-article-fr.html index 79fb5406..dc17237f 100644 --- a/pelican/tests/output/custom_locale/second-article-fr.html +++ b/pelican/tests/output/custom_locale/second-article-fr.html @@ -72,7 +72,7 @@
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/tag/bar.html b/pelican/tests/output/custom_locale/tag/bar.html index c1f33e64..12f368aa 100644 --- a/pelican/tests/output/custom_locale/tag/bar.html +++ b/pelican/tests/output/custom_locale/tag/bar.html @@ -113,7 +113,7 @@ YEAH !

                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/tag/baz.html b/pelican/tests/output/custom_locale/tag/baz.html index 597199e7..8a2c1c31 100644 --- a/pelican/tests/output/custom_locale/tag/baz.html +++ b/pelican/tests/output/custom_locale/tag/baz.html @@ -70,7 +70,7 @@
                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/tag/foo.html b/pelican/tests/output/custom_locale/tag/foo.html index 288f1768..c08baed4 100644 --- a/pelican/tests/output/custom_locale/tag/foo.html +++ b/pelican/tests/output/custom_locale/tag/foo.html @@ -83,7 +83,7 @@ as well as inline markup.

                          -

                          blogroll

                          +

                          links

                          • Biologeek
                          • Filyb
                          • diff --git a/pelican/tests/output/custom_locale/tag/foobar.html b/pelican/tests/output/custom_locale/tag/foobar.html index 59dcede1..42bb1325 100644 --- a/pelican/tests/output/custom_locale/tag/foobar.html +++ b/pelican/tests/output/custom_locale/tag/foobar.html @@ -62,7 +62,7 @@
                            -

                            blogroll

                            +

                            links

                            • Biologeek
                            • Filyb
                            • diff --git a/pelican/tests/output/custom_locale/tag/oh.html b/pelican/tests/output/custom_locale/tag/oh.html index 855d1f8b..c7f9701d 100644 --- a/pelican/tests/output/custom_locale/tag/oh.html +++ b/pelican/tests/output/custom_locale/tag/oh.html @@ -36,7 +36,7 @@
                            -

                            blogroll

                            +

                            links

                            • Biologeek
                            • Filyb
                            • diff --git a/pelican/tests/output/custom_locale/tag/yeah.html b/pelican/tests/output/custom_locale/tag/yeah.html index 4dc36ce5..581c67c0 100644 --- a/pelican/tests/output/custom_locale/tag/yeah.html +++ b/pelican/tests/output/custom_locale/tag/yeah.html @@ -54,7 +54,7 @@ YEAH !

                              -

                              blogroll

                              +

                              links

                              • Biologeek
                              • Filyb
                              • diff --git a/pelican/tests/output/custom_locale/tags.html b/pelican/tests/output/custom_locale/tags.html index aa182ab6..59a36ace 100644 --- a/pelican/tests/output/custom_locale/tags.html +++ b/pelican/tests/output/custom_locale/tags.html @@ -43,7 +43,7 @@
                                -

                                blogroll

                                +

                                links

                                • Biologeek
                                • Filyb
                                • diff --git a/pelican/themes/notmyidea/templates/base.html b/pelican/themes/notmyidea/templates/base.html index 188715d4..7818c235 100644 --- a/pelican/themes/notmyidea/templates/base.html +++ b/pelican/themes/notmyidea/templates/base.html @@ -41,7 +41,7 @@
                                  {% if LINKS %}
                                  -

                                  blogroll

                                  +

                                  {{ LINKS_WIDGET_NAME | default('links') }}

                                    {% for name, link in LINKS %}
                                  • {{ name }}
                                  • @@ -51,7 +51,7 @@ {% endif %} {% if SOCIAL or FEED_ALL_ATOM or FEED_ALL_RSS %}
    - + \ No newline at end of file diff --git a/pelican/tests/output/basic/article-1.html b/pelican/tests/output/basic/article-1.html index b09eef79..a5c92675 100644 --- a/pelican/tests/output/basic/article-1.html +++ b/pelican/tests/output/basic/article-1.html @@ -38,7 +38,7 @@ Published: Thu 17 February 2011 -

    In cat1.

    +

    In cat1.

    Article 1

    @@ -65,4 +65,4 @@
    - + \ No newline at end of file diff --git a/pelican/tests/output/basic/article-2.html b/pelican/tests/output/basic/article-2.html index e340a4f6..1a182c5f 100644 --- a/pelican/tests/output/basic/article-2.html +++ b/pelican/tests/output/basic/article-2.html @@ -38,7 +38,7 @@ Published: Thu 17 February 2011 -

    In cat1.

    +

    In cat1.

    Article 2

    @@ -65,4 +65,4 @@
    - + \ No newline at end of file diff --git a/pelican/tests/output/basic/article-3.html b/pelican/tests/output/basic/article-3.html index 08bf4fc1..7fab0edd 100644 --- a/pelican/tests/output/basic/article-3.html +++ b/pelican/tests/output/basic/article-3.html @@ -38,7 +38,7 @@ Published: Thu 17 February 2011 -

    In cat1.

    +

    In cat1.

    Article 3

    @@ -65,4 +65,4 @@
    - + \ No newline at end of file diff --git a/pelican/tests/output/basic/author/alexis-metaireau.html b/pelican/tests/output/basic/author/alexis-metaireau.html index eeca537a..3fe6c95e 100644 --- a/pelican/tests/output/basic/author/alexis-metaireau.html +++ b/pelican/tests/output/basic/author/alexis-metaireau.html @@ -38,10 +38,10 @@
    - By Alexis Métaireau + By Alexis Métaireau
    -

    In yeah.

    -

    tags: foobarfoobar

    +

    In yeah.

    +

    tags: foo bar foobar

    Some content here !

    This is a simple title

    @@ -74,10 +74,10 @@
    - By Alexis Métaireau + By Alexis Métaireau
    -

    In bar.

    -

    tags: ohbaryeah

    +

    In bar.

    +

    tags: oh bar yeah

    Why not ?

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

    read more
    - - + +
    - + \ No newline at end of file diff --git a/pelican/tests/output/basic/authors.html b/pelican/tests/output/basic/authors.html index 288543b5..c580269f 100644 --- a/pelican/tests/output/basic/authors.html +++ b/pelican/tests/output/basic/authors.html @@ -51,4 +51,4 @@
    - + \ No newline at end of file diff --git a/pelican/tests/output/basic/categories.html b/pelican/tests/output/basic/categories.html index 9a6682c0..5aa2791f 100644 --- a/pelican/tests/output/basic/categories.html +++ b/pelican/tests/output/basic/categories.html @@ -49,4 +49,4 @@
    - + \ No newline at end of file diff --git a/pelican/tests/output/basic/category/bar.html b/pelican/tests/output/basic/category/bar.html index d3eb38da..6ae80ed1 100644 --- a/pelican/tests/output/basic/category/bar.html +++ b/pelican/tests/output/basic/category/bar.html @@ -34,10 +34,10 @@
    - By Alexis Métaireau + By Alexis Métaireau
    -

    In bar.

    -

    tags: ohbaryeah

    +

    In bar.

    +

    tags: oh bar yeah

    Why not ?

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

    - + \ No newline at end of file diff --git a/pelican/tests/output/basic/category/cat1.html b/pelican/tests/output/basic/category/cat1.html index f21bc9ab..369146d0 100644 --- a/pelican/tests/output/basic/category/cat1.html +++ b/pelican/tests/output/basic/category/cat1.html @@ -33,7 +33,7 @@ Published: Wed 20 April 2011 -

    In cat1.

    +

    In cat1.

    You're mutually oblivious.

    a root-relative link to unbelievable @@ -56,7 +56,7 @@ Published: Thu 17 February 2011 -

    In cat1.

    +

    In cat1.

    Article 1

    @@ -76,7 +76,7 @@ Published: Thu 17 February 2011 -

    In cat1.

    +

    In cat1.

    Article 2

    @@ -96,15 +96,15 @@ Published: Thu 17 February 2011 -

    In cat1.

    +

    In cat1.

    Article 3

    read more
    - - + +