upgraded feedparer and PyRSS2Gen
This commit is contained in:
@@ -1 +1 @@
|
|||||||
Version 2.1.1 (2012-10-19 12:33:48) dev
|
Version 2.1.1 (2012-10-19 12:59:55) dev
|
||||||
|
|||||||
+704
-624
File diff suppressed because it is too large
Load Diff
+185
-251
@@ -1,58 +1,95 @@
|
|||||||
#!/usr/bin/env python
|
"""
|
||||||
# -*- coding: utf-8 -*-
|
PyRSS2Gen - A Python library for generating RSS 2.0 feeds.
|
||||||
|
|
||||||
"""PyRSS2Gen - A Python library for generating RSS 2.0 feeds."""
|
(This is the BSD license, based on the template at
|
||||||
|
http://www.opensource.org/licenses/bsd-license.php )
|
||||||
|
|
||||||
__name__ = 'PyRSS2Gen'
|
Copyright (c) 2003, Dalke Scientific Software, LLC
|
||||||
__version__ = (1, 0, 0)
|
|
||||||
__author__ = 'Andrew Dalke <dalke@dalkescientific.com>'
|
|
||||||
|
|
||||||
_generator_name = __name__ + '-' + '.'.join(map(str, __version__))
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
* Neither the name of the Dalke Scientific Softare, LLC, Andrew
|
||||||
|
Dalke, nor the names of its contributors may be used to endorse or
|
||||||
|
promote products derived from this software without specific prior
|
||||||
|
written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__name__ = "PyRSS2Gen"
|
||||||
|
__version__ = (1, 1, 0)
|
||||||
|
__author__ = "Andrew Dalke <dalke@dalkescientific.com>"
|
||||||
|
|
||||||
|
_generator_name = __name__ + "-" + ".".join(map(str, __version__))
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import cStringIO
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if sys.version_info[0] == 3:
|
||||||
|
# Python 3
|
||||||
|
basestring = str
|
||||||
|
from io import StringIO
|
||||||
|
else:
|
||||||
|
# Python 2
|
||||||
|
try:
|
||||||
|
from cStringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
# Very old (or memory constrained) systems might
|
||||||
|
# have left out the compiled C version. Fall back
|
||||||
|
# to the pure Python one. Haven't seen this sort
|
||||||
|
# of system since the early 2000s.
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
# Could make this the base class; will need to add 'publish'
|
# Could make this the base class; will need to add 'publish'
|
||||||
|
|
||||||
|
|
||||||
class WriteXmlMixin:
|
class WriteXmlMixin:
|
||||||
|
def write_xml(self, outfile, encoding="iso-8859-1"):
|
||||||
def write_xml(self, outfile, encoding='iso-8859-1'):
|
|
||||||
from xml.sax import saxutils
|
from xml.sax import saxutils
|
||||||
handler = saxutils.XMLGenerator(outfile, encoding)
|
handler = saxutils.XMLGenerator(outfile, encoding)
|
||||||
handler.startDocument()
|
handler.startDocument()
|
||||||
self.publish(handler)
|
self.publish(handler)
|
||||||
handler.endDocument()
|
handler.endDocument()
|
||||||
|
|
||||||
def to_xml(self, encoding='iso-8859-1'):
|
def to_xml(self, encoding="iso-8859-1"):
|
||||||
try:
|
f = StringIO()
|
||||||
import cStringIO as StringIO
|
|
||||||
except ImportError:
|
|
||||||
import StringIO
|
|
||||||
f = StringIO.StringIO()
|
|
||||||
self.write_xml(f, encoding)
|
self.write_xml(f, encoding)
|
||||||
return f.getvalue()
|
return f.getvalue()
|
||||||
|
|
||||||
|
|
||||||
def _element(
|
def _element(handler, name, obj, d={}):
|
||||||
handler,
|
|
||||||
name,
|
|
||||||
obj,
|
|
||||||
d={},
|
|
||||||
):
|
|
||||||
if isinstance(obj, basestring) or obj is None:
|
if isinstance(obj, basestring) or obj is None:
|
||||||
|
|
||||||
# special-case handling to make the API easier
|
# special-case handling to make the API easier
|
||||||
# to use for the common case.
|
# to use for the common case.
|
||||||
|
|
||||||
handler.startElement(name, d)
|
handler.startElement(name, d)
|
||||||
if obj is not None:
|
if obj is not None:
|
||||||
handler.characters(obj)
|
handler.characters(obj)
|
||||||
handler.endElement(name)
|
handler.endElement(name)
|
||||||
else:
|
else:
|
||||||
|
|
||||||
# It better know how to emit the correct XML.
|
# It better know how to emit the correct XML.
|
||||||
|
|
||||||
obj.publish(handler)
|
obj.publish(handler)
|
||||||
|
|
||||||
|
|
||||||
@@ -67,7 +104,6 @@ def _format_date(dt):
|
|||||||
|
|
||||||
Input date must be in GMT.
|
Input date must be in GMT.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Looks like:
|
# Looks like:
|
||||||
# Sat, 07 Sep 2002 00:00:01 GMT
|
# Sat, 07 Sep 2002 00:00:01 GMT
|
||||||
# Can't use strftime because that's locale dependent
|
# Can't use strftime because that's locale dependent
|
||||||
@@ -75,46 +111,18 @@ def _format_date(dt):
|
|||||||
# Isn't there a standard way to do this for Python? The
|
# Isn't there a standard way to do this for Python? The
|
||||||
# rfc822 and email.Utils modules assume a timestamp. The
|
# rfc822 and email.Utils modules assume a timestamp. The
|
||||||
# following is based on the rfc822 module.
|
# following is based on the rfc822 module.
|
||||||
|
return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
|
||||||
return '%s, %02d %s %04d %02d:%02d:%02d GMT' % (
|
["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][dt.weekday()],
|
||||||
[
|
|
||||||
'Mon',
|
|
||||||
'Tue',
|
|
||||||
'Wed',
|
|
||||||
'Thu',
|
|
||||||
'Fri',
|
|
||||||
'Sat',
|
|
||||||
'Sun',
|
|
||||||
][dt.weekday()],
|
|
||||||
dt.day,
|
dt.day,
|
||||||
[
|
["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||||
'Jan',
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][dt.month - 1],
|
||||||
'Feb',
|
dt.year, dt.hour, dt.minute, dt.second)
|
||||||
'Mar',
|
|
||||||
'Apr',
|
|
||||||
'May',
|
|
||||||
'Jun',
|
|
||||||
'Jul',
|
|
||||||
'Aug',
|
|
||||||
'Sep',
|
|
||||||
'Oct',
|
|
||||||
'Nov',
|
|
||||||
'Dec',
|
|
||||||
][dt.month - 1],
|
|
||||||
dt.year,
|
|
||||||
dt.hour,
|
|
||||||
dt.minute,
|
|
||||||
dt.second,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# A couple simple wrapper objects for the fields which
|
# A couple simple wrapper objects for the fields which
|
||||||
# take a simple value other than a string.
|
# take a simple value other than a string.
|
||||||
|
|
||||||
|
|
||||||
class IntElement:
|
class IntElement:
|
||||||
|
|
||||||
"""implements the 'publish' API for integers
|
"""implements the 'publish' API for integers
|
||||||
|
|
||||||
Takes the tag name and the integer value to publish.
|
Takes the tag name and the integer value to publish.
|
||||||
@@ -122,7 +130,6 @@ class IntElement:
|
|||||||
(Could be used for anything which uses str() to be published
|
(Could be used for anything which uses str() to be published
|
||||||
to text for XML.)
|
to text for XML.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
element_attrs = {}
|
element_attrs = {}
|
||||||
|
|
||||||
def __init__(self, name, val):
|
def __init__(self, name, val):
|
||||||
@@ -136,29 +143,23 @@ class IntElement:
|
|||||||
|
|
||||||
|
|
||||||
class DateElement:
|
class DateElement:
|
||||||
|
|
||||||
"""implements the 'publish' API for a datetime.datetime
|
"""implements the 'publish' API for a datetime.datetime
|
||||||
|
|
||||||
Takes the tag name and the datetime to publish.
|
Takes the tag name and the datetime to publish.
|
||||||
|
|
||||||
Converts the datetime to RFC 2822 timestamp (4-digit year).
|
Converts the datetime to RFC 2822 timestamp (4-digit year).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, dt):
|
def __init__(self, name, dt):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.dt = dt
|
self.dt = dt
|
||||||
|
|
||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
_element(handler, self.name, _format_date(self.dt))
|
_element(handler, self.name, _format_date(self.dt))
|
||||||
|
####
|
||||||
|
|
||||||
# ###
|
|
||||||
|
|
||||||
|
|
||||||
class Category:
|
class Category:
|
||||||
|
|
||||||
"""Publish a category element"""
|
"""Publish a category element"""
|
||||||
|
|
||||||
def __init__(self, category, domain=None):
|
def __init__(self, category, domain=None):
|
||||||
self.category = category
|
self.category = category
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
@@ -166,22 +167,14 @@ class Category:
|
|||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
d = {}
|
d = {}
|
||||||
if self.domain is not None:
|
if self.domain is not None:
|
||||||
d['domain'] = self.domain
|
d["domain"] = self.domain
|
||||||
_element(handler, 'category', self.category, d)
|
_element(handler, "category", self.category, d)
|
||||||
|
|
||||||
|
|
||||||
class Cloud:
|
class Cloud:
|
||||||
|
|
||||||
"""Publish a cloud"""
|
"""Publish a cloud"""
|
||||||
|
def __init__(self, domain, port, path,
|
||||||
def __init__(
|
registerProcedure, protocol):
|
||||||
self,
|
|
||||||
domain,
|
|
||||||
port,
|
|
||||||
path,
|
|
||||||
registerProcedure,
|
|
||||||
protocol,
|
|
||||||
):
|
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
self.port = port
|
self.port = port
|
||||||
self.path = path
|
self.path = path
|
||||||
@@ -189,30 +182,20 @@ class Cloud:
|
|||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
|
|
||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
_element(handler, 'cloud', None, {
|
_element(handler, "cloud", None, {
|
||||||
'domain': self.domain,
|
"domain": self.domain,
|
||||||
'port': str(self.port),
|
"port": str(self.port),
|
||||||
'path': self.path,
|
"path": self.path,
|
||||||
'registerProcedure': self.registerProcedure,
|
"registerProcedure": self.registerProcedure,
|
||||||
'protocol': self.protocol,
|
"protocol": self.protocol})
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class Image:
|
class Image:
|
||||||
|
|
||||||
"""Publish a channel Image"""
|
"""Publish a channel Image"""
|
||||||
|
|
||||||
element_attrs = {}
|
element_attrs = {}
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, url, title, link,
|
||||||
self,
|
width=None, height=None, description=None):
|
||||||
url,
|
|
||||||
title,
|
|
||||||
link,
|
|
||||||
width=None,
|
|
||||||
height=None,
|
|
||||||
description=None,
|
|
||||||
):
|
|
||||||
self.url = url
|
self.url = url
|
||||||
self.title = title
|
self.title = title
|
||||||
self.link = link
|
self.link = link
|
||||||
@@ -221,35 +204,33 @@ class Image:
|
|||||||
self.description = description
|
self.description = description
|
||||||
|
|
||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
handler.startElement('image', self.element_attrs)
|
handler.startElement("image", self.element_attrs)
|
||||||
|
|
||||||
_element(handler, 'url', self.url)
|
_element(handler, "url", self.url)
|
||||||
_element(handler, 'title', self.title)
|
_element(handler, "title", self.title)
|
||||||
_element(handler, 'link', self.link)
|
_element(handler, "link", self.link)
|
||||||
|
|
||||||
width = self.width
|
width = self.width
|
||||||
if isinstance(width, int):
|
if isinstance(width, int):
|
||||||
width = IntElement('width', width)
|
width = IntElement("width", width)
|
||||||
_opt_element(handler, 'width', width)
|
_opt_element(handler, "width", width)
|
||||||
|
|
||||||
height = self.height
|
height = self.height
|
||||||
if isinstance(height, int):
|
if isinstance(height, int):
|
||||||
height = IntElement('height', height)
|
height = IntElement("height", height)
|
||||||
_opt_element(handler, 'height', height)
|
_opt_element(handler, "height", height)
|
||||||
|
|
||||||
_opt_element(handler, 'description', self.description)
|
_opt_element(handler, "description", self.description)
|
||||||
|
|
||||||
handler.endElement('image')
|
handler.endElement("image")
|
||||||
|
|
||||||
|
|
||||||
class Guid:
|
class Guid:
|
||||||
|
|
||||||
"""Publish a guid
|
"""Publish a guid
|
||||||
|
|
||||||
Defaults to being a permalink, which is the assumption if it's
|
Defaults to being a permalink, which is the assumption if it's
|
||||||
omitted. Hence strings are always permalinks.
|
omitted. Hence strings are always permalinks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, guid, isPermaLink=1):
|
def __init__(self, guid, isPermaLink=1):
|
||||||
self.guid = guid
|
self.guid = guid
|
||||||
self.isPermaLink = isPermaLink
|
self.isPermaLink = isPermaLink
|
||||||
@@ -257,80 +238,64 @@ class Guid:
|
|||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
d = {}
|
d = {}
|
||||||
if self.isPermaLink:
|
if self.isPermaLink:
|
||||||
d['isPermaLink'] = 'true'
|
d["isPermaLink"] = "true"
|
||||||
else:
|
else:
|
||||||
d['isPermaLink'] = 'false'
|
d["isPermaLink"] = "false"
|
||||||
_element(handler, 'guid', self.guid, d)
|
_element(handler, "guid", self.guid, d)
|
||||||
|
|
||||||
|
|
||||||
class TextInput:
|
class TextInput:
|
||||||
|
|
||||||
"""Publish a textInput
|
"""Publish a textInput
|
||||||
|
|
||||||
Apparently this is rarely used.
|
Apparently this is rarely used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
element_attrs = {}
|
element_attrs = {}
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, title, description, name, link):
|
||||||
self,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
name,
|
|
||||||
link,
|
|
||||||
):
|
|
||||||
self.title = title
|
self.title = title
|
||||||
self.description = description
|
self.description = description
|
||||||
self.name = name
|
self.name = name
|
||||||
self.link = link
|
self.link = link
|
||||||
|
|
||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
handler.startElement('textInput', self.element_attrs)
|
handler.startElement("textInput", self.element_attrs)
|
||||||
_element(handler, 'title', self.title)
|
_element(handler, "title", self.title)
|
||||||
_element(handler, 'description', self.description)
|
_element(handler, "description", self.description)
|
||||||
_element(handler, 'name', self.name)
|
_element(handler, "name", self.name)
|
||||||
_element(handler, 'link', self.link)
|
_element(handler, "link", self.link)
|
||||||
handler.endElement('textInput')
|
handler.endElement("textInput")
|
||||||
|
|
||||||
|
|
||||||
class Enclosure:
|
class Enclosure:
|
||||||
|
|
||||||
"""Publish an enclosure"""
|
"""Publish an enclosure"""
|
||||||
|
def __init__(self, url, length, type):
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
url,
|
|
||||||
length,
|
|
||||||
type,
|
|
||||||
):
|
|
||||||
self.url = url
|
self.url = url
|
||||||
self.length = length
|
self.length = length
|
||||||
self.type = type
|
self.type = type
|
||||||
|
|
||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
_element(handler, 'enclosure', None,
|
_element(handler, "enclosure", None,
|
||||||
{'url': self.url, 'length': str(self.length), 'type': self.type})
|
{"url": self.url,
|
||||||
|
"length": str(self.length),
|
||||||
|
"type": self.type,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class Source:
|
class Source:
|
||||||
|
|
||||||
"""Publish the item's original source, used by aggregators"""
|
"""Publish the item's original source, used by aggregators"""
|
||||||
|
|
||||||
def __init__(self, name, url):
|
def __init__(self, name, url):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
_element(handler, 'source', self.name, {'url': self.url})
|
_element(handler, "source", self.name, {"url": self.url})
|
||||||
|
|
||||||
|
|
||||||
class SkipHours:
|
class SkipHours:
|
||||||
|
|
||||||
"""Publish the skipHours
|
"""Publish the skipHours
|
||||||
|
|
||||||
This takes a list of hours, as integers.
|
This takes a list of hours, as integers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
element_attrs = {}
|
element_attrs = {}
|
||||||
|
|
||||||
def __init__(self, hours):
|
def __init__(self, hours):
|
||||||
@@ -338,19 +303,17 @@ class SkipHours:
|
|||||||
|
|
||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
if self.hours:
|
if self.hours:
|
||||||
handler.startElement('skipHours', self.element_attrs)
|
handler.startElement("skipHours", self.element_attrs)
|
||||||
for hour in self.hours:
|
for hour in self.hours:
|
||||||
_element(handler, 'hour', str(hour))
|
_element(handler, "hour", str(hour))
|
||||||
handler.endElement('skipHours')
|
handler.endElement("skipHours")
|
||||||
|
|
||||||
|
|
||||||
class SkipDays:
|
class SkipDays:
|
||||||
|
|
||||||
"""Publish the skipDays
|
"""Publish the skipDays
|
||||||
|
|
||||||
This takes a list of days as strings.
|
This takes a list of days as strings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
element_attrs = {}
|
element_attrs = {}
|
||||||
|
|
||||||
def __init__(self, days):
|
def __init__(self, days):
|
||||||
@@ -358,47 +321,48 @@ class SkipDays:
|
|||||||
|
|
||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
if self.days:
|
if self.days:
|
||||||
handler.startElement('skipDays', self.element_attrs)
|
handler.startElement("skipDays", self.element_attrs)
|
||||||
for day in self.days:
|
for day in self.days:
|
||||||
_element(handler, 'day', day)
|
_element(handler, "day", day)
|
||||||
handler.endElement('skipDays')
|
handler.endElement("skipDays")
|
||||||
|
|
||||||
|
|
||||||
class RSS2(WriteXmlMixin):
|
class RSS2(WriteXmlMixin):
|
||||||
|
|
||||||
"""The main RSS class.
|
"""The main RSS class.
|
||||||
|
|
||||||
Stores the channel attributes, with the \"category\" elements under
|
Stores the channel attributes, with the "category" elements under
|
||||||
\".categories\" and the RSS items under \".items\".
|
".categories" and the RSS items under ".items".
|
||||||
"""
|
"""
|
||||||
|
|
||||||
rss_attrs = {'version': '2.0'}
|
rss_attrs = {"version": "2.0"}
|
||||||
element_attrs = {}
|
element_attrs = {}
|
||||||
|
|
||||||
def __init__(
|
def __init__(self,
|
||||||
self,
|
title,
|
||||||
title,
|
link,
|
||||||
link,
|
description,
|
||||||
description,
|
|
||||||
language=None,
|
|
||||||
copyright=None,
|
|
||||||
managingEditor=None,
|
|
||||||
webMaster=None,
|
|
||||||
pubDate=None,
|
|
||||||
lastBuildDate=None,
|
|
||||||
categories=None,
|
|
||||||
generator=_generator_name,
|
|
||||||
docs='http://blogs.law.harvard.edu/tech/rss',
|
|
||||||
cloud=None,
|
|
||||||
ttl=None,
|
|
||||||
image=None,
|
|
||||||
rating=None,
|
|
||||||
textInput=None,
|
|
||||||
skipHours=None,
|
|
||||||
skipDays=None,
|
|
||||||
items=None,
|
|
||||||
):
|
|
||||||
|
|
||||||
|
language=None,
|
||||||
|
copyright=None,
|
||||||
|
managingEditor=None,
|
||||||
|
webMaster=None,
|
||||||
|
pubDate=None, # a datetime, *in* *GMT*
|
||||||
|
lastBuildDate=None, # a datetime
|
||||||
|
|
||||||
|
categories=None, # list of strings or Category
|
||||||
|
generator=_generator_name,
|
||||||
|
docs="http://blogs.law.harvard.edu/tech/rss",
|
||||||
|
cloud=None, # a Cloud
|
||||||
|
ttl=None, # integer number of minutes
|
||||||
|
|
||||||
|
image=None, # an Image
|
||||||
|
rating=None, # a string; I don't know how it's used
|
||||||
|
textInput=None, # a TextInput
|
||||||
|
skipHours=None, # a SkipHours with a list of integers
|
||||||
|
skipDays=None, # a SkipDays with a list of strings
|
||||||
|
|
||||||
|
items=None, # list of RSSItems
|
||||||
|
):
|
||||||
self.title = title
|
self.title = title
|
||||||
self.link = link
|
self.link = link
|
||||||
self.description = description
|
self.description = description
|
||||||
@@ -428,49 +392,49 @@ class RSS2(WriteXmlMixin):
|
|||||||
self.items = items
|
self.items = items
|
||||||
|
|
||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
handler.startElement('rss', self.rss_attrs)
|
handler.startElement("rss", self.rss_attrs)
|
||||||
handler.startElement('channel', self.element_attrs)
|
handler.startElement("channel", self.element_attrs)
|
||||||
_element(handler, 'title', self.title)
|
_element(handler, "title", self.title)
|
||||||
_element(handler, 'link', self.link)
|
_element(handler, "link", self.link)
|
||||||
_element(handler, 'description', self.description)
|
_element(handler, "description", self.description)
|
||||||
|
|
||||||
self.publish_extensions(handler)
|
self.publish_extensions(handler)
|
||||||
|
|
||||||
_opt_element(handler, 'language', self.language)
|
_opt_element(handler, "language", self.language)
|
||||||
_opt_element(handler, 'copyright', self.copyright)
|
_opt_element(handler, "copyright", self.copyright)
|
||||||
_opt_element(handler, 'managingEditor', self.managingEditor)
|
_opt_element(handler, "managingEditor", self.managingEditor)
|
||||||
_opt_element(handler, 'webMaster', self.webMaster)
|
_opt_element(handler, "webMaster", self.webMaster)
|
||||||
|
|
||||||
pubDate = self.pubDate
|
pubDate = self.pubDate
|
||||||
if isinstance(pubDate, datetime.datetime):
|
if isinstance(pubDate, datetime.datetime):
|
||||||
pubDate = DateElement('pubDate', pubDate)
|
pubDate = DateElement("pubDate", pubDate)
|
||||||
_opt_element(handler, 'pubDate', pubDate)
|
_opt_element(handler, "pubDate", pubDate)
|
||||||
|
|
||||||
lastBuildDate = self.lastBuildDate
|
lastBuildDate = self.lastBuildDate
|
||||||
if isinstance(lastBuildDate, datetime.datetime):
|
if isinstance(lastBuildDate, datetime.datetime):
|
||||||
lastBuildDate = DateElement('lastBuildDate', lastBuildDate)
|
lastBuildDate = DateElement("lastBuildDate", lastBuildDate)
|
||||||
_opt_element(handler, 'lastBuildDate', lastBuildDate)
|
_opt_element(handler, "lastBuildDate", lastBuildDate)
|
||||||
|
|
||||||
for category in self.categories:
|
for category in self.categories:
|
||||||
if isinstance(category, basestring):
|
if isinstance(category, basestring):
|
||||||
category = Category(category)
|
category = Category(category)
|
||||||
category.publish(handler)
|
category.publish(handler)
|
||||||
|
|
||||||
_opt_element(handler, 'generator', self.generator)
|
_opt_element(handler, "generator", self.generator)
|
||||||
_opt_element(handler, 'docs', self.docs)
|
_opt_element(handler, "docs", self.docs)
|
||||||
|
|
||||||
if self.cloud is not None:
|
if self.cloud is not None:
|
||||||
self.cloud.publish(handler)
|
self.cloud.publish(handler)
|
||||||
|
|
||||||
ttl = self.ttl
|
ttl = self.ttl
|
||||||
if isinstance(self.ttl, int):
|
if isinstance(self.ttl, int):
|
||||||
ttl = IntElement('ttl', ttl)
|
ttl = IntElement("ttl", ttl)
|
||||||
_opt_element(handler, 'tt', ttl)
|
_opt_element(handler, "ttl", ttl)
|
||||||
|
|
||||||
if self.image is not None:
|
if self.image is not None:
|
||||||
self.image.publish(handler)
|
self.image.publish(handler)
|
||||||
|
|
||||||
_opt_element(handler, 'rating', self.rating)
|
_opt_element(handler, "rating", self.rating)
|
||||||
if self.textInput is not None:
|
if self.textInput is not None:
|
||||||
self.textInput.publish(handler)
|
self.textInput.publish(handler)
|
||||||
if self.skipHours is not None:
|
if self.skipHours is not None:
|
||||||
@@ -481,40 +445,35 @@ class RSS2(WriteXmlMixin):
|
|||||||
for item in self.items:
|
for item in self.items:
|
||||||
item.publish(handler)
|
item.publish(handler)
|
||||||
|
|
||||||
handler.endElement('channel')
|
handler.endElement("channel")
|
||||||
handler.endElement('rss')
|
handler.endElement("rss")
|
||||||
|
|
||||||
def publish_extensions(self, handler):
|
def publish_extensions(self, handler):
|
||||||
|
|
||||||
# Derived classes can hook into this to insert
|
# Derived classes can hook into this to insert
|
||||||
# output after the three required fields.
|
# output after the three required fields.
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RSSItem(WriteXmlMixin):
|
class RSSItem(WriteXmlMixin):
|
||||||
|
|
||||||
"""Publish an RSS Item"""
|
"""Publish an RSS Item"""
|
||||||
|
|
||||||
element_attrs = {}
|
element_attrs = {}
|
||||||
|
|
||||||
def __init__(
|
def __init__(self,
|
||||||
self,
|
title=None, # string
|
||||||
title=None,
|
link=None, # url as string
|
||||||
link=None,
|
description=None, # string
|
||||||
description=None,
|
author=None, # email address as string
|
||||||
author=None,
|
categories=None, # list of string or Category
|
||||||
categories=None,
|
comments=None, # url as string
|
||||||
comments=None,
|
enclosure=None, # an Enclosure
|
||||||
enclosure=None,
|
guid=None, # a unique string
|
||||||
guid=None,
|
pubDate=None, # a datetime
|
||||||
pubDate=None,
|
source=None, # a Source
|
||||||
source=None,
|
):
|
||||||
):
|
|
||||||
|
|
||||||
if title is None and description is None:
|
if title is None and description is None:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"RSSItem must define at least one of 'title' or 'description'")
|
"must define at least one of 'title' or 'description'")
|
||||||
self.title = title
|
self.title = title
|
||||||
self.link = link
|
self.link = link
|
||||||
self.description = description
|
self.description = description
|
||||||
@@ -527,62 +486,37 @@ class RSSItem(WriteXmlMixin):
|
|||||||
self.guid = guid
|
self.guid = guid
|
||||||
self.pubDate = pubDate
|
self.pubDate = pubDate
|
||||||
self.source = source
|
self.source = source
|
||||||
|
|
||||||
# It sure does get tedious typing these names three times...
|
# It sure does get tedious typing these names three times...
|
||||||
|
|
||||||
def publish(self, handler):
|
def publish(self, handler):
|
||||||
handler.startElement('item', self.element_attrs)
|
handler.startElement("item", self.element_attrs)
|
||||||
_opt_element(handler, 'title', self.title)
|
_opt_element(handler, "title", self.title)
|
||||||
_opt_element(handler, 'link', self.link)
|
_opt_element(handler, "link", self.link)
|
||||||
self.publish_extensions(handler)
|
self.publish_extensions(handler)
|
||||||
_opt_element(handler, 'description', self.description)
|
_opt_element(handler, "description", self.description)
|
||||||
_opt_element(handler, 'author', self.author)
|
_opt_element(handler, "author", self.author)
|
||||||
|
|
||||||
for category in self.categories:
|
for category in self.categories:
|
||||||
if isinstance(category, basestring):
|
if isinstance(category, basestring):
|
||||||
category = Category(category)
|
category = Category(category)
|
||||||
category.publish(handler)
|
category.publish(handler)
|
||||||
|
|
||||||
_opt_element(handler, 'comments', self.comments)
|
_opt_element(handler, "comments", self.comments)
|
||||||
if self.enclosure is not None:
|
if self.enclosure is not None:
|
||||||
self.enclosure.publish(handler)
|
self.enclosure.publish(handler)
|
||||||
_opt_element(handler, 'guid', self.guid)
|
_opt_element(handler, "guid", self.guid)
|
||||||
|
|
||||||
pubDate = self.pubDate
|
pubDate = self.pubDate
|
||||||
if isinstance(pubDate, datetime.datetime):
|
if isinstance(pubDate, datetime.datetime):
|
||||||
pubDate = DateElement('pubDate', pubDate)
|
pubDate = DateElement("pubDate", pubDate)
|
||||||
_opt_element(handler, 'pubDate', pubDate)
|
_opt_element(handler, "pubDate", pubDate)
|
||||||
|
|
||||||
if self.source is not None:
|
if self.source is not None:
|
||||||
self.source.publish(handler)
|
self.source.publish(handler)
|
||||||
|
|
||||||
handler.endElement('item')
|
handler.endElement("item")
|
||||||
|
|
||||||
def publish_extensions(self, handler):
|
def publish_extensions(self, handler):
|
||||||
|
|
||||||
# Derived classes can hook into this to insert
|
# Derived classes can hook into this to insert
|
||||||
# output after the title and link elements
|
# output after the title and link elements
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def dumps(rss, encoding='utf-8'):
|
|
||||||
s = cStringIO.StringIO()
|
|
||||||
rss.write_xml(s, encoding)
|
|
||||||
return s.getvalue()
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
rss = RSS2(title='web2py feed', link='http://www.web2py.com',
|
|
||||||
description='About web2py',
|
|
||||||
lastBuildDate=datetime.datetime.now(),
|
|
||||||
items=[RSSItem(title='web2py and PyRSS2Gen-0.0',
|
|
||||||
link='http://www.web2py.com/examples/simple_examples/getrss',
|
|
||||||
description='web2py can now make rss feeds!',
|
|
||||||
guid=Guid('http://www.web2py.com/'),
|
|
||||||
pubDate=datetime.datetime(2007, 11, 14, 10, 30))])
|
|
||||||
return dumps(rss)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print test()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user