PageRenderTime 10ms CodeModel.GetById 15ms app.highlight 60ms RepoModel.GetById 1ms app.codeStats 0ms

/atom/__init__.py

http://radioappz.googlecode.com/
Python | 1484 lines | 1460 code | 2 blank | 22 comment | 0 complexity | ef8e16177c6dfb17146d802edfceafe2 MD5 | raw file
   1#!/usr/bin/python
   2#
   3# Copyright (C) 2006 Google Inc.
   4#
   5# Licensed under the Apache License, Version 2.0 (the "License");
   6# you may not use this file except in compliance with the License.
   7# You may obtain a copy of the License at
   8#
   9#      http://www.apache.org/licenses/LICENSE-2.0
  10#
  11# Unless required by applicable law or agreed to in writing, software
  12# distributed under the License is distributed on an "AS IS" BASIS,
  13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14# See the License for the specific language governing permissions and
  15# limitations under the License.
  16
  17
  18"""Contains classes representing Atom elements.
  19
  20  Module objective: provide data classes for Atom constructs. These classes hide
  21  the XML-ness of Atom and provide a set of native Python classes to interact
  22  with.
  23
  24  Conversions to and from XML should only be necessary when the Atom classes
  25  "touch the wire" and are sent over HTTP. For this reason this module
  26  provides  methods and functions to convert Atom classes to and from strings.
  27
  28  For more information on the Atom data model, see RFC 4287
  29  (http://www.ietf.org/rfc/rfc4287.txt)
  30
  31  AtomBase: A foundation class on which Atom classes are built. It
  32      handles the parsing of attributes and children which are common to all
  33      Atom classes. By default, the AtomBase class translates all XML child
  34      nodes into ExtensionElements.
  35
  36  ExtensionElement: Atom allows Atom objects to contain XML which is not part
  37      of the Atom specification, these are called extension elements. If a
  38      classes parser encounters an unexpected XML construct, it is translated
  39      into an ExtensionElement instance. ExtensionElement is designed to fully
  40      capture the information in the XML. Child nodes in an XML extension are
  41      turned into ExtensionElements as well.
  42"""
  43
  44
  45__author__ = 'api.jscudder (Jeffrey Scudder)'
  46
  47try:
  48  from xml.etree import cElementTree as ElementTree
  49except ImportError:
  50  try:
  51    import cElementTree as ElementTree
  52  except ImportError:
  53    try:
  54      from xml.etree import ElementTree
  55    except ImportError:
  56      from elementtree import ElementTree
  57import warnings
  58
  59
  60# XML namespaces which are often used in Atom entities.
  61ATOM_NAMESPACE = 'http://www.w3.org/2005/Atom'
  62ELEMENT_TEMPLATE = '{http://www.w3.org/2005/Atom}%s'
  63APP_NAMESPACE = 'http://purl.org/atom/app#'
  64APP_TEMPLATE = '{http://purl.org/atom/app#}%s'
  65
  66# This encoding is used for converting strings before translating the XML
  67# into an object.
  68XML_STRING_ENCODING = 'utf-8'
  69# The desired string encoding for object members. set or monkey-patch to
  70# unicode if you want object members to be Python unicode strings, instead of
  71# encoded strings
  72MEMBER_STRING_ENCODING = 'utf-8'
  73#MEMBER_STRING_ENCODING = unicode
  74
  75# If True, all methods which are exclusive to v1 will raise a
  76# DeprecationWarning
  77ENABLE_V1_WARNINGS = False
  78
  79
  80def v1_deprecated(warning=None):
  81  """Shows a warning if ENABLE_V1_WARNINGS is True.
  82
  83  Function decorator used to mark methods used in v1 classes which
  84  may be removed in future versions of the library.
  85  """
  86  warning = warning or ''
  87  # This closure is what is returned from the deprecated function.
  88  def mark_deprecated(f):
  89    # The deprecated_function wraps the actual call to f.
  90    def optional_warn_function(*args, **kwargs):
  91      if ENABLE_V1_WARNINGS:
  92        warnings.warn(warning, DeprecationWarning, stacklevel=2)
  93      return f(*args, **kwargs)
  94    # Preserve the original name to avoid masking all decorated functions as
  95    # 'deprecated_function'
  96    try:
  97      optional_warn_function.func_name = f.func_name
  98    except TypeError:
  99      pass # In Python2.3 we can't set the func_name
 100    return optional_warn_function
 101  return mark_deprecated
 102
 103
 104def CreateClassFromXMLString(target_class, xml_string, string_encoding=None):
 105  """Creates an instance of the target class from the string contents.
 106
 107  Args:
 108    target_class: class The class which will be instantiated and populated
 109        with the contents of the XML. This class must have a _tag and a
 110        _namespace class variable.
 111    xml_string: str A string which contains valid XML. The root element
 112        of the XML string should match the tag and namespace of the desired
 113        class.
 114    string_encoding: str The character encoding which the xml_string should
 115        be converted to before it is interpreted and translated into
 116        objects. The default is None in which case the string encoding
 117        is not changed.
 118
 119  Returns:
 120    An instance of the target class with members assigned according to the
 121    contents of the XML - or None if the root XML tag and namespace did not
 122    match those of the target class.
 123  """
 124  encoding = string_encoding or XML_STRING_ENCODING
 125  if encoding and isinstance(xml_string, unicode):
 126    xml_string = xml_string.encode(encoding)
 127  tree = ElementTree.fromstring(xml_string)
 128  return _CreateClassFromElementTree(target_class, tree)
 129
 130
 131CreateClassFromXMLString = v1_deprecated(
 132    'Please use atom.core.parse with atom.data classes instead.')(
 133        CreateClassFromXMLString)
 134
 135
 136def _CreateClassFromElementTree(target_class, tree, namespace=None, tag=None):
 137  """Instantiates the class and populates members according to the tree.
 138
 139  Note: Only use this function with classes that have _namespace and _tag
 140  class members.
 141
 142  Args:
 143    target_class: class The class which will be instantiated and populated
 144        with the contents of the XML.
 145    tree: ElementTree An element tree whose contents will be converted into
 146        members of the new target_class instance.
 147    namespace: str (optional) The namespace which the XML tree's root node must
 148        match. If omitted, the namespace defaults to the _namespace of the
 149        target class.
 150    tag: str (optional) The tag which the XML tree's root node must match. If
 151        omitted, the tag defaults to the _tag class member of the target
 152        class.
 153
 154    Returns:
 155      An instance of the target class - or None if the tag and namespace of
 156      the XML tree's root node did not match the desired namespace and tag.
 157  """
 158  if namespace is None:
 159    namespace = target_class._namespace
 160  if tag is None:
 161    tag = target_class._tag
 162  if tree.tag == '{%s}%s' % (namespace, tag):
 163    target = target_class()
 164    target._HarvestElementTree(tree)
 165    return target
 166  else:
 167    return None
 168
 169
 170class ExtensionContainer(object):
 171
 172  def __init__(self, extension_elements=None, extension_attributes=None,
 173      text=None):
 174    self.extension_elements = extension_elements or []
 175    self.extension_attributes = extension_attributes or {}
 176    self.text = text
 177
 178  __init__ = v1_deprecated(
 179      'Please use data model classes in atom.data instead.')(
 180          __init__)
 181
 182  # Three methods to create an object from an ElementTree
 183  def _HarvestElementTree(self, tree):
 184    # Fill in the instance members from the contents of the XML tree.
 185    for child in tree:
 186      self._ConvertElementTreeToMember(child)
 187    for attribute, value in tree.attrib.iteritems():
 188      self._ConvertElementAttributeToMember(attribute, value)
 189    # Encode the text string according to the desired encoding type. (UTF-8)
 190    if tree.text:
 191      if MEMBER_STRING_ENCODING is unicode:
 192        self.text = tree.text
 193      else:
 194        self.text = tree.text.encode(MEMBER_STRING_ENCODING)
 195
 196  def _ConvertElementTreeToMember(self, child_tree, current_class=None):
 197    self.extension_elements.append(_ExtensionElementFromElementTree(
 198        child_tree))
 199
 200  def _ConvertElementAttributeToMember(self, attribute, value):
 201    # Encode the attribute value's string with the desired type Default UTF-8
 202    if value:
 203      if MEMBER_STRING_ENCODING is unicode:
 204        self.extension_attributes[attribute] = value
 205      else:
 206        self.extension_attributes[attribute] = value.encode(
 207            MEMBER_STRING_ENCODING)
 208
 209  # One method to create an ElementTree from an object
 210  def _AddMembersToElementTree(self, tree):
 211    for child in self.extension_elements:
 212      child._BecomeChildElement(tree)
 213    for attribute, value in self.extension_attributes.iteritems():
 214      if value:
 215        if isinstance(value, unicode) or MEMBER_STRING_ENCODING is unicode:
 216          tree.attrib[attribute] = value
 217        else:
 218          # Decode the value from the desired encoding (default UTF-8).
 219          tree.attrib[attribute] = value.decode(MEMBER_STRING_ENCODING)
 220    if self.text:
 221      if isinstance(self.text, unicode) or MEMBER_STRING_ENCODING is unicode:
 222        tree.text = self.text
 223      else:
 224        tree.text = self.text.decode(MEMBER_STRING_ENCODING)
 225
 226  def FindExtensions(self, tag=None, namespace=None):
 227    """Searches extension elements for child nodes with the desired name.
 228
 229    Returns a list of extension elements within this object whose tag
 230    and/or namespace match those passed in. To find all extensions in
 231    a particular namespace, specify the namespace but not the tag name.
 232    If you specify only the tag, the result list may contain extension
 233    elements in multiple namespaces.
 234
 235    Args:
 236      tag: str (optional) The desired tag
 237      namespace: str (optional) The desired namespace
 238
 239    Returns:
 240      A list of elements whose tag and/or namespace match the parameters
 241      values
 242    """
 243
 244    results = []
 245
 246    if tag and namespace:
 247      for element in self.extension_elements:
 248        if element.tag == tag and element.namespace == namespace:
 249          results.append(element)
 250    elif tag and not namespace:
 251      for element in self.extension_elements:
 252        if element.tag == tag:
 253          results.append(element)
 254    elif namespace and not tag:
 255      for element in self.extension_elements:
 256        if element.namespace == namespace:
 257          results.append(element)
 258    else:
 259      for element in self.extension_elements:
 260        results.append(element)
 261
 262    return results
 263
 264
 265class AtomBase(ExtensionContainer):
 266
 267  _children = {}
 268  _attributes = {}
 269
 270  def __init__(self, extension_elements=None, extension_attributes=None,
 271      text=None):
 272    self.extension_elements = extension_elements or []
 273    self.extension_attributes = extension_attributes or {}
 274    self.text = text
 275
 276  __init__ = v1_deprecated(
 277      'Please use data model classes in atom.data instead.')(
 278          __init__)
 279
 280  def _ConvertElementTreeToMember(self, child_tree):
 281    # Find the element's tag in this class's list of child members
 282    if self.__class__._children.has_key(child_tree.tag):
 283      member_name = self.__class__._children[child_tree.tag][0]
 284      member_class = self.__class__._children[child_tree.tag][1]
 285      # If the class member is supposed to contain a list, make sure the
 286      # matching member is set to a list, then append the new member
 287      # instance to the list.
 288      if isinstance(member_class, list):
 289        if getattr(self, member_name) is None:
 290          setattr(self, member_name, [])
 291        getattr(self, member_name).append(_CreateClassFromElementTree(
 292            member_class[0], child_tree))
 293      else:
 294        setattr(self, member_name,
 295                _CreateClassFromElementTree(member_class, child_tree))
 296    else:
 297      ExtensionContainer._ConvertElementTreeToMember(self, child_tree)
 298
 299  def _ConvertElementAttributeToMember(self, attribute, value):
 300    # Find the attribute in this class's list of attributes.
 301    if self.__class__._attributes.has_key(attribute):
 302      # Find the member of this class which corresponds to the XML attribute
 303      # (lookup in current_class._attributes) and set this member to the
 304      # desired value (using self.__dict__).
 305      if value:
 306        # Encode the string to capture non-ascii characters (default UTF-8)
 307        if MEMBER_STRING_ENCODING is unicode:
 308          setattr(self, self.__class__._attributes[attribute], value)
 309        else:
 310          setattr(self, self.__class__._attributes[attribute],
 311                  value.encode(MEMBER_STRING_ENCODING))
 312    else:
 313      ExtensionContainer._ConvertElementAttributeToMember(
 314          self, attribute, value)
 315
 316  # Three methods to create an ElementTree from an object
 317  def _AddMembersToElementTree(self, tree):
 318    # Convert the members of this class which are XML child nodes.
 319    # This uses the class's _children dictionary to find the members which
 320    # should become XML child nodes.
 321    member_node_names = [values[0] for tag, values in
 322                                       self.__class__._children.iteritems()]
 323    for member_name in member_node_names:
 324      member = getattr(self, member_name)
 325      if member is None:
 326        pass
 327      elif isinstance(member, list):
 328        for instance in member:
 329          instance._BecomeChildElement(tree)
 330      else:
 331        member._BecomeChildElement(tree)
 332    # Convert the members of this class which are XML attributes.
 333    for xml_attribute, member_name in self.__class__._attributes.iteritems():
 334      member = getattr(self, member_name)
 335      if member is not None:
 336        if isinstance(member, unicode) or MEMBER_STRING_ENCODING is unicode:
 337          tree.attrib[xml_attribute] = member
 338        else:
 339          tree.attrib[xml_attribute] = member.decode(MEMBER_STRING_ENCODING)
 340    # Lastly, call the ExtensionContainers's _AddMembersToElementTree to
 341    # convert any extension attributes.
 342    ExtensionContainer._AddMembersToElementTree(self, tree)
 343
 344
 345  def _BecomeChildElement(self, tree):
 346    """
 347
 348    Note: Only for use with classes that have a _tag and _namespace class
 349    member. It is in AtomBase so that it can be inherited but it should
 350    not be called on instances of AtomBase.
 351
 352    """
 353    new_child = ElementTree.Element('')
 354    tree.append(new_child)
 355    new_child.tag = '{%s}%s' % (self.__class__._namespace,
 356                                self.__class__._tag)
 357    self._AddMembersToElementTree(new_child)
 358
 359  def _ToElementTree(self):
 360    """
 361
 362    Note, this method is designed to be used only with classes that have a
 363    _tag and _namespace. It is placed in AtomBase for inheritance but should
 364    not be called on this class.
 365
 366    """
 367    new_tree = ElementTree.Element('{%s}%s' % (self.__class__._namespace,
 368                                               self.__class__._tag))
 369    self._AddMembersToElementTree(new_tree)
 370    return new_tree
 371
 372  def ToString(self, string_encoding='UTF-8'):
 373    """Converts the Atom object to a string containing XML."""
 374    return ElementTree.tostring(self._ToElementTree(), encoding=string_encoding)
 375
 376  def __str__(self):
 377    return self.ToString()
 378
 379
 380class Name(AtomBase):
 381  """The atom:name element"""
 382
 383  _tag = 'name'
 384  _namespace = ATOM_NAMESPACE
 385  _children = AtomBase._children.copy()
 386  _attributes = AtomBase._attributes.copy()
 387
 388  def __init__(self, text=None, extension_elements=None,
 389      extension_attributes=None):
 390    """Constructor for Name
 391
 392    Args:
 393      text: str The text data in the this element
 394      extension_elements: list A  list of ExtensionElement instances
 395      extension_attributes: dict A dictionary of attribute value string pairs
 396    """
 397
 398    self.text = text
 399    self.extension_elements = extension_elements or []
 400    self.extension_attributes = extension_attributes or {}
 401
 402
 403def NameFromString(xml_string):
 404  return CreateClassFromXMLString(Name, xml_string)
 405
 406
 407class Email(AtomBase):
 408  """The atom:email element"""
 409
 410  _tag = 'email'
 411  _namespace = ATOM_NAMESPACE
 412  _children = AtomBase._children.copy()
 413  _attributes = AtomBase._attributes.copy()
 414
 415  def __init__(self, text=None, extension_elements=None,
 416      extension_attributes=None):
 417    """Constructor for Email
 418
 419    Args:
 420      extension_elements: list A  list of ExtensionElement instances
 421      extension_attributes: dict A dictionary of attribute value string pairs
 422      text: str The text data in the this element
 423    """
 424
 425    self.text = text
 426    self.extension_elements = extension_elements or []
 427    self.extension_attributes = extension_attributes or {}
 428
 429
 430def EmailFromString(xml_string):
 431  return CreateClassFromXMLString(Email, xml_string)
 432
 433
 434class Uri(AtomBase):
 435  """The atom:uri element"""
 436
 437  _tag = 'uri'
 438  _namespace = ATOM_NAMESPACE
 439  _children = AtomBase._children.copy()
 440  _attributes = AtomBase._attributes.copy()
 441
 442  def __init__(self, text=None, extension_elements=None,
 443      extension_attributes=None):
 444    """Constructor for Uri
 445
 446    Args:
 447      extension_elements: list A  list of ExtensionElement instances
 448      extension_attributes: dict A dictionary of attribute value string pairs
 449      text: str The text data in the this element
 450    """
 451
 452    self.text = text
 453    self.extension_elements = extension_elements or []
 454    self.extension_attributes = extension_attributes or {}
 455
 456
 457def UriFromString(xml_string):
 458  return CreateClassFromXMLString(Uri, xml_string)
 459
 460
 461class Person(AtomBase):
 462  """A foundation class from which atom:author and atom:contributor extend.
 463
 464  A person contains information like name, email address, and web page URI for
 465  an author or contributor to an Atom feed.
 466  """
 467
 468  _children = AtomBase._children.copy()
 469  _attributes = AtomBase._attributes.copy()
 470  _children['{%s}name' % (ATOM_NAMESPACE)] = ('name', Name)
 471  _children['{%s}email' % (ATOM_NAMESPACE)] = ('email', Email)
 472  _children['{%s}uri' % (ATOM_NAMESPACE)] = ('uri', Uri)
 473
 474  def __init__(self, name=None, email=None, uri=None,
 475      extension_elements=None, extension_attributes=None, text=None):
 476    """Foundation from which author and contributor are derived.
 477
 478    The constructor is provided for illustrative purposes, you should not
 479    need to instantiate a Person.
 480
 481    Args:
 482      name: Name The person's name
 483      email: Email The person's email address
 484      uri: Uri The URI of the person's webpage
 485      extension_elements: list A list of ExtensionElement instances which are
 486          children of this element.
 487      extension_attributes: dict A dictionary of strings which are the values
 488          for additional XML attributes of this element.
 489      text: String The text contents of the element. This is the contents
 490          of the Entry's XML text node. (Example: <foo>This is the text</foo>)
 491    """
 492
 493    self.name = name
 494    self.email = email
 495    self.uri = uri
 496    self.extension_elements = extension_elements or []
 497    self.extension_attributes = extension_attributes or {}
 498    self.text = text
 499
 500
 501class Author(Person):
 502  """The atom:author element
 503
 504  An author is a required element in Feed.
 505  """
 506
 507  _tag = 'author'
 508  _namespace = ATOM_NAMESPACE
 509  _children = Person._children.copy()
 510  _attributes = Person._attributes.copy()
 511  #_children = {}
 512  #_attributes = {}
 513
 514  def __init__(self, name=None, email=None, uri=None,
 515      extension_elements=None, extension_attributes=None, text=None):
 516    """Constructor for Author
 517
 518    Args:
 519      name: Name
 520      email: Email
 521      uri: Uri
 522      extension_elements: list A  list of ExtensionElement instances
 523      extension_attributes: dict A dictionary of attribute value string pairs
 524      text: str The text data in the this element
 525    """
 526
 527    self.name = name
 528    self.email = email
 529    self.uri = uri
 530    self.extension_elements = extension_elements or []
 531    self.extension_attributes = extension_attributes or {}
 532    self.text = text
 533
 534
 535def AuthorFromString(xml_string):
 536  return CreateClassFromXMLString(Author, xml_string)
 537
 538
 539class Contributor(Person):
 540  """The atom:contributor element"""
 541
 542  _tag = 'contributor'
 543  _namespace = ATOM_NAMESPACE
 544  _children = Person._children.copy()
 545  _attributes = Person._attributes.copy()
 546
 547  def __init__(self, name=None, email=None, uri=None,
 548      extension_elements=None, extension_attributes=None, text=None):
 549    """Constructor for Contributor
 550
 551    Args:
 552      name: Name
 553      email: Email
 554      uri: Uri
 555      extension_elements: list A  list of ExtensionElement instances
 556      extension_attributes: dict A dictionary of attribute value string pairs
 557      text: str The text data in the this element
 558    """
 559
 560    self.name = name
 561    self.email = email
 562    self.uri = uri
 563    self.extension_elements = extension_elements or []
 564    self.extension_attributes = extension_attributes or {}
 565    self.text = text
 566
 567
 568def ContributorFromString(xml_string):
 569  return CreateClassFromXMLString(Contributor, xml_string)
 570
 571
 572class Link(AtomBase):
 573  """The atom:link element"""
 574
 575  _tag = 'link'
 576  _namespace = ATOM_NAMESPACE
 577  _children = AtomBase._children.copy()
 578  _attributes = AtomBase._attributes.copy()
 579  _attributes['rel'] = 'rel'
 580  _attributes['href'] = 'href'
 581  _attributes['type'] = 'type'
 582  _attributes['title'] = 'title'
 583  _attributes['length'] = 'length'
 584  _attributes['hreflang'] = 'hreflang'
 585
 586  def __init__(self, href=None, rel=None, link_type=None, hreflang=None,
 587      title=None, length=None, text=None, extension_elements=None,
 588      extension_attributes=None):
 589    """Constructor for Link
 590
 591    Args:
 592      href: string The href attribute of the link
 593      rel: string
 594      type: string
 595      hreflang: string The language for the href
 596      title: string
 597      length: string The length of the href's destination
 598      extension_elements: list A  list of ExtensionElement instances
 599      extension_attributes: dict A dictionary of attribute value string pairs
 600      text: str The text data in the this element
 601    """
 602
 603    self.href = href
 604    self.rel = rel
 605    self.type = link_type
 606    self.hreflang = hreflang
 607    self.title = title
 608    self.length = length
 609    self.text = text
 610    self.extension_elements = extension_elements or []
 611    self.extension_attributes = extension_attributes or {}
 612
 613
 614def LinkFromString(xml_string):
 615  return CreateClassFromXMLString(Link, xml_string)
 616
 617
 618class Generator(AtomBase):
 619  """The atom:generator element"""
 620
 621  _tag = 'generator'
 622  _namespace = ATOM_NAMESPACE
 623  _children = AtomBase._children.copy()
 624  _attributes = AtomBase._attributes.copy()
 625  _attributes['uri'] = 'uri'
 626  _attributes['version'] = 'version'
 627
 628  def __init__(self, uri=None, version=None, text=None,
 629      extension_elements=None, extension_attributes=None):
 630    """Constructor for Generator
 631
 632    Args:
 633      uri: string
 634      version: string
 635      text: str The text data in the this element
 636      extension_elements: list A  list of ExtensionElement instances
 637      extension_attributes: dict A dictionary of attribute value string pairs
 638    """
 639
 640    self.uri = uri
 641    self.version = version
 642    self.text = text
 643    self.extension_elements = extension_elements or []
 644    self.extension_attributes = extension_attributes or {}
 645
 646def GeneratorFromString(xml_string):
 647  return CreateClassFromXMLString(Generator, xml_string)
 648
 649
 650class Text(AtomBase):
 651  """A foundation class from which atom:title, summary, etc. extend.
 652
 653  This class should never be instantiated.
 654  """
 655
 656  _children = AtomBase._children.copy()
 657  _attributes = AtomBase._attributes.copy()
 658  _attributes['type'] = 'type'
 659
 660  def __init__(self, text_type=None, text=None, extension_elements=None,
 661      extension_attributes=None):
 662    """Constructor for Text
 663
 664    Args:
 665      text_type: string
 666      text: str The text data in the this element
 667      extension_elements: list A  list of ExtensionElement instances
 668      extension_attributes: dict A dictionary of attribute value string pairs
 669    """
 670
 671    self.type = text_type
 672    self.text = text
 673    self.extension_elements = extension_elements or []
 674    self.extension_attributes = extension_attributes or {}
 675
 676
 677class Title(Text):
 678  """The atom:title element"""
 679
 680  _tag = 'title'
 681  _namespace = ATOM_NAMESPACE
 682  _children = Text._children.copy()
 683  _attributes = Text._attributes.copy()
 684
 685  def __init__(self, title_type=None, text=None, extension_elements=None,
 686      extension_attributes=None):
 687    """Constructor for Title
 688
 689    Args:
 690      title_type: string
 691      text: str The text data in the this element
 692      extension_elements: list A  list of ExtensionElement instances
 693      extension_attributes: dict A dictionary of attribute value string pairs
 694    """
 695
 696    self.type = title_type
 697    self.text = text
 698    self.extension_elements = extension_elements or []
 699    self.extension_attributes = extension_attributes or {}
 700
 701
 702def TitleFromString(xml_string):
 703  return CreateClassFromXMLString(Title, xml_string)
 704
 705
 706class Subtitle(Text):
 707  """The atom:subtitle element"""
 708
 709  _tag = 'subtitle'
 710  _namespace = ATOM_NAMESPACE
 711  _children = Text._children.copy()
 712  _attributes = Text._attributes.copy()
 713
 714  def __init__(self, subtitle_type=None, text=None, extension_elements=None,
 715      extension_attributes=None):
 716    """Constructor for Subtitle
 717
 718    Args:
 719      subtitle_type: string
 720      text: str The text data in the this element
 721      extension_elements: list A  list of ExtensionElement instances
 722      extension_attributes: dict A dictionary of attribute value string pairs
 723    """
 724
 725    self.type = subtitle_type
 726    self.text = text
 727    self.extension_elements = extension_elements or []
 728    self.extension_attributes = extension_attributes or {}
 729
 730
 731def SubtitleFromString(xml_string):
 732  return CreateClassFromXMLString(Subtitle, xml_string)
 733
 734
 735class Rights(Text):
 736  """The atom:rights element"""
 737
 738  _tag = 'rights'
 739  _namespace = ATOM_NAMESPACE
 740  _children = Text._children.copy()
 741  _attributes = Text._attributes.copy()
 742
 743  def __init__(self, rights_type=None, text=None, extension_elements=None,
 744      extension_attributes=None):
 745    """Constructor for Rights
 746
 747    Args:
 748      rights_type: string
 749      text: str The text data in the this element
 750      extension_elements: list A  list of ExtensionElement instances
 751      extension_attributes: dict A dictionary of attribute value string pairs
 752    """
 753
 754    self.type = rights_type
 755    self.text = text
 756    self.extension_elements = extension_elements or []
 757    self.extension_attributes = extension_attributes or {}
 758
 759
 760def RightsFromString(xml_string):
 761  return CreateClassFromXMLString(Rights, xml_string)
 762
 763
 764class Summary(Text):
 765  """The atom:summary element"""
 766
 767  _tag = 'summary'
 768  _namespace = ATOM_NAMESPACE
 769  _children = Text._children.copy()
 770  _attributes = Text._attributes.copy()
 771
 772  def __init__(self, summary_type=None, text=None, extension_elements=None,
 773      extension_attributes=None):
 774    """Constructor for Summary
 775
 776    Args:
 777      summary_type: string
 778      text: str The text data in the this element
 779      extension_elements: list A  list of ExtensionElement instances
 780      extension_attributes: dict A dictionary of attribute value string pairs
 781    """
 782
 783    self.type = summary_type
 784    self.text = text
 785    self.extension_elements = extension_elements or []
 786    self.extension_attributes = extension_attributes or {}
 787
 788
 789def SummaryFromString(xml_string):
 790  return CreateClassFromXMLString(Summary, xml_string)
 791
 792
 793class Content(Text):
 794  """The atom:content element"""
 795
 796  _tag = 'content'
 797  _namespace = ATOM_NAMESPACE
 798  _children = Text._children.copy()
 799  _attributes = Text._attributes.copy()
 800  _attributes['src'] = 'src'
 801
 802  def __init__(self, content_type=None, src=None, text=None,
 803      extension_elements=None, extension_attributes=None):
 804    """Constructor for Content
 805
 806    Args:
 807      content_type: string
 808      src: string
 809      text: str The text data in the this element
 810      extension_elements: list A  list of ExtensionElement instances
 811      extension_attributes: dict A dictionary of attribute value string pairs
 812    """
 813
 814    self.type = content_type
 815    self.src = src
 816    self.text = text
 817    self.extension_elements = extension_elements or []
 818    self.extension_attributes = extension_attributes or {}
 819
 820def ContentFromString(xml_string):
 821  return CreateClassFromXMLString(Content, xml_string)
 822
 823
 824class Category(AtomBase):
 825  """The atom:category element"""
 826
 827  _tag = 'category'
 828  _namespace = ATOM_NAMESPACE
 829  _children = AtomBase._children.copy()
 830  _attributes = AtomBase._attributes.copy()
 831  _attributes['term'] = 'term'
 832  _attributes['scheme'] = 'scheme'
 833  _attributes['label'] = 'label'
 834
 835  def __init__(self, term=None, scheme=None, label=None, text=None,
 836      extension_elements=None, extension_attributes=None):
 837    """Constructor for Category
 838
 839    Args:
 840      term: str
 841      scheme: str
 842      label: str
 843      text: str The text data in the this element
 844      extension_elements: list A  list of ExtensionElement instances
 845      extension_attributes: dict A dictionary of attribute value string pairs
 846    """
 847
 848    self.term = term
 849    self.scheme = scheme
 850    self.label = label
 851    self.text = text
 852    self.extension_elements = extension_elements or []
 853    self.extension_attributes = extension_attributes or {}
 854
 855
 856def CategoryFromString(xml_string):
 857  return CreateClassFromXMLString(Category, xml_string)
 858
 859
 860class Id(AtomBase):
 861  """The atom:id element."""
 862
 863  _tag = 'id'
 864  _namespace = ATOM_NAMESPACE
 865  _children = AtomBase._children.copy()
 866  _attributes = AtomBase._attributes.copy()
 867
 868  def __init__(self, text=None, extension_elements=None,
 869      extension_attributes=None):
 870    """Constructor for Id
 871
 872    Args:
 873      text: str The text data in the this element
 874      extension_elements: list A  list of ExtensionElement instances
 875      extension_attributes: dict A dictionary of attribute value string pairs
 876    """
 877
 878    self.text = text
 879    self.extension_elements = extension_elements or []
 880    self.extension_attributes = extension_attributes or {}
 881
 882
 883def IdFromString(xml_string):
 884  return CreateClassFromXMLString(Id, xml_string)
 885
 886
 887class Icon(AtomBase):
 888  """The atom:icon element."""
 889
 890  _tag = 'icon'
 891  _namespace = ATOM_NAMESPACE
 892  _children = AtomBase._children.copy()
 893  _attributes = AtomBase._attributes.copy()
 894
 895  def __init__(self, text=None, extension_elements=None,
 896      extension_attributes=None):
 897    """Constructor for Icon
 898
 899    Args:
 900      text: str The text data in the this element
 901      extension_elements: list A  list of ExtensionElement instances
 902      extension_attributes: dict A dictionary of attribute value string pairs
 903    """
 904
 905    self.text = text
 906    self.extension_elements = extension_elements or []
 907    self.extension_attributes = extension_attributes or {}
 908
 909
 910def IconFromString(xml_string):
 911  return CreateClassFromXMLString(Icon, xml_string)
 912
 913
 914class Logo(AtomBase):
 915  """The atom:logo element."""
 916
 917  _tag = 'logo'
 918  _namespace = ATOM_NAMESPACE
 919  _children = AtomBase._children.copy()
 920  _attributes = AtomBase._attributes.copy()
 921
 922  def __init__(self, text=None, extension_elements=None,
 923      extension_attributes=None):
 924    """Constructor for Logo
 925
 926    Args:
 927      text: str The text data in the this element
 928      extension_elements: list A  list of ExtensionElement instances
 929      extension_attributes: dict A dictionary of attribute value string pairs
 930    """
 931
 932    self.text = text
 933    self.extension_elements = extension_elements or []
 934    self.extension_attributes = extension_attributes or {}
 935
 936
 937def LogoFromString(xml_string):
 938  return CreateClassFromXMLString(Logo, xml_string)
 939
 940
 941class Draft(AtomBase):
 942  """The app:draft element which indicates if this entry should be public."""
 943
 944  _tag = 'draft'
 945  _namespace = APP_NAMESPACE
 946  _children = AtomBase._children.copy()
 947  _attributes = AtomBase._attributes.copy()
 948
 949  def __init__(self, text=None, extension_elements=None,
 950      extension_attributes=None):
 951    """Constructor for app:draft
 952
 953    Args:
 954      text: str The text data in the this element
 955      extension_elements: list A  list of ExtensionElement instances
 956      extension_attributes: dict A dictionary of attribute value string pairs
 957    """
 958
 959    self.text = text
 960    self.extension_elements = extension_elements or []
 961    self.extension_attributes = extension_attributes or {}
 962
 963
 964def DraftFromString(xml_string):
 965  return CreateClassFromXMLString(Draft, xml_string)
 966
 967
 968class Control(AtomBase):
 969  """The app:control element indicating restrictions on publication.
 970
 971  The APP control element may contain a draft element indicating whether or
 972  not this entry should be publicly available.
 973  """
 974
 975  _tag = 'control'
 976  _namespace = APP_NAMESPACE
 977  _children = AtomBase._children.copy()
 978  _attributes = AtomBase._attributes.copy()
 979  _children['{%s}draft' % APP_NAMESPACE] = ('draft', Draft)
 980
 981  def __init__(self, draft=None, text=None, extension_elements=None,
 982        extension_attributes=None):
 983    """Constructor for app:control"""
 984
 985    self.draft = draft
 986    self.text = text
 987    self.extension_elements = extension_elements or []
 988    self.extension_attributes = extension_attributes or {}
 989
 990
 991def ControlFromString(xml_string):
 992  return CreateClassFromXMLString(Control, xml_string)
 993
 994
 995class Date(AtomBase):
 996  """A parent class for atom:updated, published, etc."""
 997
 998  #TODO Add text to and from time conversion methods to allow users to set
 999  # the contents of a Date to a python DateTime object.
