From cc1988fbda5f191768b9d20ef0f942b572d0bb39 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Thu, 14 Jun 2012 23:08:34 -0400
Subject: [PATCH 0001/1816] new HTMLReader
---
pelican/readers.py | 186 ++++++++----------
tests/content/article_with_keywords.html | 6 +
tests/content/article_with_metadata.html | 15 ++
.../article_with_uppercase_metadata.html | 6 +
tests/test_readers.py | 38 ++++
5 files changed, 150 insertions(+), 101 deletions(-)
create mode 100644 tests/content/article_with_keywords.html
create mode 100644 tests/content/article_with_metadata.html
create mode 100644 tests/content/article_with_uppercase_metadata.html
diff --git a/pelican/readers.py b/pelican/readers.py
index 83cb7e3b..9ce3e3c0 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -129,117 +129,101 @@ class MarkdownReader(Reader):
metadata[name] = self.process_metadata(name, value[0])
return content, metadata
-"""
-class HtmlReader(Reader):
- file_extensions = ['html', 'htm']
- _re = re.compile('\<\!\-\-\#\s?[A-z0-9_-]*\s?\:s?[A-z0-9\s_-]*\s?\-\-\>')
-
- def read(self, filename):
- with open(filename) as content:
- metadata = {'title': 'unnamed'}
- for i in self._re.findall(content):
- key = i.split(':')[0][5:].strip()
- value = i.split(':')[-1][:-3].strip()
- name = key.lower()
- metadata[name] = self.process_metadata(name, value)
-
- return content, metadata
-"""
-
-class PelicanHTMLParser(HTMLParser):
- def __init__(self, settings):
- HTMLParser.__init__(self)
- self.body = ''
- self.metadata = {}
- self.settings = settings
-
- self._data_buffer = ''
-
- self._in_top_level = True
- self._in_head = False
- self._in_title = False
- self._in_body = False
- self._in_tags = False
-
- def handle_starttag(self, tag, attrs):
- if tag == 'head' and self._in_top_level:
- self._in_top_level = False
- self._in_head = True
- elif tag == 'title' and self._in_head:
- self._in_title = True
- self._data_buffer = ''
- elif tag == 'body' and self._in_top_level:
- self._in_top_level = False
- self._in_body = True
- self._data_buffer = ''
- elif tag == 'meta' and self._in_head:
- self._handle_meta_tag(attrs)
-
- elif self._in_body:
- self._data_buffer += self.build_tag(tag, attrs, False)
-
- def handle_endtag(self, tag):
- if tag == 'head':
- if self._in_head:
- self._in_head = False
- self._in_top_level = True
- elif tag == 'title':
- self._in_title = False
- self.metadata['title'] = self._data_buffer
- elif tag == 'body':
- self.body = self._data_buffer
- self._in_body = False
- self._in_top_level = True
- elif self._in_body:
- self._data_buffer += '{}>'.format(cgi.escape(tag))
-
- def handle_startendtag(self, tag, attrs):
- if tag == 'meta' and self._in_head:
- self._handle_meta_tag(attrs)
- if self._in_body:
- self._data_buffer += self.build_tag(tag, attrs, True)
-
- def handle_comment(self, data):
- if self._in_body and data.strip() == 'PELICAN_END_SUMMARY':
- self.metadata['summary'] = self._data_buffer
-
- def handle_data(self, data):
- self._data_buffer += data
-
- def build_tag(self, tag, attrs, close_tag):
- result = '<{}'.format(cgi.escape(tag))
- result += ''.join((' {}="{}"'.format(cgi.escape(k), cgi.escape(v)) for k,v in attrs))
- if close_tag:
- return result + ' />'
- return result + '>'
-
- def _handle_meta_tag(self, attrs):
- name = self._attr_value(attrs, 'name')
- contents = self._attr_value(attrs, 'contents', '')
- if name == 'keywords':
- if contents:
- self.metadata['tags'] = [Tag(unicode(tag), self.settings) for tag in contents.split(',')]
- elif name == 'date':
- self.metadata['date'] = get_date(contents)
- else:
- self.metadata[name] = contents
-
- @classmethod
- def _attr_value(cls, attrs, name, default=None):
- return next((x[1] for x in attrs if x[0] == name), default)
-
class HTMLReader(Reader):
+ """Parses HTML files as input, looking for meta, title, and body tags"""
file_extensions = ['htm', 'html']
enabled = True
+ class _HTMLParser(HTMLParser):
+ def __init__(self, settings):
+ HTMLParser.__init__(self)
+ self.body = ''
+ self.metadata = {}
+ self.settings = settings
+
+ self._data_buffer = ''
+
+ self._in_top_level = True
+ self._in_head = False
+ self._in_title = False
+ self._in_body = False
+ self._in_tags = False
+
+ def handle_starttag(self, tag, attrs):
+ if tag == 'head' and self._in_top_level:
+ self._in_top_level = False
+ self._in_head = True
+ elif tag == 'title' and self._in_head:
+ self._in_title = True
+ self._data_buffer = ''
+ elif tag == 'body' and self._in_top_level:
+ self._in_top_level = False
+ self._in_body = True
+ self._data_buffer = ''
+ elif tag == 'meta' and self._in_head:
+ self._handle_meta_tag(attrs)
+
+ elif self._in_body:
+ self._data_buffer += self.build_tag(tag, attrs, False)
+
+ def handle_endtag(self, tag):
+ if tag == 'head':
+ if self._in_head:
+ self._in_head = False
+ self._in_top_level = True
+ elif tag == 'title':
+ self._in_title = False
+ self.metadata['title'] = self._data_buffer
+ elif tag == 'body':
+ self.body = self._data_buffer
+ self._in_body = False
+ self._in_top_level = True
+ elif self._in_body:
+ self._data_buffer += '{}>'.format(cgi.escape(tag))
+
+ def handle_startendtag(self, tag, attrs):
+ if tag == 'meta' and self._in_head:
+ self._handle_meta_tag(attrs)
+ if self._in_body:
+ self._data_buffer += self.build_tag(tag, attrs, True)
+
+ def handle_comment(self, data):
+ if self._in_body and data.strip() == 'PELICAN_END_SUMMARY':
+ self.metadata['summary'] = self._data_buffer
+
+ def handle_data(self, data):
+ self._data_buffer += data
+
+ def build_tag(self, tag, attrs, close_tag):
+ result = '<{}'.format(cgi.escape(tag))
+ result += ''.join((' {}="{}"'.format(cgi.escape(k), cgi.escape(v)) for k,v in attrs))
+ if close_tag:
+ return result + ' />'
+ return result + '>'
+
+ def _handle_meta_tag(self, attrs):
+ name = self._attr_value(attrs, 'name').lower()
+ contents = self._attr_value(attrs, 'contents', '')
+
+ if name == 'keywords':
+ name = 'tags'
+ self.metadata[name] = contents
+
+ @classmethod
+ def _attr_value(cls, attrs, name, default=None):
+ return next((x[1] for x in attrs if x[0] == name), default)
+
def read(self, filename):
"""Parse content and metadata of markdown files"""
with open(filename) as content:
- parser = PelicanHTMLParser(self.settings)
+ parser = self._HTMLParser(self.settings)
parser.feed(content)
parser.close()
- return parser.body, parser.metadata
+ metadata = {}
+ for k in parser.metadata:
+ metadata[k] = self.process_metadata(k, parser.metadata[k])
+ return parser.body, metadata
_EXTENSIONS = {}
diff --git a/tests/content/article_with_keywords.html b/tests/content/article_with_keywords.html
new file mode 100644
index 00000000..c869f514
--- /dev/null
+++ b/tests/content/article_with_keywords.html
@@ -0,0 +1,6 @@
+
+
+ This is a super article !
+
+
+
diff --git a/tests/content/article_with_metadata.html b/tests/content/article_with_metadata.html
new file mode 100644
index 00000000..2bd77241
--- /dev/null
+++ b/tests/content/article_with_metadata.html
@@ -0,0 +1,15 @@
+
+
+ This is a super article !
+
+
+
+
+
+
+
+ Multi-line metadata should be supported
+ as well as inline markup.
+
+
+
diff --git a/tests/content/article_with_uppercase_metadata.html b/tests/content/article_with_uppercase_metadata.html
new file mode 100644
index 00000000..4fe5a9ee
--- /dev/null
+++ b/tests/content/article_with_uppercase_metadata.html
@@ -0,0 +1,6 @@
+
+
+ This is a super article !
+
+
+
diff --git a/tests/test_readers.py b/tests/test_readers.py
index a921cfc2..52887068 100644
--- a/tests/test_readers.py
+++ b/tests/test_readers.py
@@ -86,3 +86,41 @@ class MdReaderTest(unittest.TestCase):
"
This is another markdown test file. Uses the mkd extension.
"
self.assertEqual(content, expected)
+
+class HTMLReaderTest(unittest.TestCase):
+
+ def test_article_with_metadata(self):
+ reader = readers.HTMLReader({})
+ content, metadata = reader.read(_filename('article_with_metadata.html'))
+ expected = {
+ 'category': 'yeah',
+ 'author': u'Alexis Métaireau',
+ 'title': 'This is a super article !',
+ 'summary': u'''
+ Multi-line metadata should be supported
+ as well as inline markup.
+ ''',
+ 'date': datetime.datetime(2010, 12, 2, 10, 14),
+ 'tags': ['foo', 'bar', 'foobar'],
+ 'custom_field': 'http://notmyidea.org',
+ }
+
+ for key, value in expected.items():
+ self.assertEquals(value, metadata[key], key)
+
+ def test_article_with_keywords(self):
+ reader = readers.HTMLReader({})
+ content, metadata = reader.read(_filename('article_with_keywords.html'))
+ expected = {
+ 'tags': ['foo', 'bar', 'foobar'],
+ }
+
+ for key, value in expected.items():
+ self.assertEquals(value, metadata[key], key)
+
+ def test_article_metadata_key_lowercase(self):
+ """Keys of metadata should be lowercase."""
+ reader = readers.HTMLReader({})
+ content, metadata = reader.read(_filename('article_with_uppercase_metadata.html'))
+ self.assertIn('category', metadata, "Key should be lowercase.")
+ self.assertEquals('Yeah', metadata.get('category'), "Value keeps cases.")
From 0373c15e430e168928b645be3b9513f093b97403 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Thu, 14 Jun 2012 23:16:27 -0400
Subject: [PATCH 0002/1816] include html comments properly in reader
---
pelican/readers.py | 2 ++
tests/content/article_with_comments.html | 7 +++++
tests/test_readers.py | 36 ++++++++++++++++++------
3 files changed, 36 insertions(+), 9 deletions(-)
create mode 100644 tests/content/article_with_comments.html
diff --git a/pelican/readers.py b/pelican/readers.py
index 9ce3e3c0..e3d0e0dd 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -190,6 +190,8 @@ class HTMLReader(Reader):
def handle_comment(self, data):
if self._in_body and data.strip() == 'PELICAN_END_SUMMARY':
self.metadata['summary'] = self._data_buffer
+ else:
+ self._data_buffer += ''.format(data)
def handle_data(self, data):
self._data_buffer += data
diff --git a/tests/content/article_with_comments.html b/tests/content/article_with_comments.html
new file mode 100644
index 00000000..f222682d
--- /dev/null
+++ b/tests/content/article_with_comments.html
@@ -0,0 +1,7 @@
+
+
+ Summary comment is not included.
+
+
+
+
diff --git a/tests/test_readers.py b/tests/test_readers.py
index 52887068..b3e30bfc 100644
--- a/tests/test_readers.py
+++ b/tests/test_readers.py
@@ -88,6 +88,33 @@ class MdReaderTest(unittest.TestCase):
self.assertEqual(content, expected)
class HTMLReaderTest(unittest.TestCase):
+ def test_article_with_comments(self):
+ reader = readers.HTMLReader({})
+ content, metadata = reader.read(_filename('article_with_comments.html'))
+ expected = {
+ 'summary': '''
+ Summary comment is not included.
+ ''',
+ }
+
+ for key, value in expected.items():
+ self.assertEquals(value, metadata[key], key)
+
+ self.assertEquals('''
+ Summary comment is not included.
+
+
+ ''', content)
+
+ def test_article_with_keywords(self):
+ reader = readers.HTMLReader({})
+ content, metadata = reader.read(_filename('article_with_keywords.html'))
+ expected = {
+ 'tags': ['foo', 'bar', 'foobar'],
+ }
+
+ for key, value in expected.items():
+ self.assertEquals(value, metadata[key], key)
def test_article_with_metadata(self):
reader = readers.HTMLReader({})
@@ -108,15 +135,6 @@ class HTMLReaderTest(unittest.TestCase):
for key, value in expected.items():
self.assertEquals(value, metadata[key], key)
- def test_article_with_keywords(self):
- reader = readers.HTMLReader({})
- content, metadata = reader.read(_filename('article_with_keywords.html'))
- expected = {
- 'tags': ['foo', 'bar', 'foobar'],
- }
-
- for key, value in expected.items():
- self.assertEquals(value, metadata[key], key)
def test_article_metadata_key_lowercase(self):
"""Keys of metadata should be lowercase."""
From c608d39aa40b8304f4e2e241564796201e582da4 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Wed, 20 Jun 2012 19:52:17 -0400
Subject: [PATCH 0003/1816] re-import htmlparser
---
pelican/readers.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/pelican/readers.py b/pelican/readers.py
index 870c11c8..1916fa1e 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -15,6 +15,8 @@ except ImportError:
Markdown = False # NOQA
import re
+from htmlparser import HTMLParser
+
from pelican.contents import Category, Tag, Author
from pelican.utils import get_date, open
From caa4442abb145d419a3120c7339ad7ecf91ac56c Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Wed, 20 Jun 2012 19:59:32 -0400
Subject: [PATCH 0004/1816] re-import cgi. properly turn utils.open into a
context manager
---
pelican/readers.py | 3 ++-
pelican/utils.py | 9 +++++++--
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/pelican/readers.py b/pelican/readers.py
index 1916fa1e..d05ab40f 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -15,7 +15,8 @@ except ImportError:
Markdown = False # NOQA
import re
-from htmlparser import HTMLParser
+import cgi
+from HTMLParser import HTMLParser
from pelican.contents import Category, Tag, Author
from pelican.utils import get_date, open
diff --git a/pelican/utils.py b/pelican/utils.py
index 0940bf72..088a8faa 100644
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -34,10 +34,15 @@ def get_date(string):
raise ValueError("'%s' is not a valid date" % string)
-def open(filename):
+class open(object):
"""Open a file and return it's content"""
- return _open(filename, encoding='utf-8').read()
+ def __init__(self, filename):
+ self.filename = filename
+ def __enter__(self):
+ return _open(self.filename, encoding='utf-8').read()
+ def __exit__(self, exc_type, exc_value, traceback):
+ pass
def slugify(value):
"""
From 56800a1d43ff9e07659d0f5ad570a9004d44cd74 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Wed, 20 Jun 2012 20:02:41 -0400
Subject: [PATCH 0005/1816] fix failing test with new open context manager
---
pelican/readers.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/pelican/readers.py b/pelican/readers.py
index d05ab40f..1d06bd6d 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -119,9 +119,9 @@ class MarkdownReader(Reader):
def read(self, filename):
"""Parse content and metadata of markdown files"""
- text = open(filename)
- md = Markdown(extensions=set(self.extensions + ['meta']))
- content = md.convert(text)
+ with open(filename) as text:
+ md = Markdown(extensions=set(self.extensions + ['meta']))
+ content = md.convert(text)
metadata = {}
for name, value in md.Meta.items():
From c0578eb9ab77c7be4a045f58a7844222ccbe6b95 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Wed, 20 Jun 2012 23:19:06 -0400
Subject: [PATCH 0006/1816] handle escaped chars in html properly
---
pelican/readers.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/pelican/readers.py b/pelican/readers.py
index 1d06bd6d..08ef4cf8 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -196,6 +196,12 @@ class HTMLReader(Reader):
def handle_data(self, data):
self._data_buffer += data
+ def handle_entityref(self, data):
+ self._data_buffer += '&{};'.format(data)
+
+ def handle_charref(self, data):
+ self._data_buffer += '&{};'.format(data)
+
def build_tag(self, tag, attrs, close_tag):
result = '<{}'.format(cgi.escape(tag))
result += ''.join((' {}="{}"'.format(cgi.escape(k), cgi.escape(v)) for k,v in attrs))
From 036728a194695d463123c714954c25a3d6a826d5 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Thu, 21 Jun 2012 09:05:27 -0400
Subject: [PATCH 0007/1816] properly write out charref's
---
pelican/readers.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/readers.py b/pelican/readers.py
index 08ef4cf8..93549d96 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -200,7 +200,7 @@ class HTMLReader(Reader):
self._data_buffer += '&{};'.format(data)
def handle_charref(self, data):
- self._data_buffer += '&{};'.format(data)
+ self._data_buffer += '{};'.format(data)
def build_tag(self, tag, attrs, close_tag):
result = '<{}'.format(cgi.escape(tag))
From 847a6fe3cee7f05e36679d6b12fafaf58cfc1045 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Thu, 21 Jun 2012 09:12:38 -0400
Subject: [PATCH 0008/1816] change 'markdown' to HTML in the comments
---
pelican/readers.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/readers.py b/pelican/readers.py
index 93549d96..9d200599 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -222,7 +222,7 @@ class HTMLReader(Reader):
return next((x[1] for x in attrs if x[0] == name), default)
def read(self, filename):
- """Parse content and metadata of markdown files"""
+ """Parse content and metadata of HTML files"""
with open(filename) as content:
parser = self._HTMLParser(self.settings)
parser.feed(content)
From a86d5fda71a2d2ce7295cb385641331b139bf361 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Mon, 9 Jul 2012 22:43:51 -0400
Subject: [PATCH 0009/1816] add documentation for html reader
---
docs/getting_started.rst | 30 ++++++++++++++++++++++++++++++
docs/internals.rst | 2 +-
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 93d578a0..d60cce83 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -154,6 +154,36 @@ Markdown posts should follow this pattern::
This is the content of my super blog post.
+Lastly, you can use Vanilla HTML (files ending in ``.htm`` and ``.html``). Pelican
+interprets the HTML in a very straightforward manner, reading meta data out
+of ``meta`` tags, the title out of the ``title`` tag, and the body out of the
+``body`` tag::
+
+
+
+ My super title
+
+
+
+
+
+
+ This is the content of my super blog post.
+
+ Content continues down here.
+
+
+
+With HTML, there are two simple exceptions to the standard metadata. First,
+``tags`` can be specified either with the ``tags`` metadata, as is standard in
+Pelican, or with the ``keywords`` metadata, as is standard in HTML. The two can
+be used interchangeably. The second note is that summaries are done differently
+in HTML posts. Either a ``summary`` metadata tag can be supplied, or, as seen
+above, you can place an HTML comment, ````, that
+Pelican will recognize. Everything before the comment will be treated as a
+summary. The content of the post will contain everything in the body tag, with
+the special comment stripped out.
+
Note that, aside from the title, none of this metadata is mandatory: if the date
is not specified, Pelican will rely on the file's "mtime" timestamp, and the
category can be determined by the directory in which the file resides. For
diff --git a/docs/internals.rst b/docs/internals.rst
index 6b6f991f..a94d1c56 100644
--- a/docs/internals.rst
+++ b/docs/internals.rst
@@ -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 (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).
From 4ec6cefe1db92c0bc6cea9a95c810e3f5b455865 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Mon, 9 Jul 2012 22:45:34 -0400
Subject: [PATCH 0010/1816] fix grammar
---
docs/getting_started.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index d60cce83..5e553815 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -154,7 +154,7 @@ Markdown posts should follow this pattern::
This is the content of my super blog post.
-Lastly, you can use Vanilla HTML (files ending in ``.htm`` and ``.html``). Pelican
+Lastly, you can use vanilla HTML (files ending in ``.htm`` and ``.html``). Pelican
interprets the HTML in a very straightforward manner, reading meta data out
of ``meta`` tags, the title out of the ``title`` tag, and the body out of the
``body`` tag::
From be2a3f4030e9f5f1f4b2f6d657c9f18347d6af9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexis=20M=C3=A9taireau?=
Date: Tue, 4 Dec 2012 01:27:16 +0100
Subject: [PATCH 0011/1816] bump version number
---
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 7c0675f9..05454e17 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,6 +1,11 @@
Release history
###############
+3.2 (XXXX-XX-XX)
+================
+
+* ???
+
3.1 (2012-12-04)
================
diff --git a/pelican/__init__.py b/pelican/__init__.py
index a175e2a8..6a7ee708 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -17,7 +17,7 @@ from pelican.utils import (clean_output_dir, files_changed, file_changed,
from pelican.writers import Writer
__major__ = 3
-__minor__ = 0
+__minor__ = 2
__version__ = "{0}.{1}".format(__major__, __minor__)
diff --git a/setup.py b/setup.py
index 26121423..326a3195 100755
--- a/setup.py
+++ b/setup.py
@@ -20,7 +20,7 @@ entry_points = {
setup(
name="pelican",
- version="3.1",
+ version="3.2",
url='http://getpelican.com/',
author='Alexis Metaireau',
author_email='authors@getpelican.com',
From a07b56c02bfa5ddac8de359e215d9571ee2ba96c Mon Sep 17 00:00:00 2001
From: Justin Mayer
Date: Mon, 3 Dec 2012 16:31:55 -0800
Subject: [PATCH 0012/1816] Doc fixes and improvements
---
docs/faq.rst | 36 ++++++++--------
docs/getting_started.rst | 50 ++++++++++++-----------
docs/index.rst | 2 +-
docs/plugins.rst | 88 ++++++++++++++++++++--------------------
docs/settings.rst | 75 ++++++++++++++++------------------
5 files changed, 127 insertions(+), 124 deletions(-)
diff --git a/docs/faq.rst b/docs/faq.rst
index 735fe9d0..a8617b30 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -1,7 +1,7 @@
Frequently Asked Questions (FAQ)
################################
-Here is a summary of the frequently asked questions for Pelican.
+Here are some frequently asked questions about Pelican.
What's the best way to communicate a problem, question, or suggestion?
======================================================================
@@ -21,7 +21,8 @@ How can I help?
================
There are several ways to help out. First, you can use Pelican and report any
-suggestions or problems you might have via IRC or the `issue tracker `_.
+suggestions or problems you might have via IRC or the `issue tracker
+`_.
If you want to contribute, please fork `the git repository
`_, create a new feature branch, make
@@ -57,13 +58,14 @@ I want to use Markdown, but I got an error.
===========================================
Markdown is not a hard dependency for Pelican, so you will need to explicitly
-install it. You can do so by typing::
+install it. You can do so by typing the following, including ``sudo`` if
+required::
- $ (sudo) pip install markdown
+ (sudo) pip install markdown
-In case you don't have pip installed, consider installing it via::
+If you don't have pip installed, consider installing the pip installer via::
- $ (sudo) easy_install pip
+ (sudo) easy_install pip
Can I use arbitrary meta-data in my templates?
==============================================
@@ -87,15 +89,15 @@ want to have its own template.
:template: template_name
-Then just make sure to have the template installed in to your theme as
-``template_name.html``.
+Then just make sure your theme contains the relevant template file (e.g.
+``template_name.html``).
What if I want to disable feed generation?
==========================================
To disable all feed generation, all feed settings should be set to ``None``.
-Since other feed settings already defaults to ``None``, you only need to set
-the following settings::
+All but two feed settings already default to ``None``, so if you want to disable
+all feed generation, you only need to specify the following settings::
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
@@ -129,13 +131,13 @@ setting names). Here is an exact list of the renamed setting names::
CATEGORY_FEED -> CATEGORY_FEED_ATOM
Starting in 3.1, the new feed ``FEED_ALL_ATOM`` has been introduced: this
-feed will aggregate all posts regardless of their language. It is generated to
-``'feeds/all.atom.xml'`` by default and ``FEED_ATOM`` now defaults to ``None``.
-The following FEED setting has also been renamed::
+feed will aggregate all posts regardless of their language. This setting
+generates ``'feeds/all.atom.xml'`` by default and ``FEED_ATOM`` now defaults to
+``None``. The following feed setting has also been renamed::
TRANSLATION_FEED -> TRANSLATION_FEED_ATOM
-Older 2.x themes that referenced the old setting names may not link properly.
-In order to rectify this, please update your theme for compatibility with 3.0+
-by changing the relevant values in your template files. For an example of
-complete feed headers and usage please check out the ``simple`` theme.
+Older themes that referenced the old setting names may not link properly.
+In order to rectify this, please update your theme for compatibility by changing
+the relevant values in your template files. For an example of complete feed
+headers and usage please check out the ``simple`` theme.
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index b0b5bf92..3e527611 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -161,7 +161,7 @@ following syntax (give your file the ``.rst`` extension)::
:author: Alexis Metaireau
:summary: Short version for index and feeds
-Pelican implements an extension of reStructuredText to enable support for the
+Pelican implements an extension to reStructuredText to enable support for the
``abbr`` HTML tag. To use it, write something like this in your post::
This will be turned into :abbr:`HTML (HyperText Markup Language)`.
@@ -188,16 +188,17 @@ directory in which the file resides. For example, a file located at
``python/foobar/myfoobar.rst`` will have a category of ``foobar``. If you would
like to organize your files in other ways where the name of the subfolder would
not be a good category name, you can set the setting ``USE_FOLDER_AS_CATEGORY``
-to ``False``. If summary isn't given, setting ``SUMMARY_MAX_LENGTH`` determines
-how many words from the beginning of an article are used as the summary.
+to ``False``. If there is no summary metadata for a given post, the
+``SUMMARY_MAX_LENGTH`` setting can be used to specify how many words from the
+beginning of an article are used as the summary.
-You can also extract any metadata from the filename through a regexp to be set
-in the ``FILENAME_METADATA`` setting.
+You can also extract any metadata from the filename through a regular
+expression to be set in the ``FILENAME_METADATA`` setting.
All named groups that are matched will be set in the metadata object. The
default value for the ``FILENAME_METADATA`` setting will only extract the date
from the filename. For example, if you would like to extract both the date and
the slug, you could set something like:
-``'(?P\d{4}-\d{2}-\d{2})_(?P.*)'``.
+``'(?P\d{4}-\d{2}-\d{2})_(?P.*)'``
Please note that the metadata available inside your files takes precedence over
the metadata extracted from the filename.
@@ -205,7 +206,7 @@ the metadata extracted from the filename.
Generate your blog
------------------
-The ``make`` shortcut commands mentioned in the ``Kickstart a blog`` section
+The ``make`` shortcut commands mentioned in the *Kickstart a blog* section
are mostly wrappers around the ``pelican`` command that generates the HTML from
the content. The ``pelican`` command can also be run directly::
@@ -231,11 +232,11 @@ run the ``pelican`` command with the ``-r`` or ``--autoreload`` option.
Pages
-----
-If you create a folder named ``pages``, itself in the content folder, all the
+If you create a folder named ``pages`` inside the content folder, all the
files in it will be used to generate static pages.
-Then, use the ``DISPLAY_PAGES_ON_MENU`` setting, which will add all the pages to
-the menu.
+Then, use the ``DISPLAY_PAGES_ON_MENU`` setting to add all those pages to
+the primary navigation menu.
If you want to exclude any pages from being linked to or listed in the menu
then add a ``status: hidden`` attribute to its metadata. This is useful for
@@ -244,9 +245,13 @@ things like making error pages that fit the generated theme of your site.
Linking to internal content
---------------------------
-Since Pelican 3.1, you now have the ability to do cross-site linking.
+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*
+hierarchy. This makes it easier to link from the current post to other posts
+and images that may be sitting alongside the current post (instead of having
+to determine where those resources will be placed after site generation).
-To link to an internal content, you will have to use the following syntax:
+To link to internal content, use the following syntax:
``|filename|path/to/file``.
For example, you may want to add links between "article1" and "article2" given
@@ -264,20 +269,20 @@ In this example, ``article1.rst`` could look like::
Title: The first article
Date: 2012-12-01
- See below cross-site links examples in restructured text.
+ See below intra-site link examples in reStructuredText format.
- `a root-relative link <|filename|/cat/article2.md>`_
- `a file-relative link <|filename|cat/article2.md>`_
+ `a link relative to content root <|filename|/cat/article2.md>`_
+ `a link relative to current file <|filename|cat/article2.md>`_
and ``article2.md``::
Title: The second article
Date: 2012-12-01
- See below cross-site links examples in markdown.
+ See below intra-site link examples in Markdown format.
- [a root-relative link](|filename|/article1.rst)
- [a file-relative link](|filename|../article1.rst)
+ [a link relative to content root](|filename|/article1.rst)
+ [a link relative to current file](|filename|../article1.rst)
.. note::
@@ -334,13 +339,12 @@ then instead ensure that the translated article titles are identical, since the
slug will be auto-generated from the article title.
Syntax highlighting
----------------------
+-------------------
Pelican is able to provide colorized syntax highlighting for your code blocks.
-To do so, you have to use the following conventions (you need to put this in
-your content files).
+To do so, you have to use the following conventions inside your content files.
-For RestructuredText, use the code-block directive::
+For reStructuredText, use the code-block directive::
.. code-block:: identifier
@@ -373,7 +377,7 @@ anything special to see what's happening with the generated files.
You can either use your browser to open the files on your disk::
- $ firefox output/index.html
+ firefox output/index.html
Or run a simple web server using Python::
diff --git a/docs/index.rst b/docs/index.rst
index 8206727c..ebe1ace6 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -4,7 +4,7 @@ Pelican
Pelican is a static site generator, written in Python_.
* Write your weblog entries directly with your editor of choice (vim!)
- in reStructuredText_, Markdown_ or AsciiDoc_
+ in reStructuredText_, Markdown_, or AsciiDoc_
* 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
diff --git a/docs/plugins.rst b/docs/plugins.rst
index c6b18200..6555e647 100644
--- a/docs/plugins.rst
+++ b/docs/plugins.rst
@@ -3,25 +3,25 @@
Plugins
#######
-Since version 3.0, Pelican manages plugins. Plugins are a way to add features
-to Pelican without having to directly hack Pelican code.
+Beginning with version 3.0, Pelican supports plugins. Plugins are a way to add
+features to Pelican without having to directly modify the Pelican core.
-Pelican is shipped with a set of core plugins, but you can easily implement
-your own (and this page describes how).
+Pelican is shipped with a set of bundled plugins, but you can easily implement
+your own. This page describes how to use and create plugins.
How to use plugins
==================
-To load plugins, you have to specify them in your settings file. You have two
-ways to do so.
-Either by specifying strings with the path to the callables::
+To load plugins, you have to specify them in your settings file. There are two
+ways to do so. The first method is to specify strings with the path to the
+callables::
PLUGINS = ['pelican.plugins.gravatar',]
-Or by importing them and adding them to the list::
+Alternatively, another method is to import them and add them to the list::
from pelican.plugins import gravatar
- PLUGINS = [gravatar, ]
+ PLUGINS = [gravatar,]
If your plugins are not in an importable path, you can specify a ``PLUGIN_PATH``
in the settings::
@@ -33,7 +33,7 @@ How to create plugins
=====================
Plugins are based on the concept of signals. Pelican sends signals, and plugins
-subscribe to those signals. The list of signals are defined in a following
+subscribe to those signals. The list of signals are defined in a subsequent
section.
The only rule to follow for plugins is to define a ``register`` callable, in
@@ -75,13 +75,13 @@ pages_generate_context pages_generator, metadata
pages_generator_init pages_generator invoked in the PagesGenerator.__init__
============================= ============================ ===========================================================================
-The list is currently small, don't hesitate to add signals and make a pull
+The list is currently small, so don't hesitate to add signals and make a pull
request if you need them!
.. note::
- The signal ``content_object_init`` can send different type of object as
- argument. If you want to register only one type of object then you will
+ The signal ``content_object_init`` can send a different type of object as
+ the argument. If you want to register only one type of object then you will
need to specify the sender when you are connecting to the signal.
::
@@ -122,30 +122,30 @@ Plugin descriptions
Asset management
----------------
-This plugin allows you to use the `webassets`_ module to manage assets such as
+This plugin allows you to use the `Webassets`_ module to manage assets such as
CSS and JS files. The module must first be installed::
pip install webassets
-The `webassets` module allows you to perform a number of useful asset management
+The Webassets module allows you to perform a number of useful asset management
functions, including:
-* CSS minifier (`cssmin`, `yuicompressor`, ...)
-* CSS compiler (`less`, `sass`, ...)
-* JS minifier (`uglifyjs`, `yuicompressor`, `closure`, ...)
+* CSS minifier (``cssmin``, ``yuicompressor``, ...)
+* CSS compiler (``less``, ``sass``, ...)
+* JS minifier (``uglifyjs``, ``yuicompressor``, ``closure``, ...)
Others filters include gzip compression, integration of images in CSS via data
-URIs, and more. `webassets` can also append a version identifier to your asset
+URIs, and more. Webassets can also append a version identifier to your asset
URL to convince browsers to download new versions of your assets when you use
-far-future expires headers. Please refer to the `webassets documentation`_ for
+far-future expires headers. Please refer to the `Webassets documentation`_ for
more information.
-When using with Pelican, `webassets` is configured to process assets in the
-``OUTPUT_PATH/theme`` directory. You can use `webassets` in your templates by
-including one or more template tags. The jinja variable ``{{ ASSET_URL }}`` to
-use in the templates is configured to be relative to the ``theme/`` url.
-Hence, it must be used with the ``{{ SITEURL }}`` variable which allows to
-have relative urls. For example...
+When used with Pelican, Webassets is configured to process assets in the
+``OUTPUT_PATH/theme`` directory. You can use Webassets in your templates by
+including one or more template tags. The Jinja variable ``{{ ASSET_URL }}`` can
+be used in templates and is relative to the ``theme/`` url. The
+``{{ ASSET_URL }}`` variable should be used in conjunction with the
+``{{ SITEURL }}`` variable in order to generate URLs properly. For example:
.. code-block:: jinja
@@ -153,7 +153,7 @@ have relative urls. For example...
{% endassets %}
-... will produce a minified css file with a version identifier:
+... will produce a minified css file with a version identifier that looks like:
.. code-block:: html
@@ -182,29 +182,29 @@ The above will produce a minified and gzipped JS file:
-Pelican's debug mode is propagated to `webassets` to disable asset packaging
+Pelican's debug mode is propagated to Webassets to disable asset packaging
and instead work with the uncompressed assets. However, this also means that
the LESS and SASS files are not compiled. This should be fixed in a future
-version of `webassets` (cf. the related `bug report
+version of Webassets (cf. the related `bug report
`_).
-.. _webassets: https://github.com/miracle2k/webassets
-.. _webassets documentation: http://webassets.readthedocs.org/en/latest/builtin_filters.html
+.. _Webassets: https://github.com/miracle2k/webassets
+.. _Webassets documentation: http://webassets.readthedocs.org/en/latest/builtin_filters.html
GitHub activity
---------------
-This plugin makes use of the ``feedparser`` library that you'll need to
+This plugin makes use of the `feedparser`_ library that you'll need to
install.
Set the ``GITHUB_ACTIVITY_FEED`` parameter to your GitHub activity feed.
-For example, my setting would look like::
+For example, to track Pelican project activity, the setting would be::
- GITHUB_ACTIVITY_FEED = 'https://github.com/kpanic.atom'
+ GITHUB_ACTIVITY_FEED = 'https://github.com/getpelican.atom'
-On the templates side, you just have to iterate over the ``github_activity``
-variable, as in the example::
+On the template side, you just have to iterate over the ``github_activity``
+variable, as in this example::
{% if GITHUB_ACTIVITY_FEED %}
@@ -218,15 +218,15 @@ variable, as in the example::
{% endif %}
-
-
-``github_activity`` is a list of lists. The first element is the title
+``github_activity`` is a list of lists. The first element is the title,
and the second element is the raw HTML from GitHub.
+.. _feedparser: https://crate.io/packages/feedparser/
+
Global license
--------------
-This plugin allows you to define a LICENSE setting and adds the contents of that
+This plugin allows you to define a ``LICENSE`` setting and adds the contents of that
license variable to the article's context, making that variable available to use
from within your theme's templates.
@@ -235,7 +235,7 @@ Gravatar
This plugin assigns the ``author_gravatar`` variable to the Gravatar URL and
makes the variable available within the article's context. You can add
-AUTHOR_EMAIL to your settings file to define the default author's email
+``AUTHOR_EMAIL`` to your settings file to define the default author's email
address. Obviously, that email address must be associated with a Gravatar
account.
@@ -249,7 +249,7 @@ the ``author_gravatar`` variable is added to the article's context.
Gzip cache
----------
-Certain web servers (e.g., Nginx) can use a static cache of gzip compressed
+Certain web servers (e.g., Nginx) can use a static cache of gzip-compressed
files to prevent the server from compressing files during an HTTP call. Since
compression occurs at another time, these compressed files can be compressed
at a higher compression level for increased optimization.
@@ -303,7 +303,7 @@ The sitemap plugin generates plain-text or XML sitemaps. You can use the
``SITEMAP`` variable in your settings file to configure the behavior of the
plugin.
-The ``SITEMAP`` variable must be a Python dictionary, it can contain three keys:
+The ``SITEMAP`` variable must be a Python dictionary and can contain three keys:
- ``format``, which sets the output format of the plugin (``xml`` or ``txt``)
@@ -336,7 +336,7 @@ default value.
The sitemap is saved in ``/sitemap.``.
.. note::
- ``priorities`` and ``changefreqs`` are informations for search engines.
+ ``priorities`` and ``changefreqs`` are information for search engines.
They are only used in the XML sitemaps.
For more information:
diff --git a/docs/settings.rst b/docs/settings.rst
index f629a4d6..baf9f60c 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -16,8 +16,8 @@ False, None, etc.), dictionaries, or tuples should *not* be enclosed in
quotation marks. All other values (i.e., strings) *must* be enclosed in
quotation marks.
-Unless otherwise specified, settings that refer to paths can be either absolute or relative to the
-configuration file.
+Unless otherwise specified, settings that refer to paths can be either absolute
+or relative to the configuration file.
The settings you define in the configuration file will be passed to the
templates, which allows you to use your settings to add site-wide content.
@@ -31,12 +31,11 @@ Basic settings
Setting name (default value) What does it do?
===================================================================== =====================================================================
`AUTHOR` Default author (put your name)
-`DATE_FORMATS` (``{}``) If you do manage multiple languages, you can
- set the date formatting here. See "Date format and locales"
- section below for details.
-`USE_FOLDER_AS_CATEGORY` (``True``) When you don't specify a category in your post metadata and set this
- setting to ``True`` and organize your articles in subfolders, the
- subfolder will become the category of your post. If set to ``False``
+`DATE_FORMATS` (``{}``) If you manage multiple languages, you can set the date formatting
+ here. See the "Date format and locales" 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``,
``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.
@@ -44,12 +43,12 @@ Setting name (default value) What doe
template. Templates may or not honor this
setting.
`DEFAULT_DATE` (``None``) The default date you want to use.
- If 'fs', Pelican will use the file system
+ If ``fs``, Pelican will use the file system
timestamp information (mtime) if it can't get
date information from the metadata.
- If tuple object, it will instead generate the
- default datetime object by passing the tuple to
- the datetime.datetime constructor.
+ 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
and pages.
`FILENAME_METADATA` (``'(?P\d{4}-\d{2}-\d{2}).*'``) The regexp that will be used to extract any metadata
@@ -83,11 +82,10 @@ 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 to have PDF versions
- of your documents. You will need to install
- `rst2pdf`.
+`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 ReStructeredText) to the
+ 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.
Defaults to ``.text``. If not a valid string the default value
@@ -106,10 +104,10 @@ Setting name (default value) What doe
the blog entries. See :ref:`template_pages`.
`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
+ Pelican will copy the "images" folder to the
output folder.
`TIMEZONE` The timezone used in the date information, to
- generate Atom and RSS feeds. See the "timezone"
+ generate Atom and RSS feeds. See the *Timezone*
section below for more info.
`TYPOGRIFY` (``False``) If set to True, several typographical improvements will be
incorporated into the generated HTML via the `Typogrify
@@ -117,19 +115,19 @@ Setting name (default value) What doe
library, which can be installed via: ``pip install typogrify``
`DIRECT_TEMPLATES` (``('index', 'tags', 'categories', '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.
+ index pages for collections of content (e.g. tags and
+ category index pages).
`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.
This only applies if your content does not otherwise
- specify a summary. Setting to None will cause the summary
+ 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 look for the 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 ...
- This templates need to use ``DIRECT_TEMPLATES`` setting
-`ASCIIDOC_OPTIONS` (``[]``) A list of options to pass to asciidoc, see the `manpage
+ These templates need to use ``DIRECT_TEMPLATES`` setting.
+`ASCIIDOC_OPTIONS` (``[]``) A list of options to pass to AsciiDoc. See the `manpage
`_
===================================================================== =====================================================================
@@ -145,13 +143,13 @@ 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
+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
+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::
@@ -175,8 +173,8 @@ 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'``
-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/'.
+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/``.
==================================================== =====================================================
Setting name (default value) What does it do?
@@ -206,7 +204,7 @@ Setting name (default value) What does it do?
.. note::
- When any of `*_SAVE_AS` is set to False, files will not be created.
+ When any of the `*_SAVE_AS` settings is set to False, files will not be created.
Timezone
--------
@@ -283,12 +281,12 @@ If you want to generate custom pages besides your blog entries, you can point
any Jinja2 template file with a path pointing to the file and the destination
path for the generated file.
-For instance, if you have a blog with three static pages, for a list of books,
-your resume and a contact page, you could have::
+For instance, if you have a blog with three static pages — a list of books,
+your resume, and a contact page — you could have::
TEMPLATE_PAGES = {'src/books.html': 'dest/books.html',
- 'src/resume.html': 'dest/resume.html',
- 'src/contact.html': 'dest/contact.html'}
+ 'src/resume.html': 'dest/resume.html',
+ 'src/contact.html': 'dest/contact.html'}
Feed settings
=============
@@ -326,8 +324,7 @@ Setting name (default value) What does it do?
quantity is unrestricted by default.
================================================ =====================================================
-If you don't want to generate some or any of these feeds, set ``None`` to the
-variables above.
+If you don't want to generate some or any of these feeds, set the above variables to ``None``.
.. [2] %s is the name of the category.
@@ -399,7 +396,7 @@ N matches `TAG_CLOUD_STEPS` -1).
Translations
============
-Pelican offers a way to translate articles. See the Getting Started section for
+Pelican offers a way to translate articles. See the :doc:`Getting Started ` section for
more information.
===================================================== =====================================================
@@ -467,7 +464,7 @@ Following are example ways to specify your preferred theme::
# Specify a customized theme, via absolute path
THEME = "~/projects/mysite/themes/mycustomtheme"
-The built-in `notmyidea` theme can make good use of the following settings. Feel
+The built-in ``notmyidea`` theme can make good use of the following settings. Feel
free to use them in your themes as well.
======================= =======================================================
@@ -496,7 +493,7 @@ Setting name What does it do ?
if you want this button to appear.
======================= =======================================================
-In addition, you can use the "wide" version of the `notmyidea` theme by
+In addition, you can use the "wide" version of the ``notmyidea`` theme by
adding the following to your configuration::
CSS_FILE = "wide.css"
From f92c0cb69dad13491f93872af79d3a14ec6a1e54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexis=20M=C3=A9taireau?=
Date: Tue, 4 Dec 2012 01:43:19 +0100
Subject: [PATCH 0013/1816] update the version scheme to support micro versions
---
pelican/__init__.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/pelican/__init__.py b/pelican/__init__.py
index 6a7ee708..e2ea7f76 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -18,7 +18,8 @@ from pelican.writers import Writer
__major__ = 3
__minor__ = 2
-__version__ = "{0}.{1}".format(__major__, __minor__)
+__micro__ = 0
+__version__ = "{0}.{1}.{2}".format(__major__, __minor__, __micro__)
logger = logging.getLogger(__name__)
From 625afa0621866d1bbc29ade89551b096d4362838 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexis=20M=C3=A9taireau?=
Date: Tue, 4 Dec 2012 01:53:52 +0100
Subject: [PATCH 0014/1816] add the changelog to the text on PyPI
---
MANIFEST.in | 2 +-
setup.py | 9 +++++++--
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index bec6d1a3..2f2ea824 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,6 @@
include *.rst
global-include *.py
recursive-include pelican *.html *.css *png *.in
-include LICENSE THANKS
+include LICENSE THANKS docs/changelog.rst
recursive-include tests *
recursive-exclude tests *.pyc
diff --git a/setup.py b/setup.py
index 326a3195..15ddebd6 100755
--- a/setup.py
+++ b/setup.py
@@ -18,15 +18,20 @@ entry_points = {
]
}
+
+README = open('README.rst').read()
+CHANGELOG = open('docs/changelog.rst').read()
+
+
setup(
name="pelican",
version="3.2",
url='http://getpelican.com/',
author='Alexis Metaireau',
author_email='authors@getpelican.com',
- description="A tool to generate a static blog from reStructuredText or "\
+ description="A tool to generate a static blog from reStructuredText or "
"Markdown input files.",
- long_description=open('README.rst').read(),
+ long_description=README + '\n' + CHANGELOG,
packages=['pelican', 'pelican.tools', 'pelican.plugins'],
include_package_data=True,
install_requires=requires,
From 9f66333d7726513138b9d839b853ba54e8fc308b Mon Sep 17 00:00:00 2001
From: Bruno Binet
Date: Tue, 4 Dec 2012 02:08:13 +0100
Subject: [PATCH 0015/1816] fix rst issue
---
docs/changelog.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 05454e17..ea476ca8 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,7 +4,7 @@ Release history
3.2 (XXXX-XX-XX)
================
-* ???
+* [...]
3.1 (2012-12-04)
================
From 694f318614ef6f8ccccf273ecf9ff5cb33ac99c3 Mon Sep 17 00:00:00 2001
From: Roman Skvazh
Date: Thu, 6 Dec 2012 21:15:25 +0400
Subject: [PATCH 0016/1816] Update docs/plugins.rst
Change yuicompressor to yui_js and yui_css
---
docs/plugins.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/plugins.rst b/docs/plugins.rst
index 6555e647..d9964287 100644
--- a/docs/plugins.rst
+++ b/docs/plugins.rst
@@ -130,9 +130,9 @@ CSS and JS files. The module must first be installed::
The Webassets module allows you to perform a number of useful asset management
functions, including:
-* CSS minifier (``cssmin``, ``yuicompressor``, ...)
+* CSS minifier (``cssmin``, ``yui_css``, ...)
* CSS compiler (``less``, ``sass``, ...)
-* JS minifier (``uglifyjs``, ``yuicompressor``, ``closure``, ...)
+* JS minifier (``uglifyjs``, ``yui_js``, ``closure``, ...)
Others filters include gzip compression, integration of images in CSS via data
URIs, and more. Webassets can also append a version identifier to your asset
From a5772bf3d6e0563b109dc1f2cb19743cf6a19aef Mon Sep 17 00:00:00 2001
From: Bruno Binet
Date: Fri, 7 Dec 2012 00:10:30 +0100
Subject: [PATCH 0017/1816] allow override page url and save_as values directly
from the metadata
this is similar to the template override implemented in #404
---
pelican/contents.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/pelican/contents.py b/pelican/contents.py
index d675a2ad..77ee70f8 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -44,6 +44,8 @@ class Page(object):
# set metadata as attributes
for key, value in local_metadata.items():
+ if key in ('save_as', 'url'):
+ key = 'override_' + key
setattr(self, key.lower(), value)
# also keep track of the metadata attributes available
@@ -128,6 +130,8 @@ class Page(object):
return self.settings[fq_key].format(**self.url_format)
def get_url_setting(self, key):
+ if hasattr(self, 'override_' + key):
+ return getattr(self, 'override_' + key)
key = key if self.in_default_lang else 'lang_%s' % key
return self._expand_settings(key)
From 00c7451200fd75056ebecb7cd681bda08b55d70a Mon Sep 17 00:00:00 2001
From: Bruno Binet
Date: Fri, 7 Dec 2012 00:14:00 +0100
Subject: [PATCH 0018/1816] add functional test for save_as/url override
and update functional tests output
---
samples/content/pages/override_url_saveas.rst | 9 ++
.../basic/a-markdown-powered-article.html | 3 +-
tests/output/basic/archives.html | 3 +-
tests/output/basic/article-1.html | 3 +-
tests/output/basic/article-2.html | 3 +-
tests/output/basic/article-3.html | 3 +-
.../output/basic/author/alexis-metaireau.html | 3 +-
tests/output/basic/categories.html | 3 +-
tests/output/basic/category/bar.html | 3 +-
tests/output/basic/category/cat1.html | 3 +-
tests/output/basic/category/misc.html | 3 +-
tests/output/basic/category/yeah.html | 3 +-
.../basic/filename_metadata-example.html | 3 +-
tests/output/basic/index.html | 3 +-
tests/output/basic/oh-yeah.html | 3 +-
tests/output/basic/override/index.html | 53 +++++++++++
.../pages/this-is-a-test-hidden-page.html | 3 +-
.../basic/pages/this-is-a-test-page.html | 3 +-
tests/output/basic/second-article-fr.html | 3 +-
tests/output/basic/second-article.html | 3 +-
tests/output/basic/tag/bar.html | 3 +-
tests/output/basic/tag/baz.html | 3 +-
tests/output/basic/tag/foo.html | 3 +-
tests/output/basic/tag/foobar.html | 3 +-
tests/output/basic/tag/oh.html | 3 +-
tests/output/basic/tag/yeah.html | 3 +-
.../output/basic/this-is-a-super-article.html | 3 +-
tests/output/basic/unbelievable.html | 3 +-
.../custom/a-markdown-powered-article.html | 3 +-
tests/output/custom/archives.html | 3 +-
tests/output/custom/article-1.html | 3 +-
tests/output/custom/article-2.html | 3 +-
tests/output/custom/article-3.html | 3 +-
.../custom/author/alexis-metaireau.html | 3 +-
.../custom/author/alexis-metaireau2.html | 3 +-
.../custom/author/alexis-metaireau3.html | 3 +-
tests/output/custom/categories.html | 3 +-
tests/output/custom/category/bar.html | 3 +-
tests/output/custom/category/cat1.html | 3 +-
tests/output/custom/category/misc.html | 3 +-
tests/output/custom/category/yeah.html | 3 +-
.../output/custom/drafts/a-draft-article.html | 3 +-
.../custom/filename_metadata-example.html | 3 +-
tests/output/custom/index.html | 3 +-
tests/output/custom/index2.html | 3 +-
tests/output/custom/index3.html | 3 +-
tests/output/custom/jinja2_template.html | 3 +-
tests/output/custom/oh-yeah-fr.html | 3 +-
tests/output/custom/oh-yeah.html | 3 +-
tests/output/custom/override/index.html | 88 +++++++++++++++++++
.../pages/this-is-a-test-hidden-page.html | 3 +-
.../custom/pages/this-is-a-test-page.html | 3 +-
tests/output/custom/second-article-fr.html | 3 +-
tests/output/custom/second-article.html | 3 +-
tests/output/custom/tag/bar.html | 3 +-
tests/output/custom/tag/baz.html | 3 +-
tests/output/custom/tag/foo.html | 3 +-
tests/output/custom/tag/foobar.html | 3 +-
tests/output/custom/tag/oh.html | 3 +-
tests/output/custom/tag/yeah.html | 3 +-
.../custom/this-is-a-super-article.html | 3 +-
tests/output/custom/unbelievable.html | 3 +-
62 files changed, 268 insertions(+), 59 deletions(-)
create mode 100644 samples/content/pages/override_url_saveas.rst
create mode 100644 tests/output/basic/override/index.html
create mode 100644 tests/output/custom/override/index.html
diff --git a/samples/content/pages/override_url_saveas.rst b/samples/content/pages/override_url_saveas.rst
new file mode 100644
index 00000000..8a515f60
--- /dev/null
+++ b/samples/content/pages/override_url_saveas.rst
@@ -0,0 +1,9 @@
+Override url/save_as
+####################
+
+:date: 2012-12-07
+:url: override/
+:save_as: override/index.html
+
+Test page which overrides save_as and url so that this page will be generated
+at a custom location.
diff --git a/tests/output/basic/a-markdown-powered-article.html b/tests/output/basic/a-markdown-powered-article.html
index 80a12212..9b9da171 100644
--- a/tests/output/basic/a-markdown-powered-article.html
+++ b/tests/output/basic/a-markdown-powered-article.html
@@ -22,7 +22,8 @@
A multi-line summary should be supported'\
- u'\nas well as inline markup.
'
- self.assertEquals(expected_summary, metadata['summary'], 'summary')
-
class AdReaderTest(unittest.TestCase):
@unittest.skipUnless(readers.asciidoc, "asciidoc isn't installed")
diff --git a/tests/test_settings.py b/tests/test_settings.py
index 379e3deb..0e610c55 100644
--- a/tests/test_settings.py
+++ b/tests/test_settings.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
import copy
from os.path import dirname, abspath, join
@@ -16,7 +18,7 @@ class TestSettingsConfiguration(unittest.TestCase):
self.settings = read_settings(default_conf)
def test_overwrite_existing_settings(self):
- self.assertEqual(self.settings.get('SITENAME'), u"Alexis' log")
+ self.assertEqual(self.settings.get('SITENAME'), "Alexis' log")
self.assertEqual(self.settings.get('SITEURL'),
'http://blog.notmyidea.org')
diff --git a/tests/test_utils.py b/tests/test_utils.py
index eda1388a..eddb3e25 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, print_function
import shutil
import os
import datetime
@@ -41,10 +42,10 @@ class TestUtils(unittest.TestCase):
samples = (('this is a test', 'this-is-a-test'),
('this is a test', 'this-is-a-test'),
- (u'this → is ← a ↑ test', 'this-is-a-test'),
+ ('this → is ← a ↑ test', 'this-is-a-test'),
('this--is---a test', 'this-is-a-test'),
- (u'unicode測試許功蓋,你看到了嗎?', 'unicodece-shi-xu-gong-gai-ni-kan-dao-liao-ma'),
- (u'大飯原発4号機、18日夜起動へ', 'da-fan-yuan-fa-4hao-ji-18ri-ye-qi-dong-he'),)
+ ('unicode測試許功蓋,你看到了嗎?', 'unicodece-shi-xu-gong-gai-ni-kan-dao-liao-ma'),
+ ('大飯原発4号機、18日夜起動へ', 'da-fan-yuan-fa-4hao-ji-18ri-ye-qi-dong-he'),)
for value, expected in samples:
self.assertEquals(utils.slugify(value), expected)
diff --git a/tests/test_webassets.py b/tests/test_webassets.py
index ceba7775..87b2789c 100644
--- a/tests/test_webassets.py
+++ b/tests/test_webassets.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+# from __future__ import unicode_literals
import hashlib
import os
diff --git a/tox.ini b/tox.ini
index f7d9cf82..8c3961b9 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,15 +1,74 @@
+# This tests the unified codebase (py26-py32) of Pelican.
+# depends on some external libraries that aren't released yet.
+#
+# To run Pelican, you will already have checked out and installed them.
+#
+# Now we must tell tox about this package, otherwise tox would load the old
+# libraries from PyPi.
+#
+# Run tox from the libraries source tree. It will save its package in
+# the distshare directory from where the tests here will pick it up.
+#
+# Do that for
+# https://github.com/dmdm/smartypants.git
+#
+# and typogrify:
+# https://github.com/dmdm/typogrify/tree/py3k
+#
+# and webassets:
+# https://github.com/dmdm/webassets/tree/py3k
+#
+#
+# CAVEAT:
+# -------
+#
+# 1/
+# Please be aware that my ports of typogrify and webassets are just 2to3'd.
+# They are not backwards compatible with Python 2.
+#
+# 2/
+# Webassets still has unresolved issues, so I deactivated it for Py32 tests.
+
+
[tox]
-envlist = py26,py27
+envlist = py26,py27,py32
[testenv]
commands =
- nosetests -s tests
unit2 discover []
+ nosetests -s tests
+deps =
+
+[testenv:py26]
deps =
nose
unittest2
mock
Markdown
- BeautifulSoup
+ BeautifulSoup4
+ feedgenerator
typogrify
webassets
+
+[testenv:py27]
+deps =
+ nose
+ unittest2
+ mock
+ Markdown
+ BeautifulSoup4
+ feedgenerator
+ typogrify
+ webassets
+
+[testenv:py32]
+deps =
+ nose
+ unittest2py3k
+ mock
+ Markdown
+ BeautifulSoup4
+ feedgenerator
+# {distshare}/smartypants-1.6.0.3.zip
+# {distshare}/typogrify-2.0.0.zip
+# {distshare}/webassets-0.8.dev.zip
From d1b238638cb39442a0154740c43a89c608cee634 Mon Sep 17 00:00:00 2001
From: Dirk Makowski
Date: Fri, 11 Jan 2013 03:21:06 +0100
Subject: [PATCH 0038/1816] Update the "how to contribute" docs with py3k info.
---
docs/contribute.rst | 56 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/docs/contribute.rst b/docs/contribute.rst
index c302dcc6..3ab86263 100644
--- a/docs/contribute.rst
+++ b/docs/contribute.rst
@@ -58,6 +58,21 @@ To do so, you can use the following two commands::
samples/content/
$ LC_ALL="C" pelican -o tests/output/basic/ samples/content/
+testing for python3
+-------------------
+
+On Python 3, if you have installed the Py3k compatible versions of the
+plugins manual testing with ``unit2 discover`` is also straightforward.
+
+However, you must tell tox to use those Py3k libraries. If you forget this,
+tox will pull the regular packages from PyPi and the tests will fail.
+
+Tell tox about the local packages thusly: enter the source directory of
+smartypants and run tox there. Do this again for typogrify and webassets.
+Smartypants and typogrify do not have real tests, and webassets will fail
+noisily, but as a result we get these libraries neatly packaged in tox's
+distshare directory. And this we need to run tox for Pelican.
+
Coding standards
================
@@ -67,3 +82,44 @@ 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.
+
+Python3 support
+===============
+
+Here are some tips that may be useful when doing some code for both python2 and
+python3 at the same time:
+
+- Assume, every string and literal is unicode (import unicode_literals):
+
+ - Do not use prefix ``u'``.
+ - Do not encode/decode strings in the middle of sth. Follow the code to the
+ source (or target) of a string and encode/decode at the first/last possible
+ point.
+ - In other words, write your functions to expect and to return unicode.
+ - Encode/decode strings if e.g. the source is a Python function that is known
+ to handle this badly, e.g. strftime() in Python 2.
+
+- Use new syntax: print function, "except ... *as* e" (not comma) etc.
+- Refactor method calls like ``dict.iteritems()``, ``xrange()`` etc. in a way
+ that runs without code change in both Python versions.
+- Do not use magic method ``__unicode()__`` in new classes. Use only ``__str()__``
+ and decorate the class with ``@python_2_unicode_compatible``.
+- Do not start int literals with a zero. This is a syntax error in Py3k.
+- Unfortunately I did not find an octal notation that is valid in both
+ Pythons. Use decimal instead.
+- use six, e.g.:
+
+ - ``isinstance(.., basestring) -> isinstance(.., six.string_types)``
+ - ``isinstance(.., unicode) -> isinstance(.., six.text_type)``
+
+- ``setlocale()`` in Python 2 bails when we give the locale name as unicode,
+ and since we are using ``from __future__ import unicode_literals``, we do
+ that everywhere! As a workaround, I enclosed the localename with ``str()``;
+ in Python 2 this casts the name to a byte string, in Python 3 this should do
+ nothing, because the locale name already had been unicode.
+
+- Kept range() almost everywhere as-is (2to3 suggests list(range())), just
+ changed it where I felt necessary.
+
+- Changed xrange() back to range(), so it is valid in both Python versions.
+
From 1197e09626b9c3607b8a287fff769e9c34c01503 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexis=20M=C3=A9taireau?=
Date: Fri, 11 Jan 2013 18:44:35 +0100
Subject: [PATCH 0039/1816] Revert previously erased changes
---
pelican/tools/pelican_import.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py
index b1a2865f..c373cfd4 100755
--- a/pelican/tools/pelican_import.py
+++ b/pelican/tools/pelican_import.py
@@ -303,7 +303,7 @@ def fields2pelican(fields, out_markup, output_path, dircat=False, strip_raw=Fals
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",
+ "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')
@@ -323,10 +323,10 @@ def main():
help="Strip raw HTML code that can't be converted to "
"markup such as flash embeds or iframes (wordpress import only)")
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.')
+ 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.')
args = parser.parse_args()
@@ -358,4 +358,4 @@ def main():
fields2pelican(fields, args.markup, args.output,
dircat=args.dircat or False,
strip_raw=args.strip_raw or False,
- strip_slugs=args.disable_slugs or False)
+ disable_slugs=args.disable_slugs or False)
From 4ac094966e06eac5d540debbd166162297a768d4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexis=20M=C3=A9taireau?=
Date: Fri, 11 Jan 2013 18:47:22 +0100
Subject: [PATCH 0040/1816] remove py2.6 support
---
tox.ini | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/tox.ini b/tox.ini
index 8c3961b9..bc6578ac 100644
--- a/tox.ini
+++ b/tox.ini
@@ -31,7 +31,7 @@
[tox]
-envlist = py26,py27,py32
+envlist = py27,py32
[testenv]
commands =
@@ -39,17 +39,6 @@ commands =
nosetests -s tests
deps =
-[testenv:py26]
-deps =
- nose
- unittest2
- mock
- Markdown
- BeautifulSoup4
- feedgenerator
- typogrify
- webassets
-
[testenv:py27]
deps =
nose
From 149ca493e0b904a6bafd234cf05d3ea568878dc2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexis=20M=C3=A9taireau?=
Date: Fri, 11 Jan 2013 18:55:04 +0100
Subject: [PATCH 0041/1816] Annotate py3k code when needed.
---
pelican/generators.py | 2 +-
pelican/plugins/sitemap.py | 2 ++
pelican/readers.py | 1 +
pelican/tools/pelican_import.py | 4 +++-
4 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/pelican/generators.py b/pelican/generators.py
index 0d2fa6a9..ce102a31 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -120,7 +120,7 @@ class Generator(object):
for item in items:
value = getattr(self, item)
if hasattr(value, 'items'):
- value = list(value.items())
+ value = list(value.items()) # py3k safeguard for iterators
self.context[item] = value
diff --git a/pelican/plugins/sitemap.py b/pelican/plugins/sitemap.py
index 0ade1288..8043baad 100644
--- a/pelican/plugins/sitemap.py
+++ b/pelican/plugins/sitemap.py
@@ -89,6 +89,7 @@ class SitemapGenerator(object):
'yearly', 'never')
if isinstance(pris, dict):
+ # We use items for Py3k compat. .iteritems() otherwise
for k, v in pris.items():
if k in valid_keys and not isinstance(v, (int, float)):
default = self.priorities[k]
@@ -102,6 +103,7 @@ class SitemapGenerator(object):
warning("sitemap plugin: using the default values")
if isinstance(chfreqs, dict):
+ # .items() for py3k compat.
for k, v in chfreqs.items():
if k in valid_keys and v not in valid_chfreqs:
default = self.changefreqs[k]
diff --git a/pelican/readers.py b/pelican/readers.py
index b182b6fa..5c2ae58c 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -246,6 +246,7 @@ def read_file(filename, fmt=None, settings=None):
if filename_metadata:
match = re.match(filename_metadata, base)
if match:
+ # .items() for py3k compat.
for k, v in match.groupdict().items():
if k not in metadata:
k = k.lower() # metadata must be lowercase
diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py
index c373cfd4..33041b0e 100755
--- a/pelican/tools/pelican_import.py
+++ b/pelican/tools/pelican_import.py
@@ -4,9 +4,11 @@
from __future__ import unicode_literals, print_function
import argparse
try:
+ # py3k import
from html.parser import HTMLParser
except ImportError:
- from HTMLParser import HTMLParser
+ # py2 import
+ from HTMLParser import HTMLParser # NOQA
import os
import subprocess
import sys
From d7caaded3fcb2f0a45125973fc0672c1355252f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexis=20M=C3=A9taireau?=
Date: Fri, 11 Jan 2013 21:24:04 +0100
Subject: [PATCH 0042/1816] Use the en-us locale for functional tests
---
tests/test_pelican.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/test_pelican.py b/tests/test_pelican.py
index e5de0608..4c451794 100644
--- a/tests/test_pelican.py
+++ b/tests/test_pelican.py
@@ -10,8 +10,6 @@ from shutil import rmtree
import locale
import logging
-from mock import patch
-
from pelican import Pelican
from pelican.settings import read_settings
from .support import LogCountHandler
@@ -73,6 +71,7 @@ class TestPelican(unittest.TestCase):
settings = read_settings(filename=None, override={
'PATH': INPUT_PATH,
'OUTPUT_PATH': self.temp_path,
+ 'LOCALE': locale.normalize('en_US'),
})
pelican = Pelican(settings=settings)
pelican.run()
@@ -88,6 +87,7 @@ class TestPelican(unittest.TestCase):
settings = read_settings(filename=SAMPLE_CONFIG, override={
'PATH': INPUT_PATH,
'OUTPUT_PATH': self.temp_path,
+ 'LOCALE': locale.normalize('en_US'),
})
pelican = Pelican(settings=settings)
pelican.run()
From 476f2980ceaa553d65e127b12f34e8687543e17f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexis=20M=C3=A9taireau?=
Date: Tue, 15 Jan 2013 14:23:00 +0100
Subject: [PATCH 0043/1816] Damn, I forgot to update travis.
---
.travis.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index e92b0abc..7ccdafd5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,7 @@
language: python
python:
- - "2.6"
- "2.7"
-# - "3.2"
+ - "3.2"
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq ruby-sass
From 2c434ebac1c64ac2da85cdeeadeaf2c3bd5307cc Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Thu, 3 Jan 2013 13:54:56 -0500
Subject: [PATCH 0044/1816] contents: Add rich comparisons to URLWrapper for
easy sorting
There have been earlier attempts to sort categories and authors
[1,2,3], but they either sorted based on the object id [3], or only
sorted the main author and categories list.
This patch uses rich comparisons (keyed off URLWrapper.name, but
easily adjustable in subclasses) to make the objects sortable without
specifying a key for each sort. For example, now
{% for tag, articles in tags|sort %}
works as expected in a Jinja template.
The functools.total_ordering decorator fills in the missing rich
comparisons [4,5].
[1]: 877d454c8fa979343d4881a00977d9ac8786f178
[2]: 7f36e0ed20745c73886d96d4af303b0d858b8a53
[3]: d0ec18f4dbd623c55bdf4928ee7c363f699ef696
[4]: http://docs.python.org/2/library/functools.html#functools.total_ordering
[5]: http://docs.python.org/3/library/functools.html#functools.total_ordering
---
pelican/contents.py | 16 +++++++++++++++-
pelican/generators.py | 3 +--
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/pelican/contents.py b/pelican/contents.py
index 4655d4cc..bd257ad8 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -243,7 +243,9 @@ class Article(Page):
class Quote(Page):
base_properties = ('author', 'date')
+
@python_2_unicode_compatible
+@functools.total_ordering
class URLWrapper(object):
def __init__(self, name, settings):
self.name = name
@@ -256,8 +258,20 @@ class URLWrapper(object):
def __hash__(self):
return hash(self.name)
+ def _key(self):
+ return self.name
+
+ def _normalize_key(self, key):
+ return six.text_type(key)
+
def __eq__(self, other):
- return self.name == other
+ return self._key() == self._normalize_key(other)
+
+ def __ne__(self, other):
+ return self._key() != self._normalize_key(other)
+
+ def __lt__(self, other):
+ return self._key() < self._normalize_key(other)
def __str__(self):
return self.name
diff --git a/pelican/generators.py b/pelican/generators.py
index ce102a31..49b6bc1d 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -416,11 +416,10 @@ class ArticlesGenerator(Generator):
# order the categories per name
self.categories = list(self.categories.items())
self.categories.sort(
- key=lambda item: item[0].name,
reverse=self.settings['REVERSE_CATEGORY_ORDER'])
self.authors = list(self.authors.items())
- self.authors.sort(key=lambda item: item[0].name)
+ self.authors.sort()
self._update_context(('articles', 'dates', 'tags', 'categories',
'tag_cloud', 'authors', 'related_posts'))
From 656b5150ff26988d71d41fb32f2f75aeca665617 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Tue, 15 Jan 2013 16:32:11 -0500
Subject: [PATCH 0045/1816] docs/themes.rst: Document URLWrapper sorting for
use in Jinja templates
---
docs/themes.rst | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/docs/themes.rst b/docs/themes.rst
index 664b4466..19fd9274 100644
--- a/docs/themes.rst
+++ b/docs/themes.rst
@@ -68,6 +68,19 @@ categories A list of (category, articles) tuples, containing
pages The list of pages
============= ===================================================
+Sorting
+-------
+
+URL wrappers (currently categories, tags, and authors), have
+comparison methods that allow them to be easily sorted by name:
+
+ {% for tag, articles in tags|sort %}
+
+If you want to sort based on different criteria, `Jinja's sort
+command`__ has a number of options.
+
+__ http://jinja.pocoo.org/docs/templates/#sort
+
index.html
----------
From bebb94c15be1ae7ff7ec241f6e1de5aa18556654 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Tue, 15 Jan 2013 22:50:58 -0500
Subject: [PATCH 0046/1816] test_contents.py: Add URLWrapper comparison tests
The name switch between wrapper_a and wrapper_b ensures that we're not
secretly comparing some other field (e.g. id(object)).
---
tests/test_contents.py | 31 ++++++++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/tests/test_contents.py b/tests/test_contents.py
index eb7b6514..9b5673bd 100644
--- a/tests/test_contents.py
+++ b/tests/test_contents.py
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
from .support import unittest
-from pelican.contents import Page, Article
+from pelican.contents import Page, Article, URLWrapper
from pelican.settings import _DEFAULT_CONFIG
from pelican.utils import truncate_html_words
from pelican.signals import content_object_init
@@ -187,3 +188,31 @@ class TestArticle(TestPage):
article_kwargs['metadata']['template'] = 'custom'
custom_article = Article(**article_kwargs)
self.assertEqual('custom', custom_article.template)
+
+
+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)
From 733ab8ae6ce59749ca9152bb5ad5cdca50a3e688 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Tue, 15 Jan 2013 23:05:48 -0500
Subject: [PATCH 0047/1816] test_contents: Add tests for metadata export from
Page.url_format
---
tests/test_contents.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tests/test_contents.py b/tests/test_contents.py
index a8b9877f..ba94e14e 100644
--- a/tests/test_contents.py
+++ b/tests/test_contents.py
@@ -99,6 +99,16 @@ class TestPage(unittest.TestCase):
page = Page(**self.page_kwargs)
self.assertEqual(page.save_as, "pages/foo-bar-fr.html")
+ def test_metadata_url_format(self):
+ """Arbitrary metadata should be passed through url_format()
+ """
+ page = Page(**self.page_kwargs)
+ self.assertIn('summary', page.url_format.keys())
+ page.metadata['directory'] = 'test-dir'
+ page.settings = _DEFAULT_CONFIG.copy()
+ page.settings['PAGE_SAVE_AS'] = '{directory}/{slug}'
+ self.assertEqual(page.save_as, 'test-dir/foo-bar')
+
def test_datetime(self):
"""If DATETIME is set to a tuple, it should be used to override LOCALE
"""
From d2a221c8998dff7adb4baa75c4c0f2953187cb7f Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Thu, 17 Jan 2013 09:49:03 -0500
Subject: [PATCH 0048/1816] contents: Encode Unicode locales for Python 2 in
Page.__init__
Python 2.7 chokes on Unicode locales:
$ python2.7
>>> import locale
>>> locale.setlocale(locale.LC_ALL, u'ja_JP.utf8')
Traceback (most recent call last):
...
ValueError: too many values to unpack
With the addition of:
from __future__ import unicode_literals
to tests/test_contents.py in:
commit bebb94c15be1ae7ff7ec241f6e1de5aa18556654
Author: W. Trevor King
Date: Tue Jan 15 22:50:58 2013 -0500
test_contents.py: Add URLWrapper comparison tests
the locale strings in TestPage.test_datetime are interpreted as
Unicode. Rather than fixing the encoding there, this patch updates
Page to handle Unicode locales automatically.
---
pelican/contents.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/pelican/contents.py b/pelican/contents.py
index bd257ad8..dc38e32f 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -8,6 +8,7 @@ import logging
import functools
import os
import re
+import sys
from datetime import datetime
from sys import platform, stdin
@@ -86,7 +87,10 @@ class Page(object):
self.date_format = settings['DEFAULT_DATE_FORMAT']
if isinstance(self.date_format, tuple):
- locale.setlocale(locale.LC_ALL, self.date_format[0])
+ locale_string = self.date_format[0]
+ if sys.version_info < (3, ) and isinstance(locale_string, six.text_type):
+ locale_string = locale_string.encode('ascii')
+ locale.setlocale(locale.LC_ALL, locale_string)
self.date_format = self.date_format[1]
if hasattr(self, 'date'):
From 61f05672e651f185a6e78dc5827502ba0ff93137 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 4 Jan 2013 13:08:08 -0500
Subject: [PATCH 0049/1816] content: Convert StaticContent.filepath to
.filename
For reasons that are unclear to me, StaticContent introduces the
`filepath` attribute rather than using the existing (and semantically
equivalent) Page.filename. This has caused confusion before [1], and
it's probably a good idea to merge the two.
While I was touching the line, I also updated the string formatting in
StaticGenerator.generate_output to use the forward compatible
'{}'.format() syntax.
[1]: https://github.com/getpelican/pelican/issues/162#issuecomment-3000363
---
pelican/contents.py | 4 ++--
pelican/generators.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/pelican/contents.py b/pelican/contents.py
index 43333e18..89e0397d 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -305,11 +305,11 @@ class StaticContent(object):
settings = copy.deepcopy(_DEFAULT_CONFIG)
self.src = src
self.url = dst or src
- self.filepath = os.path.join(settings['PATH'], src)
+ self.filename = os.path.join(settings['PATH'], src)
self.save_as = os.path.join(settings['OUTPUT_PATH'], self.url)
def __str__(self):
- return self.filepath
+ return self.filename
def is_valid_content(content, f):
diff --git a/pelican/generators.py b/pelican/generators.py
index ce102a31..664f666c 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -520,8 +520,8 @@ class StaticGenerator(Generator):
# copy all StaticContent files
for sc in self.staticfiles:
mkdir_p(os.path.dirname(sc.save_as))
- shutil.copy(sc.filepath, sc.save_as)
- logger.info('copying %s to %s' % (sc.filepath, sc.save_as))
+ shutil.copy(sc.filename, sc.save_as)
+ logger.info('copying {} to {}'.format(sc.filename, sc.save_as))
class PdfGenerator(Generator):
From 004adfa5ccbb840a585bff6dd6bfd7f65cfeaad9 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 4 Jan 2013 10:50:09 -0500
Subject: [PATCH 0050/1816] content: Convert Path.filename to .source_path
Making everything consistent is a bit awkward, since this is a
commonly used attribute, but I've done my best.
Reasons for not consolidating on `filename`:
* It is often used for the "basename" (last component in the path).
Using `source_path` makes it clear that this attribute can contain
multiple components.
Reasons for not consolidating on `filepath`:
* It is barely used in the Pelican source, and therefore easy to
change.
* `path` is more Pythonic. The only place `filepath` ever show up in
the documentation for `os`, `os.path`, and `shutil` is in the
`os.path.relpath` documentation [1].
Reasons for not consolidating on `path`:
* The Page elements have both a source (this attribute) and a
destination (.save_as). To avoid confusion for developers not aware
of this, make it painfully obvious that this attribute is for the
source. Explicit is better than implicit ;).
Where I was touching the line, I also updated the string formatting in
StaticGenerator.generate_output to use the forward compatible
'{}'.format() syntax.
[1]: http://docs.python.org/2/library/os.path.html#os.path.relpath
---
pelican/contents.py | 29 +++++++++++++++--------------
pelican/generators.py | 28 ++++++++++++++--------------
pelican/readers.py | 34 +++++++++++++++++-----------------
pelican/settings.py | 14 +++++++-------
pelican/utils.py | 33 +++++++++++++++++----------------
pelican/writers.py | 22 +++++++++++-----------
6 files changed, 81 insertions(+), 79 deletions(-)
diff --git a/pelican/contents.py b/pelican/contents.py
index 89e0397d..88518b0a 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -32,7 +32,7 @@ class Page(object):
default_template = 'page'
def __init__(self, content, metadata=None, settings=None,
- filename=None, context=None):
+ source_path=None, context=None):
# init parameters
if not metadata:
metadata = {}
@@ -75,8 +75,8 @@ class Page(object):
if not hasattr(self, 'slug') and hasattr(self, 'title'):
self.slug = slugify(self.title)
- if filename:
- self.filename = filename
+ if source_path:
+ self.source_path = source_path
# manage the date format
if not hasattr(self, 'date_format'):
@@ -160,8 +160,8 @@ class Page(object):
if value.startswith('/'):
value = value[1:]
else:
- # relative to the filename of this content
- value = self.get_relative_filename(
+ # relative to the source path of this content
+ value = self.get_relative_source_path(
os.path.join(self.relative_dir, value)
)
@@ -215,24 +215,25 @@ class Page(object):
else:
return self.default_template
- def get_relative_filename(self, filename=None):
+ def get_relative_source_path(self, source_path=None):
"""Return the relative path (from the content path) to the given
- filename.
+ source_path.
- If no filename is specified, use the filename of this content object.
+ If no source path is specified, use the source path of this
+ content object.
"""
- if not filename:
- filename = self.filename
+ if not source_path:
+ source_path = self.source_path
return os.path.relpath(
- os.path.abspath(os.path.join(self.settings['PATH'], filename)),
+ os.path.abspath(os.path.join(self.settings['PATH'], source_path)),
os.path.abspath(self.settings['PATH'])
)
@property
def relative_dir(self):
return os.path.dirname(os.path.relpath(
- os.path.abspath(self.filename),
+ os.path.abspath(self.source_path),
os.path.abspath(self.settings['PATH']))
)
@@ -305,11 +306,11 @@ class StaticContent(object):
settings = copy.deepcopy(_DEFAULT_CONFIG)
self.src = src
self.url = dst or src
- self.filename = os.path.join(settings['PATH'], src)
+ self.source_path = os.path.join(settings['PATH'], src)
self.save_as = os.path.join(settings['OUTPUT_PATH'], self.url)
def __str__(self):
- return self.filename
+ return self.source_path
def is_valid_content(content, f):
diff --git a/pelican/generators.py b/pelican/generators.py
index 664f666c..01d9fc9b 100644
--- a/pelican/generators.py
+++ b/pelican/generators.py
@@ -108,8 +108,8 @@ class Generator(object):
files.append(os.sep.join((root, f)))
return files
- def add_filename(self, content):
- location = os.path.relpath(os.path.abspath(content.filename),
+ def add_source_path(self, content):
+ location = os.path.relpath(os.path.abspath(content.source_path),
os.path.abspath(self.path))
self.context['filenames'][location] = content
@@ -352,11 +352,11 @@ class ArticlesGenerator(Generator):
signals.article_generate_context.send(self, metadata=metadata)
article = Article(content, metadata, settings=self.settings,
- filename=f, context=self.context)
+ source_path=f, context=self.context)
if not is_valid_content(article, f):
continue
- self.add_filename(article)
+ self.add_source_path(article)
if article.status == "published":
if hasattr(article, 'tags'):
@@ -455,11 +455,11 @@ class PagesGenerator(Generator):
continue
signals.pages_generate_context.send(self, metadata=metadata)
page = Page(content, metadata, settings=self.settings,
- filename=f, context=self.context)
+ source_path=f, context=self.context)
if not is_valid_content(page, f):
continue
- self.add_filename(page)
+ self.add_source_path(page)
if page.status == "published":
all_pages.append(page)
@@ -520,8 +520,8 @@ class StaticGenerator(Generator):
# copy all StaticContent files
for sc in self.staticfiles:
mkdir_p(os.path.dirname(sc.save_as))
- shutil.copy(sc.filename, sc.save_as)
- logger.info('copying {} to {}'.format(sc.filename, sc.save_as))
+ shutil.copy(sc.source_path, sc.save_as)
+ logger.info('copying {} to {}'.format(sc.source_path, sc.save_as))
class PdfGenerator(Generator):
@@ -544,11 +544,11 @@ class PdfGenerator(Generator):
raise Exception("unable to find rst2pdf")
def _create_pdf(self, obj, output_path):
- if obj.filename.endswith(".rst"):
+ if obj.source_path.endswith('.rst'):
filename = obj.slug + ".pdf"
output_pdf = os.path.join(output_path, filename)
- # print "Generating pdf for", obj.filename, " in ", output_pdf
- with open(obj.filename) as f:
+ # print('Generating pdf for', obj.source_path, 'in', output_pdf)
+ with open(obj.source_path) as f:
self.pdfcreator.createPdf(text=f.read(), output=output_pdf)
logger.info(' [ok] writing %s' % output_pdf)
@@ -578,9 +578,9 @@ class SourceFileGenerator(Generator):
self.output_extension = self.settings['OUTPUT_SOURCES_EXTENSION']
def _create_source(self, obj, output_path):
- filename = os.path.splitext(obj.save_as)[0]
- dest = os.path.join(output_path, filename + self.output_extension)
- copy('', obj.filename, dest)
+ output_path = os.path.splitext(obj.save_as)[0]
+ dest = os.path.join(output_path, output_path + self.output_extension)
+ copy('', obj.source_path, dest)
def generate_output(self, writer=None):
logger.info(' Generating source files...')
diff --git a/pelican/readers.py b/pelican/readers.py
index 5c2ae58c..440bbdf8 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -109,20 +109,20 @@ class RstReader(Reader):
output[name] = self.process_metadata(name, value)
return output
- def _get_publisher(self, filename):
+ def _get_publisher(self, source_path):
extra_params = {'initial_header_level': '2'}
pub = docutils.core.Publisher(
destination_class=docutils.io.StringOutput)
pub.set_components('standalone', 'restructuredtext', 'html')
pub.writer.translator_class = PelicanHTMLTranslator
pub.process_programmatic_settings(None, extra_params, None)
- pub.set_source(source_path=filename)
+ pub.set_source(source_path=source_path)
pub.publish()
return pub
- def read(self, filename):
+ def read(self, source_path):
"""Parses restructured text"""
- pub = self._get_publisher(filename)
+ pub = self._get_publisher(source_path)
parts = pub.writer.parts
content = parts.get('body')
@@ -151,9 +151,9 @@ class MarkdownReader(Reader):
output[name] = self.process_metadata(name, value[0])
return output
- def read(self, filename):
+ def read(self, source_path):
"""Parse content and metadata of markdown files"""
- text = pelican_open(filename)
+ text = pelican_open(source_path)
md = Markdown(extensions=set(self.extensions + ['meta']))
content = md.convert(text)
@@ -165,9 +165,9 @@ class HtmlReader(Reader):
file_extensions = ['html', 'htm']
_re = re.compile('\<\!\-\-\#\s?[A-z0-9_-]*\s?\:s?[A-z0-9\s_-]*\s?\-\-\>')
- def read(self, filename):
+ def read(self, source_path):
"""Parse content and metadata of (x)HTML files"""
- with open(filename) as content:
+ with open(source_path) as content:
metadata = {'title': 'unnamed'}
for i in self._re.findall(content):
key = i.split(':')[0][5:].strip()
@@ -183,10 +183,10 @@ class AsciiDocReader(Reader):
file_extensions = ['asc']
default_options = ["--no-header-footer", "-a newline=\\n"]
- def read(self, filename):
+ def read(self, source_path):
"""Parse content and metadata of asciidoc files"""
from cStringIO import StringIO
- text = StringIO(pelican_open(filename))
+ text = StringIO(pelican_open(source_path))
content = StringIO()
ad = AsciiDocAPI()
@@ -216,14 +216,14 @@ for cls in Reader.__subclasses__():
_EXTENSIONS[ext] = cls
-def read_file(filename, fmt=None, settings=None):
+def read_file(path, fmt=None, settings=None):
"""Return a reader object using the given format."""
- base, ext = os.path.splitext(os.path.basename(filename))
+ base, ext = os.path.splitext(os.path.basename(path))
if not fmt:
fmt = ext[1:]
if fmt not in _EXTENSIONS:
- raise TypeError('Pelican does not know how to parse %s' % filename)
+ raise TypeError('Pelican does not know how to parse {}'.format(path))
reader = _EXTENSIONS[fmt](settings)
settings_key = '%s_EXTENSIONS' % fmt.upper()
@@ -234,7 +234,7 @@ def read_file(filename, fmt=None, settings=None):
if not reader.enabled:
raise ValueError("Missing dependencies for %s" % fmt)
- content, metadata = reader.read(filename)
+ content, metadata = reader.read(path)
# eventually filter the content with typogrify if asked so
if settings and settings.get('TYPOGRIFY'):
@@ -242,9 +242,9 @@ def read_file(filename, fmt=None, settings=None):
content = typogrify(content)
metadata['title'] = typogrify(metadata['title'])
- filename_metadata = settings and settings.get('FILENAME_METADATA')
- if filename_metadata:
- match = re.match(filename_metadata, base)
+ file_metadata = settings and settings.get('FILENAME_METADATA')
+ if file_metadata:
+ match = re.match(file_metadata, base)
if match:
# .items() for py3k compat.
for k, v in match.groupdict().items():
diff --git a/pelican/settings.py b/pelican/settings.py
index 9c0d8434..e4e0501d 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -84,15 +84,15 @@ _DEFAULT_CONFIG = {'PATH': '.',
}
-def read_settings(filename=None, override=None):
- if filename:
- local_settings = get_settings_from_file(filename)
+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']:
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(filename), local_settings[p])))
+ os.path.dirname(path), local_settings[p])))
if p != 'THEME' or os.path.exists(absp):
local_settings[p] = absp
else:
@@ -116,14 +116,14 @@ def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG):
return context
-def get_settings_from_file(filename, default_settings=_DEFAULT_CONFIG):
+def get_settings_from_file(path, default_settings=_DEFAULT_CONFIG):
"""
Load settings from a file path, returning a dict.
"""
- name = os.path.basename(filename).rpartition(".")[0]
- module = imp.load_source(name, filename)
+ name = os.path.basename(path).rpartition('.')[0]
+ module = imp.load_source(name, path)
return get_settings_from_module(module, default_settings=default_settings)
diff --git a/pelican/utils.py b/pelican/utils.py
index 3a41d04e..48ed0757 100644
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -141,9 +141,9 @@ def get_date(string):
raise ValueError("'%s' is not a valid date" % string)
-def pelican_open(filename):
+def pelican_open(path):
"""Open a file and return it's content"""
- return open(filename, encoding='utf-8').read()
+ return open(path, encoding='utf-8').read()
def slugify(value):
@@ -245,9 +245,9 @@ def clean_output_dir(path):
logger.error("Unable to delete %s, file type unknown" % file)
-def get_relative_path(filename):
- """Return the relative path from the given filename to the root path."""
- nslashes = filename.count('/')
+def get_relative_path(path):
+ """Return the relative path from the given path to the root path."""
+ nslashes = path.count('/')
if nslashes == 0:
return '.'
else:
@@ -344,15 +344,16 @@ def process_translations(content_list):
if len_ > 1:
logger.warning('there are %s variants of "%s"' % (len_, slug))
for x in default_lang_items:
- logger.warning(' %s' % x.filename)
+ logger.warning(' {}'.format(x.source_path))
elif len_ == 0:
default_lang_items = items[:1]
if not slug:
- msg = 'empty slug for %r. ' % default_lang_items[0].filename\
- + 'You can fix this by adding a title or a slug to your '\
- + 'content'
- logger.warning(msg)
+ logger.warning((
+ 'empty slug for {!r}. '
+ 'You can fix this by adding a title or a slug to your '
+ 'content'
+ ).format(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:
@@ -388,14 +389,14 @@ def files_changed(path, extensions):
FILENAMES_MTIMES = defaultdict(int)
-def file_changed(filename):
- mtime = os.stat(filename).st_mtime
- if FILENAMES_MTIMES[filename] == 0:
- FILENAMES_MTIMES[filename] = mtime
+def file_changed(path):
+ mtime = os.stat(path).st_mtime
+ if FILENAMES_MTIMES[path] == 0:
+ FILENAMES_MTIMES[path] = mtime
return False
else:
- if mtime > FILENAMES_MTIMES[filename]:
- FILENAMES_MTIMES[filename] = mtime
+ if mtime > FILENAMES_MTIMES[path]:
+ FILENAMES_MTIMES[path] = mtime
return True
return False
diff --git a/pelican/writers.py b/pelican/writers.py
index 8374ea3e..429507a0 100644
--- a/pelican/writers.py
+++ b/pelican/writers.py
@@ -46,23 +46,23 @@ class Writer(object):
pubdate=set_date_tzinfo(item.date,
self.settings.get('TIMEZONE', None)))
- def write_feed(self, elements, context, filename=None, feed_type='atom'):
+ def write_feed(self, elements, context, path=None, feed_type='atom'):
"""Generate a feed with the list of articles provided
- Return the feed. If no output_path or filename is specified, just
+ Return the feed. If no path or output_path is specified, just
return the feed object.
:param elements: the articles to put on the feed.
:param context: the context to get the feed metadata.
- :param filename: the filename to output.
+ :param path: the path to output.
:param feed_type: the feed type to use (atom or rss)
"""
old_locale = locale.setlocale(locale.LC_ALL)
locale.setlocale(locale.LC_ALL, str('C'))
try:
- self.site_url = context.get('SITEURL', get_relative_path(filename))
+ self.site_url = context.get('SITEURL', get_relative_path(path))
self.feed_domain = context.get('FEED_DOMAIN')
- self.feed_url = '%s/%s' % (self.feed_domain, filename)
+ self.feed_url = '{}/{}'.format(self.feed_domain, path)
feed = self._create_new_feed(feed_type, context)
@@ -72,8 +72,8 @@ class Writer(object):
for i in range(max_items):
self._add_item_to_the_feed(feed, elements[i])
- if filename:
- complete_path = os.path.join(self.output_path, filename)
+ if path:
+ complete_path = os.path.join(self.output_path, path)
try:
os.makedirs(os.path.dirname(complete_path))
except Exception:
@@ -114,14 +114,14 @@ class Writer(object):
output = template.render(localcontext)
finally:
locale.setlocale(locale.LC_ALL, old_locale)
- filename = os.sep.join((output_path, name))
+ path = os.path.join(output_path, name)
try:
- os.makedirs(os.path.dirname(filename))
+ os.makedirs(os.path.dirname(path))
except Exception:
pass
- with open(filename, 'w', encoding='utf-8') as f:
+ with open(path, 'w', encoding='utf-8') as f:
f.write(output)
- logger.info('writing %s' % filename)
+ logger.info('writing {}'.format(path))
localcontext = context.copy()
if relative_urls:
From 9b574361c90e8bc394b17903802935e60ecbe3f0 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 4 Jan 2013 11:17:23 -0500
Subject: [PATCH 0051/1816] doc: convert Markdown example to source_path and
modernize
---
docs/internals.rst | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/docs/internals.rst b/docs/internals.rst
index 280e14d7..cadd300b 100644
--- a/docs/internals.rst
+++ b/docs/internals.rst
@@ -47,19 +47,17 @@ Take a look at the Markdown reader::
class MarkdownReader(Reader):
enabled = bool(Markdown)
- def read(self, filename):
+ def read(self, source_path):
"""Parse content and metadata of markdown files"""
- text = open(filename)
+ text = pelican_open(source_path)
md = Markdown(extensions = ['meta', 'codehilite'])
content = md.convert(text)
metadata = {}
for name, value in md.Meta.items():
- if name in _METADATA_FIELDS:
- meta = _METADATA_FIELDS[name](value[0])
- else:
- meta = value[0]
- metadata[name.lower()] = meta
+ name = name.lower()
+ meta = self.process_metadata(name, value[0])
+ metadata[name] = meta
return content, metadata
Simple, isn't it?
From 54a9132aea663dcdc6d8deb350e3c3e3a3381907 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 4 Jan 2013 11:22:49 -0500
Subject: [PATCH 0052/1816] tests: Update tests after filename/filepath ->
source_path
---
tests/test_generators.py | 10 +++++-----
tests/test_pelican.py | 4 ++--
tests/test_readers.py | 32 ++++++++++++++++----------------
tests/test_utils.py | 12 ++++++------
4 files changed, 29 insertions(+), 29 deletions(-)
diff --git a/tests/test_generators.py b/tests/test_generators.py
index 50f5fe3e..54fe7e61 100644
--- a/tests/test_generators.py
+++ b/tests/test_generators.py
@@ -229,20 +229,20 @@ class TestTemplatePagesGenerator(unittest.TestCase):
# create a dummy template file
template_dir = os.path.join(self.temp_content, 'template')
- template_filename = os.path.join(template_dir, 'source.html')
+ template_path = os.path.join(template_dir, 'source.html')
os.makedirs(template_dir)
- with open(template_filename, 'w') as template_file:
+ with open(template_path, 'w') as template_file:
template_file.write(self.TEMPLATE_CONTENT)
writer = Writer(self.temp_output, settings=settings)
generator.generate_output(writer)
- output_filename = os.path.join(
+ output_path = os.path.join(
self.temp_output, 'generated', 'file.html')
# output file has been generated
- self.assertTrue(os.path.exists(output_filename))
+ self.assertTrue(os.path.exists(output_path))
# output content is correct
- with open(output_filename, 'r') as output_file:
+ with open(output_path, 'r') as output_file:
self.assertEquals(output_file.read(), 'foo: bar')
diff --git a/tests/test_pelican.py b/tests/test_pelican.py
index 6a082676..ca0e22cc 100644
--- a/tests/test_pelican.py
+++ b/tests/test_pelican.py
@@ -70,7 +70,7 @@ class TestPelican(unittest.TestCase):
def test_basic_generation_works(self):
# when running pelican without settings, it should pick up the default
# ones and generate correct output without raising any exception
- settings = read_settings(filename=None, override={
+ settings = read_settings(path=None, override={
'PATH': INPUT_PATH,
'OUTPUT_PATH': self.temp_path,
'LOCALE': locale.normalize('en_US'),
@@ -86,7 +86,7 @@ class TestPelican(unittest.TestCase):
def test_custom_generation_works(self):
# the same thing with a specified set of settings should work
- settings = read_settings(filename=SAMPLE_CONFIG, override={
+ settings = read_settings(path=SAMPLE_CONFIG, override={
'PATH': INPUT_PATH,
'OUTPUT_PATH': self.temp_path,
'LOCALE': locale.normalize('en_US'),
diff --git a/tests/test_readers.py b/tests/test_readers.py
index e3cea629..75e664d5 100644
--- a/tests/test_readers.py
+++ b/tests/test_readers.py
@@ -11,7 +11,7 @@ CUR_DIR = os.path.dirname(__file__)
CONTENT_PATH = os.path.join(CUR_DIR, 'content')
-def _filename(*args):
+def _path(*args):
return os.path.join(CONTENT_PATH, *args)
@@ -19,7 +19,7 @@ class RstReaderTest(unittest.TestCase):
def test_article_with_metadata(self):
reader = readers.RstReader({})
- content, metadata = reader.read(_filename('article_with_metadata.rst'))
+ content, metadata = reader.read(_path('article_with_metadata.rst'))
expected = {
'category': 'yeah',
'author': 'Alexis Métaireau',
@@ -37,7 +37,7 @@ class RstReaderTest(unittest.TestCase):
def test_article_with_filename_metadata(self):
content, metadata = readers.read_file(
- _filename('2012-11-29_rst_w_filename_meta#foo-bar.rst'),
+ _path('2012-11-29_rst_w_filename_meta#foo-bar.rst'),
settings={})
expected = {
'category': 'yeah',
@@ -48,7 +48,7 @@ class RstReaderTest(unittest.TestCase):
self.assertEquals(value, expected[key], key)
content, metadata = readers.read_file(
- _filename('2012-11-29_rst_w_filename_meta#foo-bar.rst'),
+ _path('2012-11-29_rst_w_filename_meta#foo-bar.rst'),
settings={
'FILENAME_METADATA': '(?P\d{4}-\d{2}-\d{2}).*'
})
@@ -62,7 +62,7 @@ class RstReaderTest(unittest.TestCase):
self.assertEquals(value, expected[key], key)
content, metadata = readers.read_file(
- _filename('2012-11-29_rst_w_filename_meta#foo-bar.rst'),
+ _path('2012-11-29_rst_w_filename_meta#foo-bar.rst'),
settings={
'FILENAME_METADATA': '(?P\d{4}-\d{2}-\d{2})_' \
'_(?P.*)' \
@@ -82,7 +82,7 @@ class RstReaderTest(unittest.TestCase):
def test_article_metadata_key_lowercase(self):
"""Keys of metadata should be lowercase."""
reader = readers.RstReader({})
- content, metadata = reader.read(_filename('article_with_uppercase_metadata.rst'))
+ content, metadata = reader.read(_path('article_with_uppercase_metadata.rst'))
self.assertIn('category', metadata, "Key should be lowercase.")
self.assertEquals('Yeah', metadata.get('category'), "Value keeps cases.")
@@ -90,7 +90,7 @@ class RstReaderTest(unittest.TestCase):
def test_typogrify(self):
# if nothing is specified in the settings, the content should be
# unmodified
- content, _ = readers.read_file(_filename('article.rst'))
+ content, _ = readers.read_file(_path('article.rst'))
expected = "
This is some content. With some stuff to "\
""typogrify".
\n
Now with added "\
'support for '\
@@ -100,7 +100,7 @@ class RstReaderTest(unittest.TestCase):
try:
# otherwise, typogrify should be applied
- content, _ = readers.read_file(_filename('article.rst'),
+ content, _ = readers.read_file(_path('article.rst'),
settings={'TYPOGRIFY': True})
expected = "
This is some content. With some stuff to "\
"“typogrify”.
\n
Now with added "\
@@ -118,7 +118,7 @@ class MdReaderTest(unittest.TestCase):
def test_article_with_md_extension(self):
# test to ensure the md extension is being processed by the correct reader
reader = readers.MarkdownReader({})
- content, metadata = reader.read(_filename('article_with_md_extension.md'))
+ 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.
"
@@ -136,7 +136,7 @@ class MdReaderTest(unittest.TestCase):
def test_article_with_mkd_extension(self):
# test to ensure the mkd extension is being processed by the correct reader
reader = readers.MarkdownReader({})
- content, metadata = reader.read(_filename('article_with_mkd_extension.mkd'))
+ content, metadata = reader.read(_path('article_with_mkd_extension.mkd'))
expected = "
Test Markdown File Header
\n"\
"
Used for pelican test
\n"\
"
This is another markdown test file. Uses the mkd extension.
"
@@ -147,7 +147,7 @@ class MdReaderTest(unittest.TestCase):
def test_article_with_markdown_markup_extension(self):
# test to ensure the markdown markup extension is being processed as expected
content, metadata = readers.read_file(
- _filename('article_with_markdown_markup_extensions.md'),
+ _path('article_with_markdown_markup_extensions.md'),
settings={'MD_EXTENSIONS': ['toc', 'codehilite', 'extra']})
expected = '
\n'\
'
\n'\
@@ -165,7 +165,7 @@ class MdReaderTest(unittest.TestCase):
@unittest.skipUnless(readers.Markdown, "markdown isn't installed")
def test_article_with_filename_metadata(self):
content, metadata = readers.read_file(
- _filename('2012-11-30_md_w_filename_meta#foo-bar.md'),
+ _path('2012-11-30_md_w_filename_meta#foo-bar.md'),
settings={})
expected = {
'category': 'yeah',
@@ -175,7 +175,7 @@ class MdReaderTest(unittest.TestCase):
self.assertEquals(value, metadata[key], key)
content, metadata = readers.read_file(
- _filename('2012-11-30_md_w_filename_meta#foo-bar.md'),
+ _path('2012-11-30_md_w_filename_meta#foo-bar.md'),
settings={
'FILENAME_METADATA': '(?P\d{4}-\d{2}-\d{2}).*'
})
@@ -188,7 +188,7 @@ class MdReaderTest(unittest.TestCase):
self.assertEquals(value, metadata[key], key)
content, metadata = readers.read_file(
- _filename('2012-11-30_md_w_filename_meta#foo-bar.md'),
+ _path('2012-11-30_md_w_filename_meta#foo-bar.md'),
settings={
'FILENAME_METADATA': '(?P\d{4}-\d{2}-\d{2})'
'_(?P.*)'
@@ -210,7 +210,7 @@ class AdReaderTest(unittest.TestCase):
def test_article_with_asc_extension(self):
# test to ensure the asc extension is being processed by the correct reader
reader = readers.AsciiDocReader({})
- content, metadata = reader.read(_filename('article_with_asc_extension.asc'))
+ content, metadata = reader.read(_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(content, expected)
@@ -241,7 +241,7 @@ class AdReaderTest(unittest.TestCase):
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(_filename('article_with_asc_options.asc'))
+ 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'
diff --git a/tests/test_utils.py b/tests/test_utils.py
index eddb3e25..ea4f839c 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -79,17 +79,17 @@ class TestUtils(unittest.TestCase):
"""Test if file changes are correctly detected
Make sure to handle not getting any files correctly"""
- path = os.path.join(os.path.dirname(__file__), 'content')
- filename = os.path.join(path, 'article_with_metadata.rst')
- changed = utils.files_changed(path, 'rst')
+ dirname = os.path.join(os.path.dirname(__file__), 'content')
+ path = os.path.join(dirname, 'article_with_metadata.rst')
+ changed = utils.files_changed(dirname, 'rst')
self.assertEquals(changed, True)
- changed = utils.files_changed(path, 'rst')
+ changed = utils.files_changed(dirname, 'rst')
self.assertEquals(changed, False)
t = time.time()
- os.utime(filename, (t, t))
- changed = utils.files_changed(path, 'rst')
+ os.utime(path, (t, t))
+ changed = utils.files_changed(dirname, 'rst')
self.assertEquals(changed, True)
self.assertAlmostEqual(utils.LAST_MTIME, t, delta=1)
From c3c3037a1d726f3a1e1b04a3ee0b7d88cf1c5226 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 4 Jan 2013 07:29:23 -0500
Subject: [PATCH 0053/1816] utils: Teach mkdir_p to fail if the existing target
isn't a directory
Existing symlinks to directories will still pass, because isdir()
follows symbolic links.
---
pelican/utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/utils.py b/pelican/utils.py
index 3a41d04e..40406b1a 100644
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -416,5 +416,5 @@ def mkdir_p(path):
try:
os.makedirs(path)
except OSError as e:
- if e.errno != errno.EEXIST:
+ if e.errno != errno.EEXIST or not os.path.isdir(path):
raise
From ec50e18a3e4ed6e823126778df39c606ce7c3036 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 4 Jan 2013 13:02:30 -0500
Subject: [PATCH 0054/1816] utils: Add deprecated_attribute decorator
---
pelican/utils.py | 43 +++++++++++++++++++++++++++++++++++++++++++
tests/test_utils.py | 11 +++++++++++
2 files changed, 54 insertions(+)
diff --git a/pelican/utils.py b/pelican/utils.py
index 48ed0757..9c3bd7be 100644
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -6,6 +6,7 @@ import os
import re
import pytz
import shutil
+import traceback
import logging
import errno
import locale
@@ -122,6 +123,48 @@ class memoized(object):
'''Support instance methods.'''
return partial(self.__call__, obj)
+
+def deprecated_attribute(old, new, since=None, remove=None, doc=None):
+ """Attribute deprecation decorator for gentle upgrades
+
+ For example:
+
+ class MyClass (object):
+ @deprecated_attribute(
+ old='abc', new='xyz', since=(3, 2, 0), remove=(4, 1, 3))
+ def abc(): return None
+
+ def __init__(self):
+ xyz = 5
+
+ Note that the decorator needs a dummy method to attach to, but the
+ content of the dummy method is ignored.
+ """
+ def _warn():
+ version = '.'.join(six.text_type(x) for x in since)
+ message = ['{} has been deprecated since {}'.format(old, version)]
+ if remove:
+ version = '.'.join(six.text_type(x) for x in remove)
+ message.append(
+ ' 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()))
+
+ def fget(self):
+ _warn()
+ return getattr(self, new)
+
+ def fset(self, value):
+ _warn()
+ setattr(self, new, value)
+
+ def decorator(dummy):
+ return property(fget=fget, fset=fset, doc=doc)
+
+ return decorator
+
def get_date(string):
"""Return a datetime object from a string.
diff --git a/tests/test_utils.py b/tests/test_utils.py
index ea4f839c..75e87c04 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -11,6 +11,17 @@ from pelican.utils import NoFilesError
class TestUtils(unittest.TestCase):
+ _new_attribute = 'new_value'
+
+ @utils.deprecated_attribute(
+ old='_old_attribute', new='_new_attribute',
+ since=(3, 1, 0), remove=(4, 1, 3))
+ def _old_attribute(): return None
+
+ def test_deprecated_attribute(self):
+ value = self._old_attribute
+ self.assertEquals(value, self._new_attribute)
+ # TODO: check log warning
def test_get_date(self):
# valid ones
From 13cd0a4cb3f62b8166ff3957e3385c405b28f34d Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 4 Jan 2013 13:03:19 -0500
Subject: [PATCH 0055/1816] contents: Add deprecation warnings for
Page.filename and StaticContent.filepath
---
pelican/contents.py | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/pelican/contents.py b/pelican/contents.py
index 88518b0a..0dc0f0e9 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -15,7 +15,7 @@ from sys import platform, stdin
from pelican.settings import _DEFAULT_CONFIG
from pelican.utils import (slugify, truncate_html_words, memoized,
- python_2_unicode_compatible)
+ python_2_unicode_compatible, deprecated_attribute)
from pelican import signals
import pelican.utils
@@ -31,6 +31,10 @@ class Page(object):
mandatory_properties = ('title',)
default_template = 'page'
+ @deprecated_attribute(old='filename', new='source_path', since=(3, 2, 0))
+ def filename():
+ return None
+
def __init__(self, content, metadata=None, settings=None,
source_path=None, context=None):
# init parameters
@@ -301,6 +305,10 @@ class Author(URLWrapper):
@python_2_unicode_compatible
class StaticContent(object):
+ @deprecated_attribute(old='filepath', new='source_path', since=(3, 2, 0))
+ def filepath():
+ return None
+
def __init__(self, src, dst=None, settings=None):
if not settings:
settings = copy.deepcopy(_DEFAULT_CONFIG)
From 4fcdaa91e96988e2754b9aec5e141e7219baae89 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 18 Jan 2013 07:27:35 -0500
Subject: [PATCH 0056/1816] tests/support: Factor LogCountHandler testing out
into LoggedTestCase
To avoid duplicating boilerplate when we need to test logged messages
outside of TestPelican.
---
tests/support.py | 21 +++++++++++++++++++++
tests/test_pelican.py | 19 +++++++------------
2 files changed, 28 insertions(+), 12 deletions(-)
diff --git a/tests/support.py b/tests/support.py
index 6011e6cd..209cc665 100644
--- a/tests/support.py
+++ b/tests/support.py
@@ -176,3 +176,24 @@ class LogCountHandler(BufferingHandler):
if (msg is None or re.match(msg, l.getMessage()))
and (level is None or l.levelno == level)
])
+
+
+class LoggedTestCase(unittest.TestCase):
+ """A test case that captures log messages
+ """
+
+ def setUp(self):
+ super(LoggedTestCase, self).setUp()
+ self._logcount_handler = LogCountHandler()
+ logging.getLogger().addHandler(self._logcount_handler)
+
+ def tearDown(self):
+ logging.getLogger().removeHandler(self._logcount_handler)
+ super(LoggedTestCase, self).tearDown()
+
+ def assertLogCountEqual(self, count=None, msg=None, **kwargs):
+ actual = self._logcount_handler.count_logs(msg=msg, **kwargs)
+ self.assertEqual(
+ actual, count,
+ msg='expected {} occurrences of {!r}, but found {}'.format(
+ count, msg, actual))
diff --git a/tests/test_pelican.py b/tests/test_pelican.py
index ca0e22cc..49e20b0a 100644
--- a/tests/test_pelican.py
+++ b/tests/test_pelican.py
@@ -1,9 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
-try:
- import unittest2 as unittest
-except ImportError:
- import unittest # NOQA
import os
from filecmp import dircmp
@@ -14,7 +10,7 @@ import logging
from pelican import Pelican
from pelican.settings import read_settings
-from .support import LogCountHandler
+from .support import LoggedTestCase
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
SAMPLES_PATH = os.path.abspath(os.sep.join((CURRENT_DIR, "..", "samples")))
@@ -39,13 +35,12 @@ def recursiveDiff(dcmp):
return diff
-class TestPelican(unittest.TestCase):
+class TestPelican(LoggedTestCase):
# general functional testing for pelican. Basically, this test case tries
# to run pelican in different situations and see how it behaves
def setUp(self):
- self.logcount_handler = LogCountHandler()
- logging.getLogger().addHandler(self.logcount_handler)
+ super(TestPelican, self).setUp()
self.temp_path = mkdtemp()
self.old_locale = locale.setlocale(locale.LC_ALL)
locale.setlocale(locale.LC_ALL, str('C'))
@@ -53,7 +48,7 @@ class TestPelican(unittest.TestCase):
def tearDown(self):
rmtree(self.temp_path)
locale.setlocale(locale.LC_ALL, self.old_locale)
- logging.getLogger().removeHandler(self.logcount_handler)
+ super(TestPelican, self).tearDown()
def assertFilesEqual(self, diff):
msg = "some generated files differ from the expected functional " \
@@ -79,10 +74,10 @@ class TestPelican(unittest.TestCase):
pelican.run()
dcmp = dircmp(self.temp_path, os.sep.join((OUTPUT_PATH, "basic")))
self.assertFilesEqual(recursiveDiff(dcmp))
- self.assertEqual(self.logcount_handler.count_logs(
+ self.assertLogCountEqual(
+ count=10,
msg="Unable to find.*skipping url replacement",
- level=logging.WARNING,
- ), 10, msg="bad number of occurences found for this log")
+ level=logging.WARNING)
def test_custom_generation_works(self):
# the same thing with a specified set of settings should work
From 13b36a5c34b7a64f36662d537c2fa192994048df Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 18 Jan 2013 07:33:42 -0500
Subject: [PATCH 0057/1816] test_utils: Add log count checks to
test_deprecated_attribute
---
tests/test_utils.py | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 75e87c04..c176325e 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,16 +1,17 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
+import logging
import shutil
import os
import datetime
import time
from pelican import utils
-from .support import get_article, unittest
+from .support import get_article, LoggedTestCase
from pelican.utils import NoFilesError
-class TestUtils(unittest.TestCase):
+class TestUtils(LoggedTestCase):
_new_attribute = 'new_value'
@utils.deprecated_attribute(
@@ -21,7 +22,11 @@ class TestUtils(unittest.TestCase):
def test_deprecated_attribute(self):
value = self._old_attribute
self.assertEquals(value, self._new_attribute)
- # TODO: check log warning
+ self.assertLogCountEqual(
+ count=1,
+ msg=('_old_attribute has been deprecated since 3.1.0 and will be '
+ 'removed by version 4.1.3. Use _new_attribute instead'),
+ level=logging.WARNING)
def test_get_date(self):
# valid ones
From 6686f1c9bd2aaae633f97e48dac60ac82c9f7c68 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 18 Jan 2013 17:49:09 -0500
Subject: [PATCH 0058/1816] setup.py: Update trove classifiers to Python 2.7,
3.2, and 3.3
---
setup.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/setup.py b/setup.py
index 13afd8cf..4c791008 100755
--- a/setup.py
+++ b/setup.py
@@ -41,8 +41,10 @@ setup(
'Environment :: Console',
'License :: OSI Approved :: GNU Affero General Public License v3',
'Operating System :: OS Independent',
- 'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.2',
+ 'Programming Language :: Python :: 3.3',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Software Development :: Libraries :: Python Modules',
],
From 5466ffd61476205feb2a9a7fef737714b12ebf74 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 18 Jan 2013 19:45:39 -0500
Subject: [PATCH 0059/1816] .travis.yml: Test on Python 3.3 as well as 2.7 and
3.2
If we advertise 3.3 support in setup.py, we should test for it.
---
.travis.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.travis.yml b/.travis.yml
index 7ccdafd5..20f6b57e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,6 +2,7 @@ language: python
python:
- "2.7"
- "3.2"
+ - "3.3"
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq ruby-sass
From 763244dad53404a4ec126beca92d3f30d1325af2 Mon Sep 17 00:00:00 2001
From: "W. Trevor King"
Date: Fri, 18 Jan 2013 19:46:58 -0500
Subject: [PATCH 0060/1816] tox.ini: Test on Python 3.3 as well as 2.7 and 3.2
I just copied the 3.2 environment for 3.3. They should be similar,
but it's possible some packages have extra issues on 3.3.
---
tox.ini | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/tox.ini b/tox.ini
index bc6578ac..6d1a134a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -31,7 +31,7 @@
[tox]
-envlist = py27,py32
+envlist = py27,py32,py33
[testenv]
commands =
@@ -61,3 +61,15 @@ deps =
# {distshare}/smartypants-1.6.0.3.zip
# {distshare}/typogrify-2.0.0.zip
# {distshare}/webassets-0.8.dev.zip
+
+[testenv:py33]
+deps =
+ nose
+ unittest2py3k
+ mock
+ Markdown
+ BeautifulSoup4
+ feedgenerator
+# {distshare}/smartypants-1.6.0.3.zip
+# {distshare}/typogrify-2.0.0.zip
+# {distshare}/webassets-0.8.dev.zip
From b01ddbb29c3f6fb1de1794f20e19630cc8810fda Mon Sep 17 00:00:00 2001
From: Bruno Binet
Date: Sat, 19 Jan 2013 16:44:12 +0100
Subject: [PATCH 0061/1816] fix python 3.3 travis build
unittest2py3k is requiredfor python 3.3 (instead of unittest2)
---
.travis.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 20f6b57e..6048335f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,7 @@ before_install:
- sudo apt-get install -qq ruby-sass
install:
- pip install nose mock --use-mirrors
- - if [[ $TRAVIS_PYTHON_VERSION == '3.2' ]]; then pip install --use-mirrors unittest2py3k; else pip install --use-mirrors unittest2; fi
+ - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors unittest2; else pip install --use-mirrors unittest2py3k; fi
- pip install . --use-mirrors
- pip install Markdown
- pip install webassets
From 67a67846aaa248c4e79f74fd6f42f8f47e641437 Mon Sep 17 00:00:00 2001
From: Bruno Binet
Date: Sat, 19 Jan 2013 17:10:12 +0100
Subject: [PATCH 0062/1816] temporary deactivate travis tests for Python 3.3
the functional tests currently fail on Python 3.3 because the feeds output
differs. (see #688)
this is a temporary fix that will make travis happy while we provide a valid
fix for this issue.
---
.travis.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 6048335f..8b292101 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,6 @@ language: python
python:
- "2.7"
- "3.2"
- - "3.3"
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq ruby-sass
From 0a6294a2a3bbd0d1447e1dc1e05b5e4fc2f633d1 Mon Sep 17 00:00:00 2001
From: Eric
Date: Sun, 20 Jan 2013 17:27:28 -0600
Subject: [PATCH 0063/1816] More clear example for related posts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The existing related posts example doesn't show properties for the collection, this could be confusing to individuals who are not programmers, because as it was the code would render five bullet points.
I am also looking to add some type of loop control or if statement to the loop to detect duplicate, I tried t a dictionary sort filter through jinja, bu that through an error, the following is not guarantted to work then:
{% set LASTARTICLE = "notset" %}
{% if article.related_posts %}
{% for related_post in article.related_posts|dictsort(false, 'url') %}
{% if not (LASTARTICLE == related_post.url) %}
{% endif %}
the dicsort does not work (is this not a dict, I'm not a python guy so I'm just hacking at it.
---
docs/plugins.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/plugins.rst b/docs/plugins.rst
index 7e09810b..ca9a0dde 100644
--- a/docs/plugins.rst
+++ b/docs/plugins.rst
@@ -291,7 +291,7 @@ For example::
{% if article.related_posts %}
{% endif %}
From 688dee2dca7fdd60bf94af72d928d912f8a5ddbb Mon Sep 17 00:00:00 2001
From: Simon
Date: Tue, 18 Dec 2012 14:03:05 +0100
Subject: [PATCH 0064/1816] Improve importer documentation.
---
docs/importer.rst | 54 +++++++++++++++++++++++++----------------------
1 file changed, 29 insertions(+), 25 deletions(-)
diff --git a/docs/importer.rst b/docs/importer.rst
index c9fa3be0..7ca3142d 100644
--- a/docs/importer.rst
+++ b/docs/importer.rst
@@ -4,54 +4,56 @@
Import from other blog software
=================================
+
Description
===========
-``pelican-import`` is a command line tool for converting articles from other
-software to ReStructuredText. The supported formats are:
+``pelican-import`` is a command-line tool for converting articles from other
+software to ReStructuredText or Markdown. The supported import formats are:
- WordPress XML export
- Dotclear export
- RSS/Atom feed
-The conversion from HTML to reStructuredText relies on `pandoc
-`_. For Dotclear, if the source posts are
-written with Markdown syntax, they will not be converted (as Pelican also
-supports Markdown).
+The conversion from HTML to reStructuredText or Markdown relies on `Pandoc`_.
+For Dotclear, if the source posts are written with Markdown syntax, they will
+not be converted (as Pelican also supports Markdown).
+
Dependencies
-""""""""""""
+============
-``pelican-import`` has two dependencies not required by the rest of pelican:
+``pelican-import`` has some dependencies not required by the rest of pelican:
-- Beautiful Soup
-- pandoc
+- *BeautifulSoup*, for WordPress and Dotclear import. Can be installed like
+ any other Python package (``pip install BeautifulSoup``).
+- *Feedparser*, for feed import (``pip install feedparser``).
+- *Pandoc*, see the `Pandoc site`_ for installation instructions on your
+ operating system.
-Beautiful Soup can be installed like any other Python package::
-
- $ pip install BeautifulSoup
-
-For pandoc, install a package for your operating system from the
-`pandoc site `_.
+.. _Pandoc: http://johnmacfarlane.net/pandoc/
+.. _Pandoc site: http://johnmacfarlane.net/pandoc/installing.html
Usage
-"""""
+=====
-| pelican-import [-h] [--wpfile] [--dotclear] [--feed] [-o OUTPUT]
-| [-m MARKUP] [--dir-cat] [--strip-raw] [--disable-slugs]
-| input
+::
+
+ pelican-import [-h] [--wpfile] [--dotclear] [--feed] [-o OUTPUT]
+ [-m MARKUP] [--dir-cat] [--strip-raw] [--disable-slugs]
+ input
Positional arguments
-====================
+--------------------
input The input file to read
Optional arguments
-""""""""""""""""""
+------------------
- -h, --help show this help message and exit
- --wpfile Wordpress XML export (default: False)
+ -h, --help Show this help message and exit
+ --wpfile WordPress XML export (default: False)
--dotclear Dotclear export (default: False)
--feed Feed to parse (default: False)
-o OUTPUT, --output OUTPUT
@@ -69,6 +71,7 @@ Optional arguments
be consistent with your original posts. (default:
False)
+
Examples
========
@@ -80,10 +83,11 @@ For Dotclear::
$ pelican-import --dotclear -o ~/output ~/backup.txt
+
Tests
=====
To test the module, one can use sample files:
-- for Wordpress: http://wpcandy.com/made/the-sample-post-collection
+- for WordPress: http://wpcandy.com/made/the-sample-post-collection
- for Dotclear: http://themes.dotaddict.org/files/public/downloads/lorem-backup.txt
From 56a276d92e709b87a7f6f078e2e5fe93ad429cc5 Mon Sep 17 00:00:00 2001
From: Michael Reneer
Date: Mon, 10 Dec 2012 23:32:35 -0500
Subject: [PATCH 0065/1816] Added unit test to test the markdown file
extension.
---
.../content/article_with_markdown_extension.markdown | 10 ++++++++++
tests/test_readers.py | 11 +++++++++++
2 files changed, 21 insertions(+)
create mode 100644 tests/content/article_with_markdown_extension.markdown
diff --git a/tests/content/article_with_markdown_extension.markdown b/tests/content/article_with_markdown_extension.markdown
new file mode 100644
index 00000000..94e92871
--- /dev/null
+++ b/tests/content/article_with_markdown_extension.markdown
@@ -0,0 +1,10 @@
+title: Test markdown File
+category: test
+
+Test Markdown File Header
+=========================
+
+Used for pelican test
+---------------------
+
+This is another markdown test file. Uses the markdown extension.
diff --git a/tests/test_readers.py b/tests/test_readers.py
index 75e664d5..474b1b4b 100644
--- a/tests/test_readers.py
+++ b/tests/test_readers.py
@@ -143,6 +143,17 @@ class MdReaderTest(unittest.TestCase):
self.assertEqual(content, expected)
+ @unittest.skipUnless(readers.Markdown, "markdown isn't installed")
+ def test_article_with_mkd_extension(self):
+ # test to ensure the markdown extension is being processed by the correct reader
+ reader = readers.MarkdownReader({})
+ content, metadata = reader.read(_filename('article_with_markdown_extension.markdown'))
+ expected = "
Test Markdown File Header
\n"\
+ "
Used for pelican test
\n"\
+ "
This is another markdown test file. Uses the markdown 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
From a441596b072ee124ca5720b211dc2605880c279e Mon Sep 17 00:00:00 2001
From: Michael Reneer
Date: Tue, 11 Dec 2012 10:23:43 -0500
Subject: [PATCH 0066/1816] Cleaned up markdown test cases:
- test_article_with_md_extension
- test_article_with_mkd_extension
- test_article_with_markdown_extension
and replaced with:
- test_article_with_metadata
- test_article_with_file_extensions
---
tests/content/article_with_md_extension.md | 7 ++-
tests/test_readers.py | 51 ++++++++++++----------
2 files changed, 32 insertions(+), 26 deletions(-)
diff --git a/tests/content/article_with_md_extension.md b/tests/content/article_with_md_extension.md
index 11aa22a2..1f111796 100644
--- a/tests/content/article_with_md_extension.md
+++ b/tests/content/article_with_md_extension.md
@@ -1,5 +1,8 @@
-title: Test md File
-category: test
+Title: Test md File
+Category: test
+Tags: foo, bar, foobar
+Date: 2010-12-02 10:14
+Summary: I have a lot to test
Test Markdown File Header
=========================
diff --git a/tests/test_readers.py b/tests/test_readers.py
index 474b1b4b..f7cf71d9 100644
--- a/tests/test_readers.py
+++ b/tests/test_readers.py
@@ -115,43 +115,46 @@ class RstReaderTest(unittest.TestCase):
class MdReaderTest(unittest.TestCase):
@unittest.skipUnless(readers.Markdown, "markdown isn't installed")
- def test_article_with_md_extension(self):
- # test to ensure the md extension is being processed by the correct reader
+ def test_article_with_metadata(self):
reader = readers.MarkdownReader({})
- 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.
',
+ 'date': datetime.datetime(2010, 12, 2, 10, 14),
+ 'tags': ['foo', 'bar', 'foobar'],
}
for key, value in metadata.items():
self.assertEquals(value, expected[key], key)
@unittest.skipUnless(readers.Markdown, "markdown isn't installed")
- def test_article_with_mkd_extension(self):
- # test to ensure the mkd extension is being processed by the correct reader
+ def test_article_with_file_extensions(self):
reader = readers.MarkdownReader({})
- content, metadata = reader.read(_path('article_with_mkd_extension.mkd'))
+ # test to ensure the md file extension is being processed by the
+ # correct reader
+ content, metadata = reader.read(
+ _path('article_with_md_extension.md'))
expected = "
Test Markdown File Header
\n"\
"
Used for pelican test
\n"\
- "
This is another markdown test file. Uses the mkd extension.
"
-
+ "
The quick brown fox jumped over the lazy dog's back.
"
self.assertEqual(content, expected)
-
- @unittest.skipUnless(readers.Markdown, "markdown isn't installed")
- def test_article_with_mkd_extension(self):
- # test to ensure the markdown extension is being processed by the correct reader
- reader = readers.MarkdownReader({})
- content, metadata = reader.read(_filename('article_with_markdown_extension.markdown'))
- expected = "
Test Markdown File Header
\n"\
- "
Used for pelican test
\n"\
- "
This is another markdown test file. Uses the markdown extension.
"
-
+ # test to ensure the mkd file extension is being processed by the
+ # correct reader
+ content, metadata = reader.read(
+ _path('article_with_mkd_extension.mkd'))
+ expected = "
Test Markdown File Header
\n
Used for pelican"\
+ " test
\n
This is another markdown test file. Uses"\
+ " the mkd extension.
"
+ self.assertEqual(content, expected)
+ # test to ensure the markdown file extension is being processed by the
+ # correct reader
+ content, metadata = reader.read(
+ _path('article_with_markdown_extension.markdown'))
+ expected = "
Test Markdown File Header
\n
Used for pelican"\
+ " test
\n
This is another markdown test file. Uses"\
+ " the markdown extension.
"
self.assertEqual(content, expected)
@unittest.skipUnless(readers.Markdown, "markdown isn't installed")
From 0288bf1f68095e5be48c3ebb065a6b47ffe7e13a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C4=B1dvan=20=C3=96rsvuran?=
Date: Wed, 19 Dec 2012 16:26:00 +0200
Subject: [PATCH 0067/1816] added IGNORE_FILES setting for autoreload
---
pelican/__init__.py | 5 +++--
pelican/settings.py | 3 ++-
pelican/utils.py | 7 ++++---
3 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/pelican/__init__.py b/pelican/__init__.py
index 566cb3b0..a4d61b73 100644
--- a/pelican/__init__.py
+++ b/pelican/__init__.py
@@ -44,6 +44,7 @@ class Pelican(object):
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.init_path()
@@ -292,8 +293,8 @@ def main():
# restriction; all files are recursively checked if they
# have changed, no matter what extension the filenames
# have.
- if files_changed(pelican.path, pelican.markup) or \
- files_changed(pelican.theme, ['']):
+ if files_changed(pelican.path, pelican.markup, pelican.ignore_files) or \
+ files_changed(pelican.theme, [''], pelican.ignore_files):
if not files_found_error:
files_found_error = True
pelican.run()
diff --git a/pelican/settings.py b/pelican/settings.py
index e4e0501d..8d6c1608 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -80,7 +80,8 @@ _DEFAULT_CONFIG = {'PATH': '.',
'TYPOGRIFY': False,
'SUMMARY_MAX_LENGTH': 50,
'PLUGINS': [],
- 'TEMPLATE_PAGES': {}
+ 'TEMPLATE_PAGES': {},
+ 'IGNORE_FILES': []
}
diff --git a/pelican/utils.py b/pelican/utils.py
index edf5068b..cfbc3c23 100644
--- a/pelican/utils.py
+++ b/pelican/utils.py
@@ -10,6 +10,7 @@ import traceback
import logging
import errno
import locale
+import fnmatch
from collections import defaultdict, Hashable
from functools import partial
@@ -406,8 +407,7 @@ def process_translations(content_list):
LAST_MTIME = 0
-
-def files_changed(path, extensions):
+def files_changed(path, extensions, ignores=[]):
"""Return True if the files have changed since the last check"""
def file_times(path):
@@ -415,7 +415,8 @@ def files_changed(path, extensions):
for root, dirs, files in os.walk(path):
dirs[:] = [x for x in dirs if x[0] != '.']
for f in files:
- if any(f.endswith(ext) for ext in extensions):
+ if any(f.endswith(ext) for ext in extensions) \
+ and not any(fnmatch.fnmatch(f, ignore) for ignore in ignores):
yield os.stat(os.path.join(root, f)).st_mtime
global LAST_MTIME
From 5f3a3e4582f84cf4407875dff945a3d42f79b368 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20HERVIER?=
Date: Thu, 24 Jan 2013 14:10:26 +0100
Subject: [PATCH 0068/1816] Update pelican/contents.py
Put a space in error message to not concatain word 'about' and the missing tag
---
pelican/contents.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/contents.py b/pelican/contents.py
index 5bde3c0a..d7d7e558 100644
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -344,6 +344,6 @@ def is_valid_content(content, f):
content.check_properties()
return True
except NameError as e:
- logger.error("Skipping %s: impossible to find informations about"
+ logger.error("Skipping %s: impossible to find informations about "
"'%s'" % (f, e))
return False
From 357f3a3da211cffeda1501e1c8fb54dc069694f6 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Thu, 21 Jun 2012 09:05:27 -0400
Subject: [PATCH 0069/1816] properly write out charref's
---
pelican/readers.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pelican/readers.py b/pelican/readers.py
index 6fe8e894..de3df66f 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -235,7 +235,7 @@ class HTMLReader(Reader):
self._data_buffer += '&{};'.format(data)
def handle_charref(self, data):
- self._data_buffer += '&{};'.format(data)
+ self._data_buffer += '{};'.format(data)
def build_tag(self, tag, attrs, close_tag):
result = '<{}'.format(cgi.escape(tag))
From 5f639b9a3b79213d5fd631216888af71990723c2 Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Mon, 28 Jan 2013 21:46:54 -0500
Subject: [PATCH 0070/1816] git rebase master
---
pelican/readers.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pelican/readers.py b/pelican/readers.py
index de3df66f..60fabe82 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -257,8 +257,8 @@ class HTMLReader(Reader):
return next((x[1] for x in attrs if x[0] == name), default)
def read(self, filename):
- """Parse content and metadata of markdown files"""
- with pelican_open(filename) as content:
+ """Parse content and metadata of HTML files"""
+ with open(filename) as content:
parser = self._HTMLParser(self.settings)
parser.feed(content)
parser.close()
From bf6f16e3839be680296c8325922606410bb86d8a Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Mon, 9 Jul 2012 22:43:51 -0400
Subject: [PATCH 0071/1816] add documentation for html reader
---
docs/getting_started.rst | 36 ++++++++++++++++++++++++++++++++++++
docs/internals.rst | 4 ++--
2 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 0952c7d9..7592a5ef 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -190,6 +190,42 @@ syntax for Markdown posts should follow this pattern::
This is the content of my super blog post.
+Lastly, you can use Vanilla HTML (files ending in ``.htm`` and ``.html``). Pelican
+interprets the HTML in a very straightforward manner, reading meta data out
+of ``meta`` tags, the title out of the ``title`` tag, and the body out of the
+``body`` tag::
+
+
+
+ My super title
+
+
+
+
+
+
+ This is the content of my super blog post.
+
+ Content continues down here.
+
+
+
+With HTML, there are two simple exceptions to the standard metadata. First,
+``tags`` can be specified either with the ``tags`` metadata, as is standard in
+Pelican, or with the ``keywords`` metadata, as is standard in HTML. The two can
+be used interchangeably. The second note is that summaries are done differently
+in HTML posts. Either a ``summary`` metadata tag can be supplied, or, as seen
+above, you can place an HTML comment, ````, that
+Pelican will recognize. Everything before the comment will be treated as a
+summary. The content of the post will contain everything in the body tag, with
+the special comment stripped out.
+
+Note that, aside from the title, none of this metadata is mandatory: if the date
+is not specified, Pelican will rely on the file's "mtime" timestamp, and the
+category can be determined by the directory in which the file resides. For
+example, a file located at ``python/foobar/myfoobar.rst`` will have a category of
+``foobar``.
+
Note that, aside from the title, none of this metadata is mandatory: if the
date is not specified, Pelican can rely on the file's "mtime" timestamp through
the ``DEFAULT_DATE`` setting, and the category can be determined by the
diff --git a/docs/internals.rst b/docs/internals.rst
index cadd300b..704122ba 100644
--- a/docs/internals.rst
+++ b/docs/internals.rst
@@ -23,8 +23,8 @@ 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, Markdown and
- reStructuredText for now, but the system is extensible). Given a file, they
+* **Readers** are used to read from various formats (AsciiDoc, 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).
* **Generators** generate the different outputs. For instance, Pelican comes with
From e6a4fe3fc40f003ad9ecba183f12a2fdc6a5adeb Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Mon, 9 Jul 2012 22:45:34 -0400
Subject: [PATCH 0072/1816] fix grammar
---
docs/getting_started.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 7592a5ef..c7f2e257 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -190,7 +190,7 @@ syntax for Markdown posts should follow this pattern::
This is the content of my super blog post.
-Lastly, you can use Vanilla HTML (files ending in ``.htm`` and ``.html``). Pelican
+Lastly, you can use vanilla HTML (files ending in ``.htm`` and ``.html``). Pelican
interprets the HTML in a very straightforward manner, reading meta data out
of ``meta`` tags, the title out of the ``title`` tag, and the body out of the
``body`` tag::
From 7b59b34a73560b3eeb6c737a5e8ce2e5b9c4c36b Mon Sep 17 00:00:00 2001
From: dave mankoff
Date: Mon, 28 Jan 2013 22:11:06 -0500
Subject: [PATCH 0073/1816] get tests passing
---
pelican/readers.py | 10 ++++-----
tests/content/article_with_comments.html | 7 ++++---
tests/content/article_with_metadata.html | 2 +-
tests/test_readers.py | 26 +++++++-----------------
4 files changed, 17 insertions(+), 28 deletions(-)
diff --git a/pelican/readers.py b/pelican/readers.py
index 60fabe82..9b8be192 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -223,10 +223,10 @@ class HTMLReader(Reader):
self._data_buffer += self.build_tag(tag, attrs, True)
def handle_comment(self, data):
- if self._in_body and data.strip() == 'PELICAN_END_SUMMARY':
- self.metadata['summary'] = self._data_buffer
- else:
- self._data_buffer += ''.format(data)
+ # if self._in_body and data.strip() == 'PELICAN_END_SUMMARY':
+ # self.metadata['summary'] = self._data_buffer
+ # else:
+ self._data_buffer += ''.format(data)
def handle_data(self, data):
self._data_buffer += data
@@ -258,7 +258,7 @@ class HTMLReader(Reader):
def read(self, filename):
"""Parse content and metadata of HTML files"""
- with open(filename) as content:
+ with pelican_open(filename) as content:
parser = self._HTMLParser(self.settings)
parser.feed(content)
parser.close()
diff --git a/tests/content/article_with_comments.html b/tests/content/article_with_comments.html
index f222682d..289e4a66 100644
--- a/tests/content/article_with_comments.html
+++ b/tests/content/article_with_comments.html
@@ -1,7 +1,8 @@
+
+
- Summary comment is not included.
-
-
+ Body content
+
diff --git a/tests/content/article_with_metadata.html b/tests/content/article_with_metadata.html
index 2bd77241..b108ac8a 100644
--- a/tests/content/article_with_metadata.html
+++ b/tests/content/article_with_metadata.html
@@ -5,11 +5,11 @@
+
Multi-line metadata should be supported
as well as inline markup.
-