1
0
Fork 0
forked from github/pelican

automatically copy linked static files

This commit is contained in:
Oliver Urs Lenz 2018-07-11 15:54:47 +02:00
commit 048ea4dc0c
32 changed files with 424 additions and 224 deletions

View file

@ -14,6 +14,9 @@ Next release
and author pages 2) feeds 3) draft and hidden articles and pages and author pages 2) feeds 3) draft and hidden articles and pages
* New ``ARTICLE_TRANSLATION_ID`` and ``PAGE_TRANSLATION_ID`` settings to specify * New ``ARTICLE_TRANSLATION_ID`` and ``PAGE_TRANSLATION_ID`` settings to specify
metadata attributes used to identify translations; or to disable translations metadata attributes used to identify translations; or to disable translations
* New ``{static}`` syntax to link to static content; content linked to by
``{static}`` and ``{attach}`` is automatically copied over even if not in
``STATIC_PATHS``
3.7.1 (2017-01-10) 3.7.1 (2017-01-10)
================== ==================

View file

@ -168,6 +168,34 @@ If you want to exclude any pages from being linked to or listed in the menu
then add a ``status: hidden`` attribute to its metadata. This is useful for then add a ``status: hidden`` attribute to its metadata. This is useful for
things like making error pages that fit the generated theme of your site. things like making error pages that fit the generated theme of your site.
Static content
==============
Static files are files other than articles and pages that are copied to the
output folder as-is, without processing. You can control which static files
are copied over with the ``STATIC_PATHS`` setting of the project's
``pelicanconf.py`` file. Pelican's default configuration includes the
``images`` directory for this, but others must be added manually. In addition,
static files that are explicitly linked to are included (see below).
Mixed content in the same directory
-----------------------------------
Starting with Pelican 3.5, static files can safely share a source directory with
page source files, without exposing the page sources in the generated site.
Any such directory must be added to both ``STATIC_PATHS`` and ``PAGE_PATHS``
(or ``STATIC_PATHS`` and ``ARTICLE_PATHS``). Pelican will identify and process
the page source files normally, and copy the remaining files as if they lived
in a separate directory reserved for static files.
Note: Placing static and content source files together in the same source
directory does not guarantee that they will end up in the same place in the
generated site. The easiest way to do this is by using the ``{attach}`` link
syntax (described below). Alternatively, the ``STATIC_SAVE_AS``,
``PAGE_SAVE_AS``, and ``ARTICLE_SAVE_AS`` settings (and the corresponding
``*_URL`` settings) can be configured to place files of different types
together, just as they could in earlier versions of Pelican.
.. _ref-linking-to-internal-content: .. _ref-linking-to-internal-content:
Linking to internal content Linking to internal content
@ -221,13 +249,10 @@ and ``article2.md``::
Linking to static files Linking to static files
----------------------- -----------------------
Linking to non-article or non-page content uses the same ``{filename}`` syntax You can link to static content using ``{static}path/to/file``. Files linked to
as described above. It is important to remember that those files will not be with this syntax will automatically be copied to the output directory, even if
copied to the output directory unless the source directories containing them the source directories containing them are not included in the ``STATIC_PATHS``
are included in the ``STATIC_PATHS`` setting of the project's ``pelicanconf.py`` setting of the project's ``pelicanconf.py`` file.
file. Pelican's default configuration includes the ``images`` directory for
this, but others must be added manually. Forgetting to do so will result in
broken links.
For example, a project's content directory might be structured like this:: For example, a project's content directory might be structured like this::
@ -241,48 +266,28 @@ For example, a project's content directory might be structured like this::
``test.md`` would include:: ``test.md`` would include::
![Alt Text]({filename}/images/han.jpg) ![Alt Text]({static}/images/han.jpg)
[Our Menu]({filename}/pdfs/menu.pdf) [Our Menu]({static}/pdfs/menu.pdf)
``pelicanconf.py`` would include::
STATIC_PATHS = ['images', 'pdfs']
Site generation would then copy ``han.jpg`` to ``output/images/han.jpg``, Site generation would then copy ``han.jpg`` to ``output/images/han.jpg``,
``menu.pdf`` to ``output/pdfs/menu.pdf``, and write the appropriate links ``menu.pdf`` to ``output/pdfs/menu.pdf``, and write the appropriate links
in ``test.md``. in ``test.md``.
Mixed content in the same directory If you use ``{static}`` to link to an article or a page, this will be turned into
----------------------------------- a link to its source code.
Starting with Pelican 3.5, static files can safely share a source directory with
page source files, without exposing the page sources in the generated site.
Any such directory must be added to both ``STATIC_PATHS`` and ``PAGE_PATHS``
(or ``STATIC_PATHS`` and ``ARTICLE_PATHS``). Pelican will identify and process
the page source files normally, and copy the remaining files as if they lived
in a separate directory reserved for static files.
Note: Placing static and content source files together in the same source
directory does not guarantee that they will end up in the same place in the
generated site. The easiest way to do this is by using the ``{attach}`` link
syntax (described below). Alternatively, the ``STATIC_SAVE_AS``,
``PAGE_SAVE_AS``, and ``ARTICLE_SAVE_AS`` settings (and the corresponding
``*_URL`` settings) can be configured to place files of different types
together, just as they could in earlier versions of Pelican.
Attaching static files Attaching static files
---------------------- ----------------------
Starting with Pelican 3.5, static files can be "attached" to a page or article Starting with Pelican 3.5, static files can be "attached" to a page or article
using this syntax for the link target: ``{attach}path/to/file`` This works using this syntax for the link target: ``{attach}path/to/file`` This works
like the ``{filename}`` syntax, but also relocates the static file into the like the ``{static}`` syntax, but also relocates the static file into the
linking document's output directory. If the static file originates from a linking document's output directory. If the static file originates from a
subdirectory beneath the linking document's source, that relationship will be subdirectory beneath the linking document's source, that relationship will be
preserved on output. Otherwise, it will become a sibling of the linking preserved on output. Otherwise, it will become a sibling of the linking
document. document.
This only works for linking to static files, and only when they originate from This only works for linking to static files.
a directory included in the ``STATIC_PATHS`` setting.
For example, a project's content directory might be structured like this:: For example, a project's content directory might be structured like this::
@ -298,7 +303,6 @@ For example, a project's content directory might be structured like this::
``pelicanconf.py`` would include:: ``pelicanconf.py`` would include::
PATH = 'content' PATH = 'content'
STATIC_PATHS = ['blog', 'downloads']
ARTICLE_PATHS = ['blog'] ARTICLE_PATHS = ['blog']
ARTICLE_SAVE_AS = '{date:%Y}/{slug}.html' ARTICLE_SAVE_AS = '{date:%Y}/{slug}.html'
ARTICLE_URL = '{date:%Y}/{slug}.html' ARTICLE_URL = '{date:%Y}/{slug}.html'
@ -328,7 +332,7 @@ the article's output directory.
If a static file is linked multiple times, the relocating feature of If a static file is linked multiple times, the relocating feature of
``{attach}`` will only work in the first of those links to be processed. ``{attach}`` will only work in the first of those links to be processed.
After the first link, Pelican will treat ``{attach}`` like ``{filename}``. After the first link, Pelican will treat ``{attach}`` like ``{static}``.
This avoids breaking the already-processed links. This avoids breaking the already-processed links.
**Be careful when linking to a file from multiple documents:** **Be careful when linking to a file from multiple documents:**
@ -342,7 +346,7 @@ file's old location might then find their links broken. **It is therefore
advisable to use {attach} only if you use it in all links to a file, and only advisable to use {attach} only if you use it in all links to a file, and only
if the linking documents share a single directory.** Under these conditions, if the linking documents share a single directory.** Under these conditions,
the file's output location will not change in future builds. In cases where the file's output location will not change in future builds. In cases where
these precautions are not possible, consider using ``{filename}`` links instead these precautions are not possible, consider using ``{static}`` links instead
of ``{attach}``, and letting the file's location be determined by the project's of ``{attach}``, and letting the file's location be determined by the project's
``STATIC_SAVE_AS`` and ``STATIC_URL`` settings. (Per-file ``save_as`` and ``STATIC_SAVE_AS`` and ``STATIC_URL`` settings. (Per-file ``save_as`` and
``url`` overrides can still be set in ``EXTRA_PATH_METADATA``.) ``url`` overrides can still be set in ``EXTRA_PATH_METADATA``.)
@ -360,8 +364,11 @@ To remain compatible with earlier versions, Pelican still supports vertical bars
(``||``) in addition to curly braces (``{}``) for internal links. For example: (``||``) in addition to curly braces (``{}``) for internal links. For example:
``|filename|an_article.rst``, ``|tag|tagname``, ``|category|foobar``. ``|filename|an_article.rst``, ``|tag|tagname``, ``|category|foobar``.
The syntax was changed from ``||`` to ``{}`` to avoid collision with Markdown The syntax was changed from ``||`` to ``{}`` to avoid collision with Markdown
extensions or reST directives. Support for the old syntax may eventually be extensions or reST directives. Similarly, Pelican also still supports linking to
removed. static content with ``{filename}``. The syntax was changed to ``{static}`` to allow
linking to both generated articles and pages and their static sources.
Support for the old syntax may eventually be removed.
Importing an existing site Importing an existing site

