mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
Manual pass on sources for better standards.
This commit is contained in:
parent
20662c2a43
commit
519dcdbcb3
9 changed files with 136 additions and 105 deletions
|
|
@ -293,8 +293,15 @@ def main():
|
||||||
# restriction; all files are recursively checked if they
|
# restriction; all files are recursively checked if they
|
||||||
# have changed, no matter what extension the filenames
|
# have changed, no matter what extension the filenames
|
||||||
# have.
|
# have.
|
||||||
if files_changed(pelican.path, pelican.markup, pelican.ignore_files) or \
|
if (files_changed(
|
||||||
files_changed(pelican.theme, [''], pelican.ignore_files):
|
pelican.path,
|
||||||
|
pelican.markup,
|
||||||
|
pelican.ignore_files)
|
||||||
|
or files_changed(
|
||||||
|
pelican.theme,
|
||||||
|
[''],
|
||||||
|
pelican.ignore_files
|
||||||
|
)):
|
||||||
if not files_found_error:
|
if not files_found_error:
|
||||||
files_found_error = True
|
files_found_error = True
|
||||||
pelican.run()
|
pelican.run()
|
||||||
|
|
@ -318,8 +325,7 @@ def main():
|
||||||
time.sleep(1) # sleep to avoid cpu load
|
time.sleep(1) # sleep to avoid cpu load
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Caught exception \"{}\". Reloading.".format(e)
|
'Caught exception "{0}". Reloading.'.format(e))
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
pelican.run()
|
pelican.run()
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from sys import platform, stdin
|
|
||||||
|
|
||||||
|
|
||||||
from pelican.settings import _DEFAULT_CONFIG
|
from pelican.settings import _DEFAULT_CONFIG
|
||||||
|
|
@ -92,7 +91,8 @@ class Page(object):
|
||||||
|
|
||||||
if isinstance(self.date_format, tuple):
|
if isinstance(self.date_format, tuple):
|
||||||
locale_string = self.date_format[0]
|
locale_string = self.date_format[0]
|
||||||
if sys.version_info < (3, ) and isinstance(locale_string, six.text_type):
|
if sys.version_info < (3, ) and isinstance(locale_string,
|
||||||
|
six.text_type):
|
||||||
locale_string = locale_string.encode('ascii')
|
locale_string = locale_string.encode('ascii')
|
||||||
locale.setlocale(locale.LC_ALL, locale_string)
|
locale.setlocale(locale.LC_ALL, locale_string)
|
||||||
self.date_format = self.date_format[1]
|
self.date_format = self.date_format[1]
|
||||||
|
|
@ -289,9 +289,12 @@ class URLWrapper(object):
|
||||||
|
|
||||||
def _from_settings(self, key, get_page_name=False):
|
def _from_settings(self, key, get_page_name=False):
|
||||||
"""Returns URL information as defined in settings.
|
"""Returns URL information as defined in settings.
|
||||||
When get_page_name=True returns URL without anything after {slug}
|
|
||||||
e.g. if in settings: CATEGORY_URL="cat/{slug}.html" this returns "cat/{slug}"
|
When get_page_name=True returns URL without anything after {slug} e.g.
|
||||||
Useful for pagination."""
|
if in settings: CATEGORY_URL="cat/{slug}.html" this returns
|
||||||
|
"cat/{slug}" Useful for pagination.
|
||||||
|
|
||||||
|
"""
|
||||||
setting = "%s_%s" % (self.__class__.__name__.upper(), key)
|
setting = "%s_%s" % (self.__class__.__name__.upper(), key)
|
||||||
value = self.settings[setting]
|
value = self.settings[setting]
|
||||||
if not isinstance(value, six.string_types):
|
if not isinstance(value, six.string_types):
|
||||||
|
|
@ -303,7 +306,8 @@ class URLWrapper(object):
|
||||||
else:
|
else:
|
||||||
return value.format(**self.as_dict())
|
return value.format(**self.as_dict())
|
||||||
|
|
||||||
page_name = property(functools.partial(_from_settings, key='URL', get_page_name=True))
|
page_name = property(functools.partial(_from_settings, key='URL',
|
||||||
|
get_page_name=True))
|
||||||
url = property(functools.partial(_from_settings, key='URL'))
|
url = property(functools.partial(_from_settings, key='URL'))
|
||||||
save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
|
save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,14 @@ from functools import partial
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from operator import attrgetter, itemgetter
|
from operator import attrgetter, itemgetter
|
||||||
|
|
||||||
from jinja2 import (Environment, FileSystemLoader, PrefixLoader, ChoiceLoader,
|
from jinja2 import (
|
||||||
BaseLoader, TemplateNotFound)
|
Environment, FileSystemLoader, PrefixLoader, ChoiceLoader, BaseLoader,
|
||||||
|
TemplateNotFound
|
||||||
|
)
|
||||||
|
|
||||||
from pelican.contents import Article, Page, Category, StaticContent, \
|
from pelican.contents import (
|
||||||
is_valid_content
|
Article, Page, Category, StaticContent, is_valid_content
|
||||||
|
)
|
||||||
from pelican.readers import read_file
|
from pelican.readers import read_file
|
||||||
from pelican.utils import copy, process_translations, mkdir_p
|
from pelican.utils import copy, process_translations, mkdir_p
|
||||||
from pelican import signals
|
from pelican import signals
|
||||||
|
|
@ -76,8 +79,9 @@ class Generator(object):
|
||||||
try:
|
try:
|
||||||
self._templates[name] = self.env.get_template(name + '.html')
|
self._templates[name] = self.env.get_template(name + '.html')
|
||||||
except TemplateNotFound:
|
except TemplateNotFound:
|
||||||
raise Exception('[templates] unable to load %s.html from %s' \
|
raise Exception(
|
||||||
% (name, self._templates_path))
|
('[templates] unable to load %s.html from %s'
|
||||||
|
% (name, self._templates_path)))
|
||||||
return self._templates[name]
|
return self._templates[name]
|
||||||
|
|
||||||
def get_files(self, path, exclude=[], extensions=None):
|
def get_files(self, path, exclude=[], extensions=None):
|
||||||
|
|
@ -165,8 +169,8 @@ class ArticlesGenerator(Generator):
|
||||||
self.categories = defaultdict(list)
|
self.categories = defaultdict(list)
|
||||||
self.related_posts = []
|
self.related_posts = []
|
||||||
self.authors = defaultdict(list)
|
self.authors = defaultdict(list)
|
||||||
super(ArticlesGenerator, self).__init__(*args, **kwargs)
|
|
||||||
self.drafts = []
|
self.drafts = []
|
||||||
|
super(ArticlesGenerator, self).__init__(*args, **kwargs)
|
||||||
signals.article_generator_init.send(self)
|
signals.article_generator_init.send(self)
|
||||||
|
|
||||||
def generate_feeds(self, writer):
|
def generate_feeds(self, writer):
|
||||||
|
|
@ -180,8 +184,8 @@ class ArticlesGenerator(Generator):
|
||||||
writer.write_feed(self.articles, self.context,
|
writer.write_feed(self.articles, self.context,
|
||||||
self.settings['FEED_RSS'], feed_type='rss')
|
self.settings['FEED_RSS'], feed_type='rss')
|
||||||
|
|
||||||
if self.settings.get('FEED_ALL_ATOM') or \
|
if (self.settings.get('FEED_ALL_ATOM')
|
||||||
self.settings.get('FEED_ALL_RSS'):
|
or self.settings.get('FEED_ALL_RSS')):
|
||||||
all_articles = list(self.articles)
|
all_articles = list(self.articles)
|
||||||
for article in self.articles:
|
for article in self.articles:
|
||||||
all_articles.extend(article.translations)
|
all_articles.extend(article.translations)
|
||||||
|
|
@ -193,7 +197,8 @@ class ArticlesGenerator(Generator):
|
||||||
|
|
||||||
if self.settings.get('FEED_ALL_RSS'):
|
if self.settings.get('FEED_ALL_RSS'):
|
||||||
writer.write_feed(all_articles, self.context,
|
writer.write_feed(all_articles, self.context,
|
||||||
self.settings['FEED_ALL_RSS'], feed_type='rss')
|
self.settings['FEED_ALL_RSS'],
|
||||||
|
feed_type='rss')
|
||||||
|
|
||||||
for cat, arts in self.categories:
|
for cat, arts in self.categories:
|
||||||
arts.sort(key=attrgetter('date'), reverse=True)
|
arts.sort(key=attrgetter('date'), reverse=True)
|
||||||
|
|
@ -206,8 +211,8 @@ class ArticlesGenerator(Generator):
|
||||||
self.settings['CATEGORY_FEED_RSS'] % cat,
|
self.settings['CATEGORY_FEED_RSS'] % cat,
|
||||||
feed_type='rss')
|
feed_type='rss')
|
||||||
|
|
||||||
if self.settings.get('TAG_FEED_ATOM') \
|
if (self.settings.get('TAG_FEED_ATOM')
|
||||||
or self.settings.get('TAG_FEED_RSS'):
|
or self.settings.get('TAG_FEED_RSS')):
|
||||||
for tag, arts in self.tags.items():
|
for tag, arts in self.tags.items():
|
||||||
arts.sort(key=attrgetter('date'), reverse=True)
|
arts.sort(key=attrgetter('date'), reverse=True)
|
||||||
if self.settings.get('TAG_FEED_ATOM'):
|
if self.settings.get('TAG_FEED_ATOM'):
|
||||||
|
|
@ -219,8 +224,8 @@ class ArticlesGenerator(Generator):
|
||||||
self.settings['TAG_FEED_RSS'] % tag,
|
self.settings['TAG_FEED_RSS'] % tag,
|
||||||
feed_type='rss')
|
feed_type='rss')
|
||||||
|
|
||||||
if self.settings.get('TRANSLATION_FEED_ATOM') or \
|
if (self.settings.get('TRANSLATION_FEED_ATOM')
|
||||||
self.settings.get('TRANSLATION_FEED_RSS'):
|
or self.settings.get('TRANSLATION_FEED_RSS')):
|
||||||
translations_feeds = defaultdict(list)
|
translations_feeds = defaultdict(list)
|
||||||
for article in chain(self.articles, self.translations):
|
for article in chain(self.articles, self.translations):
|
||||||
translations_feeds[article.lang].append(article)
|
translations_feeds[article.lang].append(article)
|
||||||
|
|
@ -376,7 +381,7 @@ class ArticlesGenerator(Generator):
|
||||||
# only main articles are listed in categories, not translations
|
# only main articles are listed in categories, not translations
|
||||||
self.categories[article.category].append(article)
|
self.categories[article.category].append(article)
|
||||||
# ignore blank authors as well as undefined
|
# ignore blank authors as well as undefined
|
||||||
if hasattr(article,'author') and article.author.name != '':
|
if hasattr(article, 'author') and article.author.name != '':
|
||||||
self.authors[article.author].append(article)
|
self.authors[article.author].append(article)
|
||||||
|
|
||||||
# sort the articles by date
|
# sort the articles by date
|
||||||
|
|
@ -470,7 +475,8 @@ class PagesGenerator(Generator):
|
||||||
repr(f)))
|
repr(f)))
|
||||||
|
|
||||||
self.pages, self.translations = process_translations(all_pages)
|
self.pages, self.translations = process_translations(all_pages)
|
||||||
self.hidden_pages, self.hidden_translations = process_translations(hidden_pages)
|
self.hidden_pages, self.hidden_translations = (
|
||||||
|
process_translations(hidden_pages))
|
||||||
|
|
||||||
self._update_context(('pages', ))
|
self._update_context(('pages', ))
|
||||||
self.context['PAGES'] = self.pages
|
self.context['PAGES'] = self.pages
|
||||||
|
|
@ -574,6 +580,7 @@ class PdfGenerator(Generator):
|
||||||
for page in self.context['pages']:
|
for page in self.context['pages']:
|
||||||
self._create_pdf(page, pdf_path)
|
self._create_pdf(page, pdf_path)
|
||||||
|
|
||||||
|
|
||||||
class SourceFileGenerator(Generator):
|
class SourceFileGenerator(Generator):
|
||||||
def generate_context(self):
|
def generate_context(self):
|
||||||
self.output_extension = self.settings['OUTPUT_SOURCES_EXTENSION']
|
self.output_extension = self.settings['OUTPUT_SOURCES_EXTENSION']
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,10 @@ def ansi(color, text):
|
||||||
|
|
||||||
|
|
||||||
class ANSIFormatter(Formatter):
|
class ANSIFormatter(Formatter):
|
||||||
"""
|
"""Convert a `logging.LogRecord' object into colored text, using ANSI
|
||||||
Convert a `logging.LogRecord' object into colored text, using ANSI escape sequences.
|
escape sequences.
|
||||||
"""
|
|
||||||
## colors:
|
|
||||||
|
|
||||||
|
"""
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
msg = str(record.msg)
|
msg = str(record.msg)
|
||||||
if record.levelname == 'INFO':
|
if record.levelname == 'INFO':
|
||||||
|
|
@ -67,8 +66,8 @@ class TextFormatter(Formatter):
|
||||||
def init(level=None, logger=getLogger(), handler=StreamHandler()):
|
def init(level=None, logger=getLogger(), handler=StreamHandler()):
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
if os.isatty(sys.stdout.fileno()) \
|
if (os.isatty(sys.stdout.fileno())
|
||||||
and not sys.platform.startswith('win'):
|
and not sys.platform.startswith('win')):
|
||||||
fmt = ANSIFormatter()
|
fmt = ANSIFormatter()
|
||||||
else:
|
else:
|
||||||
fmt = TextFormatter()
|
fmt = TextFormatter()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals, print_function
|
from __future__ import unicode_literals, print_function
|
||||||
import six
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
@ -23,7 +22,6 @@ try:
|
||||||
asciidoc = True
|
asciidoc = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
asciidoc = False
|
asciidoc = False
|
||||||
import re
|
|
||||||
|
|
||||||
import cgi
|
import cgi
|
||||||
try:
|
try:
|
||||||
|
|
@ -168,6 +166,7 @@ class MarkdownReader(Reader):
|
||||||
metadata = self._parse_metadata(md.Meta)
|
metadata = self._parse_metadata(md.Meta)
|
||||||
return content, metadata
|
return content, metadata
|
||||||
|
|
||||||
|
|
||||||
class HTMLReader(Reader):
|
class HTMLReader(Reader):
|
||||||
"""Parses HTML files as input, looking for meta, title, and body tags"""
|
"""Parses HTML files as input, looking for meta, title, and body tags"""
|
||||||
file_extensions = ['htm', 'html']
|
file_extensions = ['htm', 'html']
|
||||||
|
|
@ -240,7 +239,7 @@ class HTMLReader(Reader):
|
||||||
|
|
||||||
def build_tag(self, tag, attrs, close_tag):
|
def build_tag(self, tag, attrs, close_tag):
|
||||||
result = '<{}'.format(cgi.escape(tag))
|
result = '<{}'.format(cgi.escape(tag))
|
||||||
for k,v in attrs:
|
for k, v in attrs:
|
||||||
result += ' ' + cgi.escape(k)
|
result += ' ' + cgi.escape(k)
|
||||||
if v is not None:
|
if v is not None:
|
||||||
result += '="{}"'.format(cgi.escape(v))
|
result += '="{}"'.format(cgi.escape(v))
|
||||||
|
|
@ -272,6 +271,7 @@ class HTMLReader(Reader):
|
||||||
metadata[k] = self.process_metadata(k, parser.metadata[k])
|
metadata[k] = self.process_metadata(k, parser.metadata[k])
|
||||||
return parser.body, metadata
|
return parser.body, metadata
|
||||||
|
|
||||||
|
|
||||||
class AsciiDocReader(Reader):
|
class AsciiDocReader(Reader):
|
||||||
enabled = bool(asciidoc)
|
enabled = bool(asciidoc)
|
||||||
file_extensions = ['asc']
|
file_extensions = ['asc']
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ from __future__ import print_function
|
||||||
try:
|
try:
|
||||||
import SimpleHTTPServer as srvmod
|
import SimpleHTTPServer as srvmod
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import http.server as srvmod
|
import http.server as srvmod # NOQA
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import SocketServer as socketserver
|
import SocketServer as socketserver
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import socketserver
|
import socketserver # NOQA
|
||||||
|
|
||||||
PORT = 8000
|
PORT = 8000
|
||||||
|
|
||||||
|
|
@ -17,4 +17,3 @@ httpd = socketserver.TCPServer(("", PORT), Handler)
|
||||||
|
|
||||||
print("serving at port", PORT)
|
print("serving at port", PORT)
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,8 @@ _DEFAULT_CONFIG = {'PATH': '.',
|
||||||
'DEFAULT_LANG': 'en',
|
'DEFAULT_LANG': 'en',
|
||||||
'TAG_CLOUD_STEPS': 4,
|
'TAG_CLOUD_STEPS': 4,
|
||||||
'TAG_CLOUD_MAX_ITEMS': 100,
|
'TAG_CLOUD_MAX_ITEMS': 100,
|
||||||
'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'archives'),
|
'DIRECT_TEMPLATES': ('index', 'tags', 'categories',
|
||||||
|
'archives'),
|
||||||
'EXTRA_TEMPLATES_PATHS': [],
|
'EXTRA_TEMPLATES_PATHS': [],
|
||||||
'PAGINATED_DIRECT_TEMPLATES': ('index', ),
|
'PAGINATED_DIRECT_TEMPLATES': ('index', ),
|
||||||
'PELICAN_CLASS': 'pelican.Pelican',
|
'PELICAN_CLASS': 'pelican.Pelican',
|
||||||
|
|
@ -171,7 +172,8 @@ def configure_settings(settings):
|
||||||
if (siteurl.endswith('/')):
|
if (siteurl.endswith('/')):
|
||||||
settings['SITEURL'] = siteurl[:-1]
|
settings['SITEURL'] = siteurl[:-1]
|
||||||
logger.warn("Removed extraneous trailing slash from SITEURL.")
|
logger.warn("Removed extraneous trailing slash from SITEURL.")
|
||||||
# If SITEURL is defined but FEED_DOMAIN isn't, set FEED_DOMAIN = SITEURL
|
# If SITEURL is defined but FEED_DOMAIN isn't,
|
||||||
|
# set FEED_DOMAIN to SITEURL
|
||||||
if not 'FEED_DOMAIN' in settings:
|
if not 'FEED_DOMAIN' in settings:
|
||||||
settings['FEED_DOMAIN'] = settings['SITEURL']
|
settings['FEED_DOMAIN'] = settings['SITEURL']
|
||||||
|
|
||||||
|
|
@ -185,36 +187,45 @@ def configure_settings(settings):
|
||||||
|
|
||||||
if any(settings.get(k) for k in feed_keys):
|
if any(settings.get(k) for k in feed_keys):
|
||||||
if not settings.get('FEED_DOMAIN'):
|
if not settings.get('FEED_DOMAIN'):
|
||||||
logger.warn("Since feed URLs should always be absolute, you should specify "
|
logger.warn(
|
||||||
"FEED_DOMAIN in your settings. (e.g., 'FEED_DOMAIN = "
|
"Since feed URLs should always be absolute, you should specify"
|
||||||
"http://www.example.com')")
|
" FEED_DOMAIN in your settings. (e.g., 'FEED_DOMAIN = "
|
||||||
|
"http://www.example.com')")
|
||||||
|
|
||||||
if not settings.get('SITEURL'):
|
if not settings.get('SITEURL'):
|
||||||
logger.warn("Feeds generated without SITEURL set properly may not be valid")
|
logger.warn('Feeds generated without SITEURL set properly may not'
|
||||||
|
' be valid')
|
||||||
|
|
||||||
if not 'TIMEZONE' in settings:
|
if not 'TIMEZONE' in settings:
|
||||||
logger.warn("No timezone information specified in the settings. Assuming"
|
logger.warn(
|
||||||
" your timezone is UTC for feed generation. Check "
|
'No timezone information specified in the settings. Assuming'
|
||||||
"http://docs.notmyidea.org/alexis/pelican/settings.html#timezone "
|
' your timezone is UTC for feed generation. Check '
|
||||||
"for more information")
|
'http://docs.notmyidea.org/alexis/pelican/settings.html#timezone '
|
||||||
|
'for more information')
|
||||||
|
|
||||||
if 'LESS_GENERATOR' in settings:
|
if 'LESS_GENERATOR' in settings:
|
||||||
logger.warn("The LESS_GENERATOR setting has been removed in favor "
|
logger.warn(
|
||||||
"of the Webassets plugin")
|
'The LESS_GENERATOR setting has been removed in favor '
|
||||||
|
'of the Webassets plugin')
|
||||||
|
|
||||||
if 'OUTPUT_SOURCES_EXTENSION' in settings:
|
if 'OUTPUT_SOURCES_EXTENSION' in settings:
|
||||||
if not isinstance(settings['OUTPUT_SOURCES_EXTENSION'], six.string_types):
|
if not isinstance(settings['OUTPUT_SOURCES_EXTENSION'],
|
||||||
settings['OUTPUT_SOURCES_EXTENSION'] = _DEFAULT_CONFIG['OUTPUT_SOURCES_EXTENSION']
|
six.string_types):
|
||||||
logger.warn("Detected misconfiguration with OUTPUT_SOURCES_EXTENSION."
|
settings['OUTPUT_SOURCES_EXTENSION'] = (
|
||||||
" falling back to the default extension " +
|
_DEFAULT_CONFIG['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'])
|
||||||
|
|
||||||
filename_metadata = settings.get('FILENAME_METADATA')
|
filename_metadata = settings.get('FILENAME_METADATA')
|
||||||
if filename_metadata and not isinstance(filename_metadata, six.string_types):
|
if filename_metadata and not isinstance(filename_metadata,
|
||||||
logger.error("Detected misconfiguration with FILENAME_METADATA"
|
six.string_types):
|
||||||
" setting (must be string or compiled pattern), falling"
|
logger.error(
|
||||||
"back to the default")
|
'Detected misconfiguration with FILENAME_METADATA '
|
||||||
settings['FILENAME_METADATA'] = \
|
'setting (must be string or compiled pattern), falling '
|
||||||
_DEFAULT_CONFIG['FILENAME_METADATA']
|
'back to the default')
|
||||||
|
settings['FILENAME_METADATA'] = (
|
||||||
|
_DEFAULT_CONFIG['FILENAME_METADATA'])
|
||||||
|
|
||||||
return settings
|
return settings
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ def strftime(date, date_format):
|
||||||
chars in the format string. So if e.g. your format string contains 'ø' or
|
chars in the format string. So if e.g. your format string contains 'ø' or
|
||||||
'ä', the result will be 'o' and 'a'.
|
'ä', the result will be 'o' and 'a'.
|
||||||
|
|
||||||
See here for an `extensive testcase <https://github.com/dmdm/test_strftime>`_.
|
See here for an `extensive testcase
|
||||||
|
<https://github.com/dmdm/test_strftime>`_.
|
||||||
|
|
||||||
:param date: Any object that sports a :meth:`strftime()` method.
|
:param date: Any object that sports a :meth:`strftime()` method.
|
||||||
:param date_format: Format string, can always be unicode.
|
:param date_format: Format string, can always be unicode.
|
||||||
|
|
@ -67,18 +68,12 @@ def strftime(date, date_format):
|
||||||
result = unicode(result)
|
result = unicode(result)
|
||||||
# Convert XML character references back to unicode characters.
|
# Convert XML character references back to unicode characters.
|
||||||
if "&#" in result:
|
if "&#" in result:
|
||||||
result = re.sub(r'&#(?P<num>\d+);'
|
result = re.sub(r'&#(?P<num>\d+);',
|
||||||
, lambda m: unichr(int(m.group('num')))
|
lambda m: unichr(int(m.group('num'))),
|
||||||
, result
|
result)
|
||||||
)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
|
||||||
# Stolen from Django: django.utils.encoding
|
|
||||||
#
|
|
||||||
|
|
||||||
def python_2_unicode_compatible(klass):
|
def python_2_unicode_compatible(klass):
|
||||||
"""
|
"""
|
||||||
A decorator that defines __unicode__ and __str__ methods under Python 2.
|
A decorator that defines __unicode__ and __str__ methods under Python 2.
|
||||||
|
|
@ -86,43 +81,48 @@ def python_2_unicode_compatible(klass):
|
||||||
|
|
||||||
To support Python 2 and 3 with a single code base, define a __str__ method
|
To support Python 2 and 3 with a single code base, define a __str__ method
|
||||||
returning text and apply this decorator to the class.
|
returning text and apply this decorator to the class.
|
||||||
|
|
||||||
|
From django.utils.encoding.
|
||||||
"""
|
"""
|
||||||
if not six.PY3:
|
if not six.PY3:
|
||||||
klass.__unicode__ = klass.__str__
|
klass.__unicode__ = klass.__str__
|
||||||
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
|
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
|
||||||
return klass
|
return klass
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class NoFilesError(Exception):
|
class NoFilesError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class memoized(object):
|
class memoized(object):
|
||||||
'''Decorator. Caches a function's return value each time it is called.
|
"""Function decorator to cache return values.
|
||||||
If called later with the same arguments, the cached value is returned
|
|
||||||
(not reevaluated).
|
If called later with the same arguments, the cached value is returned
|
||||||
'''
|
(not reevaluated).
|
||||||
def __init__(self, func):
|
|
||||||
self.func = func
|
"""
|
||||||
self.cache = {}
|
def __init__(self, func):
|
||||||
def __call__(self, *args):
|
self.func = func
|
||||||
if not isinstance(args, Hashable):
|
self.cache = {}
|
||||||
# uncacheable. a list, for instance.
|
|
||||||
# better to not cache than blow up.
|
def __call__(self, *args):
|
||||||
return self.func(*args)
|
if not isinstance(args, Hashable):
|
||||||
if args in self.cache:
|
# uncacheable. a list, for instance.
|
||||||
return self.cache[args]
|
# better to not cache than blow up.
|
||||||
else:
|
return self.func(*args)
|
||||||
value = self.func(*args)
|
if args in self.cache:
|
||||||
self.cache[args] = value
|
return self.cache[args]
|
||||||
return value
|
else:
|
||||||
def __repr__(self):
|
value = self.func(*args)
|
||||||
'''Return the function's docstring.'''
|
self.cache[args] = value
|
||||||
return self.func.__doc__
|
return value
|
||||||
def __get__(self, obj, objtype):
|
|
||||||
'''Support instance methods.'''
|
def __repr__(self):
|
||||||
return partial(self.__call__, obj)
|
return self.func.__doc__
|
||||||
|
|
||||||
|
def __get__(self, obj, objtype):
|
||||||
|
'''Support instance methods.'''
|
||||||
|
return partial(self.__call__, obj)
|
||||||
|
|
||||||
|
|
||||||
def deprecated_attribute(old, new, since=None, remove=None, doc=None):
|
def deprecated_attribute(old, new, since=None, remove=None, doc=None):
|
||||||
|
|
@ -166,6 +166,7 @@ def deprecated_attribute(old, new, since=None, remove=None, doc=None):
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def get_date(string):
|
def get_date(string):
|
||||||
"""Return a datetime object from a string.
|
"""Return a datetime object from a string.
|
||||||
|
|
||||||
|
|
@ -196,6 +197,7 @@ class pelican_open(object):
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def slugify(value):
|
def slugify(value):
|
||||||
"""
|
"""
|
||||||
Normalizes string, converts to lowercase, removes non-alpha characters,
|
Normalizes string, converts to lowercase, removes non-alpha characters,
|
||||||
|
|
@ -262,6 +264,7 @@ def copy(path, source, destination, destination_path=None, overwrite=False):
|
||||||
else:
|
else:
|
||||||
logger.warning('skipped copy %s to %s' % (source_, destination_))
|
logger.warning('skipped copy %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"""
|
||||||
|
|
||||||
|
|
@ -414,6 +417,7 @@ def process_translations(content_list):
|
||||||
|
|
||||||
LAST_MTIME = 0
|
LAST_MTIME = 0
|
||||||
|
|
||||||
|
|
||||||
def files_changed(path, extensions, ignores=[]):
|
def files_changed(path, extensions, ignores=[]):
|
||||||
"""Return True if the files have changed since the last check"""
|
"""Return True if the files have changed since the last check"""
|
||||||
|
|
||||||
|
|
@ -423,7 +427,8 @@ def files_changed(path, extensions, ignores=[]):
|
||||||
dirs[:] = [x for x in dirs if x[0] != '.']
|
dirs[:] = [x for x in dirs if x[0] != '.']
|
||||||
for f in files:
|
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):
|
and not any(fnmatch.fnmatch(f, ignore)
|
||||||
|
for ignore in ignores):
|
||||||
yield os.stat(os.path.join(root, f)).st_mtime
|
yield os.stat(os.path.join(root, f)).st_mtime
|
||||||
|
|
||||||
global LAST_MTIME
|
global LAST_MTIME
|
||||||
|
|
|
||||||
|
|
@ -78,11 +78,11 @@ class Writer(object):
|
||||||
os.makedirs(os.path.dirname(complete_path))
|
os.makedirs(os.path.dirname(complete_path))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
fp = open(complete_path, 'w', encoding='utf-8' if six.PY3 else None)
|
|
||||||
feed.write(fp, 'utf-8')
|
|
||||||
logger.info('writing %s' % complete_path)
|
|
||||||
|
|
||||||
fp.close()
|
encoding = 'utf-8' if six.PY3 else None
|
||||||
|
with open(complete_path, 'w', encoding=encoding) as fp:
|
||||||
|
feed.write(fp, 'utf-8')
|
||||||
|
logger.info('writing %s' % complete_path)
|
||||||
return feed
|
return feed
|
||||||
finally:
|
finally:
|
||||||
locale.setlocale(locale.LC_ALL, old_locale)
|
locale.setlocale(locale.LC_ALL, old_locale)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue