Liam Cooke  committed 252c3f5

RSS feed generation

  • Participants
  • Parent commits 903939e

Comments (0)

Files changed (3)

     dateutil    python-dateutil
     mako        Mako      
     markdown    Markdown  
+    PyRSS2Gen   PyRSS2Gen 
     yaml        PyYAML    
 At the moment there isn't any documentation, or even a list of features.
-    Template documentation
+    Mako templates
+    Markdown formatting

File example-site/deploy/feed.rss

+<?xml version="1.0" encoding="utf-8"?>
+<rss version="2.0"><channel><title>Pilcrow</title><link>http:/</link><description></description><language>en</language><lastBuildDate>Mon, 07 Dec 2009 21:21:00 GMT</lastBuildDate><generator>Pilcrow</generator><docs></docs><item><title>Lorem ipsum</title><link>http:/</link><description>&lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ultrices
+tempor tincidunt. Mauris sit amet ante augue, eu pulvinar lorem. Nam eleifend
+sodales consectetur. Sed eget mi ac nunc ultrices egestas a vitae nibh.
+Maecenas mattis pharetra lorem. Fusce faucibus lacus nec tellus interdum nec
+cursus ante consequat.&lt;/p&gt;
+&lt;p&gt;Aliquam sed nisi mauris.&lt;/p&gt;
+&lt;p&gt;Nulla sit amet lacus at massa pharetra mattis at eu tortor. Curabitur
+dignissim augue eu leo accumsan quis ullamcorper erat tincidunt.&lt;/p&gt;
+&lt;p&gt;Vill koum wéi ke, dir ze welle fergiess zwëschen. Op brét Feierwon mir, hin mä
+welle Gaart erwaacht. Da hie stét Kënnt Freiesch, rifft kommen grousse am gét,
+huet bléit d'Margréitchen blo ze. Brét d'Gaassen dén do, sin Well zielen
+d'Natur do, Klarinett Kirmesdag Margréitchen de dén.&lt;/p&gt;
+&lt;p&gt;Cras enim diam, cursus id condimentum ac, gravida et quam. Suspendisse
+ultricies libero quis quam facilisis blandit. Nunc adipiscing dolor et magna
+tincidunt venenatis. Ut vel magna et neque fringilla porttitor. Nam suscipit
+consectetur justo eget rutrum. Morbi eu eros nec nunc molestie blandit.
+Suspendisse lectus mi, sollicitudin at cursus non, congue at tortor.&lt;/p&gt;
+&lt;p&gt;Integer lacinia, dolor ac consequat mollis, neque ante sodales odio, eget
+semper quam est eu quam. Quisque et nisl sit amet urna condimentum gravida.
+Nam euismod ante at orci blandit pharetra.&lt;/p&gt;</description><category>loremipsum</category><category>typography</category><category>filler</category><guid isPermaLink="true">http:/</guid><pubDate>Mon, 07 Dec 2009 21:21:00 GMT</pubDate></item><item><title>What's new in Python 2.6</title><link>http:/</link><description>&lt;p&gt;This article explains the new features in Python 2.6, released on October 1
+2008. The release schedule is described in &lt;a href=""&gt;PEP 361&lt;/a&gt;.&lt;/p&gt;
+&lt;p&gt;The major theme of Python 2.6 is preparing the migration path to Python 3.0, a
+major redesign of the language. Whenever possible, Python 2.6 incorporates new
+features and syntax from 3.0 while remaining compatible with existing code by
+not removing older features or syntax. When it’s not possible to do that,
+Python 2.6 tries to do what it can, adding compatibility functions in a
+&lt;code&gt;future_builtins&lt;/code&gt; module and a &lt;code&gt;-3&lt;/code&gt; switch to warn about usages that will
+become unsupported in 3.0.&lt;/p&gt;
+&lt;p&gt;Some significant new packages have been added to the standard library, such as
+the &lt;code&gt;multiprocessing&lt;/code&gt; and &lt;code&gt;json&lt;/code&gt; modules, but there aren’t many new features
+that aren’t related to Python 3.0 in some way.&lt;/p&gt;
+&lt;p&gt;Python 2.6 also sees a number of improvements and bugfixes throughout the
+source. A search through the change logs finds there were 259 patches applied
+and 612 bugs fixed between Python 2.5 and 2.6. Both figures are likely to be
+&lt;p&gt;This article doesn’t attempt to provide a complete specification of the new
+features, but instead provides a convenient overview. For full details, you
+should refer to the documentation for Python 2.6. If you want to understand
+the rationale for the design and implementation, refer to the PEP for a
+particular new feature. Whenever possible, “What’s New in Python” links to the
+bug/patch item for each change.&lt;/p&gt;</description><category>python</category><guid isPermaLink="true">http:/</guid><pubDate>Sun, 06 Dec 2009 22:44:00 GMT</pubDate></item><item><title>Turritopsis nutricula</title><link>http:/</link><description>&lt;p&gt;From &lt;a href=""&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;
+&lt;p&gt;Turritopsis nutricula is a hydrozoan with a life cycle in which it reverts to
+the polyp stage after becoming sexually mature. It is the only known case of
+a metazoan capable of reverting completely to a sexually immature, colonial
+stage after having reached sexual maturity as a solitary stage. It does this
+through the cell development process of transdifferentiation. Theoretically,
+this cycle can repeat indefinitely, rendering it biologically immortal until
+its nerve center is removed from the rest of the body.&lt;/p&gt;
+&lt;/blockquote&gt;</description><category>wikipedia</category><category>immortality</category><category>wtf</category><guid isPermaLink="true">http:/</guid><pubDate>Tue, 24 Nov 2009 00:00:00 GMT</pubDate></item></channel></rss>
 from os import path
 import dateutil.parser
