changeset 226:4d8a9e03b23d trunk

Add reStructuredText documentation files.
author cmlenz
date Fri, 08 Sep 2006 08:44:31 +0000
parents 16d7b5db7ef4
children 96a7e5011c69
files doc/2000ft.graffle doc/2000ft.png doc/Makefile doc/builder.txt doc/custom.css doc/docutils.conf doc/docutils.css doc/index.txt doc/streams.txt doc/xml-templates.txt doc/xpath.txt
diffstat 11 files changed, 1282 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
old mode 100755
new mode 100644
old mode 100755
new mode 100644
new file mode 100644
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,14 @@
+HTML_FILES = \
+	builder.html \
+	index.html \
+	streams.html \
+	xml-templates.html \
+	xpath.html
+
+all: $(HTML_FILES)
+
+%.html: %.txt
+	rst2html.py --exit-status=3 $< $@
+
+clean:
+	rm -rf $(HTML_FILES)
new file mode 100644
--- /dev/null
+++ b/doc/builder.txt
@@ -0,0 +1,72 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+==================================
+Generating Markup Programmatically
+==================================
+
+Markup provides a ``builder`` module which lets you generate markup from Python
+code using a very simple syntax. The main entry point to the ``builder`` module
+is the ``tag`` object (which is actually an instance of the ``ElementFactory``
+class). You should rarely (if ever) need to directly import and use any of the
+other classes in the ``builder`` module.
+
+
+.. contents:: Contents
+   :depth: 2
+.. sectnum::
+
+
+Creating Elements
+=================
+
+Elements can be created through the `tag` object using attribute access, for
+example::
+
+  >>> from markup.builder import tag
+  >>> doc = tag.p('Some text and ', tag.a('a link', href='http://example.org/'), '.')
+  >>> doc
+  <Element "p">
+
+This produces an ``Element`` instance which can be further modified to add child
+nodes and attributes. This is done by “calling” the element: positional
+arguments are added as child nodes (alternatively, the ``append`` method can be
+used for that purpose), whereas keywords arguments are added as attributes::
+
+  >>> doc(tag.br)
+  <Element "p">
+  >>> print doc
+  <p>Some text and <a href="http://example.org/">a link</a>.<br/></p>
+
+If an attribute name collides with a Python keyword, simply append an underscore
+to the name::
+
+  >>> doc(class_='intro')
+  <Element "p">
+  >>> print doc
+  <p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p>
+
+As shown above, an ``Element`` can easily be directly rendered to XML text by
+printing it or using the Python ``str()`` function. This is basically a
+shortcut for converting the ``Element`` to a stream and serializing that
+stream::
+
+  >>> stream = doc.generate()
+  >>> stream
+  <markup.core.Stream object at 0x72d230>
+  >>> print stream
+  <p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p>
+
+
+Creating Fragments
+==================
+
+The ``tag`` object also allows creating “fragments”, which are basically lists
+of nodes (elements or text) that don't have a parent element. This can be useful
+for creating snippets of markup that are attached to a parent element later (for
+example in a template). Fragments are created by calling the ``tag`` object:
+
+  >>> fragment = tag('Hello, ', tag.em('word'), '!')
+  >>> fragment
+  <Fragment>
+  >>> print fragment
+  Hello, <em>world</em>!
new file mode 100644
--- /dev/null
+++ b/doc/custom.css
@@ -0,0 +1,34 @@
+@import(docutils.css);
+
+body { background: #fff; color: #000; margin: 10px 10px 10px 30px; padding: 0; }
+body, th, td {
+  font: normal 13px Verdana,Arial,'Bitstream Vera Sans',Helvetica,sans-serif;
+}
+h1, h2, h3, h4 {
+ font-family: arial,verdana,'Bitstream Vera Sans',helvetica,sans-serif;
+ font-weight: bold;
+ letter-spacing: -0.018em;
+}
+h1 { font-size: 19px; margin: .15em 1em 0 0 }
+h2 { font-size: 16px }
+h3 { font-size: 14px }
+hr { border: none;  border-top: 1px solid #ccb; margin: 2em 0 }
+
+:link, :visited {
+ text-decoration: none;
+ color: #b00;
+ border-bottom: 1px dotted #bbb;
+}
+:link:hover, :visited:hover {
+ background-color: #eee;
+ color: #555;
+}
+:link img, :visited img { border: none }
+h1 :link, h1 :visited ,h2 :link, h2 :visited, h3 :link, h3 :visited,
+h4 :link, h4 :visited, h5 :link, h5 :visited, h6 :link, h6 :visited {
+ color: #000;
+}
+
+pre.literal-block { background: #f7f7f7;  border: 1px solid #d7d7d7;
+ margin: 1em 1.75em; padding: .25em; overflow: auto;
+}
new file mode 100644
--- /dev/null
+++ b/doc/docutils.conf
@@ -0,0 +1,8 @@
+[general]
+input_encoding = utf-8
+strip_comments = yes
+
+[html4css1 writer]
+embed_stylesheet = no
+stylesheet = custom.css
+xml_declaration = no
new file mode 100644
--- /dev/null
+++ b/doc/docutils.css
@@ -0,0 +1,279 @@
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
+:Revision: $Revision: 4224 $
+:Copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+
+See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
+customize this style sheet.
+*/
+
+/* used to remove borders from tables and images */
+.borderless, table.borderless td, table.borderless th {
+  border: 0 }
+
+table.borderless td, table.borderless th {
+  /* Override padding for "table.docutils td" with "! important".
+     The right padding separates the table cells. */
+  padding: 0 0.5em 0 0 ! important }
+
+.first {
+  /* Override more specific margin styles with "! important". */
+  margin-top: 0 ! important }
+
+.last, .with-subtitle {
+  margin-bottom: 0 ! important }
+
+.hidden {
+  display: none }
+
+a.toc-backref {
+  text-decoration: none ;
+  color: black }
+
+blockquote.epigraph {
+  margin: 2em 5em ; }
+
+dl.docutils dd {
+  margin-bottom: 0.5em }
+
+/* Uncomment (and remove this text!) to get bold-faced definition list terms
+dl.docutils dt {
+  font-weight: bold }
+*/
+
+div.abstract {
+  margin: 2em 5em }
+
+div.abstract p.topic-title {
+  font-weight: bold ;
+  text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+  margin: 2em ;
+  border: medium outset ;
+  padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+  color: red ;
+  font-weight: bold ;
+  font-family: sans-serif }
+
+/* Uncomment (and remove this text!) to get reduced vertical space in
+   compound paragraphs.
+div.compound .compound-first, div.compound .compound-middle {
+  margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+  margin-top: 0.5em }
+*/
+
+div.dedication {
+  margin: 2em 5em ;
+  text-align: center ;
+  font-style: italic }
+
+div.dedication p.topic-title {
+  font-weight: bold ;
+  font-style: normal }
+
+div.figure {
+  margin-left: 2em ;
+  margin-right: 2em }
+
+div.footer, div.header {
+  clear: both;
+  font-size: smaller }
+
+div.line-block {
+  display: block ;
+  margin-top: 1em ;
+  margin-bottom: 1em }
+
+div.line-block div.line-block {
+  margin-top: 0 ;
+  margin-bottom: 0 ;
+  margin-left: 1.5em }
+
+div.sidebar {
+  margin-left: 1em ;
+  border: medium outset ;
+  padding: 1em ;
+  background-color: #ffffee ;
+  width: 40% ;
+  float: right ;
+  clear: right }
+
+div.sidebar p.rubric {
+  font-family: sans-serif ;
+  font-size: medium }
+
+div.system-messages {
+  margin: 5em }
+
+div.system-messages h1 {
+  color: red }
+
+div.system-message {
+  border: medium outset ;
+  padding: 1em }
+
+div.system-message p.system-message-title {
+  color: red ;
+  font-weight: bold }
+
+div.topic {
+  margin: 2em }
+
+h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
+h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
+  margin-top: 0.4em }
+
+h1.title {
+  text-align: center }
+
+h2.subtitle {
+  text-align: center }
+
+hr.docutils {
+  width: 75% }
+
+img.align-left {
+  clear: left }
+
+img.align-right {
+  clear: right }
+
+ol.simple, ul.simple {
+  margin-bottom: 1em }
+
+ol.arabic {
+  list-style: decimal }
+
+ol.loweralpha {
+  list-style: lower-alpha }
+
+ol.upperalpha {
+  list-style: upper-alpha }
+
+ol.lowerroman {
+  list-style: lower-roman }
+
+ol.upperroman {
+  list-style: upper-roman }
+
+p.attribution {
+  text-align: right ;
+  margin-left: 50% }
+
+p.caption {
+  font-style: italic }
+
+p.credits {
+  font-style: italic ;
+  font-size: smaller }
+
+p.label {
+  white-space: nowrap }
+
+p.rubric {
+  font-weight: bold ;
+  font-size: larger ;
+  color: maroon ;
+  text-align: center }
+
+p.sidebar-title {
+  font-family: sans-serif ;
+  font-weight: bold ;
+  font-size: larger }
+
+p.sidebar-subtitle {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+p.topic-title {
+  font-weight: bold }
+
+pre.address {
+  margin-bottom: 0 ;
+  margin-top: 0 ;
+  font-family: serif ;
+  font-size: 100% }
+
+pre.literal-block, pre.doctest-block {
+  margin-left: 2em ;
+  margin-right: 2em ;
+  background-color: #eeeeee }
+
+span.classifier {
+  font-family: sans-serif ;
+  font-style: oblique }
+
+span.classifier-delimiter {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+span.interpreted {
+  font-family: sans-serif }
+
+span.option {
+  white-space: nowrap }
+
+span.pre {
+  white-space: pre }
+
+span.problematic {
+  color: red }
+
+span.section-subtitle {
+  /* font-size relative to parent (h1..h6 element) */
+  font-size: 80% }
+
+table.citation {
+  border-left: solid 1px gray;
+  margin-left: 1px }
+
+table.docinfo {
+  margin: 2em 4em }
+
+table.docutils {
+  margin-top: 0.5em ;
+  margin-bottom: 0.5em }
+
+table.footnote {
+  border-left: solid 1px black;
+  margin-left: 1px }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+  padding-left: 0.5em ;
+  padding-right: 0.5em ;
+  vertical-align: top }
+
+table.docutils th.field-name, table.docinfo th.docinfo-name {
+  font-weight: bold ;
+  text-align: left ;
+  white-space: nowrap ;
+  padding-left: 0 }
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+  font-size: 100% }
+
+tt.docutils {
+  background-color: #eeeeee }
+
+ul.auto-toc {
+  list-style-type: none }
new file mode 100644
--- /dev/null
+++ b/doc/index.txt
@@ -0,0 +1,23 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+======
+Markup
+======
+
+---------------------------------------------------------
+Toolkit for stream-based generation of markup for the web
+---------------------------------------------------------
+
+Markup is a Python library that provides an integrated set of components for
+parsing, generating, and processing HTML or XML content in a uniform manner.
+The major feature is a template language that is heavily inspired by Kid.
+
+* `Markup Streams`_
+* `Generating Markup Programmatically`_
+* `Markup XML Template Language`_
+* `Using XPath in Markup`_
+
+.. _`Markup Streams`: streams.html
+.. _`Generating Markup Programmatically`: builder.html
+.. _`Markup XML Template Language`: xml-templates.html
+.. _`Using XPath in Markup`: xpath.html
new file mode 100644
--- /dev/null
+++ b/doc/streams.txt
@@ -0,0 +1,186 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+==============
+Markup Streams
+==============
+
+A stream is the common representation of markup as a *stream of events*.
+
+
+.. contents:: Contents
+   :depth: 2
+.. sectnum::
+
+
+Basics
+======
+
+A stream can be attained in a number of ways. It can be:
+
+* the result of parsing XML or HTML text, or
+* programmatically generated, or
+* the result of selecting a subset of another stream filtered by an XPath
+  expression.
+
+For example, the functions ``XML()`` and ``HTML()`` can be used to convert
+literal XML or HTML text to a markup stream::
+
+  >>> from markup import XML
+  >>> stream = XML('<p class="intro">Some text and '
+  ...              '<a href="http://example.org/">a link</a>.'
+  ...              '<br/></p>')
+  >>> stream
+  <markup.core.Stream object at 0x6bef0>
+
+The stream is the result of parsing the text into events. Each event is a tuple
+of the form ``(kind, data, pos)``, where:
+
+* ``kind`` defines what kind of event it is (such as the start of an element,
+  text, a comment, etc).
+* ``data`` is the actual data associated with the event. How this looks depends
+  on the event kind.
+* ``pos`` is a ``(filename, lineno, column)`` tuple that describes where the
+  event “comes from”.
+
+::
+
+  >>> for kind, data, pos in stream:
+  ...     print kind, `data`, pos
+  ... 
+  START (u'p', [(u'class', u'intro')]) ('<string>', 1, 0)
+  TEXT u'Some text and ' ('<string>', 1, 31)
+  START (u'a', [(u'href', u'http://example.org/')]) ('<string>', 1, 31)
+  TEXT u'a link' ('<string>', 1, 67)
+  END u'a' ('<string>', 1, 67)
+  TEXT u'.' ('<string>', 1, 72)
+  START (u'br', []) ('<string>', 1, 72)
+  END u'br' ('<string>', 1, 77)
+  END u'p' ('<string>', 1, 77)
+
+
+Filtering
+=========
+
+One important feature of markup streams is that you can apply *filters* to the
+stream, either filters that come with Markup, or your own custom filters.
+
+A filter is simply a callable that accepts the stream as parameter, and returns
+the filtered stream::
+
+  def noop(stream):
+      """A filter that doesn't actually do anything with the stream."""
+      for kind, data, pos in stream:
+          yield kind, data, pos
+
+Filters can be applied in a number of ways. The simplest is to just call the
+filter directly::
+
+  stream = noop(stream)
+
+The ``Stream`` class also provides a ``filter()`` method, which takes an
+arbitrary number of filter callables and applies them all::
+
+  stream = stream.filter(noop)
+
+Finally, filters can also be applied using the *bitwise or* operator (``|``),
+which allows a syntax similar to pipes on Unix shells::
+
+  stream = stream | noop
+
+One example of a filter included with Markup is the ``HTMLSanitizer`` in
+``markup.filters``. It processes a stream of HTML markup, and strips out any
+potentially dangerous constructs, such as Javascript event handlers.
+``HTMLSanitizer`` is not a function, but rather a class that implements
+``__call__``, which means instances of the class are callable.
+
+Both the ``filter()`` method and the pipe operator allow easy chaining of
+filters::
+
+  from markup.filters import HTMLSanitizer
+  stream = stream.filter(noop, HTMLSanitizer())
+
+That is equivalent to::
+
+  stream = stream | noop | HTMLSanitizer()
+
+
+Serialization
+=============
+
+The ``Stream`` class provides two methods for serializing this list of events:
+``serialize()`` and ``render()``. The former is a generator that yields chunks
+of ``Markup`` objects (which are basically unicode strings). The latter returns
+a single string, by default UTF-8 encoded.
+
+Here's the output from ``serialize()``::
+
+  >>> for output in stream.serialize():
+  ...     print `output`
+  ... 
+  <Markup u'<p class="intro">'>
+  <Markup u'Some text and '>
+  <Markup u'<a href="http://example.org/">'>
+  <Markup u'a link'>
+  <Markup u'</a>'>
+  <Markup u'.'>
+  <Markup u'<br/>'>
+  <Markup u'</p>'>
+
+And here's the output from ``render()``::
+
+  >>> print stream.render()
+  <p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p>
+
+Both methods can be passed a ``method`` parameter that determines how exactly
+the events are serialzed to text. This parameter can be either “xml” (the
+default), “xhtml”, “html”, “text”, or a custom serializer class::
+
+  >>> print stream.render('html')
+  <p class="intro">Some text and <a href="http://example.org/">a link</a>.<br></p>
+
+Note how the `<br>` element isn't closed, which is the right thing to do for
+HTML.
+
+In addition, the ``render()`` method takes an ``encoding`` parameter, which
+defaults to “UTF-8”. If set to ``None``, the result will be a unicode string.
+
+The different serializer classes in ``markup.output`` can also be used
+directly::
+
+  >>> from markup.filters import HTMLSanitizer
+  >>> from markup.output import TextSerializer
+  >>> print TextSerializer()(HTMLSanitizer()(stream))
+  Some text and a link.
+
+The pipe operator allows a nicer syntax::
+
+  >>> print stream | HTMLSanitizer() | TextSerializer()
+  Some text and a link.
+
+Using XPath
+===========
+
+XPath can be used to extract a specific subset of the stream via the
+``select()`` method::
+
+  >>> substream = stream.select('a')
+  >>> substream
+  <markup.core.Stream object at 0x7118b0>
+  >>> print substream
+  <a href="http://example.org/">a link</a>
+
+Often, streams cannot be reused: in the above example, the sub-stream is based
+on a generator. Once it has been serialized, it will have been fully consumed,
+and cannot be rendered again. To work around this, you can wrap such a stream
+in a ``list``::
+
+  >>> from markup import Stream
+  >>> substream = Stream(list(stream.select('a')))
+  >>> substream
+  <markup.core.Stream object at 0x7118b0>
+  >>> print substream
+  <a href="http://example.org/">a link</a>
+  >>> print substream.select('@href')
+  http://example.org/
+  >>> print substream.select('text()')
+  a link
new file mode 100644
--- /dev/null
+++ b/doc/xml-templates.txt
@@ -0,0 +1,574 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+============================
+Markup XML Template Language
+============================
+
+Markup provides a simple XML-based template language that is heavily inspired
+by Kid_, which in turn was inspired by a number of existing template languages,
+namely XSLT_, TAL_, and PHP_.
+
+.. _kid: http://kid-templating.org/
+.. _python: http://www.python.org/
+.. _xslt: http://www.w3.org/TR/xslt
+.. _tal: http://www.zope.org/Wikis/DevSite/Projects/ZPT/TAL
+.. _php: http://www.php.net/
+
+This document describes the template language and will be most useful as
+reference to those developing Markup templates. Templates are XML files of some
+kind (such as XHTML) that include processing directives_ (elements or
+attributes identified by a separate namespace) that affect how the template is
+rendered, and template expressions_ that are dynamically substituted by
+variable data.
+
+
+.. contents:: Contents
+   :depth: 3
+.. sectnum::
+
+----------
+Python API
+----------
+
+The Python code required for templating with Markup is generally based on the
+following pattern:
+
+* Attain a ``Template`` object from a string or file object containing the
+  template XML source. This can either be done directly, or through a
+  ``TemplateLoader`` instance.
+* Call the ``generate()`` method of the template, passing any data that should
+  be made available to the template as keyword arguments.
+* Serialize the resulting stream using its ``render()`` method.
+
+For example::
+
+  from markup.template import Template
+
+  tmpl = Template('<h1>$title</h1>')
+  stream = tmpl.generate(title='Hello, world!')
+  print stream.render('xhtml')
+
+That code would produce the following output::
+
+  <h1>Hello, world!</h1>
+
+However, if you want includes_ to work, you should attain the template instance
+through a ``TemplateLoader``, and load the template from a file::
+
+  from markup.template import TemplateLoader
+
+  loader = TemplateLoader([templates_dir])
+  tmpl = loader.load('test.html')
+  stream = tmpl.generate(title='Hello, world!')
+  print stream.render('xhtml')
+
+
+.. _`expressions`:
+
+--------------------
+Template Expressions
+--------------------
+
+Python_ expressions can be used in text and attribute values. An expression is
+substituted with the result of its evaluation against the template data.
+Expressions need to prefixed with a dollar sign (``$``) and usually enclosed in
+curly braces (``{…}``).
+
+If the expression starts with a letter and contains only letters and digits,
+the curly braces may be omitted. In all other cases, the braces are required so
+that the template processors knows where the expression ends::
+
+  >>> from markup.template import Context, Template
+  >>> tmpl = Template('<em>${items[0].capitalize()} item</em>')
+  >>> print tmpl.generate(Context(items=['first', 'second']))
+  <em>First item</em>
+
+Expressions support the full power of Python. In addition, it is possible to
+access items in a dictionary using “dotted notation” (i.e. as if they were
+attributes), and vice-versa (i.e. access attributes as if they were items in a
+dictionary)::
+
+  >>> from markup.template import Context, Template
+  >>> tmpl = Template('<em>${dict.foo}</em>')
+  >>> print tmpl.generate(Context(dict={'foo': 'bar'}))
+  <em>bar</em>
+
+
+.. _`directives`:
+
+-------------------
+Template Directives
+-------------------
+
+Directives are elements and/or attributes in the template that are identified
+by the namespace ``http://markup.edgewall.org/``. They can affect how the
+template is rendered in a number of ways: Markup provides directives for
+conditionals and looping, among others.
+
+To use directives in a template, the namespace should be declared, which is
+usually done on the root element::
+
+  <html xmlns="http://www.w3.org/1999/xhtml"
+        xmlns:py="http://markup.edgewall.org/"
+        lang="en">
+    ...
+  </html>
+
+In this example, the default namespace is set to the XHTML namespace, and the
+namespace for Markup directives is bound to the prefix “py”.
+
+All directives can be applied as attributes, and some can also be used as
+elements. The ``if`` directives for conditionals, for example, can be used in
+both ways::
+
+  <html xmlns="http://www.w3.org/1999/xhtml"
+        xmlns:py="http://markup.edgewall.org/"
+        lang="en">
+    ...
+    <div py:if="foo">
+      <p>Bar</p>
+    </div>
+    ...
+  </html>
+
+This is basically equivalent to the following::
+
+  <html xmlns="http://www.w3.org/1999/xhtml"
+        xmlns:py="http://markup.edgewall.org/"
+        lang="en">
+    ...
+    <py:if test="foo">
+      <div>
+        <p>Bar</p>
+      </div>
+    </py:if>
+    ...
+  </html>
+
+The rationale behind the second form is that directives do not always map
+naturally to elements in the template. In such cases, the ``py:strip``
+directive can be used to strip off the unwanted element, or the directive can
+simply be used as an element.
+
+
+Available Directives
+====================
+
+
+.. _`py:attrs`:
+
+``py:attrs``
+------------
+
+This directive adds, modifies or removes attributes from the element::
+
+  <ul>
+    <li py:attrs="foo">Bar</li>
+  </ul>
+
+Given ``foo={'class': 'collapse'}`` in the template context, this would
+produce::
+
+  <ul>
+    <li class="collapse">Bar</li>
+  </ul>
+
+Attributes with the value ``None`` are omitted, so given ``foo={'class': None}``
+in the context for the same template this would produce::
+
+  <ul>
+    <li>Bar</li>
+  </ul>
+
+This directive can only be used as an attribute.
+
+
+.. _`py:choose`:
+.. _`py:when`:
+.. _`py:otherwise`:
+
+``py:choose`` / ``py:when`` / ``py:otherwise``
+----------------------------------------------
+
+This set of directives provides advanced contional processing for rendering one
+of several alternatives. The first matching ``py:when`` branch is rendered, or,
+if no ``py:when`` branch matches, the ``py:otherwise`` branch is be rendered.
+
+If the ``py:choose`` directive is empty the nested ``py:when`` directives will
+be tested for truth::
+
+  <div py:choose="">
+    <span py:when="0 == 1">0</span>
+    <span py:when="1 == 1">1</span>
+    <span py:otherwise="">2</span>
+  </div>
+
+This would produce the following output::
+
+  <div>
+    <span>1</span>
+  </div>
+
+If the ``py:choose`` directive contains an expression the nested ``py:when``
+directives will be tested for equality to the parent ``py:choose`` value::
+
+  <div py:choose="1">
+    <span py:when="0">0</span>
+    <span py:when="1">1</span>
+    <span py:otherwise="">2</span>
+  </div>
+
+This would produce the following output::
+
+  <div>
+    <span>1</span>
+  </div>
+
+
+.. _`py:content`:
+
+``py:content``
+--------------
+
+This directive replaces any nested content with the result of evaluating the
+expression::
+
+  <ul>
+    <li py:content="bar">Hello</li>
+  </ul>
+
+Given ``bar='Bye'`` in the context data, this would produce::
+
+  <ul>
+    <li>Bye</li>
+  </ul>
+
+This directive can only be used as an attribute.
+
+
+.. _`py:def`:
+.. _`macros`:
+
+``py:def``
+----------
+
+The ``py:def`` directive can be used to create macros, i.e. snippets of
+template code that have a name and optionally some parameters, and that can be
+inserted in other places::
+
+  <div>
+    <p py:def="greeting(name)" class="greeting">
+      Hello, ${name}!
+    </p>
+    ${greeting('world')}
+    ${greeting('everyone else')}
+  </div>
+
+The above would be rendered to::
+
+  <div>
+    <p class="greeting">
+      Hello, world!
+    </p>
+    <p class="greeting">
+      Hello, everyone else!
+    </p>
+  </div>
+
+If a macro doesn't require parameters, it can be defined as well as called
+without the parenthesis. For example::
+
+  <div>
+    <p py:def="greeting" class="greeting">
+      Hello, world!
+    </p>
+    ${greeting}
+  </div>
+
+The above would be rendered to::
+
+  <div>
+    <p class="greeting">
+      Hello, world!
+    </p>
+  </div>
+
+This directive can also be used as an element::
+
+  <div>
+    <py:def function="greeting(name)">
+      <p class="greeting">Hello, ${name}!</p>
+    </py:def>
+  </div>
+
+
+.. _`py:for`:
+
+``py:for``
+----------
+
+The element is repeated for every item in an iterable::
+
+  <ul>
+    <li py:for="item in items">${item}</li>
+  </ul>
+
+Given ``items=[1, 2, 3]`` in the context data, this would produce::
+
+  <ul>
+    <li>1</li><li>2</li><li>3</li>
+  </ul>
+
+This directive can also be used as an element::
+
+  <ul>
+    <py:for each="item in items">
+      <li>${item}</li>
+    </py:for>
+  </ul>
+
+
+.. _`py:if`:
+
+``py:if``
+------------
+
+The element is only rendered if the expression evaluates to a truth value::
+
+  <div>
+    <b py:if="foo">${bar}</b>
+  </div>
+
+Given the data ``foo=True`` and ``bar='Hello'`` in the template context, this
+would produce::
+
+  <div>
+    <b>Hello</b>
+  </div>
+
+This directive can also be used as an element::
+
+  <div>
+    <py:if test="foo">
+      <b>${bar}</b>
+    </py:if>
+  </div>
+
+
+.. _`py:match`:
+.. _Match Templates:
+
+``py:match``
+------------
+
+This directive defines a *match template*: given an XPath expression, it
+replaces any element in the template that matches the expression with its own
+content.
+
+For example, the match template defined in the following template matches any
+element with the tag name “greeting”::
+
+  <div>
+    <span py:match="greeting">
+      Hello ${select('@name')}
+    </span>
+    <greeting name="Dude" />
+  </div>
+
+This would result in the following output::
+
+  <div>
+    <span>
+      Hello Dude
+    </span>
+  </div>
+
+Inside the body of a ``py:match`` directive, the ``select(path)`` function is
+made available so that parts or all of the original element can be incorporated
+in the output of the match template. See [wiki:MarkupStream#UsingXPath] for
+more information about this function.
+
+This directive can also be used as an element::
+
+  <div>
+    <py:match path="greeting">
+      <span>Hello ${select('@name')}</span>
+    </py:match>
+    <greeting name="Dude" />
+  </div>
+
+
+.. _`py:replace`:
+
+``py:replace``
+--------------
+
+This directive replaces the element itself with the result of evaluating the
+expression::
+
+  <div>
+    <span py:replace="bar">Hello</span>
+  </div>
+
+Given ``bar='Bye'`` in the context data, this would produce::
+
+  <div>
+    Bye
+  </div>
+
+This directive can only be used as an attribute.
+
+
+.. _`py:strip`:
+
+``py:strip``
+------------
+
+This directive conditionally strips the top-level element from the output. When
+the value of the ``py:strip`` attribute evaluates to ``True``, the element is
+stripped from the output::
+
+  <div>
+    <div py:strip="True"><b>foo</b></div>
+  </div>
+
+This would be rendered as::
+
+  <div>
+    <b>foo</b>
+  </div>
+
+As a shorthand, if the value of the ``py:strip`` attribute is empty, that has
+the same effect as using a truth value (i.e. the element is stripped).
+
+
+.. _`with`:
+
+``py:with``
+-----------
+
+The ``py:with`` directive lets you assign expressions to variables, which can
+be used to make expressions inside the directive less verbose and more
+efficient. For example, if you need use the expression ``author.posts`` more
+than once, and that actually results in a database query, assigning the results
+to a variable using this directive would probably help.
+
+For example::
+
+  <div>
+    <span py:with="y=7; z=x+10">$x $y $z</span>
+  </div>
+
+Given ``x=42`` in the context data, this would produce::
+
+  <div>
+    <span>42 7 52</span>
+  </div>
+
+This directive can also be used as an element::
+
+  <div>
+    <py:with vars="y=7; z=x+10">$x $y $z</py:with>
+  </div>
+
+Note that if a variable of the same name already existed outside of the scope
+of the ``py:with`` directive, it will **not** be overwritten. Instead, it
+will have the same value it had prior to the ``py:with`` assignment.
+Effectively, this means that variables are immutable in Markup.
+
+
+.. _order:
+
+Processing Order
+================
+
+It is possible to attach multiple directives to a single element, although not
+all combinations make sense. When multiple directives are encountered, they are
+processed in the following order:
+
+#. `py:def`_
+#. `py:match`_
+#. `py:when`_
+#. `py:otherwise`_
+#. `py:for`_
+#. `py:if`_
+#. `py:choose`_
+#. `py:with`_
+#. `py:replace`_
+#. `py:content`_
+#. `py:attrs`_
+#. `py:strip`_
+
+
+.. _includes:
+
+--------
+Includes
+--------
+
+To reuse common snippets of template code, you can include other files using
+XInclude_.
+
+.. _xinclude: http://www.w3.org/TR/xinclude/
+
+For this, you need to declare the XInclude namespace (commonly bound to the
+prefix “xi”) and use the ``<xi:include>`` element where you want the external
+file to be pulled in::
+
+  <html xmlns="http://www.w3.org/1999/xhtml"
+        xmlns:py="http://markup.edgewall.org/"
+        xmlns:xi="http://www.w3.org/2001/XInclude">
+    <xi:include href="base.html" />
+    ...
+  </html>
+
+Include paths are relative to the filename of the template currently being
+processed. So if the example above was in the file "``myapp/index.html``"
+(relative to the template search path), the XInclude processor would look for
+the included file at "``myapp/base.html``". You can also use Unix-style
+relative paths, for example "``../base.html``" to look in the parent directory.
+
+Any content included this way is inserted into the generated output instead of
+the ``<xi:include>`` element. The included template sees the same context data.
+`Match templates`_ and `macros`_ in the included template are also available to
+the including template after the point it was included.
+
+By default, an error will be raised if an included file is not found. If that's
+not what you want, you can specify fallback content that should be used if the
+include fails. For example, to to make the include above fail silently, you'd
+write:
+
+  <xi:include href="base.html"><xi:fallback /></xi:include>
+
+See the XInclude_ for more about fallback content. Note though that Markup
+currently only supports a small subset of XInclude.
+
+Incudes in Markup are fully dynamic: Just like normal attributes, the `href`
+attribute accepts expressions_, and directives_ can be used on the
+``<xi:include />`` element just as on any other element, meaning you can do
+things like conditional includes::
+
+  <xi:include href="${name}.html" py:if="not in_popup"
+              py:for="name in ('foo', 'bar', 'baz')" />
+
+
+.. _comments:
+
+--------
+Comments
+--------
+
+Normal XML/HTML comment syntax can be used in templates::
+
+  <!-- this is a comment -->
+
+However, such comments get passed through the processing pipeline and are by
+default included in the final output. If that's not desired, prefix the comment
+text with an exclamation mark::
+
+  <!-- !this is a comment too, but one that will be stripped from the output -->
+
+Note that it does not matter whether there's whitespace before or after the
+exclamation mark, so the above could also be written as follows::
+
+  <!--! this is a comment too, but one that will be stripped from the output -->
new file mode 100644
--- /dev/null
+++ b/doc/xpath.txt
@@ -0,0 +1,92 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+=====================
+Using XPath in Markup
+=====================
+
+Markup provides basic XPath_ support for matching and querying event streams.
+
+.. _xpath: http://www.w3.org/TR/xpath
+
+
+.. contents:: Contents
+   :depth: 2
+.. sectnum::
+
+
+-----------
+Limitations
+-----------
+
+Due to the streaming nature of the processing model, Markup uses only a subset
+of the `XPath 1.0`_ language.
+
+.. _`XPath 1.0`: http://www.w3.org/TR/xpath
+
+In particular, only the following axes are supported:
+
+* ``attribute``
+* ``child``
+* ``descendant``
+* ``descendant-or-self``
+* ``self``
+
+This means you can't use the ``parent``, ancestor, or sibling axes in Markup
+(the ``namespace`` axis isn't supported either, but what you'd ever need that
+for I don't know). Basically, any path expression that would require buffering
+of the stream is not supported.
+
+Predicates are of course supported, but Path expressions *inside* predicates
+are restricted to attribute lookups (again due to the lack of buffering).
+
+Most of the XPath functions and operators are supported, however they
+(currently) only work inside predicates. The following functions are **not**
+supported:
+
+* ``count()``
+* ``id()``
+* ``lang()``
+* ``last()``
+* ``position()``
+* ``string()``
+* ``sum()``
+
+The mathematical operators (``+``, ``-``, ``*``, ``div``, and ``mod``) are not
+yet supported, whereas the various comparison and logical operators should work
+as expected.
+
+You can also use XPath variable references (``$var``) inside predicates.
+
+
+----------------
+Querying Streams
+----------------
+
+::
+
+  from markup.input import XML
+
+  doc = XML('''<doc>
+   <items count="2">
+        <item status="new">
+          <summary>Foo</summary>
+        </item>
+        <item status="closed">
+          <summary>Bar</summary>
+        </item>
+    </items>
+  </doc>''')
+  print doc.select('items/item[@status="closed"]/summary/text()')
+
+This would result in the following output::
+
+  Bar
+
+
+---------------------
+Matching in Templates
+---------------------
+
+See the directive ``py:match`` in the `XML Template Language Specification`_.
+
+.. _`XML Template Language Specification`: xml-templates.html
Copyright (C) 2012-2017 Edgewall Software