| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751 |
- ========================
- XPath and XSLT with lxml
- ========================
- lxml supports XPath 1.0, XSLT 1.0 and the EXSLT extensions through
- libxml2 and libxslt in a standards compliant way.
- .. contents::
- ..
- 1 XPath
- 1.1 The ``xpath()`` method
- 1.2 Namespaces and prefixes
- 1.3 XPath return values
- 1.4 Generating XPath expressions
- 1.5 The ``XPath`` class
- 1.6 Regular expressions in XPath
- 1.7 The ``XPathEvaluator`` classes
- 1.8 ``ETXPath``
- 1.9 Error handling
- 2 XSLT
- 2.1 XSLT result objects
- 2.2 Stylesheet parameters
- 2.3 The ``xslt()`` tree method
- 2.4 Dealing with stylesheet complexity
- 2.5 Profiling
- The usual setup procedure:
- .. sourcecode:: pycon
- >>> from lxml import etree
- ..
- >>> try: from StringIO import StringIO
- ... except ImportError:
- ... from io import BytesIO
- ... def StringIO(s):
- ... if isinstance(s, str): s = s.encode("UTF-8")
- ... return BytesIO(s)
- >>> try: unicode = __builtins__["unicode"]
- ... except (NameError, KeyError): unicode = str
- XPath
- =====
- lxml.etree supports the simple path syntax of the `find, findall and
- findtext`_ methods on ElementTree and Element, as known from the original
- ElementTree library (ElementPath_). As an lxml specific extension, these
- classes also provide an ``xpath()`` method that supports expressions in the
- complete XPath syntax, as well as `custom extension functions`_.
- .. _ElementPath: http://effbot.org/zone/element-xpath.htm
- .. _`find, findall and findtext`: http://effbot.org/zone/element.htm#searching-for-subelements
- .. _`custom extension functions`: extensions.html#xpath-extension-functions
- .. _`XSLT extension elements`: extensions.html#xslt-extension-elements
- There are also specialized XPath evaluator classes that are more efficient for
- frequent evaluation: ``XPath`` and ``XPathEvaluator``. See the `performance
- comparison`_ to learn when to use which. Their semantics when used on
- Elements and ElementTrees are the same as for the ``xpath()`` method described
- here.
- .. _`performance comparison`: performance.html#xpath
- The ``xpath()`` method
- ----------------------
- For ElementTree, the xpath method performs a global XPath query against the
- document (if absolute) or against the root node (if relative):
- .. sourcecode:: pycon
- >>> f = StringIO('<foo><bar></bar></foo>')
- >>> tree = etree.parse(f)
- >>> r = tree.xpath('/foo/bar')
- >>> len(r)
- 1
- >>> r[0].tag
- 'bar'
- >>> r = tree.xpath('bar')
- >>> r[0].tag
- 'bar'
- When ``xpath()`` is used on an Element, the XPath expression is evaluated
- against the element (if relative) or against the root tree (if absolute):
- .. sourcecode:: pycon
- >>> root = tree.getroot()
- >>> r = root.xpath('bar')
- >>> r[0].tag
- 'bar'
- >>> bar = root[0]
- >>> r = bar.xpath('/foo/bar')
- >>> r[0].tag
- 'bar'
- >>> tree = bar.getroottree()
- >>> r = tree.xpath('/foo/bar')
- >>> r[0].tag
- 'bar'
- The ``xpath()`` method has support for XPath variables:
- .. sourcecode:: pycon
- >>> expr = "//*[local-name() = $name]"
- >>> print(root.xpath(expr, name = "foo")[0].tag)
- foo
- >>> print(root.xpath(expr, name = "bar")[0].tag)
- bar
- >>> print(root.xpath("$text", text = "Hello World!"))
- Hello World!
- Namespaces and prefixes
- -----------------------
- If your XPath expression uses namespace prefixes, you must define them
- in a prefix mapping. To this end, pass a dictionary to the
- ``namespaces`` keyword argument that maps the namespace prefixes used
- in the XPath expression to namespace URIs:
- .. sourcecode:: pycon
- >>> f = StringIO('''\
- ... <a:foo xmlns:a="http://codespeak.net/ns/test1"
- ... xmlns:b="http://codespeak.net/ns/test2">
- ... <b:bar>Text</b:bar>
- ... </a:foo>
- ... ''')
- >>> doc = etree.parse(f)
- >>> r = doc.xpath('/t:foo/b:bar',
- ... namespaces={'t': 'http://codespeak.net/ns/test1',
- ... 'b': 'http://codespeak.net/ns/test2'})
- >>> len(r)
- 1
- >>> r[0].tag
- '{http://codespeak.net/ns/test2}bar'
- >>> r[0].text
- 'Text'
- The prefixes you choose here are not linked to the prefixes used
- inside the XML document. The document may define whatever prefixes it
- likes, including the empty prefix, without breaking the above code.
- Note that XPath does not have a notion of a default namespace. The
- empty prefix is therefore undefined for XPath and cannot be used in
- namespace prefix mappings.
- There is also an optional ``extensions`` argument which is used to
- define `custom extension functions`_ in Python that are local to this
- evaluation. The namespace prefixes that they use in the XPath
- expression must also be defined in the namespace prefix mapping.
- XPath return values
- -------------------
- The return value types of XPath evaluations vary, depending on the
- XPath expression used:
- * True or False, when the XPath expression has a boolean result
- * a float, when the XPath expression has a numeric result (integer or float)
- * a 'smart' string (as described below), when the XPath expression has
- a string result.
- * a list of items, when the XPath expression has a list as result.
- The items may include Elements (also comments and processing
- instructions), strings and tuples. Text nodes and attributes in the
- result are returned as 'smart' string values. Namespace
- declarations are returned as tuples of strings: ``(prefix, URI)``.
- XPath string results are 'smart' in that they provide a
- ``getparent()`` method that knows their origin:
- * for attribute values, ``result.getparent()`` returns the Element
- that carries them. An example is ``//foo/@attribute``, where the
- parent would be a ``foo`` Element.
- * for the ``text()`` function (as in ``//text()``), it returns the
- Element that contains the text or tail that was returned.
- You can distinguish between different text origins with the boolean
- properties ``is_text``, ``is_tail`` and ``is_attribute``.
- Note that ``getparent()`` may not always return an Element. For
- example, the XPath functions ``string()`` and ``concat()`` will
- construct strings that do not have an origin. For them,
- ``getparent()`` will return None.
- There are certain cases where the smart string behaviour is
- undesirable. For example, it means that the tree will be kept alive
- by the string, which may have a considerable memory impact in the case
- that the string value is the only thing in the tree that is actually
- of interest. For these cases, you can deactivate the parental
- relationship using the keyword argument ``smart_strings``.
- .. sourcecode:: pycon
- >>> root = etree.XML("<root><a>TEXT</a></root>")
- >>> find_text = etree.XPath("//text()")
- >>> text = find_text(root)[0]
- >>> print(text)
- TEXT
- >>> print(text.getparent().text)
- TEXT
- >>> find_text = etree.XPath("//text()", smart_strings=False)
- >>> text = find_text(root)[0]
- >>> print(text)
- TEXT
- >>> hasattr(text, 'getparent')
- False
- Generating XPath expressions
- ----------------------------
- ElementTree objects have a method ``getpath(element)``, which returns a
- structural, absolute XPath expression to find that element:
- .. sourcecode:: pycon
- >>> a = etree.Element("a")
- >>> b = etree.SubElement(a, "b")
- >>> c = etree.SubElement(a, "c")
- >>> d1 = etree.SubElement(c, "d")
- >>> d2 = etree.SubElement(c, "d")
- >>> tree = etree.ElementTree(c)
- >>> print(tree.getpath(d2))
- /c/d[2]
- >>> tree.xpath(tree.getpath(d2)) == [d2]
- True
- The ``XPath`` class
- -------------------
- The ``XPath`` class compiles an XPath expression into a callable function:
- .. sourcecode:: pycon
- >>> root = etree.XML("<root><a><b/></a><b/></root>")
- >>> find = etree.XPath("//b")
- >>> print(find(root)[0].tag)
- b
- The compilation takes as much time as in the ``xpath()`` method, but it is
- done only once per class instantiation. This makes it especially efficient
- for repeated evaluation of the same XPath expression.
- Just like the ``xpath()`` method, the ``XPath`` class supports XPath
- variables:
- .. sourcecode:: pycon
- >>> count_elements = etree.XPath("count(//*[local-name() = $name])")
- >>> print(count_elements(root, name = "a"))
- 1.0
- >>> print(count_elements(root, name = "b"))
- 2.0
- This supports very efficient evaluation of modified versions of an XPath
- expression, as compilation is still only required once.
- Prefix-to-namespace mappings can be passed as second parameter:
- .. sourcecode:: pycon
- >>> root = etree.XML("<root xmlns='NS'><a><b/></a><b/></root>")
- >>> find = etree.XPath("//n:b", namespaces={'n':'NS'})
- >>> print(find(root)[0].tag)
- {NS}b
- Regular expressions in XPath
- ----------------------------
- By default, ``XPath`` supports regular expressions in the EXSLT_ namespace:
- .. sourcecode:: pycon
- >>> regexpNS = "http://exslt.org/regular-expressions"
- >>> find = etree.XPath("//*[re:test(., '^abc$', 'i')]",
- ... namespaces={'re':regexpNS})
- >>> root = etree.XML("<root><a>aB</a><b>aBc</b></root>")
- >>> print(find(root)[0].text)
- aBc
- .. _EXSLT: http://www.exslt.org/
- You can disable this with the boolean keyword argument ``regexp`` which
- defaults to True.
- The ``XPathEvaluator`` classes
- ------------------------------
- lxml.etree provides two other efficient XPath evaluators that work on
- ElementTrees or Elements respectively: ``XPathDocumentEvaluator`` and
- ``XPathElementEvaluator``. They are automatically selected if you use the
- XPathEvaluator helper for instantiation:
- .. sourcecode:: pycon
- >>> root = etree.XML("<root><a><b/></a><b/></root>")
- >>> xpatheval = etree.XPathEvaluator(root)
- >>> print(isinstance(xpatheval, etree.XPathElementEvaluator))
- True
- >>> print(xpatheval("//b")[0].tag)
- b
- This class provides efficient support for evaluating different XPath
- expressions on the same Element or ElementTree.
- ``ETXPath``
- -----------
- ElementTree supports a language named ElementPath_ in its ``find*()`` methods.
- One of the main differences between XPath and ElementPath is that the XPath
- language requires an indirection through prefixes for namespace support,
- whereas ElementTree uses the Clark notation (``{ns}name``) to avoid prefixes
- completely. The other major difference regards the capabilities of both path
- languages. Where XPath supports various sophisticated ways of restricting the
- result set through functions and boolean expressions, ElementPath only
- supports pure path traversal without nesting or further conditions. So, while
- the ElementPath syntax is self-contained and therefore easier to write and
- handle, XPath is much more powerful and expressive.
- lxml.etree bridges this gap through the class ``ETXPath``, which accepts XPath
- expressions with namespaces in Clark notation. It is identical to the
- ``XPath`` class, except for the namespace notation. Normally, you would
- write:
- .. sourcecode:: pycon
- >>> root = etree.XML("<root xmlns='ns'><a><b/></a><b/></root>")
- >>> find = etree.XPath("//p:b", namespaces={'p' : 'ns'})
- >>> print(find(root)[0].tag)
- {ns}b
- ``ETXPath`` allows you to change this to:
- .. sourcecode:: pycon
- >>> find = etree.ETXPath("//{ns}b")
- >>> print(find(root)[0].tag)
- {ns}b
- Error handling
- --------------
- lxml.etree raises exceptions when errors occur while parsing or evaluating an
- XPath expression:
- .. sourcecode:: pycon
- >>> find = etree.XPath("\\")
- Traceback (most recent call last):
- ...
- lxml.etree.XPathSyntaxError: Invalid expression
- lxml will also try to give you a hint what went wrong, so if you pass a more
- complex expression, you may get a somewhat more specific error:
- .. sourcecode:: pycon
- >>> find = etree.XPath("//*[1.1.1]")
- Traceback (most recent call last):
- ...
- lxml.etree.XPathSyntaxError: Invalid predicate
- During evaluation, lxml will emit an XPathEvalError on errors:
- .. sourcecode:: pycon
- >>> find = etree.XPath("//ns:a")
- >>> find(root)
- Traceback (most recent call last):
- ...
- lxml.etree.XPathEvalError: Undefined namespace prefix
- This works for the ``XPath`` class, however, the other evaluators (including
- the ``xpath()`` method) are one-shot operations that do parsing and evaluation
- in one step. They therefore raise evaluation exceptions in all cases:
- .. sourcecode:: pycon
- >>> root = etree.Element("test")
- >>> find = root.xpath("//*[1.1.1]")
- Traceback (most recent call last):
- ...
- lxml.etree.XPathEvalError: Invalid predicate
- >>> find = root.xpath("//ns:a")
- Traceback (most recent call last):
- ...
- lxml.etree.XPathEvalError: Undefined namespace prefix
- >>> find = root.xpath("\\")
- Traceback (most recent call last):
- ...
- lxml.etree.XPathEvalError: Invalid expression
- Note that lxml versions before 1.3 always raised an ``XPathSyntaxError`` for
- all errors, including evaluation errors. The best way to support older
- versions is to except on the superclass ``XPathError``.
- XSLT
- ====
- lxml.etree introduces a new class, lxml.etree.XSLT. The class can be
- given an ElementTree or Element object to construct an XSLT
- transformer:
- .. sourcecode:: pycon
- >>> xslt_root = etree.XML('''\
- ... <xsl:stylesheet version="1.0"
- ... xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- ... <xsl:template match="/">
- ... <foo><xsl:value-of select="/a/b/text()" /></foo>
- ... </xsl:template>
- ... </xsl:stylesheet>''')
- >>> transform = etree.XSLT(xslt_root)
- You can then run the transformation on an ElementTree document by simply
- calling it, and this results in another ElementTree object:
- .. sourcecode:: pycon
- >>> f = StringIO('<a><b>Text</b></a>')
- >>> doc = etree.parse(f)
- >>> result_tree = transform(doc)
- By default, XSLT supports all extension functions from libxslt and
- libexslt as well as Python regular expressions through the `EXSLT
- regexp functions`_. Also see the documentation on `custom extension
- functions`_, `XSLT extension elements`_ and `document resolvers`_.
- There is a separate section on `controlling access`_ to external
- documents and resources.
- .. _`EXSLT regexp functions`: http://www.exslt.org/regexp/
- .. _`document resolvers`: resolvers.html
- .. _`controlling access`: resolvers.html#i-o-access-control-in-xslt
- XSLT result objects
- -------------------
- The result of an XSL transformation can be accessed like a normal ElementTree
- document:
- .. sourcecode:: pycon
- >>> root = etree.XML('<a><b>Text</b></a>')
- >>> result = transform(root)
- >>> result.getroot().text
- 'Text'
- but, as opposed to normal ElementTree objects, can also be turned into an (XML
- or text) string by applying the str() function:
- .. sourcecode:: pycon
- >>> str(result)
- '<?xml version="1.0"?>\n<foo>Text</foo>\n'
- The result is always a plain string, encoded as requested by the
- ``xsl:output`` element in the stylesheet. If you want a Python unicode string
- instead, you should set this encoding to ``UTF-8`` (unless the `ASCII` default
- is sufficient). This allows you to call the builtin ``unicode()`` function on
- the result:
- .. sourcecode:: pycon
- >>> unicode(result)
- u'<?xml version="1.0"?>\n<foo>Text</foo>\n'
- You can use other encodings at the cost of multiple recoding. Encodings that
- are not supported by Python will result in an error:
- .. sourcecode:: pycon
- >>> xslt_tree = etree.XML('''\
- ... <xsl:stylesheet version="1.0"
- ... xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- ... <xsl:output encoding="UCS4"/>
- ... <xsl:template match="/">
- ... <foo><xsl:value-of select="/a/b/text()" /></foo>
- ... </xsl:template>
- ... </xsl:stylesheet>''')
- >>> transform = etree.XSLT(xslt_tree)
- >>> result = transform(doc)
- >>> unicode(result)
- Traceback (most recent call last):
- ...
- LookupError: unknown encoding: UCS4
- Stylesheet parameters
- ---------------------
- It is possible to pass parameters, in the form of XPath expressions, to the
- XSLT template:
- .. sourcecode:: pycon
- >>> xslt_tree = etree.XML('''\
- ... <xsl:stylesheet version="1.0"
- ... xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- ... <xsl:param name="a" />
- ... <xsl:template match="/">
- ... <foo><xsl:value-of select="$a" /></foo>
- ... </xsl:template>
- ... </xsl:stylesheet>''')
- >>> transform = etree.XSLT(xslt_tree)
- >>> doc_root = etree.XML('<a><b>Text</b></a>')
- The parameters are passed as keyword parameters to the transform call.
- First, let's try passing in a simple integer expression:
- .. sourcecode:: pycon
- >>> result = transform(doc_root, a="5")
- >>> str(result)
- '<?xml version="1.0"?>\n<foo>5</foo>\n'
- You can use any valid XPath expression as parameter value:
- .. sourcecode:: pycon
- >>> result = transform(doc_root, a="/a/b/text()")
- >>> str(result)
- '<?xml version="1.0"?>\n<foo>Text</foo>\n'
- It's also possible to pass an XPath object as a parameter:
- .. sourcecode:: pycon
- >>> result = transform(doc_root, a=etree.XPath("/a/b/text()"))
- >>> str(result)
- '<?xml version="1.0"?>\n<foo>Text</foo>\n'
- Passing a string expression looks like this:
- .. sourcecode:: pycon
- >>> result = transform(doc_root, a="'A'")
- >>> str(result)
- '<?xml version="1.0"?>\n<foo>A</foo>\n'
- To pass a string that (potentially) contains quotes, you can use the
- ``.strparam()`` class method. Note that it does not escape the
- string. Instead, it returns an opaque object that keeps the string
- value.
- .. sourcecode:: pycon
- >>> plain_string_value = etree.XSLT.strparam(
- ... """ It's "Monty Python" """)
- >>> result = transform(doc_root, a=plain_string_value)
- >>> str(result)
- '<?xml version="1.0"?>\n<foo> It\'s "Monty Python" </foo>\n'
- If you need to pass parameters that are not legal Python identifiers,
- pass them inside of a dictionary:
- .. sourcecode:: pycon
- >>> transform = etree.XSLT(etree.XML('''\
- ... <xsl:stylesheet version="1.0"
- ... xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- ... <xsl:param name="non-python-identifier" />
- ... <xsl:template match="/">
- ... <foo><xsl:value-of select="$non-python-identifier" /></foo>
- ... </xsl:template>
- ... </xsl:stylesheet>'''))
- >>> result = transform(doc_root, **{'non-python-identifier': '5'})
- >>> str(result)
- '<?xml version="1.0"?>\n<foo>5</foo>\n'
- Errors and messages
- -------------------
- Like most of the processing oriented objects in lxml.etree, ``XSLT``
- provides an error log that lists messages and error output from the
- last run. See the `parser documentation`_ for a description of the
- error log.
- .. _`parser documentation`: parsing.html#error-log
- .. sourcecode:: pycon
- >>> xslt_root = etree.XML('''\
- ... <xsl:stylesheet version="1.0"
- ... xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- ... <xsl:template match="/">
- ... <xsl:message terminate="no">STARTING</xsl:message>
- ... <foo><xsl:value-of select="/a/b/text()" /></foo>
- ... <xsl:message terminate="no">DONE</xsl:message>
- ... </xsl:template>
- ... </xsl:stylesheet>''')
- >>> transform = etree.XSLT(xslt_root)
- >>> doc_root = etree.XML('<a><b>Text</b></a>')
- >>> result = transform(doc_root)
- >>> str(result)
- '<?xml version="1.0"?>\n<foo>Text</foo>\n'
- >>> print(transform.error_log)
- <string>:0:0:ERROR:XSLT:ERR_OK: STARTING
- <string>:0:0:ERROR:XSLT:ERR_OK: DONE
- >>> for entry in transform.error_log:
- ... print('message from line %s, col %s: %s' % (
- ... entry.line, entry.column, entry.message))
- ... print('domain: %s (%d)' % (entry.domain_name, entry.domain))
- ... print('type: %s (%d)' % (entry.type_name, entry.type))
- ... print('level: %s (%d)' % (entry.level_name, entry.level))
- ... print('filename: %s' % entry.filename)
- message from line 0, col 0: STARTING
- domain: XSLT (22)
- type: ERR_OK (0)
- level: ERROR (2)
- filename: <string>
- message from line 0, col 0: DONE
- domain: XSLT (22)
- type: ERR_OK (0)
- level: ERROR (2)
- filename: <string>
- Note that there is no way in XSLT to distinguish between user
- messages, warnings and error messages that occurred during the
- run. ``libxslt`` simply does not provide this information. You can
- partly work around this limitation by making your own messages
- uniquely identifiable, e.g. with a common text prefix.
- The ``xslt()`` tree method
- --------------------------
- There's also a convenience method on ElementTree objects for doing XSL
- transformations. This is less efficient if you want to apply the same XSL
- transformation to multiple documents, but is shorter to write for one-shot
- operations, as you do not have to instantiate a stylesheet yourself:
- .. sourcecode:: pycon
- >>> result = doc.xslt(xslt_tree, a="'A'")
- >>> str(result)
- '<?xml version="1.0"?>\n<foo>A</foo>\n'
- This is a shortcut for the following code:
- .. sourcecode:: pycon
- >>> transform = etree.XSLT(xslt_tree)
- >>> result = transform(doc, a="'A'")
- >>> str(result)
- '<?xml version="1.0"?>\n<foo>A</foo>\n'
- Dealing with stylesheet complexity
- ----------------------------------
- Some applications require a larger set of rather diverse stylesheets.
- lxml.etree allows you to deal with this in a number of ways. Here are
- some ideas to try.
- The most simple way to reduce the diversity is by using XSLT
- parameters that you pass at call time to configure the stylesheets.
- The ``partial()`` function in the ``functools`` module of Python 2.5
- may come in handy here. It allows you to bind a set of keyword
- arguments (i.e. stylesheet parameters) to a reference of a callable
- stylesheet. The same works for instances of the ``XPath()``
- evaluator, obviously.
- You may also consider creating stylesheets programmatically. Just
- create an XSL tree, e.g. from a parsed template, and then add or
- replace parts as you see fit. Passing an XSL tree into the ``XSLT()``
- constructor multiple times will create independent stylesheets, so
- later modifications of the tree will not be reflected in the already
- created stylesheets. This makes stylesheet generation very straight
- forward.
- A third thing to remember is the support for `custom extension
- functions`_ and `XSLT extension elements`_. Some things are much
- easier to express in XSLT than in Python, while for others it is the
- complete opposite. Finding the right mixture of Python code and XSL
- code can help a great deal in keeping applications well designed and
- maintainable.
- Profiling
- ---------
- If you want to know how your stylesheet performed, pass the ``profile_run``
- keyword to the transform:
- .. sourcecode:: pycon
- >>> result = transform(doc, a="/a/b/text()", profile_run=True)
- >>> profile = result.xslt_profile
- The value of the ``xslt_profile`` property is an ElementTree with profiling
- data about each template, similar to the following:
- .. sourcecode:: xml
- <profile>
- <template rank="1" match="/" name="" mode="" calls="1" time="1" average="1"/>
- </profile>
- Note that this is a read-only document. You must not move any of its elements
- to other documents. Please deep-copy the document if you need to modify it.
- If you want to free it from memory, just do:
- .. sourcecode:: pycon
- >>> del result.xslt_profile
|