+import PyRSS2Gen as rss2
 import yaml
 from mako.exceptions import MakoException
 from mako.lookup import TemplateLookup
     '.less': lambda s, d: run_or_die('lessc %s %s' % (s, d)),
 site = yaml.load(r"""
+    domain: http://localhost/
+    root: /
     clean_urls: no
     content_extensions: [text, markdown, mkdn, md]
         content: content
-        deploy: deploy
         files: files
         templates: templates
+        deploy: deploy
+    feed: feed.rss
     files_exclude: "(^\\.|~$)"
     files_include: "^\\.htaccess$"
         .less: .css
+    lang: en
 alphanum = lambda s: re.sub('[^A-Za-z0-9]', '', s)
         id =
         return join_url(site['root'], id != 'index' and id)
+    @property
+    def full_url(self):
+        return join_url(site['domain'], self.url, ext=False)
 class ContentPage(Page):
     NORM = {
         'date': norm_time, 'posted': norm_time,
             return summary
         self['content'] = markdown(self.SUMMARY.sub(_summary, body).strip())
+    def feed_item(self):
+        url = self.full_url
+        return rss2.RSSItem(title=self.title, link=url, guid=rss2.Guid(url),
+            description=self.content, pubDate=self.posted or,
+            categories=self.get('tags', None),
+            enclosure=self.get('enclosure', None))
 class ArchivePage(Page):
     def __init__(self, entries, year, month=0):
         tdir = site['dirs']['templates']
         self.lookup = TemplateLookup(directories=[tdir], input_encoding='utf-8')
+    def __getitem__(self, id):
+        return self.pages[id]
+    def __iter__(self):
+        return iter(self.pages.values())
     def add(self, page):
         if in self.pages:
             die('duplicate page id: %s' %
         self.pages[] = page
-    def __getitem__(self, id):
-        return self.pages[id]
-    def all(self, sortby_origin=False):
+    def select(self, limit=None, dated=True, chrono=False, sortby_origin=None):
+        if sortby_origin is None:
+            sortby_origin = bool(chrono)
         sortkey = sortby_origin and Page.sortkey_origin or Page.sortkey_posted
-        return sorted(self.pages.values(), key=sortkey)
-    def __iter__(self):
-        return iter(self.pages.values())
+        results = sorted(self.pages.values(), key=sortkey, reverse=not chrono)
+        if dated:
+            results = [page for page in results if]
+        return tuple(results)[:limit]
     def render(self):
         for page in self:
     for d in sorted(set(dirs)):
         mkdir(os.path.join(deploy_path, d))
-    def select(limit=None, dated=True, chrono=False, sortby_origin=None):
-        if sortby_origin is None: sortby_origin = bool(chrono)
-        results = pages.all(sortby_origin)
-        if not chrono: results.reverse()
-        if dated: results = [page for page in results if]
-        return tuple(results)[:limit]
         'get': lambda id: pages[str(id)],
-        'pages': select,
+        'pages':,
         'domain': site['domain'].rstrip('/'),
         'root': '/' + site.get('root', '').lstrip('/'),
         'head_title': site.get('site_title', ''),
     try: pages.render()
     except MakoException as e: die('template error:', e)
+    if site['feed']:
+        feed_posts =
+        feed = rss2.RSS2(items=[p.feed_item() for p in feed_posts],
+            title=site['site_title'], description=site.get('description', ''),
+            link=join_url(site['domain'], site['root']), generator='Pilcrow',
+            language=site['lang'], lastBuildDate=feed_posts[0].date)
+        with open(path.join(deploy_path, site['feed']), 'w') as f:
+            feed.write_xml(f, 'utf-8')
 if __name__ == '__main__':
     parser = optparse.OptionParser()
     parser.add_option('-x', '--clean', action='store_true', default=False)