mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
Merge branch 'master' of github.com:ametaireau/pelican
This commit is contained in:
commit
bf38517a91
14 changed files with 146 additions and 86 deletions
|
|
@ -49,14 +49,14 @@ Take a look to the Markdown reader::
|
||||||
md = Markdown(extensions = ['meta', 'codehilite'])
|
md = Markdown(extensions = ['meta', 'codehilite'])
|
||||||
content = md.convert(text)
|
content = md.convert(text)
|
||||||
|
|
||||||
metadatas = {}
|
metadata = {}
|
||||||
for name, value in md.Meta.items():
|
for name, value in md.Meta.items():
|
||||||
if name in _METADATAS_FIELDS:
|
if name in _METADATA_FIELDS:
|
||||||
meta = _METADATAS_FIELDS[name](value[0])
|
meta = _METADATA_FIELDS[name](value[0])
|
||||||
else:
|
else:
|
||||||
meta = value[0]
|
meta = value[0]
|
||||||
metadatas[name.lower()] = meta
|
metadata[name.lower()] = meta
|
||||||
return content, metadatas
|
return content, metadata
|
||||||
|
|
||||||
Simple isn't it ?
|
Simple isn't it ?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ Setting name (default value) what does it do?
|
||||||
`DEFAULT_CATEGORY` (``'misc'``) The default category to fallback on.
|
`DEFAULT_CATEGORY` (``'misc'``) The default category to fallback on.
|
||||||
`DEFAULT_DATE_FORMAT` (``'%a %d %B %Y'``) The default date format you want to use.
|
`DEFAULT_DATE_FORMAT` (``'%a %d %B %Y'``) The default date format you want to use.
|
||||||
`DEFAULT_LANG` (``'en'``) The default language to use.
|
`DEFAULT_LANG` (``'en'``) The default language to use.
|
||||||
|
`DEFAULT_METADATA` (``()``) A list containing the default metadata for
|
||||||
|
each content (articles, pages, etc.)
|
||||||
`DEFAULT_ORPHANS` (0) The minimum number of articles allowed on the
|
`DEFAULT_ORPHANS` (0) The minimum number of articles allowed on the
|
||||||
last page. Use this when you don't want to
|
last page. Use this when you don't want to
|
||||||
have a last page with very few articles.
|
have a last page with very few articles.
|
||||||
|
|
@ -45,8 +47,11 @@ Setting name (default value) what does it do?
|
||||||
informations from the metadata
|
informations from the metadata
|
||||||
`FEED` (``'feeds/all.atom.xml'``) relative url to output the atom feed.
|
`FEED` (``'feeds/all.atom.xml'``) relative url to output the atom feed.
|
||||||
`FEED_RSS` (``None``, i.e. no RSS) relative url to output the rss feed.
|
`FEED_RSS` (``None``, i.e. no RSS) relative url to output the rss feed.
|
||||||
|
`FILES_TO_COPY` (``()``, no files) A list of tuples (source, destination) of files
|
||||||
|
to copy from the source directory to the
|
||||||
|
output path
|
||||||
`JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use.
|
`JINJA_EXTENSIONS` (``[]``) A list of any Jinja2 extensions you want to use.
|
||||||
`KEEP_OUTPUT_DIRECTORY` (``False``) Keep the output directory and just update all
|
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the output directory instead of just updating all
|
||||||
the generated files.
|
the generated files.
|
||||||
`LOCALE` (''[2]_) Change the locale.
|
`LOCALE` (''[2]_) Change the locale.
|
||||||
`MARKUP` (``('rst', 'md')``) A list of available markup languages you want
|
`MARKUP` (``('rst', 'md')``) A list of available markup languages you want
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
from pelican.generators import (ArticlesGenerator, PagesGenerator,
|
from pelican.generators import (ArticlesGenerator, PagesGenerator,
|
||||||
StaticGenerator, PdfGenerator)
|
StaticGenerator, PdfGenerator)
|
||||||
|
|
@ -13,7 +14,7 @@ VERSION = "2.6.0"
|
||||||
|
|
||||||
class Pelican(object):
|
class Pelican(object):
|
||||||
def __init__(self, settings=None, path=None, theme=None, output_path=None,
|
def __init__(self, settings=None, path=None, theme=None, output_path=None,
|
||||||
markup=None, keep=False):
|
markup=None, delete_outputdir=False):
|
||||||
"""Read the settings, and performs some checks on the environment
|
"""Read the settings, and performs some checks on the environment
|
||||||
before doing anything else.
|
before doing anything else.
|
||||||
"""
|
"""
|
||||||
|
|
@ -31,7 +32,7 @@ class Pelican(object):
|
||||||
output_path = output_path or settings['OUTPUT_PATH']
|
output_path = output_path or settings['OUTPUT_PATH']
|
||||||
self.output_path = os.path.realpath(output_path)
|
self.output_path = os.path.realpath(output_path)
|
||||||
self.markup = markup or settings['MARKUP']
|
self.markup = markup or settings['MARKUP']
|
||||||
self.keep = keep or settings['KEEP_OUTPUT_DIRECTORY']
|
self.delete_outputdir = delete_outputdir or settings['DELETE_OUTPUT_DIRECTORY']
|
||||||
|
|
||||||
# find the theme in pelican.theme if the given one does not exists
|
# find the theme in pelican.theme if the given one does not exists
|
||||||
if not os.path.exists(self.theme):
|
if not os.path.exists(self.theme):
|
||||||
|
|
@ -54,7 +55,7 @@ class Pelican(object):
|
||||||
self.theme,
|
self.theme,
|
||||||
self.output_path,
|
self.output_path,
|
||||||
self.markup,
|
self.markup,
|
||||||
self.keep
|
self.delete_outputdir
|
||||||
) for cls in self.get_generator_classes()
|
) for cls in self.get_generator_classes()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -62,8 +63,10 @@ class Pelican(object):
|
||||||
if hasattr(p, 'generate_context'):
|
if hasattr(p, 'generate_context'):
|
||||||
p.generate_context()
|
p.generate_context()
|
||||||
|
|
||||||
# erase the directory if it is not the source
|
# erase the directory if it is not the source and if that's
|
||||||
if os.path.realpath(self.path).startswith(self.output_path) and not self.keep:
|
# explicitely asked
|
||||||
|
if (self.delete_outputdir and
|
||||||
|
os.path.realpath(self.path).startswith(self.output_path)):
|
||||||
clean_output_dir(self.output_path)
|
clean_output_dir(self.output_path)
|
||||||
|
|
||||||
writer = self.get_writer()
|
writer = self.get_writer()
|
||||||
|
|
@ -100,11 +103,9 @@ def main():
|
||||||
help='the list of markup language to use (rst or md). Please indicate '
|
help='the list of markup language to use (rst or md). Please indicate '
|
||||||
'them separated by commas')
|
'them separated by commas')
|
||||||
parser.add_argument('-s', '--settings', dest='settings',
|
parser.add_argument('-s', '--settings', dest='settings',
|
||||||
help='the settings of the application. Default to None.')
|
help='the settings of the application. Default to False.')
|
||||||
parser.add_argument('-k', '--keep-output-directory', dest='keep',
|
parser.add_argument('-d', '--delete-output-directory', dest='delete_outputdir',
|
||||||
action='store_true',
|
action='store_true', help='Delete the output directory.')
|
||||||
help='Keep the output directory and just update all the generated files.'
|
|
||||||
'Default is to delete the output directory.')
|
|
||||||
parser.add_argument('-v', '--verbose', action='store_const', const=log.INFO, dest='verbosity',
|
parser.add_argument('-v', '--verbose', action='store_const', const=log.INFO, dest='verbosity',
|
||||||
help='Show all messages')
|
help='Show all messages')
|
||||||
parser.add_argument('-q', '--quiet', action='store_const', const=log.CRITICAL, dest='verbosity',
|
parser.add_argument('-q', '--quiet', action='store_const', const=log.CRITICAL, dest='verbosity',
|
||||||
|
|
@ -134,12 +135,14 @@ def main():
|
||||||
cls = getattr(module, cls_name)
|
cls = getattr(module, cls_name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pelican = cls(settings, args.path, args.theme, args.output, markup, args.keep)
|
pelican = cls(settings, args.path, args.theme, args.output, markup,
|
||||||
|
args.delete_outputdir)
|
||||||
if args.autoreload:
|
if args.autoreload:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
if files_changed(pelican.path, pelican.markup):
|
if files_changed(pelican.path, pelican.markup):
|
||||||
pelican.run()
|
pelican.run()
|
||||||
|
time.sleep(.5) # sleep to avoid cpu load
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,21 @@ from pelican.log import *
|
||||||
|
|
||||||
class Page(object):
|
class Page(object):
|
||||||
"""Represents a page
|
"""Represents a page
|
||||||
Given a content, and metadatas, create an adequate object.
|
Given a content, and metadata, create an adequate object.
|
||||||
|
|
||||||
:param string: the string to parse, containing the original content.
|
:param content: the string to parse, containing the original content.
|
||||||
:param markup: the markup language to use while parsing.
|
|
||||||
"""
|
"""
|
||||||
mandatory_properties = ('title',)
|
mandatory_properties = ('title',)
|
||||||
|
|
||||||
def __init__(self, content, metadatas={}, settings={}, filename=None):
|
def __init__(self, content, metadata={}, settings={}, filename=None):
|
||||||
self._content = content
|
self._content = content
|
||||||
self.translations = []
|
self.translations = []
|
||||||
|
|
||||||
self.status = "published" # default value
|
self.status = "published" # default value
|
||||||
for key, value in metadatas.items():
|
|
||||||
|
local_metadata = dict(settings['DEFAULT_METADATA'])
|
||||||
|
local_metadata.update(metadata)
|
||||||
|
for key, value in local_metadata.items():
|
||||||
setattr(self, key.lower(), value)
|
setattr(self, key.lower(), value)
|
||||||
|
|
||||||
if not hasattr(self, 'author'):
|
if not hasattr(self, 'author'):
|
||||||
|
|
@ -90,6 +92,6 @@ def is_valid_content(content, f):
|
||||||
try:
|
try:
|
||||||
content.check_properties()
|
content.check_properties()
|
||||||
return True
|
return True
|
||||||
except NameError as e:
|
except NameError, e:
|
||||||
error(u"Skipping %s: impossible to find informations about '%s'" % (f, e))
|
error(u"Skipping %s: impossible to find informations about '%s'" % (f, e))
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import random
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
from jinja2.exceptions import TemplateNotFound
|
from jinja2.exceptions import TemplateNotFound
|
||||||
|
|
||||||
from pelican.utils import copytree, get_relative_path, process_translations, open
|
from pelican.utils import copy, get_relative_path, process_translations, open
|
||||||
from pelican.contents import Article, Page, is_valid_content
|
from pelican.contents import Article, Page, is_valid_content
|
||||||
from pelican.readers import read_file
|
from pelican.readers import read_file
|
||||||
from pelican.log import *
|
from pelican.log import *
|
||||||
|
|
@ -63,7 +63,13 @@ class Generator(object):
|
||||||
extensions = self.markup
|
extensions = self.markup
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
for root, dirs, temp_files in os.walk(path, followlinks=True):
|
|
||||||
|
try:
|
||||||
|
iter = os.walk(path, followlinks=True)
|
||||||
|
except TypeError: # python 2.5 does not support followlinks
|
||||||
|
iter = os.walk(path)
|
||||||
|
|
||||||
|
for root, dirs, temp_files in iter:
|
||||||
for e in exclude:
|
for e in exclude:
|
||||||
if e in dirs:
|
if e in dirs:
|
||||||
dirs.remove(e)
|
dirs.remove(e)
|
||||||
|
|
@ -183,10 +189,10 @@ class ArticlesGenerator(Generator):
|
||||||
files = self.get_files(self.path, exclude=['pages',])
|
files = self.get_files(self.path, exclude=['pages',])
|
||||||
all_articles = []
|
all_articles = []
|
||||||
for f in files:
|
for f in files:
|
||||||
content, metadatas = read_file(f)
|
content, metadata = read_file(f)
|
||||||
|
|
||||||
# if no category is set, use the name of the path as a category
|
# if no category is set, use the name of the path as a category
|
||||||
if 'category' not in metadatas.keys():
|
if 'category' not in metadata.keys():
|
||||||
|
|
||||||
if os.path.dirname(f) == self.path:
|
if os.path.dirname(f) == self.path:
|
||||||
category = self.settings['DEFAULT_CATEGORY']
|
category = self.settings['DEFAULT_CATEGORY']
|
||||||
|
|
@ -194,13 +200,13 @@ class ArticlesGenerator(Generator):
|
||||||
category = os.path.basename(os.path.dirname(f))
|
category = os.path.basename(os.path.dirname(f))
|
||||||
|
|
||||||
if category != '':
|
if category != '':
|
||||||
metadatas['category'] = unicode(category)
|
metadata['category'] = unicode(category)
|
||||||
|
|
||||||
if 'date' not in metadatas.keys()\
|
if 'date' not in metadata.keys()\
|
||||||
and self.settings['FALLBACK_ON_FS_DATE']:
|
and self.settings['FALLBACK_ON_FS_DATE']:
|
||||||
metadatas['date'] = datetime.fromtimestamp(os.stat(f).st_ctime)
|
metadata['date'] = datetime.fromtimestamp(os.stat(f).st_ctime)
|
||||||
|
|
||||||
article = Article(content, metadatas, settings=self.settings,
|
article = Article(content, metadata, settings=self.settings,
|
||||||
filename=f)
|
filename=f)
|
||||||
if not is_valid_content(article, f):
|
if not is_valid_content(article, f):
|
||||||
continue
|
continue
|
||||||
|
|
@ -273,8 +279,8 @@ class PagesGenerator(Generator):
|
||||||
def generate_context(self):
|
def generate_context(self):
|
||||||
all_pages = []
|
all_pages = []
|
||||||
for f in self.get_files(os.sep.join((self.path, 'pages'))):
|
for f in self.get_files(os.sep.join((self.path, 'pages'))):
|
||||||
content, metadatas = read_file(f)
|
content, metadata = read_file(f)
|
||||||
page = Page(content, metadatas, settings=self.settings,
|
page = Page(content, metadata, settings=self.settings,
|
||||||
filename=f)
|
filename=f)
|
||||||
if not is_valid_content(page, f):
|
if not is_valid_content(page, f):
|
||||||
continue
|
continue
|
||||||
|
|
@ -298,9 +304,10 @@ class StaticGenerator(Generator):
|
||||||
|
|
||||||
def _copy_paths(self, paths, source, destination, output_path,
|
def _copy_paths(self, paths, source, destination, output_path,
|
||||||
final_path=None):
|
final_path=None):
|
||||||
|
"""Copy all the paths from source to destination"""
|
||||||
for path in paths:
|
for path in paths:
|
||||||
copytree(path, source, os.path.join(output_path, destination),
|
copy(path, source, os.path.join(output_path, destination), final_path,
|
||||||
final_path)
|
overwrite=True)
|
||||||
|
|
||||||
def generate_output(self, writer):
|
def generate_output(self, writer):
|
||||||
self._copy_paths(self.settings['STATIC_PATHS'], self.path,
|
self._copy_paths(self.settings['STATIC_PATHS'], self.path,
|
||||||
|
|
@ -308,6 +315,10 @@ class StaticGenerator(Generator):
|
||||||
self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme,
|
self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme,
|
||||||
'theme', self.output_path, '.')
|
'theme', self.output_path, '.')
|
||||||
|
|
||||||
|
# copy all the files needed
|
||||||
|
for source, destination in self.settings['FILES_TO_COPY']:
|
||||||
|
copy(source, self.path, self.output_path, destination, overwrite=True)
|
||||||
|
|
||||||
|
|
||||||
class PdfGenerator(Generator):
|
class PdfGenerator(Generator):
|
||||||
"""Generate PDFs on the output dir, for all articles and pages coming from
|
"""Generate PDFs on the output dir, for all articles and pages coming from
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
from logging import *
|
from logging import CRITICAL, ERROR, WARN, INFO, DEBUG
|
||||||
|
from logging import critical, error, info, warning, warn, debug
|
||||||
|
from logging import Formatter, getLogger, StreamHandler
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ try:
|
||||||
from docutils import core
|
from docutils import core
|
||||||
|
|
||||||
# import the directives to have pygments support
|
# import the directives to have pygments support
|
||||||
import rstdirectives
|
from pelican import rstdirectives
|
||||||
except ImportError:
|
except ImportError:
|
||||||
core = False
|
core = False
|
||||||
try:
|
try:
|
||||||
|
|
@ -11,15 +11,14 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
Markdown = False
|
Markdown = False
|
||||||
import re
|
import re
|
||||||
import string
|
|
||||||
|
|
||||||
from pelican.utils import get_date, open
|
from pelican.utils import get_date, open
|
||||||
|
|
||||||
|
|
||||||
_METADATAS_PROCESSORS = {
|
_METADATA_PROCESSORS = {
|
||||||
'tags': lambda x: map(string.strip, x.split(',')),
|
'tags': lambda x: map(unicode.strip, x.split(',')),
|
||||||
'date': lambda x: get_date(x),
|
'date': lambda x: get_date(x),
|
||||||
'status': string.strip,
|
'status': unicode.strip,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -31,11 +30,11 @@ class RstReader(Reader):
|
||||||
extension = "rst"
|
extension = "rst"
|
||||||
|
|
||||||
def _parse_metadata(self, content):
|
def _parse_metadata(self, content):
|
||||||
"""Return the dict containing metadatas"""
|
"""Return the dict containing metadata"""
|
||||||
output = {}
|
output = {}
|
||||||
for m in re.compile('^:([a-z]+): (.*)\s', re.M).finditer(content):
|
for m in re.compile('^:([a-z]+): (.*)\s', re.M).finditer(content):
|
||||||
name, value = m.group(1).lower(), m.group(2)
|
name, value = m.group(1).lower(), m.group(2)
|
||||||
output[name] = _METADATAS_PROCESSORS.get(
|
output[name] = _METADATA_PROCESSORS.get(
|
||||||
name, lambda x:x
|
name, lambda x:x
|
||||||
)(value)
|
)(value)
|
||||||
return output
|
return output
|
||||||
|
|
@ -43,16 +42,18 @@ class RstReader(Reader):
|
||||||
def read(self, filename):
|
def read(self, filename):
|
||||||
"""Parse restructured text"""
|
"""Parse restructured text"""
|
||||||
text = open(filename)
|
text = open(filename)
|
||||||
metadatas = self._parse_metadata(text)
|
metadata = self._parse_metadata(text)
|
||||||
extra_params = {'input_encoding': 'unicode',
|
extra_params = {'input_encoding': 'unicode',
|
||||||
'initial_header_level': '2'}
|
'initial_header_level': '2'}
|
||||||
rendered_content = core.publish_parts(text, writer_name='html',
|
rendered_content = core.publish_parts(text,
|
||||||
|
source_path=filename,
|
||||||
|
writer_name='html',
|
||||||
settings_overrides=extra_params)
|
settings_overrides=extra_params)
|
||||||
title = rendered_content.get('title')
|
title = rendered_content.get('title')
|
||||||
content = rendered_content.get('body')
|
content = rendered_content.get('body')
|
||||||
if not metadatas.has_key('title'):
|
if not metadata.has_key('title'):
|
||||||
metadatas['title'] = title
|
metadata['title'] = title
|
||||||
return content, metadatas
|
return content, metadata
|
||||||
|
|
||||||
class MarkdownReader(Reader):
|
class MarkdownReader(Reader):
|
||||||
enabled = bool(Markdown)
|
enabled = bool(Markdown)
|
||||||
|
|
@ -64,13 +65,13 @@ class MarkdownReader(Reader):
|
||||||
md = Markdown(extensions = ['meta', 'codehilite'])
|
md = Markdown(extensions = ['meta', 'codehilite'])
|
||||||
content = md.convert(text)
|
content = md.convert(text)
|
||||||
|
|
||||||
metadatas = {}
|
metadata = {}
|
||||||
for name, value in md.Meta.items():
|
for name, value in md.Meta.items():
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
metadatas[name] = _METADATAS_PROCESSORS.get(
|
metadata[name] = _METADATA_PROCESSORS.get(
|
||||||
name, lambda x:x
|
name, lambda x:x
|
||||||
)(value[0])
|
)(value[0])
|
||||||
return content, metadatas
|
return content, metadata
|
||||||
|
|
||||||
|
|
||||||
class HtmlReader(Reader):
|
class HtmlReader(Reader):
|
||||||
|
|
@ -80,13 +81,13 @@ class HtmlReader(Reader):
|
||||||
def read(self, filename):
|
def read(self, filename):
|
||||||
"""Parse content and metadata of (x)HTML files"""
|
"""Parse content and metadata of (x)HTML files"""
|
||||||
content = open(filename)
|
content = open(filename)
|
||||||
metadatas = {'title':'unnamed'}
|
metadata = {'title':'unnamed'}
|
||||||
for i in self._re.findall(content):
|
for i in self._re.findall(content):
|
||||||
key = i.split(':')[0][5:].strip()
|
key = i.split(':')[0][5:].strip()
|
||||||
value = i.split(':')[-1][:-3].strip()
|
value = i.split(':')[-1][:-3].strip()
|
||||||
metadatas[key.lower()] = value
|
metadata[key.lower()] = value
|
||||||
|
|
||||||
return content, metadatas
|
return content, metadata
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ _DEFAULT_CONFIG = {'PATH': None,
|
||||||
'CSS_FILE': 'main.css',
|
'CSS_FILE': 'main.css',
|
||||||
'REVERSE_ARCHIVE_ORDER': False,
|
'REVERSE_ARCHIVE_ORDER': False,
|
||||||
'REVERSE_CATEGORY_ORDER': False,
|
'REVERSE_CATEGORY_ORDER': False,
|
||||||
'KEEP_OUTPUT_DIRECTORY': False,
|
'DELETE_OUTPUT_DIRECTORY': False,
|
||||||
'CLEAN_URLS': False, # use /blah/ instead /blah.html in urls
|
'CLEAN_URLS': False, # use /blah/ instead /blah.html in urls
|
||||||
'RELATIVE_URLS': True,
|
'RELATIVE_URLS': True,
|
||||||
'DEFAULT_LANG': 'en',
|
'DEFAULT_LANG': 'en',
|
||||||
|
|
@ -37,6 +37,8 @@ _DEFAULT_CONFIG = {'PATH': None,
|
||||||
'WITH_PAGINATION': False,
|
'WITH_PAGINATION': False,
|
||||||
'DEFAULT_PAGINATION': 5,
|
'DEFAULT_PAGINATION': 5,
|
||||||
'DEFAULT_ORPHANS': 0,
|
'DEFAULT_ORPHANS': 0,
|
||||||
|
'DEFAULT_METADATA': (),
|
||||||
|
'FILES_TO_COPY': (),
|
||||||
}
|
}
|
||||||
|
|
||||||
def read_settings(filename):
|
def read_settings(filename):
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from datetime import datetime
|
||||||
from codecs import open as _open
|
from codecs import open as _open
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from pelican.log import *
|
from pelican.log import warning, info
|
||||||
|
|
||||||
|
|
||||||
def get_date(string):
|
def get_date(string):
|
||||||
|
|
@ -42,20 +42,38 @@ def slugify(value):
|
||||||
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
|
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
|
||||||
return re.sub('[-\s]+', '-', value)
|
return re.sub('[-\s]+', '-', value)
|
||||||
|
|
||||||
def copytree(path, origin, destination, topath=None):
|
def copy(path, source, destination, destination_path=None, overwrite=False):
|
||||||
"""Copy path from origin to destination, silent any errors"""
|
"""Copy path from origin to destination.
|
||||||
|
|
||||||
if not topath:
|
The function is able to copy either files or directories.
|
||||||
topath = path
|
|
||||||
try:
|
|
||||||
fromp = os.path.expanduser(os.path.join(origin, path))
|
|
||||||
to = os.path.expanduser(os.path.join(destination, topath))
|
|
||||||
shutil.copytree(fromp, to)
|
|
||||||
info('copying %s to %s' % (fromp, to))
|
|
||||||
|
|
||||||
except OSError:
|
:param path: the path to be copied from the source to the destination
|
||||||
pass
|
:param source: the source dir
|
||||||
|
:param destination: the destination dir
|
||||||
|
:param destination_path: the destination path (optional)
|
||||||
|
:param overwrite: wether to overwrite the destination if already exists or not
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not destination_path:
|
||||||
|
destination_path = path
|
||||||
|
|
||||||
|
source_ = os.path.abspath(os.path.expanduser(os.path.join(source, path)))
|
||||||
|
destination_ = os.path.abspath(
|
||||||
|
os.path.expanduser(os.path.join(destination, destination_path)))
|
||||||
|
|
||||||
|
if os.path.isdir(source_):
|
||||||
|
try:
|
||||||
|
shutil.copytree(source_, destination_)
|
||||||
|
info('copying %s to %s' % (source_, destination_))
|
||||||
|
except OSError:
|
||||||
|
if overwrite:
|
||||||
|
shutil.rmtree(destination_)
|
||||||
|
shutil.copytree(source_, destination_)
|
||||||
|
info('replacement of %s with %s' % (source_, destination_))
|
||||||
|
|
||||||
|
elif os.path.isfile(source_):
|
||||||
|
shutil.copy(source_, destination_)
|
||||||
|
info('copying %s to %s' % (source_, destination_))
|
||||||
|
|
||||||
def clean_output_dir(path):
|
def clean_output_dir(path):
|
||||||
"""Remove all the files from the output directory"""
|
"""Remove all the files from the output directory"""
|
||||||
|
|
@ -164,9 +182,13 @@ def process_translations(content_list):
|
||||||
len_ = len(default_lang_items)
|
len_ = len(default_lang_items)
|
||||||
if len_ > 1:
|
if len_ > 1:
|
||||||
warning(u'there are %s variants of "%s"' % (len_, slug))
|
warning(u'there are %s variants of "%s"' % (len_, slug))
|
||||||
|
for x in default_lang_items:
|
||||||
|
warning(' %s' % x.filename)
|
||||||
elif len_ == 0:
|
elif len_ == 0:
|
||||||
default_lang_items = items[:1]
|
default_lang_items = items[:1]
|
||||||
|
|
||||||
|
if not slug:
|
||||||
|
warning('empty slug for %r' %( default_lang_items[0].filename,))
|
||||||
index.extend(default_lang_items)
|
index.extend(default_lang_items)
|
||||||
translations.extend(filter(
|
translations.extend(filter(
|
||||||
lambda x: x not in default_lang_items,
|
lambda x: x not in default_lang_items,
|
||||||
|
|
@ -188,9 +210,10 @@ def files_changed(path, extensions):
|
||||||
|
|
||||||
def file_times(path):
|
def file_times(path):
|
||||||
"""Return the last time files have been modified"""
|
"""Return the last time files have been modified"""
|
||||||
for top_level in os.listdir(path):
|
for root, dirs, files in os.walk(path):
|
||||||
for root, dirs, files in os.walk(top_level):
|
dirs[:] = [x for x in dirs if x[0] != '.']
|
||||||
for file in filter(with_extension, files):
|
for file in files:
|
||||||
|
if any(file.endswith(ext) for ext in extensions):
|
||||||
yield os.stat(os.path.join(root, file)).st_mtime
|
yield os.stat(os.path.join(root, file)).st_mtime
|
||||||
|
|
||||||
global LAST_MTIME
|
global LAST_MTIME
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import with_statement
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from codecs import open
|
from codecs import open
|
||||||
|
|
@ -44,9 +45,8 @@ class Writer(object):
|
||||||
Return the feed. If no output_path or filename is specified, just return
|
Return the feed. If no output_path or filename is specified, just return
|
||||||
the feed object.
|
the feed object.
|
||||||
|
|
||||||
:param articles: the articles to put on the feed.
|
:param elements: the articles to put on the feed.
|
||||||
:param context: the context to get the feed metadata.
|
:param context: the context to get the feed metadata.
|
||||||
:param output_path: where to output the file.
|
|
||||||
:param filename: the filename to output.
|
:param filename: the filename to output.
|
||||||
:param feed_type: the feed type to use (atom or rss)
|
:param feed_type: the feed type to use (atom or rss)
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,6 @@ Article 1
|
||||||
#########
|
#########
|
||||||
|
|
||||||
:date: 2011-02-17
|
:date: 2011-02-17
|
||||||
|
:yeah: oh yeah !
|
||||||
|
|
||||||
Article 1
|
Article 1
|
||||||
|
|
|
||||||
2
samples/content/extra/robots.txt
Normal file
2
samples/content/extra/robots.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
User-agent: *
|
||||||
|
Disallow: /static/pictures
|
||||||
|
|
@ -24,4 +24,11 @@ SOCIAL = (('twitter', 'http://twitter.com/ametaireau'),
|
||||||
('lastfm', 'http://lastfm.com/user/akounet'),
|
('lastfm', 'http://lastfm.com/user/akounet'),
|
||||||
('github', 'http://github.com/ametaireau'),)
|
('github', 'http://github.com/ametaireau'),)
|
||||||
|
|
||||||
|
# global metadata to all the contents
|
||||||
|
DEFAULT_METADATA = (('yeah', 'it is'),)
|
||||||
|
|
||||||
|
# static paths will be copied under the same name
|
||||||
STATIC_PATHS = ["pictures",]
|
STATIC_PATHS = ["pictures",]
|
||||||
|
|
||||||
|
# A list of files to copy from the source to the destination
|
||||||
|
FILES_TO_COPY = (('extra/robots.txt', 'robots.txt'),)
|
||||||
|
|
|
||||||
1
setup.py
Normal file → Executable file
1
setup.py
Normal file → Executable file
|
|
@ -1,3 +1,4 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue