Some doc + various enhancements

This commit is contained in:
Alexis Métaireau 2013-03-10 20:11:36 -07:00
commit 14cf5f014c
5 changed files with 146 additions and 118 deletions

View file

@ -13,34 +13,36 @@ import sys
from datetime import datetime
from pelican.settings import _DEFAULT_CONFIG
from pelican.utils import (slugify, truncate_html_words, memoized,
python_2_unicode_compatible, deprecated_attribute)
from pelican import signals
import pelican.utils
from pelican.settings import _DEFAULT_CONFIG
from pelican.utils import (slugify, truncate_html_words, memoized, strftime,
python_2_unicode_compatible, deprecated_attribute)
# Import these so that they're avalaible when you import from pelican.contents.
from pelican.urlwrappers import (URLWrapper, Author, Category, Tag) # NOQA
logger = logging.getLogger(__name__)
class Page(object):
"""Represents a page
Given a content, and metadata, create an adequate object.
class Content(object):
"""Represents a content.
:param content: the string to parse, containing the original content.
"""
mandatory_properties = ('title',)
default_template = 'page'
:param metadata: the metadata associated to this page (optional).
:param settings: the settings dictionary (optional).
:param source_path: The location of the source of this content (if any).
:param context: The shared context between generators.
"""
@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
if not metadata:
if metadata is None:
metadata = {}
if not settings:
if settings is None:
settings = copy.deepcopy(_DEFAULT_CONFIG)
self.settings = settings
@ -68,6 +70,8 @@ class Page(object):
if 'AUTHOR' in settings:
self.author = Author(settings['AUTHOR'], settings)
# XXX Split all the following code into pieces, there is too much here.
# manage languages
self.in_default_lang = True
if 'DEFAULT_LANG' in settings:
@ -100,8 +104,7 @@ class Page(object):
self.date_format = self.date_format[1]
if hasattr(self, 'date'):
self.locale_date = pelican.utils.strftime(self.date,
self.date_format)
self.locale_date = strftime(self.date, self.date_format)
# manage status
if not hasattr(self, 'status'):
@ -117,13 +120,14 @@ class Page(object):
signals.content_object_init.send(self)
def check_properties(self):
"""test that each mandatory property is set."""
"""Test mandatory properties are set."""
for prop in self.mandatory_properties:
if not hasattr(self, prop):
raise NameError(prop)
@property
def url_format(self):
"""Returns the URL, formatted with the proper values"""
metadata = copy.copy(self.metadata)
metadata.update({
'slug': getattr(self, 'slug', ''),
@ -146,12 +150,14 @@ class Page(object):
return self._expand_settings(key)
def _update_content(self, content, siteurl):
"""Change all the relative paths of the content to relative paths
"""Update the content attribute.
Change all the relative paths of the content to relative paths
suitable for the ouput content.
:param content: content resource that will be passed to the templates.
:param siteurl: siteurl which is locally generated by the writer in
case of RELATIVE_URLS.
case of RELATIVE_URLS.
"""
hrefs = re.compile(r"""
(?P<markup><\s*[^\>]* # match tag with src and href attr
@ -165,9 +171,12 @@ class Page(object):
what = m.group('what')
value = m.group('value')
origin = m.group('path')
# we support only filename for now. the plan is to support
# categories, tags, etc. in the future, but let's keep things
# simple for now.
# XXX Put this in a different location.
if what == 'filename':
if value.startswith('/'):
value = value[1:]
@ -191,18 +200,23 @@ class Page(object):
@memoized
def get_content(self, siteurl):
return self._update_content(
self._get_content() if hasattr(self, "_get_content")
else self._content,
siteurl)
if hasattr(self, '_get_content'):
content = self._get_content()
else:
content = self._content
return self._update_content(content, siteurl)
@property
def content(self):
return self.get_content(self._context['localsiteurl'])
def _get_summary(self):
"""Returns the summary of an article, based on the summary metadata
if it is set, else truncate the content."""
"""Returns the summary of an article.
This is based on the summary metadata if set, otherwise truncate the
content.
"""
if hasattr(self, '_summary'):
return self._summary
@ -217,7 +231,6 @@ class Page(object):
summary = property(_get_summary, _set_summary, "Summary of the article."
"Based on the content. Can't be set")
url = property(functools.partial(get_url_setting, key='url'))
save_as = property(functools.partial(get_url_setting, key='save_as'))
@ -250,6 +263,11 @@ class Page(object):
)
class Page(Content):
mandatory_properties = ('title',)
default_template = 'page'
class Article(Page):
mandatory_properties = ('title', 'date', 'category')
default_template = 'article'
@ -259,78 +277,9 @@ 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
self.slug = slugify(self.name)
self.settings = settings
def as_dict(self):
return self.__dict__
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._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
def _from_settings(self, key, get_page_name=False):
"""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}" Useful for pagination.
"""
setting = "%s_%s" % (self.__class__.__name__.upper(), key)
value = self.settings[setting]
if not isinstance(value, six.string_types):
logger.warning('%s is set to %s' % (setting, value))
return value
else:
if get_page_name:
return os.path.splitext(value)[0].format(**self.as_dict())
else:
return value.format(**self.as_dict())
page_name = property(functools.partial(_from_settings, key='URL',
get_page_name=True))
url = property(functools.partial(_from_settings, key='URL'))
save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
class Category(URLWrapper):
pass
class Tag(URLWrapper):
def __init__(self, name, *args, **kwargs):
super(Tag, self).__init__(name.strip(), *args, **kwargs)
class Author(URLWrapper):
pass
@python_2_unicode_compatible
class StaticContent(object):
@deprecated_attribute(old='filepath', new='source_path', since=(3, 2, 0))
def filepath():
return None
@ -340,6 +289,7 @@ class StaticContent(object):
settings = copy.deepcopy(_DEFAULT_CONFIG)
self.src = src
self.url = dst or src
# On Windows, make sure we end up with Unix-like paths.
if os.name == 'nt':
self.url = self.url.replace('\\', '/')
@ -355,6 +305,6 @@ def is_valid_content(content, f):
content.check_properties()
return True
except NameError as e:
logger.warning("Skipping %s: impossible to find informations about "
"'%s'" % (f, e))
logger.error("Skipping %s: impossible to find informations about "
"'%s'" % (f, e))
return False

View file

@ -70,7 +70,7 @@ _DEFAULT_CONFIG = {'PATH': '.',
'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
'DATE_FORMATS': {},
'JINJA_EXTENSIONS': [],
'LOCALE': '', # default to user locale
'LOCALE': '', # defaults to user locale
'DEFAULT_PAGINATION': False,
'DEFAULT_ORPHANS': 0,
'DEFAULT_METADATA': (),
@ -107,9 +107,7 @@ def read_settings(path=None, override=None):
def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG):
"""
Load settings from a module, returning a dict.
"""
"""Loads settings from a module, returns a dictionary."""
context = copy.deepcopy(default_settings)
if module is not None:
@ -119,10 +117,7 @@ def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG):
def get_settings_from_file(path, default_settings=_DEFAULT_CONFIG):
"""
Load settings from a file path, returning a dict.
"""
"""Loads settings from a file path, returning a dict."""
name = os.path.basename(path).rpartition('.')[0]
module = imp.load_source(name, path)
@ -130,14 +125,15 @@ def get_settings_from_file(path, default_settings=_DEFAULT_CONFIG):
def configure_settings(settings):
"""
Provide optimizations, error checking, and warnings for loaded settings
"""Provide optimizations, error checking and warnings for the given
settings.
"""
if not 'PATH' in settings or not os.path.isdir(settings['PATH']):
raise Exception('You need to specify a path containing the content'
' (see pelican --help for more information)')
# find the theme in pelican.theme if the given one does not exists
# lookup the theme in "pelican/themes" if the given one doesn't exist
if not os.path.isdir(settings['THEME']):
theme_path = os.sep.join([os.path.dirname(
os.path.abspath(__file__)), "themes/%s" % settings['THEME']])

View file

@ -44,6 +44,7 @@ class TestPelican(LoggedTestCase):
super(TestPelican, self).setUp()
self.temp_path = mkdtemp()
self.old_locale = locale.setlocale(locale.LC_ALL)
self.maxDiff = None
locale.setlocale(locale.LC_ALL, str('C'))
def tearDown(self):

79
pelican/urlwrappers.py Normal file
View file

@ -0,0 +1,79 @@
import os
import functools
import logging
import six
from pelican.utils import (slugify, python_2_unicode_compatible)
logger = logging.getLogger(__name__)
@python_2_unicode_compatible
@functools.total_ordering
class URLWrapper(object):
def __init__(self, name, settings):
self.name = name
self.slug = slugify(self.name)
self.settings = settings
def as_dict(self):
return self.__dict__
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._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
def _from_settings(self, key, get_page_name=False):
"""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}" Useful for pagination.
"""
setting = "%s_%s" % (self.__class__.__name__.upper(), key)
value = self.settings[setting]
if not isinstance(value, six.string_types):
logger.warning('%s is set to %s' % (setting, value))
return value
else:
if get_page_name:
return os.path.splitext(value)[0].format(**self.as_dict())
else:
return value.format(**self.as_dict())
page_name = property(functools.partial(_from_settings, key='URL',
get_page_name=True))
url = property(functools.partial(_from_settings, key='URL'))
save_as = property(functools.partial(_from_settings, key='SAVE_AS'))
class Category(URLWrapper):
pass
class Tag(URLWrapper):
def __init__(self, name, *args, **kwargs):
super(Tag, self).__init__(name.strip(), *args, **kwargs)
class Author(URLWrapper):
pass

View file

@ -312,13 +312,13 @@ def get_relative_path(path):
def truncate_html_words(s, num, end_text='...'):
"""Truncates HTML to a certain number of words (not counting tags and
comments). Closes opened tags if they were correctly closed in the given
html. Takes an optional argument of what should be used to notify that the
string has been truncated, defaulting to ellipsis (...).
"""Truncates HTML to a certain number of words.
Newlines in the HTML are preserved.
From the django framework.
(not counting tags and comments). Closes opened tags if they were correctly
closed in the given html. Takes an optional argument of what should be used
to notify that the string has been truncated, defaulting to ellipsis (...).
Newlines in the HTML are preserved. (From the django framework).
"""
length = int(num)
if length <= 0:
@ -382,11 +382,13 @@ def truncate_html_words(s, num, end_text='...'):
def process_translations(content_list):
""" Finds all translation and returns tuple with two lists (index,
translations). Index list includes items in default language or items
which have no variant in default language.
""" Finds translation and returns them.
Also, for each content_list item, it sets attribute 'translations'
Returns a tuple with two lists (index, translations). Index list includes
items in default language or items which have no variant in default
language.
For each content_list item, sets the 'translations' attribute.
"""
content_list.sort(key=attrgetter('slug'))
grouped_by_slugs = groupby(content_list, attrgetter('slug'))