1
0
Fork 0
forked from github/pelican

merge upstream

This commit is contained in:
Alexis Métaireau 2012-10-12 23:22:55 +02:00
commit 17dc36aad6
14 changed files with 144 additions and 67 deletions

View file

@ -155,7 +155,5 @@ SITEURL :
STATIC_PATHS :
Les chemins statiques que vous voulez avoir accès sur le chemin de sortie "statique" ;
MARKDOWN_EXTENSIONS :
Liste des extentions Markdown que vous souhaitez utiliser ;

View file

@ -69,6 +69,12 @@ Setting name (default value) What doe
`PDF_GENERATOR` (``False``) Set to True if you want to have PDF versions
of your documents. 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
specified OUTPUT_PATH.
`OUTPUT_SOURCES_EXTENSION` (``.text``) Controls the extension that will be used by the SourcesGenerator.
Defaults to ``.text``. If not a valid string the default value
will be used.
`RELATIVE_URLS` (``True``) Defines whether Pelican should use document-relative URLs or
not. If set to ``False``, Pelican will use the SITEURL
setting to construct absolute URLs.
@ -108,6 +114,7 @@ Setting name (default value) What doe
Example: projects, resume, profile ...
This templates need to use ``DIRECT_TEMPLATES`` setting
`MARKDOWN_EXTENSIONS` (``['toc',]``) A list of any Markdown extensions you want to use.
===================================================================== =====================================================================
.. [#] Default is the system locale.

View file

@ -8,11 +8,13 @@ import argparse
from pelican import signals
from pelican.generators import (Generator, ArticlesGenerator, PagesGenerator,
StaticGenerator, PdfGenerator, LessCSSGenerator)
from pelican.generators import (ArticlesGenerator, PagesGenerator,
StaticGenerator, PdfGenerator,
LessCSSGenerator, SourceFileGenerator)
from pelican.log import init
from pelican.settings import read_settings, _DEFAULT_CONFIG
from pelican.utils import clean_output_dir, files_changed, file_changed, NoFilesError
from pelican.utils import (clean_output_dir, files_changed, file_changed,
NoFilesError)
from pelican.writers import Writer
__major__ = 3
@ -78,7 +80,7 @@ class Pelican(object):
logger.debug("Loading plugin `{0}' ...".format(plugin))
plugin = __import__(plugin, globals(), locals(), 'module')
logger.debug("Registering plugin `{0}' ...".format(plugin.__name__))
logger.debug("Registering plugin `{0}'".format(plugin.__name__))
plugin.register()
def _handle_deprecation(self):
@ -139,8 +141,8 @@ class Pelican(object):
'Modify CATEGORY_FEED to CATEGORY_FEED_ATOM in your settings and '
'theme for the same behavior. Temporarily setting '
'CATEGORY_FEED_ATOM for backwards compatibility.')
self.settings['CATEGORY_FEED_ATOM'] = self.settings['CATEGORY_FEED']
self.settings['CATEGORY_FEED_ATOM'] =\
self.settings['CATEGORY_FEED']
def run(self):
"""Run the generators and return"""
@ -187,6 +189,8 @@ class Pelican(object):
generators.append(PdfGenerator)
if self.settings['LESS_GENERATOR']: # can be True or PATH to lessc
generators.append(LessCSSGenerator)
if self.settings['OUTPUT_SOURCES']:
generators.append(SourceFileGenerator)
for pair in signals.get_generators.send(self):
(funct, value) = pair
@ -290,7 +294,7 @@ def main():
# have.
if files_changed(pelican.path, pelican.markup) or \
files_changed(pelican.theme, ['']):
if files_found_error == False:
if not files_found_error:
files_found_error = True
pelican.run()
@ -306,8 +310,9 @@ def main():
logger.warning("Keyboard interrupt, quitting.")
break
except NoFilesError:
if files_found_error == True:
logger.warning("No valid files found in content. Nothing to generate.")
if files_found_error:
logger.warning("No valid files found in content. "
"Nothing to generate.")
files_found_error = False
time.sleep(1) # sleep to avoid cpu load
except Exception, e:

View file

@ -518,6 +518,19 @@ class PdfGenerator(Generator):
for page in self.context['pages']:
self._create_pdf(page, pdf_path)
class SourceFileGenerator(Generator):
def generate_context(self):
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)
def generate_output(self, writer=None):
logger.info(u' Generating source files...')
for object in chain(self.context['articles'], self.context['pages']):
self._create_source(object, self.output_path)
class LessCSSGenerator(Generator):
"""Compile less css files."""

View file

