=====================
APIs specific to lxml
=====================

lxml tries to follow established APIs wherever possible.  Sometimes, however,
the need to expose a feature in an easy way led to the invention of a new API.
This page describes the major differences and a few additions to the main
ElementTree API.

Separate pages describe the support for `parsing XML`_, executing `XPath and
XSLT`_, `validating XML`_ and interfacing with other XML tools through the
`SAX-API`_.

lxml is extremely extensible through `XPath functions in Python`_, custom
`Python element classes`_, custom `URL resolvers`_ and even `at the C-level`_.

.. _`parsing XML`: parsing.html
.. _`XPath and XSLT`: xpathxslt.html
.. _`validating XML`: validation.html
.. _`SAX-API`: sax.html
.. _`XPath functions in Python`: extensions.html
.. _`Python element classes`: element_classes.html
.. _`at the C-level`: capi.html
.. _`URL resolvers`: resolvers.html


.. contents::
.. 
   1  lxml.etree
   2  Other Element APIs
   3  Trees and Documents
   4  Iteration
   5  Error handling on exceptions
   6  xinclude
   7  write_c14n on ElementTree


lxml.etree
----------

lxml.etree tries to follow the `ElementTree API`_ wherever it can.  There are
however some incompatibilities (see `compatibility`_).  The extensions are
documented here.

.. _`ElementTree API`: http://effbot.org/zone/element-index.htm
.. _`compatibility`:   compatibility.html

If you need to know which version of lxml is installed, you can access the
``lxml.etree.LXML_VERSION`` attribute to retrieve a version tuple.  Note,
however, that it did not exist before version 1.0, so you will get an
AttributeError in older versions.  The versions of libxml2 and libxslt are
available through the attributes ``LIBXML_VERSION`` and ``LIBXSLT_VERSION``.

The following examples usually assume this to be executed first::

  >>> from lxml import etree
  >>> from StringIO import StringIO


Other Element APIs
------------------

While lxml.etree itself uses the ElementTree API, it is possible to replace
the Element implementation by `custom element subclasses`_.  This has been
used to implement well-known XML APIs on top of lxml.  The ``lxml.elements``
package contains examples.  Currently, there is a data-binding implementation
called `objectify`_, which is similar to the `Amara bindery`_ tool.

Additionally, the `lxml.elements.classlookup`_ module provides a number of
different schemes to customize the mapping between libxml2 nodes and the
Element classes used by lxml.etree.

.. _`custom element subclasses`: namespace_extensions.html
.. _`objectify`: objectify.html
.. _`lxml.elements.classlookup`: elements.html#lxml.elements.classlookup
.. _`Amara bindery`: http://uche.ogbuji.net/tech/4suite/amara/


Trees and Documents
-------------------

Compared to the original ElementTree API, lxml.etree has an extended tree
model.  It knows about parents and siblings of elements::

  >>> root = etree.Element("root")
  >>> a = etree.SubElement(root, "a")
  >>> b = etree.SubElement(root, "b")
  >>> c = etree.SubElement(root, "c")
  >>> d = etree.SubElement(root, "d")
  >>> e = etree.SubElement(d,    "e")
  >>> b.getparent() == root
  True
  >>> print b.getnext().tag
  c
  >>> print c.getprevious().tag
  b

Elements always live within a document context in lxml.  This implies that
there is also a notion of an absolute document root.  You can retrieve an
ElementTree for the root node of a document from any of its elements::

  >>> tree = d.getroottree()
  >>> print tree.getroot().tag
  root

Note that this is different from wrapping an Element in an ElementTree.  You
can use ElementTrees to create XML trees with an explicit root node::

  >>> tree = etree.ElementTree(d)
  >>> print tree.getroot().tag
  d
  >>> print etree.tostring(tree)
  <d><e/></d>

All operations that you run on such an ElementTree (like XPath, XSLT, etc.)
will understand the explicitly chosen root as root node of a document.  They
will not see any elements outside the ElementTree.  However, ElementTrees do
not modify their Elements::

  >>> element = tree.getroot()
  >>> print element.tag
  d
  >>> print element.getparent().tag
  root
  >>> print element.getroottree().getroot().tag
  root

The rule is that all operations that are applied to Elements use either the
Element itself as reference point, or the absolute root of the document that
contains this Element (e.g. for absolute XPath expressions).  All operations
on an ElementTree use its explicit root node as reference.


Iteration
---------

The ElementTree API makes Elements iterable to supports iteration over their
children.  Using the tree defined above, we get::

  >>> [ el.tag for el in root ]
  ['a', 'b', 'c', 'd']

Tree traversal is commonly based on the ``element.getiterator()`` method::

  >>> [ el.tag for el in root.getiterator() ]
  ['root', 'a', 'b', 'c', 'd', 'e']

