341 lines
7.9 KiB
Python
341 lines
7.9 KiB
Python
|
#!/usr/bin/env python
|
||
|
#
|
||
|
# Copyright (C) 2009 Google Inc.
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
|
||
|
|
||
|
# This module is used for version 2 of the Google Data APIs.
|
||
|
|
||
|
|
||
|
__author__ = 'j.s@google.com (Jeff Scudder)'
|
||
|
|
||
|
|
||
|
import atom.core
|
||
|
|
||
|
|
||
|
XML_TEMPLATE = '{http://www.w3.org/XML/1998/namespace}%s'
|
||
|
ATOM_TEMPLATE = '{http://www.w3.org/2005/Atom}%s'
|
||
|
APP_TEMPLATE_V1 = '{http://purl.org/atom/app#}%s'
|
||
|
APP_TEMPLATE_V2 = '{http://www.w3.org/2007/app}%s'
|
||
|
|
||
|
|
||
|
class Name(atom.core.XmlElement):
|
||
|
"""The atom:name element."""
|
||
|
_qname = ATOM_TEMPLATE % 'name'
|
||
|
|
||
|
|
||
|
class Email(atom.core.XmlElement):
|
||
|
"""The atom:email element."""
|
||
|
_qname = ATOM_TEMPLATE % 'email'
|
||
|
|
||
|
|
||
|
class Uri(atom.core.XmlElement):
|
||
|
"""The atom:uri element."""
|
||
|
_qname = ATOM_TEMPLATE % 'uri'
|
||
|
|
||
|
|
||
|
class Person(atom.core.XmlElement):
|
||
|
"""A foundation class which atom:author and atom:contributor extend.
|
||
|
|
||
|
A person contains information like name, email address, and web page URI for
|
||
|
an author or contributor to an Atom feed.
|
||
|
"""
|
||
|
name = Name
|
||
|
email = Email
|
||
|
uri = Uri
|
||
|
|
||
|
|
||
|
class Author(Person):
|
||
|
"""The atom:author element.
|
||
|
|
||
|
An author is a required element in Feed unless each Entry contains an Author.
|
||
|
"""
|
||
|
_qname = ATOM_TEMPLATE % 'author'
|
||
|
|
||
|
|
||
|
class Contributor(Person):
|
||
|
"""The atom:contributor element."""
|
||
|
_qname = ATOM_TEMPLATE % 'contributor'
|
||
|
|
||
|
|
||
|
class Link(atom.core.XmlElement):
|
||
|
"""The atom:link element."""
|
||
|
_qname = ATOM_TEMPLATE % 'link'
|
||
|
href = 'href'
|
||
|
rel = 'rel'
|
||
|
type = 'type'
|
||
|
hreflang = 'hreflang'
|
||
|
title = 'title'
|
||
|
length = 'length'
|
||
|
|
||
|
|
||
|
class Generator(atom.core.XmlElement):
|
||
|
"""The atom:generator element."""
|
||
|
_qname = ATOM_TEMPLATE % 'generator'
|
||
|
uri = 'uri'
|
||
|
version = 'version'
|
||
|
|
||
|
|
||
|
class Text(atom.core.XmlElement):
|
||
|
"""A foundation class from which atom:title, summary, etc. extend.
|
||
|
|
||
|
This class should never be instantiated.
|
||
|
"""
|
||
|
type = 'type'
|
||
|
|
||
|
|
||
|
class Title(Text):
|
||
|
"""The atom:title element."""
|
||
|
_qname = ATOM_TEMPLATE % 'title'
|
||
|
|
||
|
|
||
|
class Subtitle(Text):
|
||
|
"""The atom:subtitle element."""
|
||
|
_qname = ATOM_TEMPLATE % 'subtitle'
|
||
|
|
||
|
|
||
|
class Rights(Text):
|
||
|
"""The atom:rights element."""
|
||
|
_qname = ATOM_TEMPLATE % 'rights'
|
||
|
|
||
|
|
||
|
class Summary(Text):
|
||
|
"""The atom:summary element."""
|
||
|
_qname = ATOM_TEMPLATE % 'summary'
|
||
|
|
||
|
|
||
|
class Content(Text):
|
||
|
"""The atom:content element."""
|
||
|
_qname = ATOM_TEMPLATE % 'content'
|
||
|
src = 'src'
|
||
|
|
||
|
|
||
|
class Category(atom.core.XmlElement):
|
||
|
"""The atom:category element."""
|
||
|
_qname = ATOM_TEMPLATE % 'category'
|
||
|
term = 'term'
|
||
|
scheme = 'scheme'
|
||
|
label = 'label'
|
||
|
|
||
|
|
||
|
class Id(atom.core.XmlElement):
|
||
|
"""The atom:id element."""
|
||
|
_qname = ATOM_TEMPLATE % 'id'
|
||
|
|
||
|
|
||
|
class Icon(atom.core.XmlElement):
|
||
|
"""The atom:icon element."""
|
||
|
_qname = ATOM_TEMPLATE % 'icon'
|
||
|
|
||
|
|
||
|
class Logo(atom.core.XmlElement):
|
||
|
"""The atom:logo element."""
|
||
|
_qname = ATOM_TEMPLATE % 'logo'
|
||
|
|
||
|
|
||
|
class Draft(atom.core.XmlElement):
|
||
|
"""The app:draft element which indicates if this entry should be public."""
|
||
|
_qname = (APP_TEMPLATE_V1 % 'draft', APP_TEMPLATE_V2 % 'draft')
|
||
|
|
||
|
|
||
|
class Control(atom.core.XmlElement):
|
||
|
"""The app:control element indicating restrictions on publication.
|
||
|
|
||
|
The APP control element may contain a draft element indicating whether or
|
||
|
not this entry should be publicly available.
|
||
|
"""
|
||
|
_qname = (APP_TEMPLATE_V1 % 'control', APP_TEMPLATE_V2 % 'control')
|
||
|
draft = Draft
|
||
|
|
||
|
|
||
|
class Date(atom.core.XmlElement):
|
||
|
"""A parent class for atom:updated, published, etc."""
|
||
|
|
||
|
|
||
|
class Updated(Date):
|
||
|
"""The atom:updated element."""
|
||
|
_qname = ATOM_TEMPLATE % 'updated'
|
||
|
|
||
|
|
||
|
class Published(Date):
|
||
|
"""The atom:published element."""
|
||
|
_qname = ATOM_TEMPLATE % 'published'
|
||
|
|
||
|
|
||
|
class LinkFinder(object):
|
||
|
"""An "interface" providing methods to find link elements
|
||
|
|
||
|
Entry elements often contain multiple links which differ in the rel
|
||
|
attribute or content type. Often, developers are interested in a specific
|
||
|
type of link so this class provides methods to find specific classes of
|
||
|
links.
|
||
|
|
||
|
This class is used as a mixin in Atom entries and feeds.
|
||
|
"""
|
||
|
|
||
|
def find_url(self, rel):
|
||
|
"""Returns the URL in a link with the desired rel value."""
|
||
|
for link in self.link:
|
||
|
if link.rel == rel and link.href:
|
||
|
return link.href
|
||
|
return None
|
||
|
|
||
|
FindUrl = find_url
|
||
|
|
||
|
def get_link(self, rel):
|
||
|
"""Returns a link object which has the desired rel value.
|
||
|
|
||
|
If you are interested in the URL instead of the link object,
|
||
|
consider using find_url instead.
|
||
|
"""
|
||
|
for link in self.link:
|
||
|
if link.rel == rel and link.href:
|
||
|
return link
|
||
|
return None
|
||
|
|
||
|
GetLink = get_link
|
||
|
|
||
|
def find_self_link(self):
|
||
|
"""Find the first link with rel set to 'self'
|
||
|
|
||
|
Returns:
|
||
|
A str containing the link's href or None if none of the links had rel
|
||
|
equal to 'self'
|
||
|
"""
|
||
|
return self.find_url('self')
|
||
|
|
||
|
FindSelfLink = find_self_link
|
||
|
|
||
|
def get_self_link(self):
|
||
|
return self.get_link('self')
|
||
|
|
||
|
GetSelfLink = get_self_link
|
||
|
|
||
|
def find_edit_link(self):
|
||
|
return self.find_url('edit')
|
||
|
|
||
|
FindEditLink = find_edit_link
|
||
|
|
||
|
def get_edit_link(self):
|
||
|
return self.get_link('edit')
|
||
|
|
||
|
GetEditLink = get_edit_link
|
||
|
|
||
|
def find_edit_media_link(self):
|
||
|
link = self.find_url('edit-media')
|
||
|
# Search for media-edit as well since Picasa API used media-edit instead.
|
||
|
if link is None:
|
||
|
return self.find_url('media-edit')
|
||
|
return link
|
||
|
|
||
|
FindEditMediaLink = find_edit_media_link
|
||
|
|
||
|
def get_edit_media_link(self):
|
||
|
link = self.get_link('edit-media')
|
||
|
if link is None:
|
||
|
return self.get_link('media-edit')
|
||
|
return link
|
||
|
|
||
|
GetEditMediaLink = get_edit_media_link
|
||
|
|
||
|
def find_next_link(self):
|
||
|
return self.find_url('next')
|
||
|
|
||
|
FindNextLink = find_next_link
|
||
|
|
||
|
def get_next_link(self):
|
||
|
return self.get_link('next')
|
||
|
|
||
|
GetNextLink = get_next_link
|
||
|
|
||
|
def find_license_link(self):
|
||
|
return self.find_url('license')
|
||
|
|
||
|
FindLicenseLink = find_license_link
|
||
|
|
||
|
def get_license_link(self):
|
||
|
return self.get_link('license')
|
||
|
|
||
|
GetLicenseLink = get_license_link
|
||
|
|
||
|
def find_alternate_link(self):
|
||
|
return self.find_url('alternate')
|
||
|
|
||
|
FindAlternateLink = find_alternate_link
|
||
|
|
||
|
def get_alternate_link(self):
|
||
|
return self.get_link('alternate')
|
||
|
|
||
|
GetAlternateLink = get_alternate_link
|
||
|
|
||
|
|
||
|
class FeedEntryParent(atom.core.XmlElement, LinkFinder):
|
||
|
"""A super class for atom:feed and entry, contains shared attributes"""
|
||
|
author = [Author]
|
||
|
category = [Category]
|
||
|
contributor = [Contributor]
|
||
|
id = Id
|
||
|
link = [Link]
|
||
|
rights = Rights
|
||
|
title = Title
|
||
|
updated = Updated
|
||
|
|
||
|
def __init__(self, atom_id=None, text=None, *args, **kwargs):
|
||
|
if atom_id is not None:
|
||
|
self.id = atom_id
|
||
|
atom.core.XmlElement.__init__(self, text=text, *args, **kwargs)
|
||
|
|
||
|
|
||
|
class Source(FeedEntryParent):
|
||
|
"""The atom:source element."""
|
||
|
_qname = ATOM_TEMPLATE % 'source'
|
||
|
generator = Generator
|
||
|
icon = Icon
|
||
|
logo = Logo
|
||
|
subtitle = Subtitle
|
||
|
|
||
|
|
||
|
class Entry(FeedEntryParent):
|
||
|
"""The atom:entry element."""
|
||
|
_qname = ATOM_TEMPLATE % 'entry'
|
||
|
content = Content
|
||
|
published = Published
|
||
|
source = Source
|
||
|
summary = Summary
|
||
|
control = Control
|
||
|
|
||
|
|
||
|
class Feed(Source):
|
||
|
"""The atom:feed element which contains entries."""
|
||
|
_qname = ATOM_TEMPLATE % 'feed'
|
||
|
entry = [Entry]
|
||
|
|
||
|
|
||
|
class ExtensionElement(atom.core.XmlElement):
|
||
|
"""Provided for backwards compatibility to the v1 atom.ExtensionElement."""
|
||
|
|
||
|
def __init__(self, tag=None, namespace=None, attributes=None,
|
||
|
children=None, text=None, *args, **kwargs):
|
||
|
if namespace:
|
||
|
self._qname = '{%s}%s' % (namespace, tag)
|
||
|
else:
|
||
|
self._qname = tag
|
||
|
self.children = children or []
|
||
|
self.attributes = attributes or {}
|
||
|
self.text = text
|
||
|
|
||
|
_BecomeChildElement = atom.core.XmlElement._become_child
|
||
|
|
||
|
|