View file

@ -150,8 +150,11 @@ class Pelican(object):
start_time = time.time() start_time = time.time()
context = self.settings.copy() context = self.settings.copy()
# Share these among all the generators and content objects: # Share these among all the generators and content objects
context['filenames'] = {} # maps source path to Content object or None # They map source paths to Content objects or None
context['generated_content'] = {}
context['static_links'] = set()
context['static_content'] = {}
context['localsiteurl'] = self.settings['SITEURL'] context['localsiteurl'] = self.settings['SITEURL']
generators = [ generators = [

View file

@ -142,7 +142,8 @@ class Content(object):
if not hasattr(self, 'status'): if not hasattr(self, 'status'):
self.status = getattr(self, 'default_status', None) self.status = getattr(self, 'default_status', None)
if len(self._context.get('filenames', [])) > 0: if (len(self._context.get('generated_content', [])) > 0 or
len(self._context.get('static_content', [])) > 0):
self.refresh_metadata_intersite_links() self.refresh_metadata_intersite_links()
signals.content_object_init.send(self) signals.content_object_init.send(self)
@ -255,7 +256,7 @@ class Content(object):
siteurl += '/' siteurl += '/'
# XXX Put this in a different location. # XXX Put this in a different location.
if what in {'filename', 'attach'}: if what in {'filename', 'static', 'attach'}:
if path.startswith('/'): if path.startswith('/'):
path = path[1:] path = path[1:]
else: else:
@ -264,22 +265,33 @@ class Content(object):
os.path.join(self.relative_dir, path) os.path.join(self.relative_dir, path)
) )
if path not in self._context['filenames']: key = 'static_content' if what in ('static', 'attach')\
unquoted_path = path.replace('%20', ' ') else 'generated_content'
if unquoted_path in self._context['filenames']: def _get_linked_content(key, path):
path = unquoted_path try:
return self._context[key][path]
except KeyError:
try:
# Markdown escapes spaces, try unescaping
return self._context[key][path.replace('%20', ' ')]
except KeyError:
if what == 'filename' and key == 'generated_content':
key = 'static_content'
linked_content = _get_linked_content(key, path)
if linked_content:
logger.warning(
'{filename} used for linking to static'
'content %s in %s. Use {static} instead',
path,
self.get_relative_source_path())
return linked_content
return None
linked_content = self._context['filenames'].get(path) linked_content = _get_linked_content(key, path)
if linked_content: if linked_content:
if what == 'attach': if what == 'attach':
if isinstance(linked_content, Static): linked_content.attach_to(self)
linked_content.attach_to(self)
else:
logger.warning(
"%s used {attach} link syntax on a "
"non-static file. Use {filename} instead.",
self.get_relative_source_path())
origin = joiner(siteurl, linked_content.url) origin = joiner(siteurl, linked_content.url)
origin = origin.replace('\\', '/') # for Windows paths. origin = origin.replace('\\', '/') # for Windows paths.
else: else:
@ -310,6 +322,17 @@ class Content(object):
return ''.join((m.group('markup'), m.group('quote'), origin, return ''.join((m.group('markup'), m.group('quote'), origin,
m.group('quote'))) m.group('quote')))
def _get_intrasite_link_regex(self):
intrasite_link_regex = self.settings['INTRASITE_LINK_REGEX']
regex = r"""
(?P<markup><[^\>]+ # match tag with all url-value attributes
(?:href|src|poster|data|cite|formaction|action)\s*=\s*)
(?P<quote>["\']) # require value to be quoted
(?P<path>{0}(?P<value>.*?)) # the url value
\2""".format(intrasite_link_regex)
return re.compile(regex, re.X)
def _update_content(self, content, siteurl): def _update_content(self, content, siteurl):
"""Update the content attribute. """Update the content attribute.
@ -323,18 +346,29 @@ class Content(object):
if not content: if not content:
return content return content
instrasite_link_regex = self.settings['INTRASITE_LINK_REGEX'] hrefs = self._get_intrasite_link_regex()
regex = r"""
(?P<markup><[^\>]+ # match tag with all url-value attributes
(?:href|src|poster|data|cite|formaction|action)\s*=\s*)
(?P<quote>["\']) # require value to be quoted
(?P<path>{0}(?P<value>.*?)) # the url value
\2""".format(instrasite_link_regex)
hrefs = re.compile(regex, re.X)
return hrefs.sub(lambda m: self._link_replacer(siteurl, m), content) return hrefs.sub(lambda m: self._link_replacer(siteurl, m), content)
def get_static_links(self):
static_links = set()
hrefs = self._get_intrasite_link_regex()
for m in hrefs.finditer(self._content):
what = m.group('what')
value = urlparse(m.group('value'))
path = value.path
if what not in {'static', 'attach'}:
continue
if path.startswith('/'):
path = path[1:]
else:
# relative to the source path of this content
path = self.get_relative_source_path(
os.path.join(self.relative_dir, path)
)
path = path.replace('%20', ' ')
static_links.add(path)
return static_links
def get_siteurl(self): def get_siteurl(self):
return self._context.get('localsiteurl', '') return self._context.get('localsiteurl', '')

View file