@ -1,7 +1,8 @@
import collections
import os.path
from datetime import datetime
from logging import debug, warning, error, info
from logging import warning, info
from codecs import open
from pelican import signals, contents
@ -14,45 +15,17 @@ TXT_HEADER = u"""{0}/index.html
XML_HEADER = u"""<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>{0}/index.html</loc>
<lastmod>{1}</lastmod>
<changefreq>{2}</changefreq>
<priority>{3}</priority>
</url>
<url>
<loc>{0}/archives.html</loc>
<lastmod>{1}</lastmod>
<changefreq>{2}</changefreq>
<priority>{3}</priority>
</url>
<url>
<loc>{0}/tags.html</loc>
<lastmod>{1}</lastmod>
<changefreq>{2}</changefreq>
<priority>{3}</priority>
</url>
<url>
<loc>{0}/categories.html</loc>
<lastmod>{1}</lastmod>
<changefreq>{2}</changefreq>
<priority>{3}</priority>
</url>
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
"""
XML_URL = u"""
<url>
<loc>{0}/{1}</loc>
<lastmod>{2}</lastmod>
<changefreq>{3}</changefreq>
<priority>{4}</priority>
</url>
<url>
<loc>{0}/{1}</loc>
<lastmod>{2}</lastmod>
<changefreq>{3}</changefreq>
<priority>{4}</priority>
</url>
"""
XML_FOOTER = u"""
@ -69,7 +42,6 @@ def format_date(date):
return date.strftime("%Y-%m-%dT%H:%M:%S") + tz
class SitemapGenerator(object):
def __init__(self, context, settings, path, theme, output_path, *null):
@ -146,6 +118,10 @@ class SitemapGenerator(object):
if getattr(page, 'status', 'published') != 'published':
return
page_path = os.path.join(self.output_path, page.url)
if not os.path.exists(page_path):
return
lastmod = format_date(getattr(page, 'date', self.now))
if isinstance(page, contents.Article):
@ -176,22 +152,29 @@ class SitemapGenerator(object):
for article in self.context['articles']:
pages += article.translations
info('writing {0}'.format(path))
with open(path, 'w', encoding='utf-8') as fd:
if self.format == 'xml':
fd.write(XML_HEADER.format(
self.siteurl,
format_date(self.now),
self.changefreqs['indexes'],
self.priorities['indexes']
)
)
fd.write(XML_HEADER)
else:
fd.write(TXT_HEADER.format(self.siteurl))
FakePage = collections.namedtuple('FakePage',
['status',
'date',
'url'])
for standard_page_url in ['index.html',
'archives.html',
'tags.html',
'categories.html']:
fake = FakePage(status='published',
date=self.now,
url=standard_page_url)
self.write_url(fake, fd)
for page in pages:
self.write_url(page, fd)
@ -199,7 +182,6 @@ class SitemapGenerator(object):
fd.write(XML_FOOTER)
def get_generators(generators):
return SitemapGenerator

View file

@ -102,7 +102,7 @@ class RstReader(Reader):
def _get_publisher(self, filename):
extra_params = {'initial_header_level': '2'}
pub = docutils.core.Publisher(
destination_class=docutils.io.StringOutput)
destination_class=docutils.io.StringOutput)
pub.set_components('standalone', 'restructuredtext', 'html')
pub.writer.translator_class = PelicanHTMLTranslator
pub.process_programmatic_settings(None, extra_params, None)
@ -129,8 +129,13 @@ class MarkdownReader(Reader):
def read(self, filename):
"""Parse content and metadata of markdown files"""
markdown_extensions = self.settings.get('MARKDOWN_EXTENSIONS', [])
if isinstance(markdown_extensions, (str, unicode)):
markdown_extensions = [m.strip() for m in
markdown_extensions.split(',')]
text = pelican_open(filename)
md = Markdown(extensions=set(self.extensions + ['meta']))
md = Markdown(extensions=set(
self.extensions + markdown_extensions + ['meta']))
content = md.convert(text)
metadata = {}

View file

