Posts for the month of January 2010

Use Nagare namespaces to create an rss feed

In Nagare HTML and XML generation is done through Renderers. Thus, to generate RSS feeds it is possible to use Nagare XML Renderer, which accept any tag or attribute. Another way is to create a RSS Renderer for Nagare which matches RSS namespace.

To do so we are extending Nagare XmlRender, which enforces possible xml tags and attributes (e.g. namespace).

Simple RSSRenderer

RSS specification, leads to the following namespace definition in Nagare:

class RssRenderer(xml.XmlRenderer):
    """RSS renderer"""
    # RSS tags
    # ------------
    rss = TagProp('rss', set(('version', )))
    channel = TagProp('channel', set())
    title = TagProp('title', set())
    link = TagProp('link', set())
    description = TagProp('description', set())
    language = TagProp('language', set())
    copyright = TagProp('copyright', set())
    managingEditor = TagProp('managingEditor', set())
    webMaster = TagProp('webMaster', set())
    pubDate = TagProp('pubDate', set())
    lastBuildDate = TagProp('lastBuildDate', set())
    category = TagProp('category', set())
    generator = TagProp('generator', set())
    docs = TagProp('docs', set())
    cloud = TagProp('cloud', set(('domain', 'port',
        'path', 'registerProcedure', 'protocol')))
    ttl = TagProp('ttl', set())
    image = TagProp('image', set())
    url = TagProp('url', set())
    width = TagProp('width', set())
    height = TagProp('height', set())
    rating = TagProp('rating', set())
    textInput = TagProp('textInput', set())
    name = TagProp('name', set())
    skipHours = TagProp('skipHours', set())
    skipDays = TagProp('skipDays', set())
    item = TagProp('item', set())
    title = TagProp('title', set())
    link = TagProp('link', set())
    description = TagProp('description', set())
    author = TagProp('author', set())
    category = TagProp('category', set(('domain', )))
    comments = TagProp('comments', set())
    enclosure = TagProp('enclosure', set(('url', 'length', 'type')))
    guid = TagProp('guid', set(('isPermaLink', )))
    pubDate = TagProp('pubDate', set())
    source = TagProp('source', set(('url', )))

Note

Notes:

  • RssRenderer checks if tags and attibutes exist in namespace
  • RssRenderer does not validate against any DTD or schema

Let's look at the following example:

<?xml version="1.0"?>
<rss version="2.0">
    <channel>
        <title>Some Blog Title</title>
        <link>http://someblog.example.com</link>
        <description>Description</description>
        <language>en-us</language>
        <pubDate>Tue, 25 Jan 2010 14:00:00 GMT</pubDate>
        <lastBuildDate>Tue, 25 Jan 2010 14:00:00 GMT</lastBuildDate>
        <docs>http://blogs.law.harvard.edu/tech/rss</docs>
        <generator>Some Generator</generator>
        <managingEditor>editor@example.com</managingEditor>
        <webMaster>webmaster@example.com</webMaster>
        <item>
            <title>Some Post Title</title>
            <link>http://someblog.example.com/post1</link>
            <description>Some Post Description</description>
            <pubDate>Tue, 25 Jan 2010 14:00:00 GMT</pubDate>
            <guid>Some Global Unique Identifier</guid>
        </item>
    </channel>
</rss>

With RssRenderer, this feed is generated this way:

r = rss.RssRenderer()
with r.rss(version="2.0"):
    with r.channel:
        r << r.title('Some Blog Title')
        r << r.link('http://someblog.example.com')
        r << r.description('Description')
        r << r.language('en-us')
        r << r.pubDate('Tue, 25 Jan 2010 14:00:00 GMT')
        r << r.lastBuildDate('Tue, 25 Jan 2010 14:00:00 GMT')
        r << r.docs('http://blogs.law.harvard.edu/tech/rss')
        r << r.generator('Some Generator')
        r << r.managingEditor('editor@example.com')
        r << r.webMaster('webmaster@example.com')
        with r.item:
            r << r.title('Some Post Title')
            r << r.link('http://someblog.example.com/post1')
            r << r.description('Some Post Description')
            r << r.pubDate('Tue, 25 Jan 2010 14:00:00 GMT')
            r << r.guid('Some Global Unique Identifier')

Now RSSRenderer can be used to generate any RSS feed.

Mixing multiple namespaces