1000
1001  _children = AtomBase._children.copy()
1002  _attributes = AtomBase._attributes.copy()
1003
1004  def __init__(self, text=None, extension_elements=None,
1005      extension_attributes=None):
1006    self.text = text
1007    self.extension_elements = extension_elements or []
1008    self.extension_attributes = extension_attributes or {}
1009
1010
1011class Updated(Date):
1012  """The atom:updated element."""
1013
1014  _tag = 'updated'
1015  _namespace = ATOM_NAMESPACE
1016  _children = Date._children.copy()
1017  _attributes = Date._attributes.copy()
1018
1019  def __init__(self, text=None, extension_elements=None,
1020      extension_attributes=None):
1021    """Constructor for Updated
1022
1023    Args:
1024      text: str The text data in the this element
1025      extension_elements: list A  list of ExtensionElement instances
1026      extension_attributes: dict A dictionary of attribute value string pairs
1027    """
1028
1029    self.text = text
1030    self.extension_elements = extension_elements or []
1031    self.extension_attributes = extension_attributes or {}
1032
1033
1034def UpdatedFromString(xml_string):
1035  return CreateClassFromXMLString(Updated, xml_string)
1036
1037
1038class Published(Date):
1039  """The atom:published element."""
1040
1041  _tag = 'published'
1042  _namespace = ATOM_NAMESPACE
1043  _children = Date._children.copy()
1044  _attributes = Date._attributes.copy()
1045
1046  def __init__(self, text=None, extension_elements=None,
1047      extension_attributes=None):
1048    """Constructor for Published
1049
1050    Args:
1051      text: str The text data in the this element
1052      extension_elements: list A  list of ExtensionElement instances
1053      extension_attributes: dict A dictionary of attribute value string pairs
1054    """
1055
1056    self.text = text
1057    self.extension_elements = extension_elements or []
1058    self.extension_attributes = extension_attributes or {}
1059
1060
1061def PublishedFromString(xml_string):
1062  return CreateClassFromXMLString(Published, xml_string)
1063
1064
1065class LinkFinder(object):
1066  """An "interface" providing methods to find link elements
1067
1068  Entry elements often contain multiple links which differ in the rel
1069  attribute or content type. Often, developers are interested in a specific
1070  type of link so this class provides methods to find specific classes of
1071  links.
1072
1073  This class is used as a mixin in Atom entries and feeds.
1074  """
1075
1076  def GetSelfLink(self):
1077    """Find the first link with rel set to 'self'
1078
1079    Returns:
1080      An atom.Link or none if none of the links had rel equal to 'self'
1081    """
1082
1083    for a_link in self.link:
1084      if a_link.rel == 'self':
1085        return a_link
1086    return None
1087
1088  def GetEditLink(self):
1089    for a_link in self.link:
1090      if a_link.rel == 'edit':
1091        return a_link
1092    return None
1093
1094  def GetEditMediaLink(self):
1095    for a_link in self.link:
1096      if a_link.rel == 'edit-media':
1097        return a_link
1098    return None
1099
1100  def GetNextLink(self):
1101    for a_link in self.link:
1102      if a_link.rel == 'next':
1103        return a_link
1104    return None
1105
1106  def GetLicenseLink(self):
1107    for a_link in self.link:
1108      if a_link.rel == 'license':
1109        return a_link
1110    return None
1111
1112  def GetAlternateLink(self):
1113    for a_link in self.link:
1114      if a_link.rel == 'alternate':
1115        return a_link
1116    return None
1117
1118
1119class FeedEntryParent(AtomBase, LinkFinder):
1120  """A super class for atom:feed and entry, contains shared attributes"""
1121
1122  _children = AtomBase._children.copy()
1123  _attributes = AtomBase._attributes.copy()
1124  _children['{%s}author' % ATOM_NAMESPACE] = ('author', [Author])
1125  _children['{%s}category' % ATOM_NAMESPACE] = ('category', [Category])
1126  _children['{%s}contributor' % ATOM_NAMESPACE] = ('contributor', [Contributor])
1127  _children['{%s}id' % ATOM_NAMESPACE] = ('id', Id)
1128  _children['{%s}link' % ATOM_NAMESPACE] = ('link', [Link])
1129  _children['{%s}rights' % ATOM_NAMESPACE] = ('rights', Rights)
1130  _children['{%s}title' % ATOM_NAMESPACE] = ('title', Title)
1131  _children['{%s}updated' % ATOM_NAMESPACE] = ('updated', Updated)
1132
1133  def __init__(self, author=None, category=None, contributor=None,
1134      atom_id=None, link=None, rights=None, title=None, updated=None,
1135      text=None, extension_elements=None, extension_attributes=None):
1136    self.author = author or []
1137    self.category = category or []
1138    self.contributor = contributor or []
1139    self.id = atom_id
1140    self.link = link or []
1141    self.rights = rights
1142    self.title = title
1143    self.updated = updated
1144    self.text = text
1145    self.extension_elements = extension_elements or []
1146    self.extension_attributes = extension_attributes or {}
1147
1148
1149class Source(FeedEntryParent):
1150  """The atom:source element"""
1151
1152  _tag = 'source'
1153  _namespace = ATOM_NAMESPACE
1154  _children = FeedEntryParent._children.copy()
1155  _attributes = FeedEntryParent._attributes.copy()
1156  _children['{%s}generator' % ATOM_NAMESPACE] = ('generator', Generator)
1157  _children['{%s}icon' % ATOM_NAMESPACE] = ('icon', Icon)
1158  _children['{%s}logo' % ATOM_NAMESPACE] = ('logo', Logo)
1159  _children['{%s}subtitle' % ATOM_NAMESPACE] = ('subtitle', Subtitle)
1160
1161  def __init__(self, author=None, category=None, contributor=None,
1162      generator=None, icon=None, atom_id=None, link=None, logo=None,
1163      rights=None, subtitle=None, title=None, updated=None, text=None,
1164      extension_elements=None, extension_attributes=None):
1165    """Constructor for Source
1166
1167    Args:
1168      author: list (optional) A list of Author instances which belong to this
1169          class.
1170      category: list (optional) A list of Category instances
1171      contributor: list (optional) A list on Contributor instances
1172      generator: Generator (optional)
1173      icon: Icon (optional)
1174      id: Id (optional) The entry's Id element
1175      link: list (optional) A list of Link instances
1176      logo: Logo (optional)
1177      rights: Rights (optional) The entry's Rights element
1178      subtitle: Subtitle (optional) The entry's subtitle element
1179      title: Title (optional) the entry's title element
1180      updated: Updated (optional) the entry's updated element
1181      text: String (optional) The text contents of the element. This is the
1182          contents of the Entry's XML text node.
1183          (Example: <foo>This is the text</foo>)
1184      extension_elements: list (optional) A list of ExtensionElement instances
1185          which are children of this element.
1186      extension_attributes: dict (optional) A dictionary of strings which are
1187          the values for additional XML attributes of this element.
1188    """
1189
1190    self.author = author or []
1191    self.category = category or []
1192    self.contributor = contributor or []
1193    self.generator = generator
1194    self.icon = icon
1195    self.id = atom_id
1196    self.link = link or []
1197    self.logo = logo
1198    self.rights = rights
1199    self.subtitle = subtitle
1200    self.title = title
1201    self.updated = updated
1202    self.text = text
1203    self.extension_elements = extension_elements or []
1204    self.extension_attributes = extension_attributes or {}
1205
1206
1207def SourceFromString(xml_string):
1208  return CreateClassFromXMLString(Source, xml_string)
1209
1210
1211class Entry(FeedEntryParent):
1212  """The atom:entry element"""
1213
1214  _tag = 'entry'
1215  _namespace = ATOM_NAMESPACE
1216  _children = FeedEntryParent._children.copy()
1217  _attributes = FeedEntryParent._attributes.copy()
1218  _children['{%s}content' % ATOM_NAMESPACE] = ('content', Content)
1219  _children['{%s}published' % ATOM_NAMESPACE] = ('published', Published)
1220  _children['{%s}source' % ATOM_NAMESPACE] = ('source', Source)
1221  _children['{%s}summary' % ATOM_NAMESPACE] = ('summary', Summary)
1222  _children['{%s}control' % APP_NAMESPACE] = ('control', Control)
1223
1224  def __init__(self, author=None, category=None, content=None,
1225      contributor=None, atom_id=None, link=None, published=None, rights=None,
1226      source=None, summary=None, control=None, title=None, updated=None,
1227      extension_elements=None, extension_attributes=None, text=None):
1228    """Constructor for atom:entry
1229
1230    Args:
1231      author: list A list of Author instances which belong to this class.
1232      category: list A list of Category instances
1233      content: Content The entry's Content
1234      contributor: list A list on Contributor instances
1235      id: Id The entry's Id element
1236      link: list A list of Link instances
1237      published: Published The entry's Published element
1238      rights: Rights The entry's Rights element
1239      source: Source the entry's source element
1240      summary: Summary the entry's summary element
1241      title: Title the entry's title element
1242      updated: Updated the entry's updated element
1243      control: The entry's app:control element which can be used to mark an
1244          entry as a draft which should not be publicly viewable.
1245      text: String The text contents of the element. This is the contents
1246          of the Entry's XML text node. (Example: <foo>This is the text</foo>)
1247      extension_elements: list A list of ExtensionElement instances which are
1248          children of this element.
1249      extension_attributes: dict A dictionary of strings which are the values
1250          for additional XML attributes of this element.
1251    """
1252
1253    self.author = author or []
1254    self.category = category or []
1255    self.content = content
1256    self.contributor = contributor or []
1257    self.id = atom_id
1258    self.link = link or []
1259    self.published = published
1260    self.rights = rights
1261    self.source = source
1262    self.summary = summary
1263    self.title = title
1264    self.updated = updated
1265    self.control = control
1266    self.text = text
1267    self.extension_elements = extension_elements or []
1268    self.extension_attributes = extension_attributes or {}
1269
1270  __init__ = v1_deprecated('Please use atom.data.Entry instead.')(__init__)
1271
1272
1273def EntryFromString(xml_string):
1274  return CreateClassFromXMLString(Entry, xml_string)
1275
1276
1277class Feed(Source):
1278  """The atom:feed element"""
1279
1280  _tag = 'feed'
1281  _namespace = ATOM_NAMESPACE
1282  _children = Source._children.copy()
1283  _attributes = Source._attributes.copy()
1284  _children['{%s}entry' % ATOM_NAMESPACE] = ('entry', [Entry])
1285
1286  def __init__(self, author=None, category=None, contributor=None,
1287      generator=None, icon=None, atom_id=None, link=None, logo=None,
1288      rights=None, subtitle=None, title=None, updated=None, entry=None,
1289      text=None, extension_elements=None, extension_attributes=None):
1290    """Constructor for Source
1291
1292    Args:
1293      author: list (optional) A list of Author instances which belong to this
1294          class.
1295      category: list (optional) A list of Category instances
1296      contributor: list (optional) A list on Contributor instances
1297      generator: Generator (optional)
1298      icon: Icon (optional)
1299      id: Id (optional) The entry's Id element
1300      link: list (optional) A list of Link instances
1301      logo: Logo (optional)
1302      rights: Rights (optional) The entry's Rights element
1303      subtitle: Subtitle (optional) The entry's subtitle element
1304      title: Title (optional) the entry's title element
1305      updated: Updated (optional) the entry's updated element
1306      entry: list (optional) A list of the Entry instances contained in the
1307          feed.
1308      text: String (optional) The text contents of the element. This is the
1309          contents of the Entry's XML text node.
1310          (Example: <foo>This is the text</foo>)
1311      extension_elements: list (optional) A list of ExtensionElement instances
1312          which are children of this element.
1313      extension_attributes: dict (optional) A dictionary of strings which are
1314          the values for additional XML attributes of this element.
1315    """
1316
1317    self.author = author or []
1318    self.category = category or []
1319    self.contributor = contributor or []
1320    self.generator = generator
1321    self.icon = icon
1322    self.id = atom_id
1323    self.link = link or []
1324    self.logo = logo
1325    self.rights = rights
1326    self.subtitle = subtitle
1327    self.title = title
1328    self.updated = updated
1329    self.entry = entry or []
1330    self.text = text
1331    self.extension_elements = extension_elements or []
1332    self.extension_attributes = extension_attributes or {}
1333
1334  __init__ = v1_deprecated('Please use atom.data.Feed instead.')(__init__)
1335
1336
1337def FeedFromString(xml_string):
1338  return CreateClassFromXMLString(Feed, xml_string)
1339
1340
1341class ExtensionElement(object):
1342  """Represents extra XML elements contained in Atom classes."""
1343
1344  def __init__(self, tag, namespace=None, attributes=None,
1345      children=None, text=None):
1346    """Constructor for EtensionElement
1347
1348    Args:
1349      namespace: string (optional) The XML namespace for this element.
1350      tag: string (optional) The tag (without the namespace qualifier) for
1351          this element. To reconstruct the full qualified name of the element,
1352          combine this tag with the namespace.
1353      attributes: dict (optinal) The attribute value string pairs for the XML
1354          attributes of this element.
1355      children: list (optional) A list of ExtensionElements which represent
1356          the XML child nodes of this element.
1357    """
1358
1359    self.namespace = namespace
1360    self.tag = tag
1361    self.attributes = attributes or {}
1362    self.children = children or []
1363    self.text = text
1364
1365  def ToString(self):
1366    element_tree = self._TransferToElementTree(ElementTree.Element(''))
1367    return ElementTree.tostring(element_tree, encoding="UTF-8")
1368
1369  def _TransferToElementTree(self, element_tree):
1370    if self.tag is None:
1371      return None
1372
1373    if self.namespace is not None:
1374      element_tree.tag = '{%s}%s' % (self.namespace, self.tag)
1375    else:
1376      element_tree.tag = self.tag
1377
1378    for key, value in self.attributes.iteritems():
1379      element_tree.attrib[key] = value
1380
1381    for child in self.children:
1382      child._BecomeChildElement(element_tree)
1383
1384    element_tree.text = self.text
1385
1386    return element_tree
1387
1388  def _BecomeChildElement(self, element_tree):
1389    """Converts this object into an etree element and adds it as a child node.
1390
1391    Adds self to the ElementTree. This method is required to avoid verbose XML
1392    which constantly redefines the namespace.
1393
1394    Args:
1395      element_tree: ElementTree._Element The element to which this object's XML
1396          will be added.
1397    """
1398    new_element = ElementTree.Element('')
1399    element_tree.append(new_element)
1400    self._TransferToElementTree(new_element)
1401
1402  def FindChildren(self, tag=None, namespace=None):
1403    """Searches child nodes for objects with the desired tag/namespace.
1404
1405    Returns a list of extension elements within this object whose tag
1406    and/or namespace match those passed in. To find all children in
1407    a particular namespace, specify the namespace but not the tag name.
1408    If you specify only the tag, the result list may contain extension
1409    elements in multiple namespaces.
1410
1411    Args:
1412      tag: str (optional) The desired tag
1413      namespace: str (optional) The desired namespace
1414
1415    Returns:
1416      A list of elements whose tag and/or namespace match the parameters
1417      values
1418    """
1419
1420    results = []
1421
1422    if tag and namespace:
1423      for element in self.children:
1424        if element.tag == tag and element.namespace == namespace:
1425          results.append(element)
1426    elif tag and not namespace:
1427      for element in self.children:
1428        if element.tag == tag:
1429          results.append(element)
1430    elif namespace and not tag:
1431      for element in self.children:
1432        if element.namespace == namespace:
1433          results.append(element)
1434    else:
1435      for element in self.children:
1436        results.append(element)
1437
1438    return results
1439
1440
1441def ExtensionElementFromString(xml_string):
1442  element_tree = ElementTree.fromstring(xml_string)
1443  return _ExtensionElementFromElementTree(element_tree)
1444
1445
1446def _ExtensionElementFromElementTree(element_tree):
1447  element_tag = element_tree.tag
1448  if '}' in element_tag:
1449    namespace = element_tag[1:element_tag.index('}')]
1450    tag = element_tag[element_tag.index('}')+1:]
1451  else:
1452    namespace = None
1453    tag = element_tag
1454  extension = ExtensionElement(namespace=namespace, tag=tag)
1455  for key, value in element_tree.attrib.iteritems():
1456    extension.attributes[key] = value
1457  for child in element_tree:
1458    extension.children.append(_ExtensionElementFromElementTree(child))
1459  extension.text = element_tree.text
1460  return extension
1461
1462
1463def deprecated(warning=None):
1464  """Decorator to raise warning each time the function is called.
1465
1466  Args:
1467    warning: The warning message to be displayed as a string (optinoal).
1468  """
1469  warning = warning or ''
1470  # This closure is what is returned from the deprecated function.
1471  def mark_deprecated(f):
1472    # The deprecated_function wraps the actual call to f.
1473    def deprecated_function(*args, **kwargs):
1474      warnings.warn(warning, DeprecationWarning, stacklevel=2)
1475      return f(*args, **kwargs)
1476    # Preserve the original name to avoid masking all decorated functions as
1477    # 'deprecated_function'
1478    try:
1479      deprecated_function.func_name = f.func_name
1480    except TypeError:
1481      # Setting the func_name is not allowed in Python2.3.
1482      pass
1483    return deprecated_function
1484  return mark_deprecated