2012-03-09 16:21:38 +01:00
|
|
|
import os
|
2011-12-24 00:37:18 +00:00
|
|
|
import re
|
2012-03-20 13:01:21 +00:00
|
|
|
import sys
|
2011-05-06 22:05:13 +02:00
|
|
|
import time
|
2012-03-20 13:01:21 +00:00
|
|
|
import logging
|
|
|
|
|
import argparse
|
2010-12-02 03:22:24 +00:00
|
|
|
|
2011-06-18 01:03:53 +02:00
|
|
|
from pelican import signals
|
2010-12-02 03:22:24 +00:00
|
|
|
|
2012-10-12 23:22:55 +02:00
|
|
|
from pelican.generators import (ArticlesGenerator, PagesGenerator,
|
|
|
|
|
StaticGenerator, PdfGenerator,
|
2012-03-05 15:52:14 +01:00
|
|
|
LessCSSGenerator, SourceFileGenerator,
|
|
|
|
|
StaticPageGenerator)
|
2012-03-20 13:01:21 +00:00
|
|
|
from pelican.log import init
|
2012-10-16 01:35:35 +02:00
|
|
|
from pelican.settings import read_settings
|
2012-10-12 23:22:55 +02:00
|
|
|
from pelican.utils import (clean_output_dir, files_changed, file_changed,
|
|
|
|
|
NoFilesError)
|
2010-12-02 03:22:24 +00:00
|
|
|
from pelican.writers import Writer
|
|
|
|
|
|
2012-03-16 14:27:26 +00:00
|
|
|
__major__ = 3
|
|
|
|
|
__minor__ = 0
|
|
|
|
|
__version__ = "{0}.{1}".format(__major__, __minor__)
|
2010-12-18 20:36:16 +00:00
|
|
|
|
2010-12-02 03:22:24 +00:00
|
|
|
|
2012-03-20 13:01:21 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
2010-12-25 17:26:24 +03:00
|
|
|
class Pelican(object):
|
2012-10-16 01:35:35 +02:00
|
|
|
def __init__(self, settings):
|
|
|
|
|
"""
|
|
|
|
|
Pelican initialisation, performs some checks on the environment before
|
|
|
|
|
doing anything else.
|
2010-12-25 17:26:24 +03:00
|
|
|
"""
|
|
|
|
|
|
2012-03-11 01:59:04 +01:00
|
|
|
# define the default settings
|
|
|
|
|
self.settings = settings
|
|
|
|
|
self._handle_deprecation()
|
|
|
|
|
|
2012-10-16 01:35:35 +02:00
|
|
|
self.path = settings['PATH']
|
|
|
|
|
self.theme = settings['THEME']
|
|
|
|
|
self.output_path = settings['OUTPUT_PATH']
|
|
|
|
|
self.markup = settings['MARKUP']
|
|
|
|
|
self.delete_outputdir = settings['DELETE_OUTPUT_DIRECTORY']
|
2012-03-11 01:59:04 +01:00
|
|
|
|
2012-07-20 07:38:16 -04:00
|
|
|
self.init_path()
|
2011-06-17 23:37:08 +02:00
|
|
|
self.init_plugins()
|
2011-06-18 01:03:53 +02:00
|
|
|
signals.initialized.send(self)
|
2010-12-25 17:26:24 +03:00
|
|
|
|
2012-07-20 07:38:16 -04:00
|
|
|
def init_path(self):
|
|
|
|
|
if not any(p in sys.path for p in ['', '.']):
|
|
|
|
|
logger.debug("Adding current directory to system path")
|
|
|
|
|
sys.path.insert(0, '')
|
|
|
|
|
|
2011-06-17 23:37:08 +02:00
|
|
|
def init_plugins(self):
|
|
|
|
|
self.plugins = self.settings['PLUGINS']
|
|
|
|
|
for plugin in self.plugins:
|
|
|
|
|
# if it's a string, then import it
|
2012-03-20 23:34:53 +01:00
|
|
|
if isinstance(plugin, basestring):
|
2012-07-03 15:19:38 +02:00
|
|
|
logger.debug("Loading plugin `{0}' ...".format(plugin))
|
2011-06-17 23:37:08 +02:00
|
|
|
plugin = __import__(plugin, globals(), locals(), 'module')
|
|
|
|
|
|
2012-10-12 23:22:55 +02:00
|
|
|
logger.debug("Registering plugin `{0}'".format(plugin.__name__))
|
2011-06-17 23:37:08 +02:00
|
|
|
plugin.register()
|
|
|
|
|
|
2012-03-11 01:59:04 +01:00
|
|
|
def _handle_deprecation(self):
|
|
|
|
|
|
|
|
|
|
if self.settings.get('CLEAN_URLS', False):
|
2012-04-15 14:40:10 +01:00
|
|
|
logger.warning('Found deprecated `CLEAN_URLS` in settings.'
|
|
|
|
|
' Modifying the following settings for the'
|
2012-04-07 18:02:40 -06:00
|
|
|
' same behaviour.')
|
2011-12-24 00:37:18 +00:00
|
|
|
|
2012-03-11 01:59:04 +01:00
|
|
|
self.settings['ARTICLE_URL'] = '{slug}/'
|
|
|
|
|
self.settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/'
|
|
|
|
|
self.settings['PAGE_URL'] = 'pages/{slug}/'
|
|
|
|
|
self.settings['PAGE_LANG_URL'] = 'pages/{slug}-{lang}/'
|
2011-12-24 00:37:18 +00:00
|
|
|
|
|
|
|
|
for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL',
|
|
|
|
|
'PAGE_LANG_URL'):
|
2012-03-20 13:01:21 +00:00
|
|
|
logger.warning("%s = '%s'" % (setting, self.settings[setting]))
|
2011-12-24 00:37:18 +00:00
|
|
|
|
2012-03-11 01:59:04 +01:00
|
|
|
if self.settings.get('ARTICLE_PERMALINK_STRUCTURE', False):
|
2012-03-20 13:01:21 +00:00
|
|
|
logger.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in'
|
2012-04-07 18:02:40 -06:00
|
|
|
' settings. Modifying the following settings for'
|
2011-12-24 00:37:18 +00:00
|
|
|
' the same behaviour.')
|
|
|
|
|
|
2012-03-11 01:59:04 +01:00
|
|
|
structure = self.settings['ARTICLE_PERMALINK_STRUCTURE']
|
2011-12-24 00:37:18 +00:00
|
|
|
|
|
|
|
|
# Convert %(variable) into {variable}.
|
|
|
|
|
structure = re.sub('%\((\w+)\)s', '{\g<1>}', structure)
|
|
|
|
|
|
|
|
|
|
# Convert %x into {date:%x} for strftime
|
|
|
|
|
structure = re.sub('(%[A-z])', '{date:\g<1>}', structure)
|
|
|
|
|
|
|
|
|
|
# Strip a / prefix
|
|
|
|
|
structure = re.sub('^/', '', structure)
|
|
|
|
|
|
|
|
|
|
for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL',
|
|
|
|
|
'PAGE_LANG_URL', 'ARTICLE_SAVE_AS',
|
|
|
|
|
'ARTICLE_LANG_SAVE_AS', 'PAGE_SAVE_AS',
|
|
|
|
|
'PAGE_LANG_SAVE_AS'):
|
2012-03-11 01:59:04 +01:00
|
|
|
self.settings[setting] = os.path.join(structure,
|
|
|
|
|
self.settings[setting])
|
2012-03-20 13:01:21 +00:00
|
|
|
logger.warning("%s = '%s'" % (setting, self.settings[setting]))
|
2010-12-25 17:26:24 +03:00
|
|
|
|
2012-07-16 13:47:39 -07:00
|
|
|
if self.settings.get('FEED', False):
|
2012-07-18 11:30:02 -07:00
|
|
|
logger.warning('Found deprecated `FEED` in settings. Modify FEED'
|
2012-07-16 13:47:39 -07:00
|
|
|
' to FEED_ATOM in your settings and theme for the same behavior.'
|
|
|
|
|
' Temporarily setting FEED_ATOM for backwards compatibility.')
|
|
|
|
|
self.settings['FEED_ATOM'] = self.settings['FEED']
|
|
|
|
|
|
|
|
|
|
if self.settings.get('TAG_FEED', False):
|
2012-07-18 11:30:02 -07:00
|
|
|
logger.warning('Found deprecated `TAG_FEED` in settings. Modify '
|
2012-07-16 13:47:39 -07:00
|
|
|
' TAG_FEED to TAG_FEED_ATOM in your settings and theme for the '
|
|
|
|
|
'same behavior. Temporarily setting TAG_FEED_ATOM for backwards '
|
|
|
|
|
'compatibility.')
|
|
|
|
|
self.settings['TAG_FEED_ATOM'] = self.settings['TAG_FEED']
|
|
|
|
|
|
|
|
|
|
if self.settings.get('CATEGORY_FEED', False):
|
2012-07-18 11:30:02 -07:00
|
|
|
logger.warning('Found deprecated `CATEGORY_FEED` in settings. '
|
2012-07-16 13:47:39 -07:00
|
|
|
'Modify CATEGORY_FEED to CATEGORY_FEED_ATOM in your settings and '
|
|
|
|
|
'theme for the same behavior. Temporarily setting '
|
|
|
|
|
'CATEGORY_FEED_ATOM for backwards compatibility.')
|
2012-10-12 23:22:55 +02:00
|
|
|
self.settings['CATEGORY_FEED_ATOM'] =\
|
|
|
|
|
self.settings['CATEGORY_FEED']
|
2012-07-16 13:47:39 -07:00
|
|
|
|
2012-10-22 16:34:55 +02:00
|
|
|
if self.settings.get('TRANSLATION_FEED', False):
|
|
|
|
|
logger.warning('Found deprecated `TRANSLATION_FEED` in settings. '
|
2012-10-22 23:05:18 +02:00
|
|
|
'Modify TRANSLATION_FEED to TRANSLATION_FEED_ATOM in your '
|
|
|
|
|
'settings and theme for the same behavior. Temporarily setting '
|
2012-10-22 16:34:55 +02:00
|
|
|
'TRANSLATION_FEED_ATOM for backwards compatibility.')
|
|
|
|
|
self.settings['TRANSLATION_FEED_ATOM'] =\
|
|
|
|
|
self.settings['TRANSLATION_FEED']
|
|
|
|
|
|
2010-12-25 17:26:24 +03:00
|
|
|
def run(self):
|
|
|
|
|
"""Run the generators and return"""
|
|
|
|
|
|
|
|
|
|
context = self.settings.copy()
|
|
|
|
|
generators = [
|
|
|
|
|
cls(
|
|
|
|
|
context,
|
|
|
|
|
self.settings,
|
|
|
|
|
self.path,
|
|
|
|
|
self.theme,
|
|
|
|
|
self.output_path,
|
|
|
|
|
self.markup,
|
2011-05-07 19:27:33 +01:00
|
|
|
self.delete_outputdir
|
2010-12-25 17:26:24 +03:00
|
|
|
) for cls in self.get_generator_classes()
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
for p in generators:
|
|
|
|
|
if hasattr(p, 'generate_context'):
|
|
|
|
|
p.generate_context()
|
|
|
|
|
|
2012-02-28 18:59:46 +01:00
|
|
|
# erase the directory if it is not the source and if that's
|
2011-05-07 19:27:33 +01:00
|
|
|
# explicitely asked
|
2011-11-27 11:43:18 +00:00
|
|
|
if (self.delete_outputdir and not
|
2012-02-28 18:59:46 +01:00
|
|
|
os.path.realpath(self.path).startswith(self.output_path)):
|
2010-12-25 17:26:24 +03:00
|
|
|
clean_output_dir(self.output_path)
|
|
|
|
|
|
|
|
|
|
writer = self.get_writer()
|
|
|
|
|
|
2012-05-11 22:19:03 +02:00
|
|
|
# pass the assets environment to the generators
|
|
|
|
|
if self.settings['WEBASSETS']:
|
|
|
|
|
generators[1].env.assets_environment = generators[0].assets_env
|
|
|
|
|
generators[2].env.assets_environment = generators[0].assets_env
|
2012-05-07 17:11:57 +02:00
|
|
|
|
2010-12-25 17:26:24 +03:00
|
|
|
for p in generators:
|
|
|
|
|
if hasattr(p, 'generate_output'):
|
|
|
|
|
p.generate_output(writer)
|
|
|
|
|
|
2012-08-22 23:05:07 +02:00
|
|
|
signals.finalized.send(self)
|
|
|
|
|
|
2010-12-25 17:26:24 +03:00
|
|
|
def get_generator_classes(self):
|
2012-03-05 15:52:14 +01:00
|
|
|
generators = [StaticGenerator, ArticlesGenerator, PagesGenerator,
|
|
|
|
|
StaticPageGenerator]
|
2010-12-25 17:26:24 +03:00
|
|
|
if self.settings['PDF_GENERATOR']:
|
|
|
|
|
generators.append(PdfGenerator)
|
2012-06-10 00:24:26 +02:00
|
|
|
if self.settings['LESS_GENERATOR']: # can be True or PATH to lessc
|
2012-04-15 02:20:20 +03:00
|
|
|
generators.append(LessCSSGenerator)
|
2012-09-07 08:46:38 +02:00
|
|
|
if self.settings['OUTPUT_SOURCES']:
|
|
|
|
|
generators.append(SourceFileGenerator)
|
2012-08-21 13:08:21 +02:00
|
|
|
|
|
|
|
|
for pair in signals.get_generators.send(self):
|
|
|
|
|
(funct, value) = pair
|
|
|
|
|
|
|
|
|
|
if not isinstance(value, (tuple, list)):
|
|
|
|
|
value = (value, )
|
|
|
|
|
|
|
|
|
|
for v in value:
|
|
|
|
|
if isinstance(v, type):
|
|
|
|
|
logger.debug('Found generator: {0}'.format(v))
|
|
|
|
|
generators.append(v)
|
|
|
|
|
|
2010-12-25 17:26:24 +03:00
|
|
|
return generators
|
|
|
|
|
|
|
|
|
|
def get_writer(self):
|
2011-02-15 14:36:55 +01:00
|
|
|
return Writer(self.output_path, settings=self.settings)
|
2010-12-02 03:22:24 +00:00
|
|
|
|
|
|
|
|
|
2012-03-22 07:02:06 +00:00
|
|
|
def parse_arguments():
|
2010-12-02 03:22:24 +00:00
|
|
|
parser = argparse.ArgumentParser(description="""A tool to generate a
|
2012-03-14 19:54:28 +00:00
|
|
|
static blog, with restructured text input files.""",
|
|
|
|
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
2010-12-02 03:22:24 +00:00
|
|
|
|
2011-02-11 15:36:51 +01:00
|
|
|
parser.add_argument(dest='path', nargs='?',
|
2012-03-22 07:02:06 +00:00
|
|
|
help='Path where to find the content files.',
|
2012-03-23 09:04:57 +00:00
|
|
|
default=None)
|
2012-03-22 07:02:06 +00:00
|
|
|
|
2010-12-02 03:22:24 +00:00
|
|
|
parser.add_argument('-t', '--theme-path', dest='theme',
|
2011-04-26 02:37:56 +02:00
|
|
|
help='Path where to find the theme templates. If not specified, it'
|
|
|
|
|
'will use the default one included with pelican.')
|
2012-03-22 07:02:06 +00:00
|
|
|
|
2010-12-02 03:22:24 +00:00
|
|
|
parser.add_argument('-o', '--output', dest='output',
|
2012-03-09 16:21:38 +01:00
|
|
|
help='Where to output the generated files. If not specified, a '
|
|
|
|
|
'directory will be created, named "output" in the current path.')
|
2012-03-22 07:02:06 +00:00
|
|
|
|
2012-03-14 19:54:28 +00:00
|
|
|
parser.add_argument('-m', '--markup', dest='markup',
|
2012-03-12 01:22:54 +09:00
|
|
|
help='The list of markup language to use (rst or md). Please indicate '
|
|
|
|
|
'them separated by commas.')
|
2012-03-22 07:02:06 +00:00
|
|
|
|
2012-03-14 19:54:28 +00:00
|
|
|
parser.add_argument('-s', '--settings', dest='settings',
|
|
|
|
|
help='The settings of the application.')
|
2012-03-22 07:02:06 +00:00
|
|
|
|
2012-03-09 16:21:38 +01:00
|
|
|
parser.add_argument('-d', '--delete-output-directory',
|
2012-03-22 07:02:06 +00:00
|
|
|
dest='delete_outputdir',
|
2011-05-07 19:27:33 +01:00
|
|
|
action='store_true', help='Delete the output directory.')
|
2012-03-22 07:02:06 +00:00
|
|
|
|
2012-03-09 16:21:38 +01:00
|
|
|
parser.add_argument('-v', '--verbose', action='store_const',
|
2012-03-22 07:02:06 +00:00
|
|
|
const=logging.INFO, dest='verbosity',
|
|
|
|
|
help='Show all messages.')
|
|
|
|
|
|
2012-03-09 16:21:38 +01:00
|
|
|
parser.add_argument('-q', '--quiet', action='store_const',
|
2012-03-22 07:02:06 +00:00
|
|
|
const=logging.CRITICAL, dest='verbosity',
|
|
|
|
|
help='Show only critical errors.')
|
|
|
|
|
|
2012-03-09 16:21:38 +01:00
|
|
|
parser.add_argument('-D', '--debug', action='store_const',
|
2012-03-22 07:02:06 +00:00
|
|
|
const=logging.DEBUG, dest='verbosity',
|
|
|
|
|
help='Show all message, including debug messages.')
|
|
|
|
|
|
2011-08-11 23:34:53 +02:00
|
|
|
parser.add_argument('--version', action='version', version=__version__,
|
2012-03-22 07:02:06 +00:00
|
|
|
help='Print the pelican version and exit.')
|
|
|
|
|
|
2012-03-09 16:21:38 +01:00
|
|
|
parser.add_argument('-r', '--autoreload', dest='autoreload',
|
2012-03-22 07:02:06 +00:00
|
|
|
action='store_true',
|
|
|
|
|
help="Relaunch pelican each time a modification occurs"
|
2012-03-12 01:22:54 +09:00
|
|
|
" on the content files.")
|
2012-03-22 07:02:06 +00:00
|
|
|
return parser.parse_args()
|
|
|
|
|
|
2010-12-18 20:18:48 +00:00
|
|
|
|
2012-10-16 01:35:35 +02:00
|
|
|
def get_config(args):
|
|
|
|
|
config = {}
|
|
|
|
|
if args.path:
|
|
|
|
|
config['PATH'] = os.path.abspath(os.path.expanduser(args.path))
|
|
|
|
|
if args.output:
|
|
|
|
|
config['OUTPUT_PATH'] = \
|
|
|
|
|
os.path.abspath(os.path.expanduser(args.output))
|
|
|
|
|
if args.markup:
|
|
|
|
|
config['MARKUP'] = [a.strip().lower() for a in args.markup.split(',')]
|
|
|
|
|
if args.theme:
|
|
|
|
|
abstheme = os.path.abspath(os.path.expanduser(args.theme))
|
|
|
|
|
config['THEME'] = abstheme if os.path.exists(abstheme) else args.theme
|
|
|
|
|
if args.delete_outputdir is not None:
|
|
|
|
|
config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir
|
|
|
|
|
return config
|
|
|
|
|
|
|
|
|
|
|
2012-06-10 00:24:26 +02:00
|
|
|
def get_instance(args):
|
2010-12-02 03:22:24 +00:00
|
|
|
|
2012-10-16 01:35:35 +02:00
|
|
|
settings = read_settings(args.settings, override=get_config(args))
|
2010-12-25 17:26:24 +03:00
|
|
|
|
|
|
|
|
cls = settings.get('PELICAN_CLASS')
|
|
|
|
|
if isinstance(cls, basestring):
|
|
|
|
|
module, cls_name = cls.rsplit('.', 1)
|
|
|
|
|
module = __import__(module)
|
|
|
|
|
cls = getattr(module, cls_name)
|
|
|
|
|
|
2012-10-16 01:35:35 +02:00
|
|
|
return cls(settings)
|
2012-06-10 00:24:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
args = parse_arguments()
|
|
|
|
|
init(args.verbosity)
|
|
|
|
|
pelican = get_instance(args)
|
|
|
|
|
|
2011-04-20 14:44:25 +02:00
|
|
|
try:
|
|
|
|
|
if args.autoreload:
|
2012-08-23 12:44:22 -07:00
|
|
|
files_found_error = True
|
2011-04-20 14:44:25 +02:00
|
|
|
while True:
|
|
|
|
|
try:
|
2011-05-15 12:14:03 +02:00
|
|
|
# Check source dir for changed files ending with the given
|
|
|
|
|
# extension in the settings. In the theme dir is no such
|
|
|
|
|
# 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, ['']):
|
2012-10-12 23:22:55 +02:00
|
|
|
if not files_found_error:
|
2012-08-23 12:44:22 -07:00
|
|
|
files_found_error = True
|
2011-04-20 14:44:25 +02:00
|
|
|
pelican.run()
|
2012-06-10 00:24:26 +02:00
|
|
|
|
|
|
|
|
# reload also if settings.py changed
|
|
|
|
|
if file_changed(args.settings):
|
|
|
|
|
logger.info('%s changed, re-generating' %
|
|
|
|
|
args.settings)
|
|
|
|
|
pelican = get_instance(args)
|
|
|
|
|
pelican.run()
|
|
|
|
|
|
2011-05-06 22:09:49 +02:00
|
|
|
time.sleep(.5) # sleep to avoid cpu load
|
2011-04-20 14:44:25 +02:00
|
|
|
except KeyboardInterrupt:
|
2012-07-11 20:30:56 -07:00
|
|
|
logger.warning("Keyboard interrupt, quitting.")
|
2011-04-20 14:44:25 +02:00
|
|
|
break
|
2012-08-23 12:44:22 -07:00
|
|
|
except NoFilesError:
|
2012-10-12 23:22:55 +02:00
|
|
|
if files_found_error:
|
|
|
|
|
logger.warning("No valid files found in content. "
|
|
|
|
|
"Nothing to generate.")
|
2012-08-23 12:44:22 -07:00
|
|
|
files_found_error = False
|
2012-09-28 08:53:59 -07:00
|
|
|
time.sleep(1) # sleep to avoid cpu load
|
2012-07-11 20:30:56 -07:00
|
|
|
except Exception, e:
|
|
|
|
|
logger.warning(
|
|
|
|
|
"Caught exception \"{}\". Reloading.".format(e)
|
|
|
|
|
)
|
|
|
|
|
continue
|
2011-04-20 14:44:25 +02:00
|
|
|
else:
|
2011-04-19 14:49:46 +02:00
|
|
|
pelican.run()
|
2011-04-20 14:44:25 +02:00
|
|
|
except Exception, e:
|
2012-03-20 13:01:21 +00:00
|
|
|
logger.critical(unicode(e))
|
2010-12-02 03:22:24 +00:00
|
|
|
|
2012-03-20 13:01:21 +00:00
|
|
|
if (args.verbosity == logging.DEBUG):
|
2011-09-30 16:25:32 +02:00
|
|
|
raise
|
|
|
|
|
else:
|
2011-11-25 18:39:19 +01:00
|
|
|
sys.exit(getattr(e, 'exitcode', 1))
|