Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit

  • Loading branch information...
commit ef710cc4dec3a23f6f1a22855ecac4aded665483 0 parents
@progrium progrium authored
Showing with 28,117 additions and 0 deletions.
  1. +8 −0 app.yaml
  2. +1,484 −0 atom/__init__.py
  3. +43 −0 atom/auth.py
  4. +182 −0 atom/client.py
  5. +534 −0 atom/core.py
  6. +339 −0 atom/data.py
  7. +332 −0 atom/http.py
  8. +573 −0 atom/http_core.py
  9. +158 −0 atom/http_interface.py
  10. +132 −0 atom/mock_http.py
  11. +323 −0 atom/mock_http_core.py
  12. +243 −0 atom/mock_service.py
  13. +740 −0 atom/service.py
  14. +117 −0 atom/token_store.py
  15. +139 −0 atom/url.py
  16. +33 −0 gdata/Crypto/Cipher/__init__.py
  17. +108 −0 gdata/Crypto/Hash/HMAC.py
  18. +13 −0 gdata/Crypto/Hash/MD5.py
  19. +11 −0 gdata/Crypto/Hash/SHA.py
  20. +24 −0 gdata/Crypto/Hash/__init__.py
  21. +295 −0 gdata/Crypto/Protocol/AllOrNothing.py
  22. +229 −0 gdata/Crypto/Protocol/Chaffing.py
  23. +17 −0 gdata/Crypto/Protocol/__init__.py
  24. +238 −0 gdata/Crypto/PublicKey/DSA.py
  25. +132 −0 gdata/Crypto/PublicKey/ElGamal.py
  26. +256 −0 gdata/Crypto/PublicKey/RSA.py
  27. +17 −0 gdata/Crypto/PublicKey/__init__.py
  28. +172 −0 gdata/Crypto/PublicKey/pubkey.py
  29. +170 −0 gdata/Crypto/PublicKey/qNEW.py
  30. +342 −0 gdata/Crypto/Util/RFC1751.py
  31. +16 −0 gdata/Crypto/Util/__init__.py
  32. +201 −0 gdata/Crypto/Util/number.py
  33. +421 −0 gdata/Crypto/Util/randpool.py
  34. +453 −0 gdata/Crypto/Util/test.py
  35. +25 −0 gdata/Crypto/__init__.py
  36. +38 −0 gdata/Crypto/test.py
  37. +835 −0 gdata/__init__.py
  38. +15 −0 gdata/acl/__init__.py
  39. +55 −0 gdata/acl/data.py
  40. +20 −0 gdata/alt/__init__.py
  41. +101 −0 gdata/alt/app_engine.py
  42. +311 −0 gdata/alt/appengine.py
  43. +223 −0 gdata/analytics/__init__.py
  44. +129 −0 gdata/analytics/client.py
  45. +273 −0 gdata/analytics/data.py
  46. +331 −0 gdata/analytics/service.py
  47. +526 −0 gdata/apps/__init__.py
  48. +16 −0 gdata/apps/adminsettings/__init__.py
  49. +476 −0 gdata/apps/adminsettings/service.py
  50. +15 −0 gdata/apps/emailsettings/__init__.py
  51. +269 −0 gdata/apps/emailsettings/service.py
  52. 0  gdata/apps/groups/__init__.py
  53. +310 −0 gdata/apps/groups/service.py
  54. +212 −0 gdata/apps/migration/__init__.py
  55. +129 −0 gdata/apps/migration/service.py
  56. +553 −0 gdata/apps/service.py
  57. +952 −0 gdata/auth.py
  58. +687 −0 gdata/base/__init__.py
  59. +256 −0 gdata/base/service.py
  60. +202 −0 gdata/blogger/__init__.py
  61. +153 −0 gdata/blogger/client.py
  62. +146 −0 gdata/blogger/data.py
  63. +142 −0 gdata/blogger/service.py
  64. +473 −0 gdata/books/__init__.py
  65. +90 −0 gdata/books/data.py
  66. +266 −0 gdata/books/service.py
  67. +1,044 −0 gdata/calendar/__init__.py
  68. +300 −0 gdata/calendar/data.py
  69. +595 −0 gdata/calendar/service.py
  70. +821 −0 gdata/client.py
  71. +136 −0 gdata/codesearch/__init__.py
  72. +109 −0 gdata/codesearch/service.py
  73. +741 −0 gdata/contacts/__init__.py
  74. +468 −0 gdata/contacts/client.py
  75. +474 −0 gdata/contacts/data.py
  76. +427 −0 gdata/contacts/service.py
  77. +1,172 −0 gdata/data.py
  78. +269 −0 gdata/docs/__init__.py
  79. +579 −0 gdata/docs/client.py
  80. +237 −0 gdata/docs/data.py
  81. +611 −0 gdata/docs/service.py
  82. +15 −0 gdata/dublincore/__init__.py
  83. +78 −0 gdata/dublincore/data.py
  84. +217 −0 gdata/exif/__init__.py
  85. +486 −0 gdata/finance/__init__.py
  86. +156 −0 gdata/finance/data.py
  87. +243 −0 gdata/finance/service.py
  88. +1,143 −0 gdata/gauth.py
  89. +185 −0 gdata/geo/__init__.py
  90. +92 −0 gdata/geo/data.py
  91. +229 −0 gdata/health/__init__.py
  92. +263 −0 gdata/health/service.py
  93. 0  gdata/maps/__init__.py
  94. +178 −0 gdata/maps/client.py
  95. +125 −0 gdata/maps/data.py