lxml.etree also supports this, but additionally features an extended API for
iteration over the children, following/preceding siblings, ancestors and
descendants of an element, as defined by the respective XPath axis::

  >>> [ el.tag for el in root.iterchildren() ]
  ['a', 'b', 'c', 'd']
  >>> [ el.tag for el in root.iterchildren(reversed=True) ]
  ['d', 'c', 'b', 'a']
  >>> [ el.tag for el in b.itersiblings() ]
  ['c', 'd']
  >>> [ el.tag for el in c.itersiblings(preceding=True) ]
  ['b', 'a']
  >>> [ el.tag for el in e.iterancestors() ]
  ['d', 'root']
  >>> [ el.tag for el in root.iterdescendants() ]
  ['a', 'b', 'c', 'd', 'e']

Note how ``element.iterdescendants()`` does not include the element itself, as
opposed to ``element.getiterator()``.  The latter effectively implements the
'descendant-or-self' axis in XPath.

All of these iterators support an additional ``tag`` keyword argument that
filters the generated elements by tag name::

  >>> [ el.tag for el in root.iterchildren(tag='a') ]
  ['a']
  >>> [ el.tag for el in d.iterchildren(tag='a') ]
  []
  >>> [ el.tag for el in root.iterdescendants(tag='d') ]
  ['d']
  >>> [ el.tag for el in root.getiterator(tag='d') ]
  ['d']

See also the section on the utility functions ``iterparse()`` and
``iterwalk()`` in the `parser documentation`_.

.. _`parser documentation`: parsing.html#iterparse-and-iterwalk


Error handling on exceptions
----------------------------

Libxml2 provides error messages for failures, be it during parsing, XPath
evaluation or schema validation.  Whenever an exception is raised, you can
retrieve the errors that occured and "might have" lead to the problem::

  >>> etree.clearErrorLog()
  >>> broken_xml = '<a>'
  >>> try:
  ...   etree.parse(StringIO(broken_xml))
  ... except etree.XMLSyntaxError, e:
  ...   pass # just put the exception into e
  >>> log = e.error_log.filter_levels(etree.ErrorLevels.FATAL)
  >>> print log
  <string>:1:FATAL:PARSER:ERR_TAG_NOT_FINISHED: Premature end of data in tag a line 1

This might look a little cryptic at first, but it is the information that
libxml2 gives you.  At least the message at the end should give you a hint
what went wrong and you can see that the fatal error (FATAL) happened during
parsing (PARSER) line 1 of a string (<string>, or filename if available).
Here, PARSER is the so-called error domain, see lxml.etree.ErrorDomains for
that.  You can get it from a log entry like this::

  >>> entry = log[0]
  >>> print entry.domain_name, entry.type_name, entry.filename
  PARSER ERR_TAG_NOT_FINISHED <string>

There is also a convenience attribute ``last_error`` that returns the last
error or fatal error that occurred::

  >>> entry = e.error_log.last_error
  >>> print entry.domain_name, entry.type_name, entry.filename
  PARSER ERR_TAG_NOT_FINISHED <string>

Alternatively, lxml.etree supports logging libxml2 messages to the Python
stdlib logging module.  This is done through the ``etree.PyErrorLog`` class.
It disables the error reporting from exceptions and forwards log messages to a
Python logger.  To use it, see the descriptions of the function
``etree.useGlobalPythonLog`` and the class ``etree.PyErrorLog`` for help.
Note that this does not affect the local error logs of XSLT, XMLSchema,
etc. which are described in their respective sections below.


xinclude
--------

Simple XInclude support exists.  You can let lxml process xinclude statements
in a document by calling the xinclude() method on a tree::

  >>> data = StringIO('''\
  ... <doc xmlns:xi="http://www.w3.org/2001/XInclude">
  ... <foo/>
  ... <xi:include href="doc/test.xml" />
  ... </doc>''')

  >>> tree = etree.parse(data)
  >>> tree.xinclude()
  >>> etree.tostring(tree.getroot())
  '<doc xmlns:xi="http://www.w3.org/2001/XInclude">\n<foo/>\n<a xml:base="doc/test.xml"/>\n</doc>'


write_c14n on ElementTree
-------------------------

The lxml.etree.ElementTree class has a method write_c14n, which takes a file
object as argument.  This file object will receive an UTF-8 representation of
the canonicalized form of the XML, following the W3C C14N recommendation.  For
example::

  >>> f = StringIO('<a><b/></a>')
  >>> tree = etree.parse(f)
  >>> f2 = StringIO()
  >>> tree.write_c14n(f2)
  >>> f2.getvalue()
  '<a><b></b></a>'