@ -32,6 +32,8 @@ _DEFAULT_CONFIG = {'PATH': '.',
'SITENAME': 'A Pelican Blog',
'DISPLAY_PAGES_ON_MENU': True,
'PDF_GENERATOR': False,
'OUTPUT_SOURCES': False,
'OUTPUT_SOURCES_EXTENSION': '.text',
'DEFAULT_CATEGORY': 'misc',
'DEFAULT_DATE': 'fs',
'WITH_FUTURE_DATES': True,
@ -76,6 +78,7 @@ _DEFAULT_CONFIG = {'PATH': '.',
'SUMMARY_MAX_LENGTH': 50,
'WEBASSETS': False,
'PLUGINS': [],
'MARKDOWN_EXTENSIONS': ['toc', ],
}
@ -175,4 +178,11 @@ def configure_settings(settings, default_settings=None, filename=None):
logger.warn("You must install the webassets module to use WEBASSETS.")
settings['WEBASSETS'] = False
if 'OUTPUT_SOURCES_EXTENSION' in settings:
if not isinstance(settings['OUTPUT_SOURCES_EXTENSION'], str):
settings['OUTPUT_SOURCES_EXTENSION'] = _DEFAULT_CONFIG['OUTPUT_SOURCES_EXTENSION']
logger.warn("Detected misconfiguration with OUTPUT_SOURCES_EXTENSION."
" falling back to the default extension " +
_DEFAULT_CONFIG['OUTPUT_SOURCES_EXTENSION'])
return settings

View file

@ -184,6 +184,8 @@ def build_header(title, date, author, categories, tags):
header = '%s\n%s\n' % (title, '#' * len(title))
if date:
header += ':date: %s\n' % date
if author:
header += ':author: %s\n' % author
if categories:
header += ':category: %s\n' % ', '.join(categories)
if tags:
@ -196,6 +198,8 @@ def build_markdown_header(title, date, author, categories, tags):
header = 'Title: %s\n' % title
if date:
header += 'Date: %s\n' % date
if author:
header += 'Author: %s\n' % author
if categories:
header += 'Category: %s\n' % ', '.join(categories)
if tags:

View file

@ -97,6 +97,17 @@ def copy(path, source, destination, destination_path=None, overwrite=False):
def clean_output_dir(path):
"""Remove all the files from the output directory"""
if not os.path.exists(path):
logger.debug("Directory already removed: %s" % path)
return
if not os.path.isdir(path):
try:
os.remove(path)
except Exception, e:
logger.error("Unable to delete file %s; %e" % path, e)
return
# remove all the existing content from the output folder
for filename in os.listdir(path):
file = os.path.join(path, filename)

View file

@ -148,9 +148,9 @@ class Writer(object):
paginators[key] = Paginator(object_list, len(object_list))
# generated pages, and write
name_root, ext = os.path.splitext(name)
for page_num in range(paginators.values()[0].num_pages):
paginated_localcontext = localcontext.copy()
paginated_name = name
for key in paginators.iterkeys():
paginator = paginators[key]
page = paginator.page(page_num + 1)
@ -158,9 +158,10 @@ class Writer(object):
{'%s_paginator' % key: paginator,
'%s_page' % key: page})
if page_num > 0:
ext = '.' + paginated_name.rsplit('.')[-1]
paginated_name = paginated_name.replace(ext,
'%s%s' % (page_num + 1, ext))
paginated_name = '%s%s%s' % (
name_root, page_num + 1, ext)
else:
paginated_name = name
_write_file(template, paginated_localcontext, self.output_path,
paginated_name)

View file

@ -0,0 +1,8 @@
Title: Test Markdown extensions
[TOC]
## Level1
### Level2

View file

@ -69,6 +69,7 @@ class TestArticlesGenerator(unittest.TestCase):
[u'Article title', 'published', 'Default', 'article'],
[u'Article with template', 'published', 'Default', 'custom'],
[u'Test md File', 'published', 'test', 'article'],
[u'Test Markdown extensions', 'published', u'Default', 'article'],
[u'This is a super article !', 'published', 'Yeah', 'article'],
[u'This is an article with category !', 'published', 'yeah', 'article'],
[u'This is an article without category !', 'published', 'Default', 'article'],

View file

@ -70,7 +70,7 @@ class RstReaderTest(unittest.TestCase):
class MdReaderTest(unittest.TestCase):
@unittest.skipUnless(readers.Markdown, "markdown isn't installed")
def test_article_with_md_extention(self):
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'))
@ -90,3 +90,22 @@ class MdReaderTest(unittest.TestCase):
"<p>This is another markdown test file. Uses the mkd extension.</p>"
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
reader = readers.MarkdownReader({})
reader.settings.update(dict(MARKDOWN_EXTENSIONS=['toc', ]))
content, metadata = reader.read(_filename('article_with_markdown_markup_extensions.md'))
expected = '<div class="toc">\n'\
'<ul>\n'\
'<li><a href="#level1">Level1</a><ul>\n'\
'<li><a href="#level2">Level2</a></li>\n'\
'</ul>\n'\
'</li>\n'\
'</ul>\n'\
'</div>\n'\
'<h2 id="level1">Level1</h2>\n'\
'<h3 id="level2">Level2</h3>'
self.assertEqual(content, expected)

View file

@ -112,3 +112,16 @@ class TestUtils(unittest.TestCase):
self.assertTrue(os.path.isdir(test_directory))
self.assertListEqual([], os.listdir(test_directory))
shutil.rmtree(test_directory)
def test_clean_output_dir_not_there(self):
test_directory = os.path.join(os.path.dirname(__file__), 'does_not_exist')
utils.clean_output_dir(test_directory)
self.assertTrue(not os.path.exists(test_directory))
def test_clean_output_dir_is_file(self):
test_directory = os.path.join(os.path.dirname(__file__), 'this_is_a_file')
f = open(test_directory, 'w')
f.write('')
f.close()
utils.clean_output_dir(test_directory)
self.assertTrue(not os.path.exists(test_directory))