Sorry, we could not display the entire diff because it was too big.
8 app.yaml
@@ -0,0 +1,8 @@
+application: hd-wifi
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: .*
+ script: main.py
1,484 atom/__init__.py
@@ -0,0 +1,1484 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2006 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.
+
+
+"""Contains classes representing Atom elements.
+
+ Module objective: provide data classes for Atom constructs. These classes hide
+ the XML-ness of Atom and provide a set of native Python classes to interact
+ with.
+
+ Conversions to and from XML should only be necessary when the Atom classes
+ "touch the wire" and are sent over HTTP. For this reason this module
+ provides methods and functions to convert Atom classes to and from strings.
+
+ For more information on the Atom data model, see RFC 4287
+ (http://www.ietf.org/rfc/rfc4287.txt)
+
+ AtomBase: A foundation class on which Atom classes are built. It
+ handles the parsing of attributes and children which are common to all
+ Atom classes. By default, the AtomBase class translates all XML child
+ nodes into ExtensionElements.
+
+ ExtensionElement: Atom allows Atom objects to contain XML which is not part
+ of the Atom specification, these are called extension elements. If a
+ classes parser encounters an unexpected XML construct, it is translated
+ into an ExtensionElement instance. ExtensionElement is designed to fully
+ capture the information in the XML. Child nodes in an XML extension are
+ turned into ExtensionElements as well.
+"""
+
+
+__author__ = 'api.jscudder (Jeffrey Scudder)'
+
+try:
+ from xml.etree import cElementTree as ElementTree
+except ImportError:
+ try:
+ import cElementTree as ElementTree
+ except ImportError:
+ try:
+ from xml.etree import ElementTree
+ except ImportError:
+ from elementtree import ElementTree
+import warnings
+
+
+# XML namespaces which are often used in Atom entities.
+ATOM_NAMESPACE = 'http://www.w3.org/2005/Atom'
+ELEMENT_TEMPLATE = '{http://www.w3.org/2005/Atom}%s'
+APP_NAMESPACE = 'http://purl.org/atom/app#'
+APP_TEMPLATE = '{http://purl.org/atom/app#}%s'
+
+# This encoding is used for converting strings before translating the XML
+# into an object.
+XML_STRING_ENCODING = 'utf-8'
+# The desired string encoding for object members. set or monkey-patch to
+# unicode if you want object members to be Python unicode strings, instead of
+# encoded strings
+MEMBER_STRING_ENCODING = 'utf-8'
+#MEMBER_STRING_ENCODING = unicode
+
+# If True, all methods which are exclusive to v1 will raise a
+# DeprecationWarning
+ENABLE_V1_WARNINGS = False
+
+
+def v1_deprecated(warning=None):
+ """Shows a warning if ENABLE_V1_WARNINGS is True.
+
+ Function decorator used to mark methods used in v1 classes which
+ may be removed in future versions of the library.
+ """
+ warning = warning or ''
+ # This closure is what is returned from the deprecated function.
+ def mark_deprecated(f):
+ # The deprecated_function wraps the actual call to f.
+ def optional_warn_function(*args, **kwargs):
+ if ENABLE_V1_WARNINGS:
+ warnings.warn(warning, DeprecationWarning, stacklevel=2)
+ return f(*args, **kwargs)
+ # Preserve the original name to avoid masking all decorated functions as
+ # 'deprecated_function'
+ try:
+ optional_warn_function.func_name = f.func_name
+ except TypeError:
+ pass # In Python2.3 we can't set the func_name
+ return optional_warn_function
+ return mark_deprecated
+
+
+def CreateClassFromXMLString(target_class, xml_string, string_encoding=None):
+ """Creates an instance of the target class from the string contents.
+
+ Args:
+ target_class: class The class which will be instantiated and populated
+ with the contents of the XML. This class must have a _tag and a
+ _namespace class variable.
+ xml_string: str A string which contains valid XML. The root element
+ of the XML string should match the tag and namespace of the desired
+ class.
+ string_encoding: str The character encoding which the xml_string should
+ be converted to before it is interpreted and translated into
+ objects. The default is None in which case the string encoding
+ is not changed.
+
+ Returns:
+ An instance of the target class with members assigned according to the
+ contents of the XML - or None if the root XML tag and namespace did not
+ match those of the target class.
+ """
+ encoding = string_encoding or XML_STRING_ENCODING
+ if encoding and isinstance(xml_string, unicode):
+ xml_string = xml_string.encode(encoding)
+ tree = ElementTree.fromstring(xml_string)
+ return _CreateClassFromElementTree(target_class, tree)
+
+
+CreateClassFromXMLString = v1_deprecated(
+ 'Please use atom.core.parse with atom.data classes instead.')(
+ CreateClassFromXMLString)
+
+
+def _CreateClassFromElementTree(target_class, tree, namespace=None, tag=None):
+ """Instantiates the class and populates members according to the tree.
+
+ Note: Only use this function with classes that have _namespace and _tag
+ class members.
+
+ Args:
+ target_class: class The class which will be instantiated and populated
+ with the contents of the XML.
+ tree: ElementTree An element tree whose contents will be converted into
+ members of the new target_class instance.
+ namespace: str (optional) The namespace which the XML tree's root node must
+ match. If omitted, the namespace defaults to the _namespace of the
+ target class.
+ tag: str (optional) The tag which the XML tree's root node must match. If
+ omitted, the tag defaults to the _tag class member of the target
+ class.
+
+ Returns:
+ An instance of the target class - or None if the tag and namespace of
+ the XML tree's root node did not match the desired namespace and tag.
+ """
+ if namespace is None:
+ namespace = target_class._namespace
+ if tag is None:
+ tag = target_class._tag
+ if tree.tag == '{%s}%s' % (namespace, tag):
+ target = target_class()
+ target._HarvestElementTree(tree)
+ return target
+ else:
+ return None
+
+
+class ExtensionContainer(object):
+
+ def __init__(self, extension_elements=None, extension_attributes=None,
+ text=None):
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+ self.text = text
+
+ __init__ = v1_deprecated(
+ 'Please use data model classes in atom.data instead.')(
+ __init__)
+
+ # Three methods to create an object from an ElementTree
+ def _HarvestElementTree(self, tree):
+ # Fill in the instance members from the contents of the XML tree.
+ for child in tree:
+ self._ConvertElementTreeToMember(child)
+ for attribute, value in tree.attrib.iteritems():
+ self._ConvertElementAttributeToMember(attribute, value)
+ # Encode the text string according to the desired encoding type. (UTF-8)
+ if tree.text:
+ if MEMBER_STRING_ENCODING is unicode:
+ self.text = tree.text
+ else:
+ self.text = tree.text.encode(MEMBER_STRING_ENCODING)
+
+ def _ConvertElementTreeToMember(self, child_tree, current_class=None):
+ self.extension_elements.append(_ExtensionElementFromElementTree(
+ child_tree))
+
+ def _ConvertElementAttributeToMember(self, attribute, value):
+ # Encode the attribute value's string with the desired type Default UTF-8
+ if value:
+ if MEMBER_STRING_ENCODING is unicode:
+ self.extension_attributes[attribute] = value
+ else:
+ self.extension_attributes[attribute] = value.encode(
+ MEMBER_STRING_ENCODING)
+
+ # One method to create an ElementTree from an object
+ def _AddMembersToElementTree(self, tree):
+ for child in self.extension_elements:
+ child._BecomeChildElement(tree)
+ for attribute, value in self.extension_attributes.iteritems():
+ if value:
+ if isinstance(value, unicode) or MEMBER_STRING_ENCODING is unicode:
+ tree.attrib[attribute] = value
+ else:
+ # Decode the value from the desired encoding (default UTF-8).
+ tree.attrib[attribute] = value.decode(MEMBER_STRING_ENCODING)
+ if self.text:
+ if isinstance(self.text, unicode) or MEMBER_STRING_ENCODING is unicode:
+ tree.text = self.text
+ else:
+ tree.text = self.text.decode(MEMBER_STRING_ENCODING)
+
+ def FindExtensions(self, tag=None, namespace=None):
+ """Searches extension elements for child nodes with the desired name.
+
+ Returns a list of extension elements within this object whose tag
+ and/or namespace match those passed in. To find all extensions in
+ a particular namespace, specify the namespace but not the tag name.
+ If you specify only the tag, the result list may contain extension
+ elements in multiple namespaces.
+
+ Args:
+ tag: str (optional) The desired tag
+ namespace: str (optional) The desired namespace
+
+ Returns:
+ A list of elements whose tag and/or namespace match the parameters
+ values
+ """
+
+ results = []
+
+ if tag and namespace:
+ for element in self.extension_elements:
+ if element.tag == tag and element.namespace == namespace:
+ results.append(element)
+ elif tag and not namespace:
+ for element in self.extension_elements:
+ if element.tag == tag:
+ results.append(element)
+ elif namespace and not tag:
+ for element in self.extension_elements:
+ if element.namespace == namespace:
+ results.append(element)
+ else:
+ for element in self.extension_elements:
+ results.append(element)
+
+ return results
+
+
+class AtomBase(ExtensionContainer):
+
+ _children = {}
+ _attributes = {}
+
+ def __init__(self, extension_elements=None, extension_attributes=None,
+ text=None):
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+ self.text = text
+
+ __init__ = v1_deprecated(
+ 'Please use data model classes in atom.data instead.')(
+ __init__)
+
+ def _ConvertElementTreeToMember(self, child_tree):
+ # Find the element's tag in this class's list of child members
+ if self.__class__._children.has_key(child_tree.tag):
+ member_name = self.__class__._children[child_tree.tag][0]
+ member_class = self.__class__._children[child_tree.tag][1]
+ # If the class member is supposed to contain a list, make sure the
+ # matching member is set to a list, then append the new member
+ # instance to the list.
+ if isinstance(member_class, list):
+ if getattr(self, member_name) is None:
+ setattr(self, member_name, [])
+ getattr(self, member_name).append(_CreateClassFromElementTree(
+ member_class[0], child_tree))
+ else:
+ setattr(self, member_name,
+ _CreateClassFromElementTree(member_class, child_tree))
+ else:
+ ExtensionContainer._ConvertElementTreeToMember(self, child_tree)
+
+ def _ConvertElementAttributeToMember(self, attribute, value):
+ # Find the attribute in this class's list of attributes.
+ if self.__class__._attributes.has_key(attribute):
+ # Find the member of this class which corresponds to the XML attribute
+ # (lookup in current_class._attributes) and set this member to the
+ # desired value (using self.__dict__).
+ if value:
+ # Encode the string to capture non-ascii characters (default UTF-8)
+ if MEMBER_STRING_ENCODING is unicode:
+ setattr(self, self.__class__._attributes[attribute], value)
+ else:
+ setattr(self, self.__class__._attributes[attribute],
+ value.encode(MEMBER_STRING_ENCODING))
+ else:
+ ExtensionContainer._ConvertElementAttributeToMember(
+ self, attribute, value)
+
+ # Three methods to create an ElementTree from an object
+ def _AddMembersToElementTree(self, tree):
+ # Convert the members of this class which are XML child nodes.
+ # This uses the class's _children dictionary to find the members which
+ # should become XML child nodes.
+ member_node_names = [values[0] for tag, values in
+ self.__class__._children.iteritems()]
+ for member_name in member_node_names:
+ member = getattr(self, member_name)
+ if member is None:
+ pass
+ elif isinstance(member, list):
+ for instance in member:
+ instance._BecomeChildElement(tree)
+ else:
+ member._BecomeChildElement(tree)
+ # Convert the members of this class which are XML attributes.
+ for xml_attribute, member_name in self.__class__._attributes.iteritems():
+ member = getattr(self, member_name)
+ if member is not None:
+ if isinstance(member, unicode) or MEMBER_STRING_ENCODING is unicode:
+ tree.attrib[xml_attribute] = member
+ else:
+ tree.attrib[xml_attribute] = member.decode(MEMBER_STRING_ENCODING)
+ # Lastly, call the ExtensionContainers's _AddMembersToElementTree to
+ # convert any extension attributes.
+ ExtensionContainer._AddMembersToElementTree(self, tree)
+
+
+ def _BecomeChildElement(self, tree):
+ """
+
+ Note: Only for use with classes that have a _tag and _namespace class
+ member. It is in AtomBase so that it can be inherited but it should
+ not be called on instances of AtomBase.
+
+ """
+ new_child = ElementTree.Element('')
+ tree.append(new_child)
+ new_child.tag = '{%s}%s' % (self.__class__._namespace,
+ self.__class__._tag)
+ self._AddMembersToElementTree(new_child)
+
+ def _ToElementTree(self):
+ """
+
+ Note, this method is designed to be used only with classes that have a
+ _tag and _namespace. It is placed in AtomBase for inheritance but should
+ not be called on this class.
+
+ """
+ new_tree = ElementTree.Element('{%s}%s' % (self.__class__._namespace,
+ self.__class__._tag))
+ self._AddMembersToElementTree(new_tree)
+ return new_tree
+
+ def ToString(self, string_encoding='UTF-8'):
+ """Converts the Atom object to a string containing XML."""
+ return ElementTree.tostring(self._ToElementTree(), encoding=string_encoding)
+
+ def __str__(self):
+ return self.ToString()
+
+
+class Name(AtomBase):
+ """The atom:name element"""
+
+ _tag = 'name'
+ _namespace = ATOM_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+
+ def __init__(self, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Name
+
+ Args:
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def NameFromString(xml_string):
+ return CreateClassFromXMLString(Name, xml_string)
+
+
+class Email(AtomBase):
+ """The atom:email element"""
+
+ _tag = 'email'
+ _namespace = ATOM_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+
+ def __init__(self, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Email
+
+ Args:
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ text: str The text data in the this element
+ """
+
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def EmailFromString(xml_string):
+ return CreateClassFromXMLString(Email, xml_string)
+
+
+class Uri(AtomBase):
+ """The atom:uri element"""
+
+ _tag = 'uri'
+ _namespace = ATOM_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+
+ def __init__(self, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Uri
+
+ Args:
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ text: str The text data in the this element
+ """
+
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def UriFromString(xml_string):
+ return CreateClassFromXMLString(Uri, xml_string)
+
+
+class Person(AtomBase):
+ """A foundation class from 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.
+ """
+
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+ _children['{%s}name' % (ATOM_NAMESPACE)] = ('name', Name)
+ _children['{%s}email' % (ATOM_NAMESPACE)] = ('email', Email)
+ _children['{%s}uri' % (ATOM_NAMESPACE)] = ('uri', Uri)
+
+ def __init__(self, name=None, email=None, uri=None,
+ extension_elements=None, extension_attributes=None, text=None):
+ """Foundation from which author and contributor are derived.
+
+ The constructor is provided for illustrative purposes, you should not
+ need to instantiate a Person.
+
+ Args:
+ name: Name The person's name
+ email: Email The person's email address
+ uri: Uri The URI of the person's webpage
+ extension_elements: list A list of ExtensionElement instances which are
+ children of this element.
+ extension_attributes: dict A dictionary of strings which are the values
+ for additional XML attributes of this element.
+ text: String The text contents of the element. This is the contents
+ of the Entry's XML text node. (Example: <foo>This is the text</foo>)
+ """
+
+ self.name = name
+ self.email = email
+ self.uri = uri
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+ self.text = text
+
+
+class Author(Person):
+ """The atom:author element
+
+ An author is a required element in Feed.
+ """
+
+ _tag = 'author'
+ _namespace = ATOM_NAMESPACE
+ _children = Person._children.copy()
+ _attributes = Person._attributes.copy()
+ #_children = {}
+ #_attributes = {}
+
+ def __init__(self, name=None, email=None, uri=None,
+ extension_elements=None, extension_attributes=None, text=None):
+ """Constructor for Author
+
+ Args:
+ name: Name
+ email: Email
+ uri: Uri
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ text: str The text data in the this element
+ """
+
+ self.name = name
+ self.email = email
+ self.uri = uri
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+ self.text = text
+
+
+def AuthorFromString(xml_string):
+ return CreateClassFromXMLString(Author, xml_string)
+
+
+class Contributor(Person):
+ """The atom:contributor element"""
+
+ _tag = 'contributor'
+ _namespace = ATOM_NAMESPACE
+ _children = Person._children.copy()
+ _attributes = Person._attributes.copy()
+
+ def __init__(self, name=None, email=None, uri=None,
+ extension_elements=None, extension_attributes=None, text=None):
+ """Constructor for Contributor
+
+ Args:
+ name: Name
+ email: Email
+ uri: Uri
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ text: str The text data in the this element
+ """
+
+ self.name = name
+ self.email = email
+ self.uri = uri
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+ self.text = text
+
+
+def ContributorFromString(xml_string):
+ return CreateClassFromXMLString(Contributor, xml_string)
+
+
+class Link(AtomBase):
+ """The atom:link element"""
+
+ _tag = 'link'
+ _namespace = ATOM_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+ _attributes['rel'] = 'rel'
+ _attributes['href'] = 'href'
+ _attributes['type'] = 'type'
+ _attributes['title'] = 'title'
+ _attributes['length'] = 'length'
+ _attributes['hreflang'] = 'hreflang'
+
+ def __init__(self, href=None, rel=None, link_type=None, hreflang=None,
+ title=None, length=None, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Link
+
+ Args:
+ href: string The href attribute of the link
+ rel: string
+ type: string
+ hreflang: string The language for the href
+ title: string
+ length: string The length of the href's destination
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ text: str The text data in the this element
+ """
+
+ self.href = href
+ self.rel = rel
+ self.type = link_type
+ self.hreflang = hreflang
+ self.title = title
+ self.length = length
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def LinkFromString(xml_string):
+ return CreateClassFromXMLString(Link, xml_string)
+
+
+class Generator(AtomBase):
+ """The atom:generator element"""
+
+ _tag = 'generator'
+ _namespace = ATOM_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+ _attributes['uri'] = 'uri'
+ _attributes['version'] = 'version'
+
+ def __init__(self, uri=None, version=None, text=None,
+ extension_elements=None, extension_attributes=None):
+ """Constructor for Generator
+
+ Args:
+ uri: string
+ version: string
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.uri = uri
+ self.version = version
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+def GeneratorFromString(xml_string):
+ return CreateClassFromXMLString(Generator, xml_string)
+
+
+class Text(AtomBase):
+ """A foundation class from which atom:title, summary, etc. extend.
+
+ This class should never be instantiated.
+ """
+
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+ _attributes['type'] = 'type'
+
+ def __init__(self, text_type=None, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Text
+
+ Args:
+ text_type: string
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.type = text_type
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+class Title(Text):
+ """The atom:title element"""
+
+ _tag = 'title'
+ _namespace = ATOM_NAMESPACE
+ _children = Text._children.copy()
+ _attributes = Text._attributes.copy()
+
+ def __init__(self, title_type=None, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Title
+
+ Args:
+ title_type: string
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.type = title_type
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def TitleFromString(xml_string):
+ return CreateClassFromXMLString(Title, xml_string)
+
+
+class Subtitle(Text):
+ """The atom:subtitle element"""
+
+ _tag = 'subtitle'
+ _namespace = ATOM_NAMESPACE
+ _children = Text._children.copy()
+ _attributes = Text._attributes.copy()
+
+ def __init__(self, subtitle_type=None, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Subtitle
+
+ Args:
+ subtitle_type: string
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.type = subtitle_type
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def SubtitleFromString(xml_string):
+ return CreateClassFromXMLString(Subtitle, xml_string)
+
+
+class Rights(Text):
+ """The atom:rights element"""
+
+ _tag = 'rights'
+ _namespace = ATOM_NAMESPACE
+ _children = Text._children.copy()
+ _attributes = Text._attributes.copy()
+
+ def __init__(self, rights_type=None, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Rights
+
+ Args:
+ rights_type: string
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.type = rights_type
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def RightsFromString(xml_string):
+ return CreateClassFromXMLString(Rights, xml_string)
+
+
+class Summary(Text):
+ """The atom:summary element"""
+
+ _tag = 'summary'
+ _namespace = ATOM_NAMESPACE
+ _children = Text._children.copy()
+ _attributes = Text._attributes.copy()
+
+ def __init__(self, summary_type=None, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Summary
+
+ Args:
+ summary_type: string
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.type = summary_type
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def SummaryFromString(xml_string):
+ return CreateClassFromXMLString(Summary, xml_string)
+
+
+class Content(Text):
+ """The atom:content element"""
+
+ _tag = 'content'
+ _namespace = ATOM_NAMESPACE
+ _children = Text._children.copy()
+ _attributes = Text._attributes.copy()
+ _attributes['src'] = 'src'
+
+ def __init__(self, content_type=None, src=None, text=None,
+ extension_elements=None, extension_attributes=None):
+ """Constructor for Content
+
+ Args:
+ content_type: string
+ src: string
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.type = content_type
+ self.src = src
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+def ContentFromString(xml_string):
+ return CreateClassFromXMLString(Content, xml_string)
+
+
+class Category(AtomBase):
+ """The atom:category element"""
+
+ _tag = 'category'
+ _namespace = ATOM_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+ _attributes['term'] = 'term'
+ _attributes['scheme'] = 'scheme'
+ _attributes['label'] = 'label'
+
+ def __init__(self, term=None, scheme=None, label=None, text=None,
+ extension_elements=None, extension_attributes=None):
+ """Constructor for Category
+
+ Args:
+ term: str
+ scheme: str
+ label: str
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.term = term
+ self.scheme = scheme
+ self.label = label
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def CategoryFromString(xml_string):
+ return CreateClassFromXMLString(Category, xml_string)
+
+
+class Id(AtomBase):
+ """The atom:id element."""
+
+ _tag = 'id'
+ _namespace = ATOM_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+
+ def __init__(self, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Id
+
+ Args:
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def IdFromString(xml_string):
+ return CreateClassFromXMLString(Id, xml_string)
+
+
+class Icon(AtomBase):
+ """The atom:icon element."""
+
+ _tag = 'icon'
+ _namespace = ATOM_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+
+ def __init__(self, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Icon
+
+ Args:
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def IconFromString(xml_string):
+ return CreateClassFromXMLString(Icon, xml_string)
+
+
+class Logo(AtomBase):
+ """The atom:logo element."""
+
+ _tag = 'logo'
+ _namespace = ATOM_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+
+ def __init__(self, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Logo
+
+ Args:
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def LogoFromString(xml_string):
+ return CreateClassFromXMLString(Logo, xml_string)
+
+
+class Draft(AtomBase):
+ """The app:draft element which indicates if this entry should be public."""
+
+ _tag = 'draft'
+ _namespace = APP_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+
+ def __init__(self, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for app:draft
+
+ Args:
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def DraftFromString(xml_string):
+ return CreateClassFromXMLString(Draft, xml_string)
+
+
+class Control(AtomBase):
+ """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.
+ """
+
+ _tag = 'control'
+ _namespace = APP_NAMESPACE
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+ _children['{%s}draft' % APP_NAMESPACE] = ('draft', Draft)
+
+ def __init__(self, draft=None, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for app:control"""
+
+ self.draft = draft
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def ControlFromString(xml_string):
+ return CreateClassFromXMLString(Control, xml_string)
+
+
+class Date(AtomBase):
+ """A parent class for atom:updated, published, etc."""
+
+ #TODO Add text to and from time conversion methods to allow users to set
+ # the contents of a Date to a python DateTime object.
+
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+
+ def __init__(self, text=None, extension_elements=None,
+ extension_attributes=None):
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+class Updated(Date):
+ """The atom:updated element."""
+
+ _tag = 'updated'
+ _namespace = ATOM_NAMESPACE
+ _children = Date._children.copy()
+ _attributes = Date._attributes.copy()
+
+ def __init__(self, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Updated
+
+ Args:
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def UpdatedFromString(xml_string):
+ return CreateClassFromXMLString(Updated, xml_string)
+
+
+class Published(Date):
+ """The atom:published element."""
+
+ _tag = 'published'
+ _namespace = ATOM_NAMESPACE
+ _children = Date._children.copy()
+ _attributes = Date._attributes.copy()
+
+ def __init__(self, text=None, extension_elements=None,
+ extension_attributes=None):
+ """Constructor for Published
+
+ Args:
+ text: str The text data in the this element
+ extension_elements: list A list of ExtensionElement instances
+ extension_attributes: dict A dictionary of attribute value string pairs
+ """
+
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def PublishedFromString(xml_string):
+ return CreateClassFromXMLString(Published, xml_string)
+
+
+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 GetSelfLink(self):
+ """Find the first link with rel set to 'self'
+
+ Returns:
+ An atom.Link or none if none of the links had rel equal to 'self'
+ """
+
+ for a_link in self.link:
+ if a_link.rel == 'self':
+ return a_link
+ return None
+
+ def GetEditLink(self):
+ for a_link in self.link:
+ if a_link.rel == 'edit':
+ return a_link
+ return None
+
+ def GetEditMediaLink(self):
+ for a_link in self.link:
+ if a_link.rel == 'edit-media':
+ return a_link
+ return None
+
+ def GetNextLink(self):
+ for a_link in self.link:
+ if a_link.rel == 'next':
+ return a_link
+ return None
+
+ def GetLicenseLink(self):
+ for a_link in self.link:
+ if a_link.rel == 'license':
+ return a_link
+ return None
+
+ def GetAlternateLink(self):
+ for a_link in self.link:
+ if a_link.rel == 'alternate':
+ return a_link
+ return None
+
+
+class FeedEntryParent(AtomBase, LinkFinder):
+ """A super class for atom:feed and entry, contains shared attributes"""
+
+ _children = AtomBase._children.copy()
+ _attributes = AtomBase._attributes.copy()
+ _children['{%s}author' % ATOM_NAMESPACE] = ('author', [Author])
+ _children['{%s}category' % ATOM_NAMESPACE] = ('category', [Category])
+ _children['{%s}contributor' % ATOM_NAMESPACE] = ('contributor', [Contributor])
+ _children['{%s}id' % ATOM_NAMESPACE] = ('id', Id)
+ _children['{%s}link' % ATOM_NAMESPACE] = ('link', [Link])
+ _children['{%s}rights' % ATOM_NAMESPACE] = ('rights', Rights)
+ _children['{%s}title' % ATOM_NAMESPACE] = ('title', Title)
+ _children['{%s}updated' % ATOM_NAMESPACE] = ('updated', Updated)
+
+ def __init__(self, author=None, category=None, contributor=None,
+ atom_id=None, link=None, rights=None, title=None, updated=None,
+ text=None, extension_elements=None, extension_attributes=None):
+ self.author = author or []
+ self.category = category or []
+ self.contributor = contributor or []
+ self.id = atom_id
+ self.link = link or []
+ self.rights = rights
+ self.title = title
+ self.updated = updated
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+class Source(FeedEntryParent):
+ """The atom:source element"""
+
+ _tag = 'source'
+ _namespace = ATOM_NAMESPACE
+ _children = FeedEntryParent._children.copy()
+ _attributes = FeedEntryParent._attributes.copy()
+ _children['{%s}generator' % ATOM_NAMESPACE] = ('generator', Generator)
+ _children['{%s}icon' % ATOM_NAMESPACE] = ('icon', Icon)
+ _children['{%s}logo' % ATOM_NAMESPACE] = ('logo', Logo)
+ _children['{%s}subtitle' % ATOM_NAMESPACE] = ('subtitle', Subtitle)
+
+ def __init__(self, author=None, category=None, contributor=None,
+ generator=None, icon=None, atom_id=None, link=None, logo=None,
+ rights=None, subtitle=None, title=None, updated=None, text=None,
+ extension_elements=None, extension_attributes=None):
+ """Constructor for Source
+
+ Args:
+ author: list (optional) A list of Author instances which belong to this
+ class.
+ category: list (optional) A list of Category instances
+ contributor: list (optional) A list on Contributor instances
+ generator: Generator (optional)
+ icon: Icon (optional)
+ id: Id (optional) The entry's Id element
+ link: list (optional) A list of Link instances
+ logo: Logo (optional)
+ rights: Rights (optional) The entry's Rights element
+ subtitle: Subtitle (optional) The entry's subtitle element
+ title: Title (optional) the entry's title element
+ updated: Updated (optional) the entry's updated element
+ text: String (optional) The text contents of the element. This is the
+ contents of the Entry's XML text node.
+ (Example: <foo>This is the text</foo>)
+ extension_elements: list (optional) A list of ExtensionElement instances
+ which are children of this element.
+ extension_attributes: dict (optional) A dictionary of strings which are
+ the values for additional XML attributes of this element.
+ """
+
+ self.author = author or []
+ self.category = category or []
+ self.contributor = contributor or []
+ self.generator = generator
+ self.icon = icon
+ self.id = atom_id
+ self.link = link or []
+ self.logo = logo
+ self.rights = rights
+ self.subtitle = subtitle
+ self.title = title
+ self.updated = updated
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+
+def SourceFromString(xml_string):
+ return CreateClassFromXMLString(Source, xml_string)
+
+
+class Entry(FeedEntryParent):
+ """The atom:entry element"""
+
+ _tag = 'entry'
+ _namespace = ATOM_NAMESPACE
+ _children = FeedEntryParent._children.copy()
+ _attributes = FeedEntryParent._attributes.copy()
+ _children['{%s}content' % ATOM_NAMESPACE] = ('content', Content)
+ _children['{%s}published' % ATOM_NAMESPACE] = ('published', Published)
+ _children['{%s}source' % ATOM_NAMESPACE] = ('source', Source)
+ _children['{%s}summary' % ATOM_NAMESPACE] = ('summary', Summary)
+ _children['{%s}control' % APP_NAMESPACE] = ('control', Control)
+
+ def __init__(self, author=None, category=None, content=None,
+ contributor=None, atom_id=None, link=None, published=None, rights=None,
+ source=None, summary=None, control=None, title=None, updated=None,
+ extension_elements=None, extension_attributes=None, text=None):
+ """Constructor for atom:entry
+
+ Args:
+ author: list A list of Author instances which belong to this class.
+ category: list A list of Category instances
+ content: Content The entry's Content
+ contributor: list A list on Contributor instances
+ id: Id The entry's Id element
+ link: list A list of Link instances
+ published: Published The entry's Published element
+ rights: Rights The entry's Rights element
+ source: Source the entry's source element
+ summary: Summary the entry's summary element
+ title: Title the entry's title element
+ updated: Updated the entry's updated element
+ control: The entry's app:control element which can be used to mark an
+ entry as a draft which should not be publicly viewable.
+ text: String The text contents of the element. This is the contents
+ of the Entry's XML text node. (Example: <foo>This is the text</foo>)
+ extension_elements: list A list of ExtensionElement instances which are
+ children of this element.
+ extension_attributes: dict A dictionary of strings which are the values
+ for additional XML attributes of this element.
+ """
+
+ self.author = author or []
+ self.category = category or []
+ self.content = content
+ self.contributor = contributor or []
+ self.id = atom_id
+ self.link = link or []
+ self.published = published
+ self.rights = rights
+ self.source = source
+ self.summary = summary
+ self.title = title
+ self.updated = updated
+ self.control = control
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+ __init__ = v1_deprecated('Please use atom.data.Entry instead.')(__init__)
+
+
+def EntryFromString(xml_string):
+ return CreateClassFromXMLString(Entry, xml_string)
+
+
+class Feed(Source):
+ """The atom:feed element"""
+
+ _tag = 'feed'
+ _namespace = ATOM_NAMESPACE
+ _children = Source._children.copy()
+ _attributes = Source._attributes.copy()
+ _children['{%s}entry' % ATOM_NAMESPACE] = ('entry', [Entry])
+
+ def __init__(self, author=None, category=None, contributor=None,
+ generator=None, icon=None, atom_id=None, link=None, logo=None,
+ rights=None, subtitle=None, title=None, updated=None, entry=None,
+ text=None, extension_elements=None, extension_attributes=None):
+ """Constructor for Source
+
+ Args:
+ author: list (optional) A list of Author instances which belong to this
+ class.
+ category: list (optional) A list of Category instances
+ contributor: list (optional) A list on Contributor instances
+ generator: Generator (optional)
+ icon: Icon (optional)
+ id: Id (optional) The entry's Id element
+ link: list (optional) A list of Link instances
+ logo: Logo (optional)
+ rights: Rights (optional) The entry's Rights element
+ subtitle: Subtitle (optional) The entry's subtitle element
+ title: Title (optional) the entry's title element
+ updated: Updated (optional) the entry's updated element
+ entry: list (optional) A list of the Entry instances contained in the
+ feed.
+ text: String (optional) The text contents of the element. This is the
+ contents of the Entry's XML text node.
+ (Example: <foo>This is the text</foo>)
+ extension_elements: list (optional) A list of ExtensionElement instances
+ which are children of this element.
+ extension_attributes: dict (optional) A dictionary of strings which are
+ the values for additional XML attributes of this element.
+ """
+
+ self.author = author or []
+ self.category = category or []
+ self.contributor = contributor or []
+ self.generator = generator
+ self.icon = icon
+ self.id = atom_id
+ self.link = link or []
+ self.logo = logo
+ self.rights = rights
+ self.subtitle = subtitle
+ self.title = title
+ self.updated = updated
+ self.entry = entry or []
+ self.text = text
+ self.extension_elements = extension_elements or []
+ self.extension_attributes = extension_attributes or {}
+
+ __init__ = v1_deprecated('Please use atom.data.Feed instead.')(__init__)
+
+
+def FeedFromString(xml_string):
+ return CreateClassFromXMLString(Feed, xml_string)
+
+
+class ExtensionElement(object):
+ """Represents extra XML elements contained in Atom classes."""
+
+ def __init__(self, tag, namespace=None, attributes=None,
+ children=None, text=None):
+ """Constructor for EtensionElement
+
+ Args:
+ namespace: string (optional) The XML namespace for this element.
+ tag: string (optional) The tag (without the namespace qualifier) for
+ this element. To reconstruct the full qualified name of the element,
+ combine this tag with the namespace.
+ attributes: dict (optinal) The attribute value string pairs for the XML
+ attributes of this element.
+ children: list (optional) A list of ExtensionElements which represent
+ the XML child nodes of this element.
+ """
+
+ self.namespace = namespace
+ self.tag = tag
+ self.attributes = attributes or {}
+ self.children = children or []
+ self.text = text
+
+ def ToString(self):
+ element_tree = self._TransferToElementTree(ElementTree.Element(''))
+ return ElementTree.tostring(element_tree, encoding="UTF-8")
+
+ def _TransferToElementTree(self, element_tree):
+ if self.tag is None:
+ return None
+
+ if self.namespace is not None:
+ element_tree.tag = '{%s}%s' % (self.namespace, self.tag)
+ else:
+ element_tree.tag = self.tag
+
+ for key, value in self.attributes.iteritems():
+ element_tree.attrib[key] = value
+
+ for child in self.children:
+ child._BecomeChildElement(element_tree)
+
+ element_tree.text = self.text
+
+ return element_tree
+
+ def _BecomeChildElement(self, element_tree):
+ """Converts this object into an etree element and adds it as a child node.
+
+ Adds self to the ElementTree. This method is required to avoid verbose XML
+ which constantly redefines the namespace.
+
+ Args:
+ element_tree: ElementTree._Element The element to which this object's XML
+ will be added.
+ """
+ new_element = ElementTree.Element('')
+ element_tree.append(new_element)
+ self._TransferToElementTree(new_element)
+
+ def FindChildren(self, tag=None, namespace=None):
+ """Searches child nodes for objects with the desired tag/namespace.
+
+ Returns a list of extension elements within this object whose tag
+ and/or namespace match those passed in. To find all children in
+ a particular namespace, specify the namespace but not the tag name.
+ If you specify only the tag, the result list may contain extension
+ elements in multiple namespaces.
+
+ Args:
+ tag: str (optional) The desired tag
+ namespace: str (optional) The desired namespace
+
+ Returns:
+ A list of elements whose tag and/or namespace match the parameters
+ values
+ """
+
+ results = []
+
+ if tag and namespace:
+ for element in self.children:
+ if element.tag == tag and element.namespace == namespace:
+ results.append(element)
+ elif tag and not namespace:
+ for element in self.children:
+ if element.tag == tag:
+ results.append(element)
+ elif namespace and not tag:
+ for element in self.children:
+ if element.namespace == namespace:
+ results.append(element)
+ else:
+ for element in self.children:
+ results.append(element)
+
+ return results
+
+
+def ExtensionElementFromString(xml_string):
+ element_tree = ElementTree.fromstring(xml_string)
+ return _ExtensionElementFromElementTree(element_tree)
+
+
+def _ExtensionElementFromElementTree(element_tree):
+ element_tag = element_tree.tag
+ if '}' in element_tag:
+ namespace = element_tag[1:element_tag.index('}')]
+ tag = element_tag[element_tag.index('}')+1:]
+ else:
+ namespace = None
+ tag = element_tag
+ extension = ExtensionElement(namespace=namespace, tag=tag)
+ for key, value in element_tree.attrib.iteritems():
+ extension.attributes[key] = value
+ for child in element_tree:
+ extension.children.append(_ExtensionElementFromElementTree(child))
+ extension.text = element_tree.text
+ return extension
+
+
+def deprecated(warning=None):
+ """Decorator to raise warning each time the function is called.
+
+ Args:
+ warning: The warning message to be displayed as a string (optinoal).
+ """
+ warning = warning or ''
+ # This closure is what is returned from the deprecated function.
+ def mark_deprecated(f):
+ # The deprecated_function wraps the actual call to f.
+ def deprecated_function(*args, **kwargs):
+ warnings.warn(warning, DeprecationWarning, stacklevel=2)
+ return f(*args, **kwargs)
+ # Preserve the original name to avoid masking all decorated functions as
+ # 'deprecated_function'
+ try:
+ deprecated_function.func_name = f.func_name
+ except TypeError:
+ # Setting the func_name is not allowed in Python2.3.
+ pass
+ return deprecated_function
+ return mark_deprecated
43 atom/auth.py
@@ -0,0 +1,43 @@
+#!/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 base64
+
+
+class BasicAuth(object):
+ """Sets the Authorization header as defined in RFC1945"""
+
+ def __init__(self, user_id, password):
+ self.basic_cookie = base64.encodestring(
+ '%s:%s' % (user_id, password)).strip()
+
+ def modify_request(self, http_request):
+ http_request.headers['Authorization'] = 'Basic %s' % self.basic_cookie
+
+ ModifyRequest = modify_request
+
+
+class NoAuth(object):
+
+ def modify_request(self, http_request):
+ pass
182 atom/client.py
@@ -0,0 +1,182 @@
+#!/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.
+
+
+"""AtomPubClient provides CRUD ops. in line with the Atom Publishing Protocol.
+
+"""
+
+__author__ = 'j.s@google.com (Jeff Scudder)'
+
+
+import atom.http_core
+
+
+class Error(Exception):
+ pass
+
+
+class MissingHost(Error):
+ pass
+
+
+class AtomPubClient(object):
+ host = None
+ auth_token = None
+ ssl = False # Whether to force all requests over https
+
+ def __init__(self, http_client=None, host=None,
+ auth_token=None, source=None, **kwargs):
+ """Creates a new AtomPubClient instance.
+
+ Args:
+ source: The name of your application.
+ http_client: An object capable of performing HTTP requests through a
+ request method. This object is used to perform the request
+ when the AtomPubClient's request method is called. Used to
+ allow HTTP requests to be directed to a mock server, or use
+ an alternate library instead of the default of httplib to
+ make HTTP requests.
+ host: str The default host name to use if a host is not specified in the
+ requested URI.
+ auth_token: An object which sets the HTTP Authorization header when its
+ modify_request method is called.
+ """
+ self.http_client = http_client or atom.http_core.ProxiedHttpClient()
+ if host is not None:
+ self.host = host
+ if auth_token is not None:
+ self.auth_token = auth_token
+ self.source = source
+
+ def request(self, method=None, uri=None, auth_token=None,
+ http_request=None, **kwargs):
+ """Performs an HTTP request to the server indicated.
+
+ Uses the http_client instance to make the request.
+
+ Args:
+ method: The HTTP method as a string, usually one of 'GET', 'POST',
+ 'PUT', or 'DELETE'
+ uri: The URI desired as a string or atom.http_core.Uri.
+ http_request:
+ auth_token: An authorization token object whose modify_request method
+ sets the HTTP Authorization header.
+
+ Returns:
+ The results of calling self.http_client.request. With the default
+ http_client, this is an HTTP response object.
+ """
+ # Modify the request based on the AtomPubClient settings and parameters
+ # passed in to the request.
+ http_request = self.modify_request(http_request)
+ if isinstance(uri, (str, unicode)):
+ uri = atom.http_core.Uri.parse_uri(uri)
+ if uri is not None:
+ uri.modify_request(http_request)
+ if isinstance(method, (str, unicode)):
+ http_request.method = method
+ # Any unrecognized arguments are assumed to be capable of modifying the
+ # HTTP request.
+ for name, value in kwargs.iteritems():
+ if value is not None:
+ value.modify_request(http_request)
+ # Default to an http request if the protocol scheme is not set.
+ if http_request.uri.scheme is None:
+ http_request.uri.scheme = 'http'
+ # Override scheme. Force requests over https.
+ if self.ssl:
+ http_request.uri.scheme = 'https'
+ if http_request.uri.path is None:
+ http_request.uri.path = '/'
+ # Add the Authorization header at the very end. The Authorization header
+ # value may need to be calculated using information in the request.
+ if auth_token:
+ auth_token.modify_request(http_request)
+ elif self.auth_token:
+ self.auth_token.modify_request(http_request)
+ # Check to make sure there is a host in the http_request.
+ if http_request.uri.host is None:
+ raise MissingHost('No host provided in request %s %s' % (
+ http_request.method, str(http_request.uri)))
+ # Perform the fully specified request using the http_client instance.
+ # Sends the request to the server and returns the server's response.
+ return self.http_client.request(http_request)
+
+ Request = request
+
+ def get(self, uri=None, auth_token=None, http_request=None, **kwargs):
+ """Performs a request using the GET method, returns an HTTP response."""
+ return self.request(method='GET', uri=uri, auth_token=auth_token,
+ http_request=http_request, **kwargs)
+
+ Get = get
+
+ def post(self, uri=None, data=None, auth_token=None, http_request=None,
+ **kwargs):
+ """Sends data using the POST method, returns an HTTP response."""
+ return self.request(method='POST', uri=uri, auth_token=auth_token,
+ http_request=http_request, data=data, **kwargs)
+
+ Post = post
+
+ def put(self, uri=None, data=None, auth_token=None, http_request=None,
+ **kwargs):
+ """Sends data using the PUT method, returns an HTTP response."""
+ return self.request(method='PUT', uri=uri, auth_token=auth_token,
+ http_request=http_request, data=data, **kwargs)
+
+ Put = put
+
+ def delete(self, uri=None, auth_token=None, http_request=None, **kwargs):
+ """Performs a request using the DELETE method, returns an HTTP response."""
+ return self.request(method='DELETE', uri=uri, auth_token=auth_token,
+ http_request=http_request, **kwargs)
+
+ Delete = delete
+
+ def modify_request(self, http_request):
+ """Changes the HTTP request before sending it to the server.
+
+ Sets the User-Agent HTTP header and fills in the HTTP host portion
+ of the URL if one was not included in the request (for this it uses
+ the self.host member if one is set). This method is called in
+ self.request.
+
+ Args:
+ http_request: An atom.http_core.HttpRequest() (optional) If one is
+ not provided, a new HttpRequest is instantiated.
+
+ Returns:
+ An atom.http_core.HttpRequest() with the User-Agent header set and
+ if this client has a value in its host member, the host in the request
+ URL is set.
+ """
+ if http_request is None:
+ http_request = atom.http_core.HttpRequest()
+
+ if self.host is not None and http_request.uri.host is None:
+ http_request.uri.host = self.host
+
+ # Set the user agent header for logging purposes.
+ if self.source:
+ http_request.headers['User-Agent'] = '%s gdata-py/2.0.6' % self.source
+ else:
+ http_request.headers['User-Agent'] = 'gdata-py/2.0.6'
+
+ return http_request
+
+ ModifyRequest = modify_request
534 atom/core.py
@@ -0,0 +1,534 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2008 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 inspect
+try:
+ from xml.etree import cElementTree as ElementTree
+except ImportError:
+ try:
+ import cElementTree as ElementTree
+ except ImportError:
+ try:
+ from xml.etree import ElementTree
+ except ImportError:
+ from elementtree import ElementTree
+
+
+STRING_ENCODING = 'utf-8'
+
+
+class XmlElement(object):
+ """Represents an element node in an XML document.
+
+ The text member is a UTF-8 encoded str or unicode.
+ """
+ _qname = None
+ _other_elements = None
+ _other_attributes = None
+ # The rule set contains mappings for XML qnames to child members and the
+ # appropriate member classes.
+ _rule_set = None
+ _members = None
+ text = None
+
+ def __init__(self, text=None, *args, **kwargs):
+ if ('_members' not in self.__class__.__dict__
+ or self.__class__._members is None):
+ self.__class__._members = tuple(self.__class__._list_xml_members())
+ for member_name, member_type in self.__class__._members:
+ if member_name in kwargs:
+ setattr(self, member_name, kwargs[member_name])
+ else:
+ if isinstance(member_type, list):
+ setattr(self, member_name, [])
+ else:
+ setattr(self, member_name, None)
+ self._other_elements = []
+ self._other_attributes = {}
+ if text is not None:
+ self.text = text
+
+ def _list_xml_members(cls):
+ """Generator listing all members which are XML elements or attributes.
+
+ The following members would be considered XML members:
+ foo = 'abc' - indicates an XML attribute with the qname abc
+ foo = SomeElement - indicates an XML child element
+ foo = [AnElement] - indicates a repeating XML child element, each instance
+ will be stored in a list in this member
+ foo = ('att1', '{http://example.com/namespace}att2') - indicates an XML
+ attribute which has different parsing rules in different versions of
+ the protocol. Version 1 of the XML parsing rules will look for an
+ attribute with the qname 'att1' but verion 2 of the parsing rules will
+ look for a namespaced attribute with the local name of 'att2' and an
+ XML namespace of 'http://example.com/namespace'.
+ """
+ members = []
+ for pair in inspect.getmembers(cls):
+ if not pair[0].startswith('_') and pair[0] != 'text':
+ member_type = pair[1]
+ if (isinstance(member_type, tuple) or isinstance(member_type, list)
+ or isinstance(member_type, (str, unicode))
+ or (inspect.isclass(member_type)
+ and issubclass(member_type, XmlElement))):
+ members.append(pair)
+ return members
+
+ _list_xml_members = classmethod(_list_xml_members)
+
+ def _get_rules(cls, version):
+ """Initializes the _rule_set for the class which is used when parsing XML.
+
+ This method is used internally for parsing and generating XML for an
+ XmlElement. It is not recommended that you call this method directly.
+
+ Returns:
+ A tuple containing the XML parsing rules for the appropriate version.
+
+ The tuple looks like:
+ (qname, {sub_element_qname: (member_name, member_class, repeating), ..},
+ {attribute_qname: member_name})
+
+ To give a couple of concrete example, the atom.data.Control _get_rules
+ with version of 2 will return:
+ ('{http://www.w3.org/2007/app}control',
+ {'{http://www.w3.org/2007/app}draft': ('draft',
+ <class 'atom.data.Draft'>,
+ False)},
+ {})
+ Calling _get_rules with version 1 on gdata.data.FeedLink will produce:
+ ('{http://schemas.google.com/g/2005}feedLink',
+ {'{http://www.w3.org/2005/Atom}feed': ('feed',
+ <class 'gdata.data.GDFeed'>,
+ False)},
+ {'href': 'href', 'readOnly': 'read_only', 'countHint': 'count_hint',
+ 'rel': 'rel'})
+ """
+ # Initialize the _rule_set to make sure there is a slot available to store
+ # the parsing rules for this version of the XML schema.
+ # Look for rule set in the class __dict__ proxy so that only the
+ # _rule_set for this class will be found. By using the dict proxy
+ # we avoid finding rule_sets defined in superclasses.
+ # The four lines below provide support for any number of versions, but it
+ # runs a bit slower then hard coding slots for two versions, so I'm using
+ # the below two lines.
+ #if '_rule_set' not in cls.__dict__ or cls._rule_set is None:
+ # cls._rule_set = []
+ #while len(cls.__dict__['_rule_set']) < version:
+ # cls._rule_set.append(None)
+ # If there is no rule set cache in the class, provide slots for two XML
+ # versions. If and when there is a version 3, this list will need to be
+ # expanded.
+ if '_rule_set' not in cls.__dict__ or cls._rule_set is None:
+ cls._rule_set = [None, None]
+ # If a version higher than 2 is requested, fall back to version 2 because
+ # 2 is currently the highest supported version.
+ if version > 2:
+ return cls._get_rules(2)
+ # Check the dict proxy for the rule set to avoid finding any rule sets
+ # which belong to the superclass. We only want rule sets for this class.
+ if cls._rule_set[version-1] is None:
+ # The rule set for each version consists of the qname for this element
+ # ('{namespace}tag'), a dictionary (elements) for looking up the
+ # corresponding class member when given a child element's qname, and a
+ # dictionary (attributes) for looking up the corresponding class member
+ # when given an XML attribute's qname.
+ elements = {}
+ attributes = {}
+ if ('_members' not in cls.__dict__ or cls._members is None):
+ cls._members = tuple(cls._list_xml_members())
+ for member_name, target in cls._members:
+ if isinstance(target, list):
+ # This member points to a repeating element.
+ elements[_get_qname(target[0], version)] = (member_name, target[0],
+ True)
+ elif isinstance(target, tuple):
+ # This member points to a versioned XML attribute.
+ if version <= len(target):
+ attributes[target[version-1]] = member_name
+ else:
+ attributes[target[-1]] = member_name
+ elif isinstance(target, (str, unicode)):
+ # This member points to an XML attribute.
+ attributes[target] = member_name
+ elif issubclass(target, XmlElement):
+ # This member points to a single occurance element.
+ elements[_get_qname(target, version)] = (member_name, target, False)
+ version_rules = (_get_qname(cls, version), elements, attributes)
+ cls._rule_set[version-1] = version_rules
+ return version_rules
+ else:
+ return cls._rule_set[version-1]
+
+ _get_rules = classmethod(_get_rules)
+
+ def get_elements(self, tag=None, namespace=None, version=1):
+ """Find all sub elements which match the tag and namespace.
+
+ To find all elements in this object, call get_elements with the tag and
+ namespace both set to None (the default). This method searches through
+ the object's members and the elements stored in _other_elements which
+ did not match any of the XML parsing rules for this class.
+
+ Args:
+ tag: str
+ namespace: str
+ version: int Specifies the version of the XML rules to be used when
+ searching for matching elements.
+
+ Returns:
+ A list of the matching XmlElements.
+ """
+ matches = []
+ ignored1, elements, ignored2 = self.__class__._get_rules(version)
+ if elements:
+ for qname, element_def in elements.iteritems():
+ member = getattr(self, element_def[0])
+ if member:
+ if _qname_matches(tag, namespace, qname):
+ if element_def[2]:
+ # If this is a repeating element, copy all instances into the
+ # result list.
+ matches.extend(member)
+ else:
+ matches.append(member)
+ for element in self._other_elements:
+ if _qname_matches(tag, namespace, element._qname):
+ matches.append(element)
+ return matches
+
+ GetElements = get_elements
+ # FindExtensions and FindChildren are provided for backwards compatibility
+ # to the atom.AtomBase class.
+ # However, FindExtensions may return more results than the v1 atom.AtomBase
+ # method does, because get_elements searches both the expected children
+ # and the unexpected "other elements". The old AtomBase.FindExtensions
+ # method searched only "other elements" AKA extension_elements.
+ FindExtensions = get_elements
+ FindChildren = get_elements
+
+ def get_attributes(self, tag=None, namespace=None, version=1):
+ """Find all attributes which match the tag and namespace.
+
+ To find all attributes in this object, call get_attributes with the tag
+ and namespace both set to None (the default). This method searches
+ through the object's members and the attributes stored in
+ _other_attributes which did not fit any of the XML parsing rules for this
+ class.
+
+ Args:
+ tag: str
+ namespace: str
+ version: int Specifies the version of the XML rules to be used when
+ searching for matching attributes.
+
+ Returns:
+ A list of XmlAttribute objects for the matching attributes.
+ """
+ matches = []
+ ignored1, ignored2, attributes = self.__class__._get_rules(version)
+ if attributes:
+ for qname, attribute_def in attributes.iteritems():
+ if isinstance(attribute_def, (list, tuple)):
+ attribute_def = attribute_def[0]
+ member = getattr(self, attribute_def)
+ # TODO: ensure this hasn't broken existing behavior.
+ #member = getattr(self, attribute_def[0])
+ if member:
+ if _qname_matches(tag, namespace, qname):
+ matches.append(XmlAttribute(qname, member))
+ for qname, value in self._other_attributes.iteritems():
+ if _qname_matches(tag, namespace, qname):
+ matches.append(XmlAttribute(qname, value))
+ return matches
+
+ GetAttributes = get_attributes
+
+ def _harvest_tree(self, tree, version=1):
+ """Populates object members from the data in the tree Element."""
+ qname, elements, attributes = self.__class__._get_rules(version)
+ for element in tree:
+ if elements and element.tag in elements:
+ definition = elements[element.tag]
+ # If this is a repeating element, make sure the member is set to a
+ # list.
+ if definition[2]:
+ if getattr(self, definition[0]) is None:
+ setattr(self, definition[0], [])
+ getattr(self, definition[0]).append(_xml_element_from_tree(element,
+ definition[1], version))
+ else:
+ setattr(self, definition[0], _xml_element_from_tree(element,
+ definition[1], version))
+ else:
+ self._other_elements.append(_xml_element_from_tree(element, XmlElement,
+ version))
+ for attrib, value in tree.attrib.iteritems():
+ if attributes and attrib in attributes:
+ setattr(self, attributes[attrib], value)
+ else:
+ self._other_attributes[attrib] = value
+ if tree.text:
+ self.text = tree.text
+
+ def _to_tree(self, version=1, encoding=None):
+ new_tree = ElementTree.Element(_get_qname(self, version))
+ self._attach_members(new_tree, version, encoding)
+ return new_tree
+
+ def _attach_members(self, tree, version=1, encoding=None):
+ """Convert members to XML elements/attributes and add them to the tree.
+
+ Args:
+ tree: An ElementTree.Element which will be modified. The members of
+ this object will be added as child elements or attributes
+ according to the rules described in _expected_elements and
+ _expected_attributes. The elements and attributes stored in
+ other_attributes and other_elements are also added a children
+ of this tree.
+ version: int Ingnored in this method but used by VersionedElement.
+ encoding: str (optional)
+ """
+ qname, elements, attributes = self.__class__._get_rules(version)
+ encoding = encoding or STRING_ENCODING
+ # Add the expected elements and attributes to the tree.
+ if elements:
+ for tag, element_def in elements.iteritems():
+ member = getattr(self, element_def[0])
+ # If this is a repeating element and there are members in the list.
+ if member and element_def[2]:
+ for instance in member:
+ instance._become_child(tree, version)
+ elif member:
+ member._become_child(tree, version)
+ if attributes:
+ for attribute_tag, member_name in attributes.iteritems():
+ value = getattr(self, member_name)
+ if value:
+ tree.attrib[attribute_tag] = value
+ # Add the unexpected (other) elements and attributes to the tree.
+ for element in self._other_elements:
+ element._become_child(tree, version)
+ for key, value in self._other_attributes.iteritems():
+ # I'm not sure if unicode can be used in the attribute name, so for now
+ # we assume the encoding is correct for the attribute name.
+ if not isinstance(value, unicode):
+ value = value.decode(encoding)
+ tree.attrib[key] = value
+ if self.text:
+ if isinstance(self.text, unicode):
+ tree.text = self.text
+ else:
+ tree.text = self.text.decode(encoding)
+
+ def to_string(self, version=1, encoding=None):
+ """Converts this object to XML."""
+ return ElementTree.tostring(self._to_tree(version, encoding))
+
+ ToString = to_string
+
+ def __str__(self):
+ return self.to_string()
+
+ def _become_child(self, tree, version=1):
+ """Adds a child element to tree with the XML data in self."""
+ new_child = ElementTree.Element('')
+ tree.append(new_child)
+ new_child.tag = _get_qname(self, version)
+ self._attach_members(new_child, version)
+
+ def __get_extension_elements(self):
+ return self._other_elements
+
+ def __set_extension_elements(self, elements):
+ self._other_elements = elements
+
+ extension_elements = property(__get_extension_elements,
+ __set_extension_elements,
+ """Provides backwards compatibility for v1 atom.AtomBase classes.""")
+
+ def __get_extension_attributes(self):
+ return self._other_attributes
+
+ def __set_extension_attributes(self, attributes):
+ self._other_attributes = attributes
+
+ extension_attributes = property(__get_extension_attributes,
+ __set_extension_attributes,
+ """Provides backwards compatibility for v1 atom.AtomBase classes.""")
+
+ def _get_tag(self, version=1):
+ qname = _get_qname(self, version)
+ return qname[qname.find('}')+1:]
+
+ def _get_namespace(self, version=1):
+ qname = _get_qname(self, version)
+ if qname.startswith('{'):
+ return qname[1:qname.find('}')]
+ else:
+ return None
+
+ def _set_tag(self, tag):
+ if isinstance(self._qname, tuple):
+ self._qname = self._qname.copy()
+ if self._qname[0].startswith('{'):
+ self._qname[0] = '{%s}%s' % (self._get_namespace(1), tag)
+ else:
+ self._qname[0] = tag
+ else:
+ if self._qname.startswith('{'):
+ self._qname = '{%s}%s' % (self._get_namespace(), tag)
+ else:
+ self._qname = tag
+
+ def _set_namespace(self, namespace):
+ if isinstance(self._qname, tuple):
+ self._qname = self._qname.copy()
+ if namespace:
+ self._qname[0] = '{%s}%s' % (namespace, self._get_tag(1))
+ else:
+ self._qname[0] = self._get_tag(1)
+ else:
+ if namespace:
+ self._qname = '{%s}%s' % (namespace, self._get_tag(1))
+ else:
+ self._qname = self._get_tag(1)
+
+ tag = property(_get_tag, _set_tag,
+ """Provides backwards compatibility for v1 atom.AtomBase classes.""")
+
+ namespace = property(_get_namespace, _set_namespace,
+ """Provides backwards compatibility for v1 atom.AtomBase classes.""")
+
+ # Provided for backwards compatibility to atom.ExtensionElement
+ children = extension_elements
+ attributes = extension_attributes
+
+
+def _get_qname(element, version):
+ if isinstance(element._qname, tuple):
+ if version <= len(element._qname):
+ return element._qname[version-1]
+ else:
+ return element._qname[-1]
+ else:
+ return element._qname
+
+
+def _qname_matches(tag, namespace, qname):
+ """Logic determines if a QName matches the desired local tag and namespace.
+
+ This is used in XmlElement.get_elements and XmlElement.get_attributes to
+ find matches in the element's members (among all expected-and-unexpected
+ elements-and-attributes).
+
+ Args:
+ expected_tag: string
+ expected_namespace: string
+ qname: string in the form '{xml_namespace}localtag' or 'tag' if there is
+ no namespace.
+
+ Returns:
+ boolean True if the member's tag and namespace fit the expected tag and
+ namespace.
+ """
+ # If there is no expected namespace or tag, then everything will match.
+ if qname is None:
+ member_tag = None
+ member_namespace = None
+ else:
+ if qname.startswith('{'):
+ member_namespace = qname[1:qname.index('}')]
+ member_tag = qname[qname.index('}') + 1:]
+ else:
+ member_namespace = None
+ member_tag = qname
+ return ((tag is None and namespace is None)
+ # If there is a tag, but no namespace, see if the local tag matches.
+ or (namespace is None and member_tag == tag)
+ # There was no tag, but there was a namespace so see if the namespaces
+ # match.
+ or (tag is None and member_namespace == namespace)
+ # There was no tag, and the desired elements have no namespace, so check
+ # to see that the member's namespace is None.
+ or (tag is None and namespace == ''
+ and member_namespace is None)
+ # The tag and the namespace both match.
+ or (tag == member_tag
+ and namespace == member_namespace)
+ # The tag matches, and the expected namespace is the empty namespace,
+ # check to make sure the member's namespace is None.
+ or (tag == member_tag and namespace == ''
+ and member_namespace is None))
+
+
+def parse(xml_string, target_class=None, version=1, encoding=None):
+ """Parses the XML string according to the rules for the target_class.
+
+ Args:
+ xml_string: str or unicode
+ target_class: XmlElement or a subclass. If None is specified, the
+ XmlElement class is used.
+ version: int (optional) The version of the schema which should be used when
+ converting the XML into an object. The default is 1.
+ encoding: str (optional) The character encoding of the bytes in the
+ xml_string. Default is 'UTF-8'.
+ """
+ if target_class is None:
+ target_class = XmlElement
+ if isinstance(xml_string, unicode):
+ if encoding is None:
+ xml_string = xml_string.encode(STRING_ENCODING)
+ else:
+ xml_string = xml_string.encode(encoding)
+ tree = ElementTree.fromstring(xml_string)
+ return _xml_element_from_tree(tree, target_class, version)
+
+
+Parse = parse
+xml_element_from_string = parse
+XmlElementFromString = xml_element_from_string
+
+
+def _xml_element_from_tree(tree, target_class, version=1):
+ if target_class._qname is None:
+ instance = target_class()
+ instance._qname = tree.tag
+ instance._harvest_tree(tree, version)
+ return instance
+ # TODO handle the namespace-only case
+ # Namespace only will be used with Google Spreadsheets rows and
+ # Google Base item attributes.
+ elif tree.tag == _get_qname(target_class, version):
+ instance = target_class()
+ instance._harvest_tree(tree, version)
+ return instance
+ return None
+
+
+class XmlAttribute(object):
+
+ def __init__(self, qname, value):
+ self._qname = qname
+ self.value = value
+
339 atom/data.py
@@ -0,0 +1,339 @@
+#!/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.