@ -147,7 +147,7 @@ class Generator(object):
parent_path, subdir = os.path.split(os.path.join(self.path, e)) parent_path, subdir = os.path.split(os.path.join(self.path, e))
exclusions_by_dirpath.setdefault(parent_path, set()).add(subdir) exclusions_by_dirpath.setdefault(parent_path, set()).add(subdir)
files = [] files = set()
ignores = self.settings['IGNORE_FILES'] ignores = self.settings['IGNORE_FILES']
for path in paths: for path in paths:
# careful: os.path.join() will add a slash when path == ''. # careful: os.path.join() will add a slash when path == ''.
@ -170,33 +170,41 @@ class Generator(object):
for f in temp_files: for f in temp_files:
fp = os.path.join(reldir, f) fp = os.path.join(reldir, f)
if self._include_path(fp, extensions): if self._include_path(fp, extensions):
files.append(fp) files.add(fp)
elif os.path.exists(root) and self._include_path(path, extensions): elif os.path.exists(root) and self._include_path(path, extensions):
files.append(path) # can't walk non-directories files.add(path) # can't walk non-directories
return files return files
def add_source_path(self, content): def add_source_path(self, content, static=False):
"""Record a source file path that a Generator found and processed. """Record a source file path that a Generator found and processed.
Store a reference to its Content object, for url lookups later. Store a reference to its Content object, for url lookups later.
""" """
location = content.get_relative_source_path() location = content.get_relative_source_path()
self.context['filenames'][location] = content key = 'static_content' if static else 'generated_content'
self.context[key][location] = content
def _add_failed_source_path(self, path): def _add_failed_source_path(self, path, static=False):
"""Record a source file path that a Generator failed to process. """Record a source file path that a Generator failed to process.
(For example, one that was missing mandatory metadata.) (For example, one that was missing mandatory metadata.)
The path argument is expected to be relative to self.path. The path argument is expected to be relative to self.path.
""" """
self.context['filenames'][posixize_path(os.path.normpath(path))] = None key = 'static_content' if static else 'generated_content'
self.context[key][posixize_path(os.path.normpath(path))] = None
def _is_potential_source_path(self, path): def _is_potential_source_path(self, path, static=False):
"""Return True if path was supposed to be used as a source file. """Return True if path was supposed to be used as a source file.
(This includes all source files that have been found by generators (This includes all source files that have been found by generators
before this method is called, even if they failed to process.) before this method is called, even if they failed to process.)
The path argument is expected to be relative to self.path. The path argument is expected to be relative to self.path.
""" """
return (posixize_path(os.path.normpath(path)) key = 'static_content' if static else 'generated_content'
in self.context['filenames']) return (posixize_path(os.path.normpath(path)) in self.context[key])
def add_static_links(self, content):
"""Add file links in content to context to be processed as Static
content.
"""
self.context['static_links'] |= content.get_static_links()
def _update_context(self, items): def _update_context(self, items):
"""Update the context with the given items from the currrent """Update the context with the given items from the currrent
@ -596,6 +604,7 @@ class ArticlesGenerator(CachingGenerator):
elif article.status == "draft": elif article.status == "draft":
all_drafts.append(article) all_drafts.append(article)
self.add_source_path(article) self.add_source_path(article)
self.add_static_links(article)
def _process(arts): def _process(arts):
origs, translations = process_translations( origs, translations = process_translations(
@ -702,6 +711,7 @@ class PagesGenerator(CachingGenerator):
elif page.status == "draft": elif page.status == "draft":
draft_pages.append(page) draft_pages.append(page)
self.add_source_path(page) self.add_source_path(page)
self.add_static_links(page)
def _process(pages): def _process(pages):
origs, translations = process_translations( origs, translations = process_translations(
@ -753,9 +763,12 @@ class StaticGenerator(Generator):
def generate_context(self): def generate_context(self):
self.staticfiles = [] self.staticfiles = []
for f in self.get_files(self.settings['STATIC_PATHS'], linked_files = {os.path.join(self.path, path)
exclude=self.settings['STATIC_EXCLUDES'], for path in self.context['static_links']}
extensions=False): found_files = self.get_files(self.settings['STATIC_PATHS'],
exclude=self.settings['STATIC_EXCLUDES'],
extensions=False)
for f in linked_files | found_files:
# skip content source files unless the user explicitly wants them # skip content source files unless the user explicitly wants them
if self.settings['STATIC_EXCLUDE_SOURCES']: if self.settings['STATIC_EXCLUDE_SOURCES']:
@ -770,7 +783,7 @@ class StaticGenerator(Generator):
context_signal=signals.static_generator_context, context_signal=signals.static_generator_context,
context_sender=self) context_sender=self)
self.staticfiles.append(static) self.staticfiles.append(static)
self.add_source_path(static) self.add_source_path(static, static=True)
self._update_context(('staticfiles',)) self._update_context(('staticfiles',))
signals.static_generator_finalized.send(self) signals.static_generator_finalized.send(self)

View file

@ -0,0 +1,7 @@
Title: Page with static links
My links:
[Link 0]({static}image0.jpg)
[Link 1]({attach}image1.jpg)

View file

@ -42,8 +42,8 @@
<div class="section" id="this-is-a-simple-title"> <div class="section" id="this-is-a-simple-title">
<h2>This is a simple title</h2> <h2>This is a simple title</h2>
<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> <p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<pre class="literal-block"> <pre class="literal-block">
&gt;&gt;&gt; from ipdb import set_trace &gt;&gt;&gt; from ipdb import set_trace
&gt;&gt;&gt; set_trace() &gt;&gt;&gt; set_trace()
@ -78,7 +78,7 @@
<h2>Why not ?</h2> <h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! <p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p> YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div> </div>
<a class="readmore" href="/oh-yeah.html">read more</a> <a class="readmore" href="/oh-yeah.html">read more</a>

View file

@ -38,7 +38,7 @@
<h2>Why not ?</h2> <h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! <p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p> YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div> </div>
</article> </article>
</aside><!-- /#featured --> </aside><!-- /#featured -->

View file

@ -42,8 +42,8 @@
<div class="section" id="this-is-a-simple-title"> <div class="section" id="this-is-a-simple-title">
<h2>This is a simple title</h2> <h2>This is a simple title</h2>
<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> <p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<pre class="literal-block"> <pre class="literal-block">
&gt;&gt;&gt; from ipdb import set_trace &gt;&gt;&gt; from ipdb import set_trace
&gt;&gt;&gt; set_trace() &gt;&gt;&gt; set_trace()

View file

@ -5,8 +5,8 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="section" id="this-is-a-simple-title"&gt; &lt;div class="section" id="this-is-a-simple-title"&gt;
&lt;h2&gt;This is a simple title&lt;/h2&gt; &lt;h2&gt;This is a simple title&lt;/h2&gt;
&lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;pre class="literal-block"&gt; &lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace &amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace
&amp;gt;&amp;gt;&amp;gt; set_trace() &amp;gt;&amp;gt;&amp;gt; set_trace()
@ -17,12 +17,12 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt; &lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! &lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt; YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt; &lt;/div&gt;
</summary><content type="html">&lt;div class="section" id="why-not"&gt; </summary><content type="html">&lt;div class="section" id="why-not"&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt; &lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! &lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt; YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt; &lt;/div&gt;
</content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry></feed> </content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry></feed>

View file

@ -5,6 +5,6 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt; &lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! &lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt; YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt; &lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Wed, 20 Oct 2010 10:14:00 +0000</pubDate><guid isPermaLink="false">tag:None,2010-10-20:/oh-yeah.html</guid><category>oh</category><category>bar</category><category>yeah</category></item></channel></rss> </description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Wed, 20 Oct 2010 10:14:00 +0000</pubDate><guid isPermaLink="false">tag:None,2010-10-20:/oh-yeah.html</guid><category>oh</category><category>bar</category><category>yeah</category></item></channel></rss>

View file

@ -19,8 +19,8 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="section" id="this-is-a-simple-title"&gt; &lt;div class="section" id="this-is-a-simple-title"&gt;
&lt;h2&gt;This is a simple title&lt;/h2&gt; &lt;h2&gt;This is a simple title&lt;/h2&gt;
&lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;pre class="literal-block"&gt; &lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace &amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace
&amp;gt;&amp;gt;&amp;gt; set_trace() &amp;gt;&amp;gt;&amp;gt; set_trace()
@ -31,13 +31,13 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt; &lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! &lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt; YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt; &lt;/div&gt;
</summary><content type="html">&lt;div class="section" id="why-not"&gt; </summary><content type="html">&lt;div class="section" id="why-not"&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt; &lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! &lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt; YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt; &lt;/div&gt;
</content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry><entry><title>Unbelievable !</title><link href="/unbelievable.html" rel="alternate"></link><published>2010-10-15T20:30:00+00:00</published><updated>2010-10-15T20:30:00+00:00</updated><author><name></name></author><id>tag:None,2010-10-15:/unbelievable.html</id><summary type="html">&lt;p&gt;Or completely awesome. Depends the needs.&lt;/p&gt; </content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry><entry><title>Unbelievable !</title><link href="/unbelievable.html" rel="alternate"></link><published>2010-10-15T20:30:00+00:00</published><updated>2010-10-15T20:30:00+00:00</updated><author><name></name></author><id>tag:None,2010-10-15:/unbelievable.html</id><summary type="html">&lt;p&gt;Or completely awesome. Depends the needs.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/a-markdown-powered-article.html"&gt;a root-relative link to markdown-article&lt;/a&gt; &lt;p&gt;&lt;a class="reference external" href="/a-markdown-powered-article.html"&gt;a root-relative link to markdown-article&lt;/a&gt;

View file

@ -21,8 +21,8 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="section" id="this-is-a-simple-title"&gt; &lt;div class="section" id="this-is-a-simple-title"&gt;
&lt;h2&gt;This is a simple title&lt;/h2&gt; &lt;h2&gt;This is a simple title&lt;/h2&gt;
&lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;pre class="literal-block"&gt; &lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace &amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace
&amp;gt;&amp;gt;&amp;gt; set_trace() &amp;gt;&amp;gt;&amp;gt; set_trace()
@ -33,13 +33,13 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt; &lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! &lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt; YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt; &lt;/div&gt;
</summary><content type="html">&lt;div class="section" id="why-not"&gt; </summary><content type="html">&lt;div class="section" id="why-not"&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt; &lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! &lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt; YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt; &lt;/div&gt;
</content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry><entry><title>Unbelievable !</title><link href="/unbelievable.html" rel="alternate"></link><published>2010-10-15T20:30:00+00:00</published><updated>2010-10-15T20:30:00+00:00</updated><author><name></name></author><id>tag:None,2010-10-15:/unbelievable.html</id><summary type="html">&lt;p&gt;Or completely awesome. Depends the needs.&lt;/p&gt; </content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry><entry><title>Unbelievable !</title><link href="/unbelievable.html" rel="alternate"></link><published>2010-10-15T20:30:00+00:00</published><updated>2010-10-15T20:30:00+00:00</updated><author><name></name></author><id>tag:None,2010-10-15:/unbelievable.html</id><summary type="html">&lt;p&gt;Or completely awesome. Depends the needs.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/a-markdown-powered-article.html"&gt;a root-relative link to markdown-article&lt;/a&gt; &lt;p&gt;&lt;a class="reference external" href="/a-markdown-powered-article.html"&gt;a root-relative link to markdown-article&lt;/a&gt;

View file

@ -3,12 +3,12 @@
&lt;h2&gt;Why not ?&lt;/h2&gt; &lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! &lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt; YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt; &lt;/div&gt;
</summary><content type="html">&lt;div class="section" id="why-not"&gt; </summary><content type="html">&lt;div class="section" id="why-not"&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt; &lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! &lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt; YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt; &lt;/div&gt;
</content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry></feed> </content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry></feed>

View file

@ -5,8 +5,8 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="section" id="this-is-a-simple-title"&gt; &lt;div class="section" id="this-is-a-simple-title"&gt;
&lt;h2&gt;This is a simple title&lt;/h2&gt; &lt;h2&gt;This is a simple title&lt;/h2&gt;
&lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt; &lt;img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;pre class="literal-block"&gt; &lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace &amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace
&amp;gt;&amp;gt;&amp;gt; set_trace() &amp;gt;&amp;gt;&amp;gt; set_trace()

View file

@ -191,7 +191,7 @@ as well as <strong>inline markup</strong>.</p>
<h2>Why not ?</h2> <h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! <p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p> YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div> </div>
<a class="readmore" href="/oh-yeah.html">read more</a> <a class="readmore" href="/oh-yeah.html">read more</a>

View file

@ -43,7 +43,7 @@
<h2>Why not ?</h2> <h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! <p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p> YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div> </div>
</div><!-- /.entry-content --> </div><!-- /.entry-content -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -90,7 +90,7 @@ as well as <strong>inline markup</strong>.</p>
<h2>Why not ?</h2> <h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! <p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p> YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div> </div>
<a class="readmore" href="/oh-yeah.html">read more</a> <a class="readmore" href="/oh-yeah.html">read more</a>

View file

@ -42,8 +42,8 @@
<div class="section" id="this-is-a-simple-title"> <div class="section" id="this-is-a-simple-title">
<h2>This is a simple title</h2> <h2>This is a simple title</h2>
<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> <p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<pre class="literal-block"> <pre class="literal-block">
&gt;&gt;&gt; from ipdb import set_trace &gt;&gt;&gt; from ipdb import set_trace
&gt;&gt;&gt; set_trace() &gt;&gt;&gt; set_trace()

View file

@ -38,7 +38,7 @@
<h2>Why not ?</h2> <h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! <p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p> YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div> </div>
</article> </article>
</aside><!-- /#featured --> </aside><!-- /#featured -->

View file

@ -47,8 +47,8 @@
<div class="section" id="this-is-a-simple-title"> <div class="section" id="this-is-a-simple-title">
<h2>This is a simple title</h2> <h2>This is a simple title</h2>
<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p> <p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /> <img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<pre class="literal-block"> <pre class="literal-block">
&gt;&gt;&gt; from ipdb import set_trace &gt;&gt;&gt; from ipdb import set_trace
&gt;&gt;&gt; set_trace() &gt;&gt;&gt; set_trace()

View file

@ -175,6 +175,15 @@ def get_settings(**kwargs):
return settings return settings
def get_context(settings=None, **kwargs):
context = settings.copy() if settings else {}
context['generated_content'] = {}
context['static_links'] = set()
context['static_content'] = {}
context.update(kwargs)
return context
class LogCountHandler(BufferingHandler): class LogCountHandler(BufferingHandler):
"""Capturing and counting logged messages.""" """Capturing and counting logged messages."""

View file

@ -6,7 +6,7 @@ from shutil import rmtree
from tempfile import mkdtemp from tempfile import mkdtemp
from pelican.generators import ArticlesGenerator, PagesGenerator from pelican.generators import ArticlesGenerator, PagesGenerator
from pelican.tests.support import get_settings, unittest from pelican.tests.support import get_context, get_settings, unittest
try: try:
from unittest.mock import MagicMock from unittest.mock import MagicMock
@ -29,7 +29,7 @@ class TestCache(unittest.TestCase):
rmtree(self.temp_cache) rmtree(self.temp_cache)
def _get_cache_enabled_settings(self): def _get_cache_enabled_settings(self):
settings = get_settings(filenames={}) settings = get_settings()
settings['CACHE_CONTENT'] = True settings['CACHE_CONTENT'] = True
settings['LOAD_CONTENT_CACHE'] = True settings['LOAD_CONTENT_CACHE'] = True
settings['CACHE_PATH'] = self.temp_cache settings['CACHE_PATH'] = self.temp_cache
@ -42,20 +42,21 @@ class TestCache(unittest.TestCase):
settings['PAGE_PATHS'] = ['TestPages'] settings['PAGE_PATHS'] = ['TestPages']
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['READERS'] = {'asc': None} settings['READERS'] = {'asc': None}
context = get_context(settings)
def sorted_titles(items): def sorted_titles(items):
return sorted(item.title for item in items) return sorted(item.title for item in items)
# Articles # Articles
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
uncached_articles = sorted_titles(generator.articles) uncached_articles = sorted_titles(generator.articles)
uncached_drafts = sorted_titles(generator.drafts) uncached_drafts = sorted_titles(generator.drafts)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
cached_articles = sorted_titles(generator.articles) cached_articles = sorted_titles(generator.articles)
@ -66,7 +67,7 @@ class TestCache(unittest.TestCase):
# Pages # Pages
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
uncached_pages = sorted_titles(generator.pages) uncached_pages = sorted_titles(generator.pages)
@ -74,7 +75,7 @@ class TestCache(unittest.TestCase):
uncached_draft_pages = sorted_titles(generator.draft_pages) uncached_draft_pages = sorted_titles(generator.draft_pages)
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
cached_pages = sorted_titles(generator.pages) cached_pages = sorted_titles(generator.pages)
@ -92,20 +93,21 @@ class TestCache(unittest.TestCase):
settings['PAGE_PATHS'] = ['TestPages'] settings['PAGE_PATHS'] = ['TestPages']
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['READERS'] = {'asc': None} settings['READERS'] = {'asc': None}
context = get_context(settings)
def sorted_titles(items): def sorted_titles(items):
return sorted(item.title for item in items) return sorted(item.title for item in items)
# Articles # Articles
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
uncached_articles = sorted_titles(generator.articles) uncached_articles = sorted_titles(generator.articles)
uncached_drafts = sorted_titles(generator.drafts) uncached_drafts = sorted_titles(generator.drafts)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
cached_articles = sorted_titles(generator.articles) cached_articles = sorted_titles(generator.articles)
@ -116,14 +118,14 @@ class TestCache(unittest.TestCase):
# Pages # Pages
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
uncached_pages = sorted_titles(generator.pages) uncached_pages = sorted_titles(generator.pages)
uncached_hidden_pages = sorted_titles(generator.hidden_pages) uncached_hidden_pages = sorted_titles(generator.hidden_pages)
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
cached_pages = sorted_titles(generator.pages) cached_pages = sorted_titles(generator.pages)
@ -139,15 +141,16 @@ class TestCache(unittest.TestCase):
settings['CONTENT_CACHING_LAYER'] = 'generator' settings['CONTENT_CACHING_LAYER'] = 'generator'
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['READERS'] = {'asc': None} settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
self.assertTrue(hasattr(generator, '_cache')) self.assertTrue(hasattr(generator, '_cache'))
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock() generator.readers.read_file = MagicMock()
generator.generate_context() generator.generate_context()
@ -167,15 +170,16 @@ class TestCache(unittest.TestCase):
"""Test raw article content caching at the reader level""" """Test raw article content caching at the reader level"""
settings = self._get_cache_enabled_settings() settings = self._get_cache_enabled_settings()
settings['READERS'] = {'asc': None} settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
self.assertTrue(hasattr(generator.readers, '_cache')) self.assertTrue(hasattr(generator.readers, '_cache'))
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
readers = generator.readers.readers readers = generator.readers.readers
for reader in readers.values(): for reader in readers.values():
@ -191,9 +195,10 @@ class TestCache(unittest.TestCase):
used in --ignore-cache or autoreload mode""" used in --ignore-cache or autoreload mode"""
settings = self._get_cache_enabled_settings() settings = self._get_cache_enabled_settings()
settings['READERS'] = {'asc': None} settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock() generator.readers.read_file = MagicMock()
generator.generate_context() generator.generate_context()
@ -202,7 +207,7 @@ class TestCache(unittest.TestCase):
settings['LOAD_CONTENT_CACHE'] = False settings['LOAD_CONTENT_CACHE'] = False
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock() generator.readers.read_file = MagicMock()
generator.generate_context() generator.generate_context()
@ -217,15 +222,16 @@ class TestCache(unittest.TestCase):
settings['CONTENT_CACHING_LAYER'] = 'generator' settings['CONTENT_CACHING_LAYER'] = 'generator'
settings['PAGE_PATHS'] = ['TestPages'] settings['PAGE_PATHS'] = ['TestPages']
settings['READERS'] = {'asc': None} settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
self.assertTrue(hasattr(generator, '_cache')) self.assertTrue(hasattr(generator, '_cache'))
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock() generator.readers.read_file = MagicMock()
generator.generate_context() generator.generate_context()
@ -241,15 +247,16 @@ class TestCache(unittest.TestCase):
settings = self._get_cache_enabled_settings() settings = self._get_cache_enabled_settings()
settings['PAGE_PATHS'] = ['TestPages'] settings['PAGE_PATHS'] = ['TestPages']
settings['READERS'] = {'asc': None} settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
self.assertTrue(hasattr(generator.readers, '_cache')) self.assertTrue(hasattr(generator.readers, '_cache'))
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
readers = generator.readers.readers readers = generator.readers.readers
for reader in readers.values(): for reader in readers.values():
@ -266,9 +273,10 @@ class TestCache(unittest.TestCase):
settings = self._get_cache_enabled_settings() settings = self._get_cache_enabled_settings()
settings['PAGE_PATHS'] = ['TestPages'] settings['PAGE_PATHS'] = ['TestPages']
settings['READERS'] = {'asc': None} settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock() generator.readers.read_file = MagicMock()
generator.generate_context() generator.generate_context()
@ -277,7 +285,7 @@ class TestCache(unittest.TestCase):
settings['LOAD_CONTENT_CACHE'] = False settings['LOAD_CONTENT_CACHE'] = False
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock() generator.readers.read_file = MagicMock()
generator.generate_context() generator.generate_context()

View file

@ -14,7 +14,8 @@ import six
from pelican.contents import Article, Author, Category, Page, Static, Tag from pelican.contents import Article, Author, Category, Page, Static, Tag
from pelican.settings import DEFAULT_CONFIG from pelican.settings import DEFAULT_CONFIG
from pelican.signals import content_object_init from pelican.signals import content_object_init
from pelican.tests.support import LoggedTestCase, get_settings, unittest from pelican.tests.support import LoggedTestCase, get_context, get_settings,\
unittest
from pelican.utils import SafeDatetime, path_to_url, truncate_html_words from pelican.utils import SafeDatetime, path_to_url, truncate_html_words
@ -264,7 +265,7 @@ class TestPage(LoggedTestCase):
args = self.page_kwargs.copy() args = self.page_kwargs.copy()
args['settings'] = get_settings() args['settings'] = get_settings()
args['source_path'] = 'content' args['source_path'] = 'content'
args['context']['filenames'] = {'article.rst': article} args['context']['generated_content'] = {'article.rst': article}
# Classic intrasite link via filename # Classic intrasite link via filename
args['content'] = ( args['content'] = (
@ -343,22 +344,24 @@ class TestPage(LoggedTestCase):
args = self.page_kwargs.copy() args = self.page_kwargs.copy()
args['settings'] = get_settings() args['settings'] = get_settings()
args['source_path'] = 'content' args['source_path'] = 'content'
args['context']['filenames'] = { args['context']['static_content'] = {
'images/poster.jpg': type( 'images/poster.jpg':
cls_name, (object,), {'url': 'images/poster.jpg'}), type(cls_name, (object,), {'url': 'images/poster.jpg'}),
'assets/video.mp4': type( 'assets/video.mp4':
cls_name, (object,), {'url': 'assets/video.mp4'}), type(cls_name, (object,), {'url': 'assets/video.mp4'}),
'images/graph.svg': type( 'images/graph.svg':
cls_name, (object,), {'url': 'images/graph.svg'}), type(cls_name, (object,), {'url': 'images/graph.svg'}),
'reference.rst': type( }
cls_name, (object,), {'url': 'reference.html'}), args['context']['generated_content'] = {
'reference.rst':
type(cls_name, (object,), {'url': 'reference.html'}),
} }
# video.poster # video.poster
args['content'] = ( args['content'] = (
'There is a video with poster ' 'There is a video with poster '
'<video controls poster="{filename}/images/poster.jpg">' '<video controls poster="{static}/images/poster.jpg">'
'<source src="|filename|/assets/video.mp4" type="video/mp4">' '<source src="|static|/assets/video.mp4" type="video/mp4">'
'</video>' '</video>'
) )
content = Page(**args).get_content('http://notmyidea.org') content = Page(**args).get_content('http://notmyidea.org')
@ -374,7 +377,7 @@ class TestPage(LoggedTestCase):
# object.data # object.data
args['content'] = ( args['content'] = (
'There is a svg object ' 'There is a svg object '
'<object data="{filename}/images/graph.svg"' '<object data="{static}/images/graph.svg"'
' type="image/svg+xml">' ' type="image/svg+xml">'
'</object>' '</object>'
) )
@ -409,14 +412,15 @@ class TestPage(LoggedTestCase):
STATIC_URL='http://static.cool.site/{path}', STATIC_URL='http://static.cool.site/{path}',
ARTICLE_URL='http://blog.cool.site/{slug}.html') ARTICLE_URL='http://blog.cool.site/{slug}.html')
args['source_path'] = 'content' args['source_path'] = 'content'
args['context']['filenames'] = { args['context']['static_content'] = {
'images/poster.jpg': Static('', 'images/poster.jpg':
settings=args['settings'], Static('', settings=args['settings'],
source_path='images/poster.jpg'), source_path='images/poster.jpg'),
'article.rst': Article('', }
settings=args['settings'], args['context']['generated_content'] = {
metadata={'slug': 'article', 'article.rst':
'title': 'Article'}) Article('', settings=args['settings'], metadata={
'slug': 'article', 'title': 'Article'})
} }
# Article link will go to blog # Article link will go to blog
@ -441,7 +445,7 @@ class TestPage(LoggedTestCase):
# Image link will go to static # Image link will go to static
args['content'] = ( args['content'] = (
'<img src="{filename}/images/poster.jpg"/>' '<img src="{static}/images/poster.jpg"/>'
) )
content = Page(**args).get_content('http://cool.site') content = Page(**args).get_content('http://cool.site')
self.assertEqual( self.assertEqual(
@ -458,7 +462,7 @@ class TestPage(LoggedTestCase):
args = self.page_kwargs.copy() args = self.page_kwargs.copy()
args['settings'] = get_settings() args['settings'] = get_settings()
args['source_path'] = 'content' args['source_path'] = 'content'
args['context']['filenames'] = {'article spaces.rst': article} args['context']['generated_content'] = {'article spaces.rst': article}
# An intrasite link via filename with %20 as a space # An intrasite link via filename with %20 as a space
args['content'] = ( args['content'] = (
@ -472,6 +476,55 @@ class TestPage(LoggedTestCase):
'<a href="http://notmyidea.org/article-spaces.html">link</a>' '<a href="http://notmyidea.org/article-spaces.html">link</a>'
) )
def test_intrasite_link_source_and_generated(self):
"""Test linking both to the source and the generated article
"""
cls_name = '_DummyAsset' if six.PY3 else b'_DummyAsset'
args = self.page_kwargs.copy()
args['settings'] = get_settings()
args['source_path'] = 'content'
args['context']['generated_content'] = {
'article.rst': type(cls_name, (object,), {'url': 'article.html'})}
args['context']['static_content'] = {
'article.rst': type(cls_name, (object,), {'url': 'article.rst'})}
args['content'] = (
'A simple test, with a link to an'
'<a href="{filename}article.rst">article</a> and its'
'<a href="{static}article.rst">source</a>'
)
content = Page(**args).get_content('http://notmyidea.org')
self.assertEqual(
content,
'A simple test, with a link to an'
'<a href="http://notmyidea.org/article.html">article</a> and its'
'<a href="http://notmyidea.org/article.rst">source</a>'
)
def test_intrasite_link_to_static_content_with_filename(self):
"""Test linking to a static resource with deprecated {filename}
"""
cls_name = '_DummyAsset' if six.PY3 else b'_DummyAsset'
args = self.page_kwargs.copy()
args['settings'] = get_settings()
args['source_path'] = 'content'
args['context']['static_content'] = {
'poster.jpg':
type(cls_name, (object,), {'url': 'images/poster.jpg'})}
args['content'] = (
'A simple test, with a link to a'
'<a href="{filename}poster.jpg">poster</a>'
)
content = Page(**args).get_content('http://notmyidea.org')
self.assertEqual(
content,
'A simple test, with a link to a'
'<a href="http://notmyidea.org/images/poster.jpg">poster</a>'
)
def test_multiple_authors(self): def test_multiple_authors(self):
"""Test article with multiple authors.""" """Test article with multiple authors."""
args = self.page_kwargs.copy() args = self.page_kwargs.copy()
@ -599,13 +652,13 @@ class TestStatic(LoggedTestCase):
STATIC_URL='{path}', STATIC_URL='{path}',
PAGE_SAVE_AS=os.path.join('outpages', '{slug}.html'), PAGE_SAVE_AS=os.path.join('outpages', '{slug}.html'),
PAGE_URL='outpages/{slug}.html') PAGE_URL='outpages/{slug}.html')
self.context = self.settings.copy() self.context = get_context(self.settings)
self.static = Static(content=None, metadata={}, settings=self.settings, self.static = Static(content=None, metadata={}, settings=self.settings,
source_path=posix_join('dir', 'foo.jpg'), source_path=posix_join('dir', 'foo.jpg'),
context=self.context) context=self.context)
self.context['filenames'] = {self.static.source_path: self.static} self.context['static_content'][self.static.source_path] = self.static
def tearDown(self): def tearDown(self):
pass pass
@ -674,7 +727,7 @@ class TestStatic(LoggedTestCase):
def test_attach_to_does_nothing_after_save_as_referenced(self): def test_attach_to_does_nothing_after_save_as_referenced(self):
"""attach_to() does nothing if the save_as was already referenced. """attach_to() does nothing if the save_as was already referenced.
(For example, by a {filename} link an a document processed earlier.) (For example, by a {static} link an a document processed earlier.)
""" """
original_save_as = self.static.save_as original_save_as = self.static.save_as
@ -690,7 +743,7 @@ class TestStatic(LoggedTestCase):
def test_attach_to_does_nothing_after_url_referenced(self): def test_attach_to_does_nothing_after_url_referenced(self):
"""attach_to() does nothing if the url was already referenced. """attach_to() does nothing if the url was already referenced.
(For example, by a {filename} link an a document processed earlier.) (For example, by a {static} link an a document processed earlier.)
""" """
original_url = self.static.url original_url = self.static.url

View file

@ -10,7 +10,7 @@ from tempfile import mkdtemp
from pelican.generators import (ArticlesGenerator, Generator, PagesGenerator, from pelican.generators import (ArticlesGenerator, Generator, PagesGenerator,
PelicanTemplateNotFound, StaticGenerator, PelicanTemplateNotFound, StaticGenerator,
TemplatePagesGenerator) TemplatePagesGenerator)
from pelican.tests.support import get_settings, unittest from pelican.tests.support import get_context, get_settings, unittest
from pelican.writers import Writer from pelican.writers import Writer
try: try:
@ -178,14 +178,15 @@ class TestArticlesGenerator(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
settings = get_settings(filenames={}) settings = get_settings()
settings['DEFAULT_CATEGORY'] = 'Default' settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['READERS'] = {'asc': None} settings['READERS'] = {'asc': None}
settings['CACHE_CONTENT'] = False settings['CACHE_CONTENT'] = False
context = get_context(settings)
cls.generator = ArticlesGenerator( cls.generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
cls.generator.generate_context() cls.generator.generate_context()
cls.articles = cls.distill_articles(cls.generator.articles) cls.articles = cls.distill_articles(cls.generator.articles)
@ -299,15 +300,15 @@ class TestArticlesGenerator(unittest.TestCase):
self.assertEqual(sorted(categories), sorted(categories_expected)) self.assertEqual(sorted(categories), sorted(categories_expected))
def test_do_not_use_folder_as_category(self): def test_do_not_use_folder_as_category(self):
settings = get_settings(filenames={}) settings = get_settings()
settings['DEFAULT_CATEGORY'] = 'Default' settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['USE_FOLDER_AS_CATEGORY'] = False settings['USE_FOLDER_AS_CATEGORY'] = False
settings['CACHE_PATH'] = self.temp_cache settings['CACHE_PATH'] = self.temp_cache
settings['READERS'] = {'asc': None} settings['READERS'] = {'asc': None}
settings['filenames'] = {} context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
# test for name # test for name
@ -328,15 +329,16 @@ class TestArticlesGenerator(unittest.TestCase):
@unittest.skipUnless(MagicMock, 'Needs Mock module') @unittest.skipUnless(MagicMock, 'Needs Mock module')
def test_direct_templates_save_as_url_default(self): def test_direct_templates_save_as_url_default(self):
settings = get_settings(filenames={}) settings = get_settings()
settings['CACHE_PATH'] = self.temp_cache settings['CACHE_PATH'] = self.temp_cache
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings, settings=settings, context=context, settings=settings,
path=None, theme=settings['THEME'], output_path=None) path=None, theme=settings['THEME'], output_path=None)
write = MagicMock() write = MagicMock()
generator.generate_direct_templates(write) generator.generate_direct_templates(write)
write.assert_called_with("archives.html", write.assert_called_with("archives.html",
generator.get_template("archives"), settings, generator.get_template("archives"), context,
articles=generator.articles, articles=generator.articles,
dates=generator.dates, blog=True, dates=generator.dates, blog=True,
template_name='archives', template_name='archives',
@ -396,13 +398,14 @@ class TestArticlesGenerator(unittest.TestCase):
""" """
old_locale = locale.setlocale(locale.LC_ALL) old_locale = locale.setlocale(locale.LC_ALL)
locale.setlocale(locale.LC_ALL, str('C')) locale.setlocale(locale.LC_ALL, str('C'))
settings = get_settings(filenames={}) settings = get_settings()
settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html' settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html'
settings['YEAR_ARCHIVE_URL'] = 'posts/{date:%Y}/' settings['YEAR_ARCHIVE_URL'] = 'posts/{date:%Y}/'
settings['CACHE_PATH'] = self.temp_cache settings['CACHE_PATH'] = self.temp_cache
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings, settings=settings, context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
write = MagicMock() write = MagicMock()
@ -411,20 +414,20 @@ class TestArticlesGenerator(unittest.TestCase):
articles = [d for d in generator.articles if d.date.year == 1970] articles = [d for d in generator.articles if d.date.year == 1970]
self.assertEqual(len(dates), 1) self.assertEqual(len(dates), 1)
# among other things it must have at least been called with this # among other things it must have at least been called with this
settings["period"] = (1970,) context["period"] = (1970,)
write.assert_called_with("posts/1970/index.html", write.assert_called_with("posts/1970/index.html",
generator.get_template("period_archives"), generator.get_template("period_archives"),
settings, blog=True, articles=articles, context, blog=True, articles=articles,
dates=dates, template_name='period_archives', dates=dates, template_name='period_archives',
url="posts/1970/") url="posts/1970/")
del settings["period"]
settings['MONTH_ARCHIVE_SAVE_AS'] = \ settings['MONTH_ARCHIVE_SAVE_AS'] = \
'posts/{date:%Y}/{date:%b}/index.html' 'posts/{date:%Y}/{date:%b}/index.html'
settings['MONTH_ARCHIVE_URL'] = \ settings['MONTH_ARCHIVE_URL'] = \
'posts/{date:%Y}/{date:%b}/' 'posts/{date:%Y}/{date:%b}/'
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings, settings=settings, context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
write = MagicMock() write = MagicMock()
@ -434,21 +437,21 @@ class TestArticlesGenerator(unittest.TestCase):
articles = [d for d in generator.articles articles = [d for d in generator.articles
if d.date.year == 1970 and d.date.month == 1] if d.date.year == 1970 and d.date.month == 1]
self.assertEqual(len(dates), 1) self.assertEqual(len(dates), 1)
settings["period"] = (1970, "January") context["period"] = (1970, "January")
# among other things it must have at least been called with this # among other things it must have at least been called with this
write.assert_called_with("posts/1970/Jan/index.html", write.assert_called_with("posts/1970/Jan/index.html",
generator.get_template("period_archives"), generator.get_template("period_archives"),
settings, blog=True, articles=articles, context, blog=True, articles=articles,
dates=dates, template_name='period_archives', dates=dates, template_name='period_archives',
url="posts/1970/Jan/") url="posts/1970/Jan/")
del settings["period"]
settings['DAY_ARCHIVE_SAVE_AS'] = \ settings['DAY_ARCHIVE_SAVE_AS'] = \
'posts/{date:%Y}/{date:%b}/{date:%d}/index.html' 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html'
settings['DAY_ARCHIVE_URL'] = \ settings['DAY_ARCHIVE_URL'] = \
'posts/{date:%Y}/{date:%b}/{date:%d}/' 'posts/{date:%Y}/{date:%b}/{date:%d}/'
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings, settings=settings, context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
write = MagicMock() write = MagicMock()
@ -466,20 +469,21 @@ class TestArticlesGenerator(unittest.TestCase):
d.date.day == 1 d.date.day == 1
] ]
self.assertEqual(len(dates), 1) self.assertEqual(len(dates), 1)
settings["period"] = (1970, "January", 1) context["period"] = (1970, "January", 1)
# among other things it must have at least been called with this # among other things it must have at least been called with this
write.assert_called_with("posts/1970/Jan/01/index.html", write.assert_called_with("posts/1970/Jan/01/index.html",
generator.get_template("period_archives"), generator.get_template("period_archives"),
settings, blog=True, articles=articles, context, blog=True, articles=articles,
dates=dates, template_name='period_archives', dates=dates, template_name='period_archives',
url="posts/1970/Jan/01/") url="posts/1970/Jan/01/")
locale.setlocale(locale.LC_ALL, old_locale) locale.setlocale(locale.LC_ALL, old_locale)
def test_nonexistent_template(self): def test_nonexistent_template(self):
"""Attempt to load a non-existent template""" """Attempt to load a non-existent template"""
settings = get_settings(filenames={}) settings = get_settings()
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings, settings=settings, context=context, settings=settings,
path=None, theme=settings['THEME'], output_path=None) path=None, theme=settings['THEME'], output_path=None)
self.assertRaises(Exception, generator.get_template, "not_a_template") self.assertRaises(Exception, generator.get_template, "not_a_template")
@ -497,7 +501,7 @@ class TestArticlesGenerator(unittest.TestCase):
self.assertEqual(sorted(authors), sorted(authors_expected)) self.assertEqual(sorted(authors), sorted(authors_expected))
def test_standard_metadata_in_default_metadata(self): def test_standard_metadata_in_default_metadata(self):
settings = get_settings(filenames={}) settings = get_settings()
settings['CACHE_CONTENT'] = False settings['CACHE_CONTENT'] = False
settings['DEFAULT_CATEGORY'] = 'Default' settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
@ -506,8 +510,9 @@ class TestArticlesGenerator(unittest.TestCase):
# DEFAULT_CATEGORY # DEFAULT_CATEGORY
('category', 'Random'), ('category', 'Random'),
('tags', 'general, untagged')) ('tags', 'general, untagged'))
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
@ -530,13 +535,14 @@ class TestArticlesGenerator(unittest.TestCase):
self.assertEqual(tags, tags_expected) self.assertEqual(tags, tags_expected)
def test_article_order_by(self): def test_article_order_by(self):
settings = get_settings(filenames={}) settings = get_settings()
settings['DEFAULT_CATEGORY'] = 'Default' settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['ARTICLE_ORDER_BY'] = 'title' settings['ARTICLE_ORDER_BY'] = 'title'
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
@ -575,13 +581,14 @@ class TestArticlesGenerator(unittest.TestCase):
self.assertEqual(articles, expected) self.assertEqual(articles, expected)
# reversed title # reversed title
settings = get_settings(filenames={}) settings = get_settings()
settings['DEFAULT_CATEGORY'] = 'Default' settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['ARTICLE_ORDER_BY'] = 'reversed-title' settings['ARTICLE_ORDER_BY'] = 'reversed-title'
context = get_context(settings)
generator = ArticlesGenerator( generator = ArticlesGenerator(
context=settings.copy(), settings=settings, context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None) path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
@ -605,13 +612,14 @@ class TestPageGenerator(unittest.TestCase):
return [[page.title, page.status, page.template] for page in pages] return [[page.title, page.status, page.template] for page in pages]
def test_generate_context(self): def test_generate_context(self):
settings = get_settings(filenames={}) settings = get_settings()
settings['CACHE_PATH'] = self.temp_cache settings['CACHE_PATH'] = self.temp_cache
settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
context = get_context(settings)
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context, settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
pages = self.distill_pages(generator.pages) pages = self.distill_pages(generator.pages)
@ -624,6 +632,7 @@ class TestPageGenerator(unittest.TestCase):
['This is a test page with a preset template', 'published', ['This is a test page with a preset template', 'published',
'custom'], 'custom'],
['Page with a bunch of links', 'published', 'page'], ['Page with a bunch of links', 'published', 'page'],
['Page with static links', 'published', 'page'],
['A Page (Test) for sorting', 'published', 'page'], ['A Page (Test) for sorting', 'published', 'page'],
] ]
hidden_pages_expected = [ hidden_pages_expected = [
@ -653,10 +662,11 @@ class TestPageGenerator(unittest.TestCase):
sorted(self.distill_pages(generator.context['draft_pages']))) sorted(self.distill_pages(generator.context['draft_pages'])))
def test_generate_sorted(self): def test_generate_sorted(self):
settings = get_settings(filenames={}) settings = get_settings()
settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
settings['CACHE_PATH'] = self.temp_cache settings['CACHE_PATH'] = self.temp_cache
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
context = get_context(settings)
# default sort (filename) # default sort (filename)
pages_expected_sorted_by_filename = [ pages_expected_sorted_by_filename = [
@ -664,11 +674,12 @@ class TestPageGenerator(unittest.TestCase):
['This is a markdown test page', 'published', 'page'], ['This is a markdown test page', 'published', 'page'],
['A Page (Test) for sorting', 'published', 'page'], ['A Page (Test) for sorting', 'published', 'page'],
['Page with a bunch of links', 'published', 'page'], ['Page with a bunch of links', 'published', 'page'],
['Page with static links', 'published', 'page'],
['This is a test page with a preset template', 'published', ['This is a test page with a preset template', 'published',
'custom'], 'custom'],
] ]
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context, settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
pages = self.distill_pages(generator.pages) pages = self.distill_pages(generator.pages)
@ -678,14 +689,16 @@ class TestPageGenerator(unittest.TestCase):
pages_expected_sorted_by_title = [ pages_expected_sorted_by_title = [
['A Page (Test) for sorting', 'published', 'page'], ['A Page (Test) for sorting', 'published', 'page'],
['Page with a bunch of links', 'published', 'page'], ['Page with a bunch of links', 'published', 'page'],
['Page with static links', 'published', 'page'],
['This is a markdown test page', 'published', 'page'], ['This is a markdown test page', 'published', 'page'],
['This is a test page', 'published', 'page'], ['This is a test page', 'published', 'page'],
['This is a test page with a preset template', 'published', ['This is a test page with a preset template', 'published',
'custom'], 'custom'],
] ]
settings['PAGE_ORDER_BY'] = 'title' settings['PAGE_ORDER_BY'] = 'title'
context = get_context(settings)
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
pages = self.distill_pages(generator.pages) pages = self.distill_pages(generator.pages)
@ -697,12 +710,14 @@ class TestPageGenerator(unittest.TestCase):
'custom'], 'custom'],
['This is a test page', 'published', 'page'], ['This is a test page', 'published', 'page'],
['This is a markdown test page', 'published', 'page'], ['This is a markdown test page', 'published', 'page'],
['Page with static links', 'published', 'page'],
['Page with a bunch of links', 'published', 'page'], ['Page with a bunch of links', 'published', 'page'],
['A Page (Test) for sorting', 'published', 'page'], ['A Page (Test) for sorting', 'published', 'page'],
] ]
settings['PAGE_ORDER_BY'] = 'reversed-title' settings['PAGE_ORDER_BY'] = 'reversed-title'
context = get_context(settings)
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context, settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
pages = self.distill_pages(generator.pages) pages = self.distill_pages(generator.pages)
@ -713,21 +728,43 @@ class TestPageGenerator(unittest.TestCase):
Test to ensure links of the form {tag}tagname and {category}catname Test to ensure links of the form {tag}tagname and {category}catname
are generated correctly on pages are generated correctly on pages
""" """
settings = get_settings(filenames={}) settings = get_settings()
settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
settings['CACHE_PATH'] = self.temp_cache settings['CACHE_PATH'] = self.temp_cache
settings['DEFAULT_DATE'] = (1970, 1, 1) settings['DEFAULT_DATE'] = (1970, 1, 1)
context = get_context(settings)
generator = PagesGenerator( generator = PagesGenerator(
context=settings.copy(), settings=settings, context=context, settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None) path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context() generator.generate_context()
pages_by_title = {p.title: p.content for p in generator.pages} pages_by_title = {p.title: p for p in generator.pages}
test_content = pages_by_title['Page with a bunch of links'] test_content = pages_by_title['Page with a bunch of links'].content
self.assertIn('<a href="/category/yeah.html">', test_content) self.assertIn('<a href="/category/yeah.html">', test_content)
self.assertIn('<a href="/tag/matsuku.html">', test_content) self.assertIn('<a href="/tag/matsuku.html">', test_content)
def test_static_and_attach_links_on_generated_pages(self):
"""
Test to ensure links of the form {static}filename and {attach}filename
are included in context['static_links']
"""
settings = get_settings()
settings['PAGE_PATHS'] = ['TestPages/page_with_static_links.md']
settings['CACHE_PATH'] = self.temp_cache
settings['DEFAULT_DATE'] = (1970, 1, 1)
context = get_context(settings)
generator = PagesGenerator(
context=context, settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
self.assertIn('pelican/tests/TestPages/image0.jpg',
context['static_links'])
self.assertIn('pelican/tests/TestPages/image1.jpg',
context['static_links'])
class TestTemplatePagesGenerator(unittest.TestCase): class TestTemplatePagesGenerator(unittest.TestCase):
@ -791,7 +828,7 @@ class TestStaticGenerator(unittest.TestCase):
"static", "staticfile") "static", "staticfile")
self.endfile = os.path.join(self.temp_output, "static", "staticfile") self.endfile = os.path.join(self.temp_output, "static", "staticfile")
self.generator = StaticGenerator( self.generator = StaticGenerator(
context={'filenames': {}}, context=get_context(),
settings=self.settings, settings=self.settings,
path=self.temp_content, path=self.temp_content,
theme="", theme="",
@ -808,11 +845,8 @@ class TestStaticGenerator(unittest.TestCase):
def test_theme_static_paths_dirs(self): def test_theme_static_paths_dirs(self):
"""Test that StaticGenerator properly copies also files mentioned in """Test that StaticGenerator properly copies also files mentioned in
TEMPLATE_STATIC_PATHS, not just directories.""" TEMPLATE_STATIC_PATHS, not just directories."""
settings = get_settings( settings = get_settings(PATH=self.content_path)
PATH=self.content_path, context = get_context(settings, staticfiles=[])
filenames={})
context = settings.copy()
context['staticfiles'] = []
StaticGenerator( StaticGenerator(
context=context, settings=settings, context=context, settings=settings,
@ -831,10 +865,8 @@ class TestStaticGenerator(unittest.TestCase):
TEMPLATE_STATIC_PATHS, not just directories.""" TEMPLATE_STATIC_PATHS, not just directories."""
settings = get_settings( settings = get_settings(
PATH=self.content_path, PATH=self.content_path,
THEME_STATIC_PATHS=['static/css/fonts.css', 'static/fonts/'], THEME_STATIC_PATHS=['static/css/fonts.css', 'static/fonts/'],)
filenames={}) context = get_context(settings, staticfiles=[])
context = settings.copy()
context['staticfiles'] = []
StaticGenerator( StaticGenerator(
context=context, settings=settings, context=context, settings=settings,
@ -869,9 +901,8 @@ class TestStaticGenerator(unittest.TestCase):
settings = get_settings( settings = get_settings(
STATIC_EXCLUDES=['subdir'], STATIC_EXCLUDES=['subdir'],
PATH=self.content_path, PATH=self.content_path,
STATIC_PATHS=[''], STATIC_PATHS=[''],)
filenames={}) context = get_context(settings)
context = settings.copy()
StaticGenerator( StaticGenerator(
context=context, settings=settings, context=context, settings=settings,
@ -897,9 +928,8 @@ class TestStaticGenerator(unittest.TestCase):
PATH=self.content_path, PATH=self.content_path,
PAGE_PATHS=[''], PAGE_PATHS=[''],
STATIC_PATHS=[''], STATIC_PATHS=[''],
CACHE_CONTENT=False, CACHE_CONTENT=False,)
filenames={}) context = get_context(settings)
context = settings.copy()
for generator_class in (PagesGenerator, StaticGenerator): for generator_class in (PagesGenerator, StaticGenerator):
generator_class( generator_class(
@ -915,8 +945,7 @@ class TestStaticGenerator(unittest.TestCase):
"STATIC_EXCLUDE_SOURCES=True failed to exclude a markdown file") "STATIC_EXCLUDE_SOURCES=True failed to exclude a markdown file")
settings.update(STATIC_EXCLUDE_SOURCES=False) settings.update(STATIC_EXCLUDE_SOURCES=False)
context = settings.copy() context = get_context(settings)
context['filenames'] = {}
for generator_class in (PagesGenerator, StaticGenerator): for generator_class in (PagesGenerator, StaticGenerator):
generator_class( generator_class(
@ -931,6 +960,40 @@ class TestStaticGenerator(unittest.TestCase):
any(name.endswith(".md") for name in staticnames), any(name.endswith(".md") for name in staticnames),
"STATIC_EXCLUDE_SOURCES=False failed to include a markdown file") "STATIC_EXCLUDE_SOURCES=False failed to include a markdown file")
def test_static_links(self):
"""Test that StaticGenerator uses files in static_links
"""
settings = get_settings(
STATIC_EXCLUDES=['subdir'],
PATH=self.content_path,
STATIC_PATHS=[],)
context = get_context(settings)
context['static_links'] |= {'short_page.md', 'subdir_fake_image.jpg'}
StaticGenerator(
context=context, settings=settings,
path=settings['PATH'], output_path=self.temp_output,
theme=settings['THEME']).generate_context()
staticfiles_names = [
os.path.basename(c.source_path) for c in context['staticfiles']]
static_content_names = [
os.path.basename(c) for c in context['static_content']]
self.assertIn(
'short_page.md', staticfiles_names,
"StaticGenerator skipped a file that it should have included")
self.assertIn(
'short_page.md', static_content_names,
"StaticGenerator skipped a file that it should have included")
self.assertIn(
'subdir_fake_image.jpg', staticfiles_names,
"StaticGenerator skipped a file that it should have included")
self.assertIn(
'subdir_fake_image.jpg', static_content_names,
"StaticGenerator skipped a file that it should have included")
def test_copy_one_file(self): def test_copy_one_file(self):
with open(self.startfile, "w") as f: with open(self.startfile, "w") as f:
f.write("staticcontent") f.write("staticcontent")

View file

@ -112,7 +112,7 @@ class TestPelican(LoggedTestCase):
self.assertDirsEqual( self.assertDirsEqual(
self.temp_path, os.path.join(OUTPUT_PATH, 'basic')) self.temp_path, os.path.join(OUTPUT_PATH, 'basic'))
self.assertLogCountEqual( self.assertLogCountEqual(
count=3, count=1,
msg="Unable to find.*skipping url replacement", msg="Unable to find.*skipping url replacement",
level=logging.WARNING) level=logging.WARNING)

View file

@ -761,7 +761,7 @@ def update_links_to_attached_files(content, attachments):
http_url = old_url.replace('https://', 'http://') http_url = old_url.replace('https://', 'http://')
https_url = old_url.replace('http://', 'https://') https_url = old_url.replace('http://', 'https://')
for url in [http_url, https_url]: for url in [http_url, https_url]:
content = content.replace(url, '{filename}' + new_path) content = content.replace(url, '{static}' + new_path)
return content return content

View file

@ -435,7 +435,7 @@ def path_to_url(path):
def posixize_path(rel_path): def posixize_path(rel_path):
"""Use '/' as path separator, so that source references, """Use '/' as path separator, so that source references,
like '{filename}/foo/bar.jpg' or 'extras/favicon.ico', like '{static}/foo/bar.jpg' or 'extras/favicon.ico',
will work on Windows as well as on Mac and Linux.""" will work on Windows as well as on Mac and Linux."""
return rel_path.replace(os.sep, '/') return rel_path.replace(os.sep, '/')

View file

@ -14,7 +14,7 @@ Why not ?
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst ! After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH ! YEAH !
.. image:: |filename|/pictures/Sushi.jpg .. image:: |static|/pictures/Sushi.jpg
:height: 450 px :height: 450 px
:width: 600 px :width: 600 px
:alt: alternate text :alt: alternate text

View file

@ -17,12 +17,12 @@ This is a simple title
And here comes the cool stuff_. And here comes the cool stuff_.
.. image:: |filename|/pictures/Sushi.jpg .. image:: |static|/pictures/Sushi.jpg
:height: 450 px :height: 450 px
:width: 600 px :width: 600 px
:alt: alternate text :alt: alternate text
.. image:: |filename|/pictures/Sushi_Macro.jpg .. image:: |static|/pictures/Sushi_Macro.jpg
:height: 450 px :height: 450 px
:width: 600 px :width: 600 px
:alt: alternate text :alt: alternate text