diff --git a/docs/changelog.rst b/docs/changelog.rst
index f25ee3d9..ab3a4233 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -25,6 +25,8 @@ Release history
file within the same directory as the original file to prevent the server
(e.g. Nginx) from compressing files during an HTTP call.
* Add AsciiDoc support
+* Add ``FILENAME_METADATA`` new setting which adds support for metadata
+ extraction from the filename.
3.0 (2012-08-08)
==================
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 73378fc4..b0b5bf92 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -181,15 +181,26 @@ syntax for Markdown posts should follow this pattern::
This is the content of my super blog post.
-Note that, aside from the title, none of this metadata is mandatory: if the date
-is not specified, Pelican will rely on the file's "mtime" timestamp, and the
-category can be determined by the directory in which the file resides. For
-example, a file located at ``python/foobar/myfoobar.rst`` will have a category of
-``foobar``. If you would like to organize your files in other ways where the
-name of the subfolder would not be a good category name, you can set the
-setting ``USE_FOLDER_AS_CATEGORY`` to ``False``. If summary isn't given, setting
-``SUMMARY_MAX_LENGTH`` determines how many words from the beginning of an article
-are used as the summary.
+Note that, aside from the title, none of this metadata is mandatory: if the
+date is not specified, Pelican can rely on the file's "mtime" timestamp through
+the ``DEFAULT_DATE`` setting, and the category can be determined by the
+directory in which the file resides. For example, a file located at
+``python/foobar/myfoobar.rst`` will have a category of ``foobar``. If you would
+like to organize your files in other ways where the name of the subfolder would
+not be a good category name, you can set the setting ``USE_FOLDER_AS_CATEGORY``
+to ``False``. If summary isn't given, setting ``SUMMARY_MAX_LENGTH`` determines
+how many words from the beginning of an article are used as the summary.
+
+You can also extract any metadata from the filename through a regexp to be set
+in the ``FILENAME_METADATA`` setting.
+All named groups that are matched will be set in the metadata object. The
+default value for the ``FILENAME_METADATA`` setting will only extract the date
+from the filename. For example, if you would like to extract both the date and
+the slug, you could set something like:
+``'(?P\d{4}-\d{2}-\d{2})_(?P.*)'``.
+
+Please note that the metadata available inside your files takes precedence over
+the metadata extracted from the filename.
Generate your blog
------------------
diff --git a/docs/settings.rst b/docs/settings.rst
index 7d73afe0..fc019757 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -50,6 +50,16 @@ Setting name (default value) What doe
If tuple object, it will instead generate the
default datetime object by passing the tuple to
the datetime.datetime constructor.
+`DEFAULT_METADATA` (``()``) The default metadata you want to use for all articles
+ and pages.
+`FILENAME_METADATA` (``'(?P\d{4}-\d{2}-\d{2}).*'``) The regexp that will be used to extract any metadata
+ from the filename. All named groups that are matched
+ will be set in the metadata object.
+ The default value will only extract the date from
+ the filename.
+ For example, if you would like to extract both the
+ date and the slug, you could set something like:
+ ``'(?P\d{4}-\d{2}-\d{2})_(?P.*)'``.
`DELETE_OUTPUT_DIRECTORY` (``False``) Delete the content of the output directory before
generating new files.
`FILES_TO_COPY` (``()``) A list of files (or directories) to copy from the source (inside the
diff --git a/pelican/readers.py b/pelican/readers.py
index f04b4a03..74cec02f 100644
--- a/pelican/readers.py
+++ b/pelican/readers.py
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
+import os
+import re
try:
import docutils
import docutils.core
@@ -207,8 +209,9 @@ for cls in Reader.__subclasses__():
def read_file(filename, fmt=None, settings=None):
"""Return a reader object using the given format."""
+ base, ext = os.path.splitext(os.path.basename(filename))
if not fmt:
- fmt = filename.split('.')[-1]
+ fmt = ext[1:]
if fmt not in _EXTENSIONS:
raise TypeError('Pelican does not know how to parse %s' % filename)
@@ -225,9 +228,18 @@ def read_file(filename, fmt=None, settings=None):
content, metadata = reader.read(filename)
# eventually filter the content with typogrify if asked so
- if settings and settings['TYPOGRIFY']:
+ if settings and settings.get('TYPOGRIFY'):
from typogrify.filters import typogrify
content = typogrify(content)
metadata['title'] = typogrify(metadata['title'])
+ filename_metadata = settings and settings.get('FILENAME_METADATA')
+ if filename_metadata:
+ match = re.match(filename_metadata, base)
+ if match:
+ for k, v in match.groupdict().iteritems():
+ if k not in metadata:
+ k = k.lower() # metadata must be lowercase
+ metadata[k] = reader.process_metadata(k, v)
+
return content, metadata
diff --git a/pelican/settings.py b/pelican/settings.py
index d2c0cef5..a98f3538 100644
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -5,6 +5,7 @@ import inspect
import os
import locale
import logging
+import re
from os.path import isabs
@@ -60,7 +61,7 @@ _DEFAULT_CONFIG = {'PATH': '.',
'TAG_CLOUD_STEPS': 4,
'TAG_CLOUD_MAX_ITEMS': 100,
'DIRECT_TEMPLATES': ('index', 'tags', 'categories', 'archives'),
- 'EXTRA_TEMPLATES_PATHS' : [],
+ 'EXTRA_TEMPLATES_PATHS': [],
'PAGINATED_DIRECT_TEMPLATES': ('index', ),
'PELICAN_CLASS': 'pelican.Pelican',
'DEFAULT_DATE_FORMAT': '%a %d %B %Y',
@@ -70,6 +71,7 @@ _DEFAULT_CONFIG = {'PATH': '.',
'DEFAULT_PAGINATION': False,
'DEFAULT_ORPHANS': 0,
'DEFAULT_METADATA': (),
+ 'FILENAME_METADATA': '(?P\d{4}-\d{2}-\d{2}).*',
'FILES_TO_COPY': (),
'DEFAULT_STATUS': 'published',
'ARTICLE_PERMALINK_STRUCTURE': '',
@@ -205,4 +207,12 @@ def configure_settings(settings):
" falling back to the default extension " +
_DEFAULT_CONFIG['OUTPUT_SOURCES_EXTENSION'])
+ filename_metadata = settings.get('FILENAME_METADATA')
+ if filename_metadata and not isinstance(filename_metadata, basestring):
+ logger.error("Detected misconfiguration with FILENAME_METADATA"
+ " setting (must be string or compiled pattern), falling"
+ "back to the default")
+ settings['FILENAME_METADATA'] = \
+ _DEFAULT_CONFIG['FILENAME_METADATA']
+
return settings
diff --git a/samples/content/2012-11-30_filename-metadata.rst b/samples/content/2012-11-30_filename-metadata.rst
new file mode 100644
index 00000000..b048103d
--- /dev/null
+++ b/samples/content/2012-11-30_filename-metadata.rst
@@ -0,0 +1,4 @@
+FILENAME_METADATA example
+#########################
+
+Some cool stuff!
diff --git a/tests/content/2012-11-29_rst_w_filename_meta#foo-bar.rst b/tests/content/2012-11-29_rst_w_filename_meta#foo-bar.rst
new file mode 100644
index 00000000..43f05a15
--- /dev/null
+++ b/tests/content/2012-11-29_rst_w_filename_meta#foo-bar.rst
@@ -0,0 +1,6 @@
+
+Rst with filename metadata
+##########################
+
+:category: yeah
+:author: Alexis Métaireau
diff --git a/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md b/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md
new file mode 100644
index 00000000..cdccfc8a
--- /dev/null
+++ b/tests/content/2012-11-30_md_w_filename_meta#foo-bar.md
@@ -0,0 +1,6 @@
+category: yeah
+author: Alexis Métaireau
+
+Markdown with filename metadata
+===============================
+
diff --git a/tests/output/basic/archives.html b/tests/output/basic/archives.html
index 0bccff9b..cb06dfa1 100644
--- a/tests/output/basic/archives.html
+++ b/tests/output/basic/archives.html
@@ -33,6 +33,8 @@
diff --git a/tests/output/basic/feeds/all-en.atom.xml b/tests/output/basic/feeds/all-en.atom.xml
index fc05c4b3..54c87905 100644
--- a/tests/output/basic/feeds/all-en.atom.xml
+++ b/tests/output/basic/feeds/all-en.atom.xml
@@ -1,5 +1,6 @@
-A Pelican Blog/2012-02-29T00:00:00ZSecond article2012-02-29T00:00:00Ztag:,2012-02-29:second-article.html<p>This is some article, in english</p>
+A Pelican Blog/2012-11-30T00:00:00ZFILENAME_METADATA example2012-11-30T00:00:00Ztag:,2012-11-30:filename_metadata-example.html<p>Some cool stuff!</p>
+Second article2012-02-29T00:00:00Ztag:,2012-02-29:second-article.html<p>This is some article, in english</p>
A markdown powered article2011-04-20T00:00:00Ztag:,2011-04-20:a-markdown-powered-article.html<p>You're mutually oblivious.</p>
<p><a href="/unbelievable.html">a root-relative link to unbelievable</a>
<a href="/unbelievable.html">a file-relative link to unbelievable</a></p>Article 12011-02-17T00:00:00Ztag:,2011-02-17:article-1.html<p>Article 1</p>
diff --git a/tests/output/basic/feeds/all.atom.xml b/tests/output/basic/feeds/all.atom.xml
index f3a1a6f4..3081adc6 100644
--- a/tests/output/basic/feeds/all.atom.xml
+++ b/tests/output/basic/feeds/all.atom.xml
@@ -1,5 +1,6 @@
-A Pelican Blog/2012-02-29T00:00:00ZSecond article2012-02-29T00:00:00Ztag:,2012-02-29:second-article.html<p>This is some article, in english</p>
+A Pelican Blog/2012-11-30T00:00:00ZFILENAME_METADATA example2012-11-30T00:00:00Ztag:,2012-11-30:filename_metadata-example.html<p>Some cool stuff!</p>
+Second article2012-02-29T00:00:00Ztag:,2012-02-29:second-article.html<p>This is some article, in english</p>
Deuxième article2012-02-29T00:00:00Ztag:,2012-02-29:second-article-fr.html<p>Ceci est un article, en français.</p>
A markdown powered article2011-04-20T00:00:00Ztag:,2011-04-20:a-markdown-powered-article.html<p>You're mutually oblivious.</p>
<p><a href="/unbelievable.html">a root-relative link to unbelievable</a>
diff --git a/tests/output/basic/feeds/misc.atom.xml b/tests/output/basic/feeds/misc.atom.xml
index 40ad44c5..e71bd151 100644
--- a/tests/output/basic/feeds/misc.atom.xml
+++ b/tests/output/basic/feeds/misc.atom.xml
@@ -1,5 +1,6 @@
-A Pelican Blog/2012-02-29T00:00:00ZSecond article2012-02-29T00:00:00Ztag:,2012-02-29:second-article.html<p>This is some article, in english</p>
+A Pelican Blog/2012-11-30T00:00:00ZFILENAME_METADATA example2012-11-30T00:00:00Ztag:,2012-11-30:filename_metadata-example.html<p>Some cool stuff!</p>
+Second article2012-02-29T00:00:00Ztag:,2012-02-29:second-article.html<p>This is some article, in english</p>
Unbelievable !2010-10-15T20:30:00Ztag:,2010-10-15:unbelievable.html<p>Or completely awesome. Depends the needs.</p>
<p><a class="reference external" href="/a-markdown-powered-article.html">a root-relative link to markdown-article</a>
<a class="reference external" href="/a-markdown-powered-article.html">a file-relative link to markdown-article</a></p>
diff --git a/tests/output/basic/filename_metadata-example.html b/tests/output/basic/filename_metadata-example.html
new file mode 100644
index 00000000..b7ae95a7
--- /dev/null
+++ b/tests/output/basic/filename_metadata-example.html
@@ -0,0 +1,66 @@
+
+
+
+ FILENAME_METADATA example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Comments !
+ + +