There's several extensions to RSS, actually extensions are just XML namespaces added in the feed. One simple extension is blogChannel namespace.

First create a BlogChannelRenderer:

class BlogChannelRenderer(xml.XmlRenderer):
    blogRoll = TagProp('blogRoll', set())
    mySubscriptions = TagProp('mySubscriptions', set())
    blink = TagProp('blink', set())
    changes = TagProp('changes', set())

Now we can mix RssRenderer and BlogChannelRenderer to create one feed:

r = rss.RssRenderer()
r.namespaces = {'blogChannel': 'http://backend.userland.com/blogChannelModule'}
b = rss.BlogChannelRenderer(r)
b.default_namespace = 'blogChannel'
with r.rss(version="2.0"):
    with r.channel:
        r << r.title('Some Blog Title')
        r << r.link('http://someblog.example.com')
        r << r.description('Description')
        r << r.language('en-us')
        r << b.blogRoll('http://someblog.example.com/blogroll')
        r << b.mySubscriptions('http://someblog.example.com/subscriptions')
        r << b.blink('http://someblog.example.com/backlink')
        r << b.changes('http://someblog.example.com/changes')
        r << r.pubDate('Tue, 25 Jan 2010 14:00:00 GMT')
        r << r.lastBuildDate('Tue, 25 Jan 2010 14:00:00 GMT')
        r << r.docs('http://blogs.law.harvard.edu/tech/rss')
        r << r.generator('Some Generator')
        r << r.managingEditor('editor@example.com')
        r << r.webMaster('webmaster@example.com')
        with r.item:
            r << r.title('Some Post Title')
            r << r.link('http://someblog.example.com/post1')
            r << r.description('Some Post Description')
            r << r.pubDate('Tue, 25 Jan 2010 14:00:00 GMT')
            r << r.guid('Some Global Unique Identifier')

This produces the following RSS feed:

<rss xmlns:blogChannel="http://backend.userland.com/blogChannelModule" version="2.0">
  <channel>
    <title>Some Blog Title</title>
    <link>http://someblog.example.com</link>
    <description>Description</description>
    <language>en-us</language>
    <blogChannel:blogRoll>http://someblog.example.com/blogroll</blogChannel:blogRoll>
    <blogChannel:mySubscriptions>http://someblog.example.com/subscriptions</blogChannel:mySubscriptions>
    <blogChannel:blink>http://someblog.example.com/backlink</blogChannel:blink>
    <blogChannel:changes>http://someblog.example.com/changes</blogChannel:changes>
    <pubDate>Tue, 25 Jan 2010 14:00:00 GMT</pubDate>
    <lastBuildDate>Tue, 25 Jan 2010 14:00:00 GMT</lastBuildDate>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>Some Generator</generator>
    <managingEditor>editor@example.com</managingEditor>
    <webMaster>webmaster@example.com</webMaster>
    <item>
      <title>Some Post Title</title>
      <link>http://someblog.example.com/post1</link>
      <description>Some Post Description</description>
      <pubDate>Tue, 25 Jan 2010 14:00:00 GMT</pubDate>
      <guid>Some Global Unique Identifier</guid>
    </item>
  </channel>
</rss>

With this process any XML namespace can be added to Nagare. For example, Nagare XHTML namespace is implemented as a Renderer.

Nagare installation on Mac OS X (Snow Leopard)

Snow Leopard has been released this summer.

The 64-bits Mac OS X version is installed by default on new Mac. Currently, the use of Nagare with the 64-bits stackless Mac OS X version leads to a Bus Error .

To get around this, we need to install a 32-bits stackless python, thus changing slightly the install procedure for Mac OS X described previously .

Install MacPorts

Install instructions here.

Configure MacPorts

MacPorts has to use universal variant (64 + 32 bits compilation).

In order to do so, you add +universal into variants.conf file, i.e. for default macports installation:

echo +universal | sudo tee -a /opt/local/etc/macports/variants.conf

Install Stackless

Now we have to run configure for 32-bits architecture:

cd /tmp
wget http://www.stackless.com/binaries/stackless-264-export.tar.bz2
tar jxvf stackless-264-export.tar.bz2
cd stackless-2.6.4*
CC="gcc -arch i386" ./configure --enable-stacklessfewerregisters --prefix=<STACKLESS_HOME>
make all
sudo make install

Once Stackless Python installed, Nagare installation goes on as usual.