[pypy-commit] benchmarks default: merge
fijal
noreply at buildbot.pypy.org
Tue Jan 24 12:10:15 CET 2012
Author: Maciej Fijalkowski <fijall at gmail.com>
Branch:
Changeset: r163:f4a31718810d
Date: 2012-01-24 13:09 +0200
http://bitbucket.org/pypy/benchmarks/changeset/f4a31718810d/
Log: merge
diff too long, truncating to 10000 out of 32654 lines
diff --git a/benchmarks.py b/benchmarks.py
--- a/benchmarks.py
+++ b/benchmarks.py
@@ -52,6 +52,11 @@
extra_args=['--benchmark=' + name],
iteration_scaling=0.1)
+for name in ['xml', 'text']:
+ _register_new_bm('bm_genshi', 'genshi_' + name,
+ globals(), bm_env={'PYTHONPATH': relative('lib/genshi')},
+ extra_args=['--benchmark=' + name])
+
for name in ['float', 'nbody_modified', 'meteor-contest', 'fannkuch',
'spectral-norm', 'chaos', 'telco', 'go', 'pyflate-fast',
'raytrace-simple', 'crypto_pyaes', 'bm_mako', 'bm_chameleon',
diff --git a/lib/genshi/COPYING b/lib/genshi/COPYING
new file mode 100644
--- /dev/null
+++ b/lib/genshi/COPYING
@@ -0,0 +1,28 @@
+Copyright (C) 2006-2010 Edgewall Software
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/genshi/ChangeLog b/lib/genshi/ChangeLog
new file mode 100644
--- /dev/null
+++ b/lib/genshi/ChangeLog
@@ -0,0 +1,472 @@
+Version 0.6.1
+http://svn.edgewall.org/repos/genshi/tags/0.6.1/
+(???, from branches/stable/0.6.x)
+
+ * Fix for error in how `HTMLFormFiller` would handle `textarea` elements if
+ no value was not supplied form them.
+ * The `HTMLFormFiller` now correctly handles check boxes and radio buttons
+ with an empty `value` attribute.
+
+
+Version 0.6
+http://svn.edgewall.org/repos/genshi/tags/0.6.0/
+(Apr 22 2010, from branches/stable/0.6.x)
+
+ * Support for Python 2.3 has been dropped.
+ * Rewrite of the XPath evaluation engine for better performance and improved
+ correctness. This is the result of integrating work done by Marcin Kurczych
+ during GSoC 2008.
+ * Updated the Python AST processing for template code evaluation to use the
+ `_ast` module instead of the deprecated `compiler` package, including an
+ adapter layer for Python 2.4. This, too, is the result of integrating work
+ done by Marcin Kurczych during GSoC 2008.
+ * Added caching in the serialization stage for improved performance in some
+ cases.
+ * Various improvements to the HTML sanitization filter.
+ * Fix problem with I18n filter that would get confused by expressions in
+ attribute values when inside an `i18n:msg` block (ticket #250).
+ * Fix problem with the transformation filter dropping events after the
+ selection (ticket #290).
+ * `for` loops in template code blocks no longer establish their own locals
+ scope, meaning you can now access variables assigned in the loop outside
+ of the loop, just as you can in regular Python code (ticket #259).
+ * Import statements inside function definitions in template code blocks no
+ longer result in an UndefinedError when the imported name is accessed
+ (ticket #276).
+ * Fixed handling of relative URLs with fragment identifiers containing colons
+ in the `HTMLSanitizer` (ticket #274).
+ * Added an option to the `HTMLFiller` to also populate password fields.
+ * Match template processing no longer produces unwanted duplicate output in
+ some cases (ticket #254).
+ * Templates instantiated without a loader now get an implicit loader based on
+ their file path, or the current directory as a fallback (ticket #320).
+ * Added documentation for the `TemplateLoader`.
+ * Enhanced documentation for internationalization.
+
+
+Version 0.5.1
+http://svn.edgewall.org/repos/genshi/tags/0.5.1/
+(Jul 9 2008, from branches/stable/0.5.x)
+
+ * Fix problem with nested match templates not being applied when buffering
+ on the outer `py:match` is disabled. Thanks to Erik Bray for reporting the
+ problem and providing a test case!
+ * Fix problem in `Translator` filter that would cause the translation of
+ text nodes to fail if the translation function returned an object that was
+ not directly a string, but rather something like an instance of the
+ `LazyProxy` class in Babel (ticket #145).
+ * Fix problem with match templates incorrectly being applied multiple times.
+ * Includes from templates loaded via an absolute path now include the correct
+ file in nested directories as long if no search path has been configured
+ (ticket #240).
+ * Unbuffered match templates could result in parts of the matched content
+ being included in the output if the match template didn't actually consume
+ it via one or more calls to the `select()` function (ticket #243).
+
+
+Version 0.5
+http://svn.edgewall.org/repos/genshi/tags/0.5.0/
+(Jun 9 2008, from branches/stable/0.5.x)
+
+ * Added #include directive for text templates (ticket #115).
+ * Added new markup transformation filter contributed by Alec Thomas. This
+ provides gorgeous jQuery-inspired stream transformation capabilities based
+ on XPath expressions.
+ * When using HTML or XHTML serialization, the `xml:lang` attribute is
+ automatically translated to the `lang` attribute which HTML user agents
+ understand.
+ * Added support for the XPath 2 `matches()` function in XPath expressions,
+ which allow matching against regular expressions.
+ * Support for Python code blocks in templates can now be disabled
+ (ticket #123).
+ * Includes are now processed when the template is parsed if possible, but
+ only if the template loader is not set to do automatic reloading. Included
+ templates are basically inlined into the including template, which can
+ speed up rendering of that template a bit.
+ * Added new syntax for text templates, which is more powerful and flexible
+ with respect to white-space and line breaks. It also supports Python code
+ blocks. The old syntax is still available and the default for now, but in a
+ future release the new syntax will become the default, and some time after
+ that the old syntax will be removed.
+ * Added support for passing optimization hints to `<py:match>` directives,
+ which can speed up match templates in many cases, for example when a match
+ template should only be applied once to a stream, or when it should not be
+ applied recursively.
+ * Text templates now default to rendering as plain text; it is no longer
+ necessary to explicitly specify the "text" method to the `render()` or
+ `serialize()` method of the generated markup stream.
+ * XInclude elements in markup templates now support the `parse` attribute;
+ when set to "xml" (the default), the include is processed as before, but
+ when set to "text", the included template is parsed as a text template using
+ the new syntax (ticket #101).
+ * Python code blocks inside match templates are now executed (ticket #155).
+ * The template engine plugin no longer adds the `default_doctype` when the
+ `fragment` parameter is `True`.
+ * The `striptags` function now also removes HTML/XML-style comments (ticket
+ #150).
+ * The `py:replace` directive can now also be used as an element, with an
+ attribute named `value` (ticket #144).
+ * The `TextSerializer` class no longer strips all markup in text by default,
+ so that it is still possible to use the Genshi `escape` function even with
+ text templates. The old behavior is available via the `strip_markup` option
+ of the serializer (ticket #146).
+ * Assigning to a variable named `data` in a Python code block no longer
+ breaks context lookup.
+ * The `Stream.render` now accepts an optional `out` parameter that can be
+ used to pass in a writable file-like object to use for assembling the
+ output, instead of building a big string and returning it.
+ * The XHTML serializer now strips `xml:space` attributes as they are only
+ allowed on very few tags.
+ * Match templates are now applied in a more controlled fashion: in the order
+ they are declared in the template source, all match templates up to (and
+ including) the matching template itself are applied to the matched content,
+ whereas the match templates declared after the matching template are only
+ applied to the generated content (ticket #186).
+ * The `TemplateLoader` class now provides an `_instantiate()` method that can
+ be overridden by subclasses to implement advanced template instantiation
+ logic (ticket #204).
+ * The search path of the `TemplateLoader` class can now contain ''load
+ functions'' in addition to path strings. A load function is passed the
+ name of the requested template file, and should return a file-like object
+ and some metadata. New load functions are supplied for loading from egg
+ package data, and loading from different loaders depending on the path
+ prefix of the requested filename (ticket #182).
+ * Match templates can now be processed without keeping the complete matched
+ content in memory, which could cause excessive memory use on long pages.
+ The buffering can be disabled using the new `buffer` optimization hint on
+ the `<py:match>` directive.
+ * Improve error reporting when accessing an attribute in a Python expression
+ raises an `AttributeError` (ticket #191).
+ * The `Markup` class now supports mappings for right hand of the `%` (modulo)
+ operator in the same way the Python string classes do, except that the
+ substituted values are escape. Also, the special constructor which took
+ positional arguments that would be substituted was removed. Thus the
+ `Markup` class now supports the same arguments as that of its `unicode`
+ base class (ticket #211).
+ * The `Template` class and its subclasses, as well as the interpolation API,
+ now take an `filepath` parameter instead of `basedir` (ticket #207).
+ * The `XHTMLSerializer` now has a `drop_xml_decl` option that defaults to
+ `True`. Setting it to `False` will cause any XML decl in the serialized
+ stream to be included in the output as it would for XML serialization.
+ * Add support for a protocol that would allow interoperability of different
+ Python packages that generate and/or consume markup, based on the special
+ `__html__()` method (ticket #202).
+
+
+Version 0.4.4
+http://svn.edgewall.org/repos/genshi/tags/0.4.4/
+(Aug 14, 2007, from branches/stable/0.4.x)
+
+ * Fixed augmented assignment to local variables in Python code blocks.
+ * Fixed handling of nested function and class definitions in Python code
+ blocks.
+ * Includes were not raising `TemplateNotFound` exceptions even when no
+ fallback has been specified. That has been corrected.
+ * The template loader now raises a `TemplateNotFound` error when a previously
+ cached template is removed or renamed, where it previously was passing up
+ an `OSError`.
+ * The Genshi I18n filter can be configured to only extract messages found in
+ `gettext` function calls, ignoring any text nodes and attribute values
+ (ticket #138).
+
+
+Version 0.4.3
+http://svn.edgewall.org/repos/genshi/tags/0.4.3/
+(Jul 17 2007, from branches/stable/0.4.x)
+
+ * The I18n filter no longer extracts or translates literal strings in
+ attribute values that also contain expressions.
+ * Added `loader_callback` option to plugin interface, which allows specifying
+ a callback function that the template loader should invoke whenever a new
+ template is loaded (ticket #130). Note that the value for this option can
+ not be specified as a string, only as an actual function object, which means
+ it is not available for use through configuration files.
+ * The I18n filter now extracts messages from gettext functions even inside
+ ignored tags (ticket #132).
+ * The HTML sanitizer now strips any CSS comments in style attributes, which
+ could previously be used to hide malicious property values.
+ * The HTML sanitizer now also removes any HTML comments encountered, as those
+ may be used to hide malicious payloads targetting a certain "innovative"
+ browser that goes and interprets the content of specially prepared comments.
+ * Attribute access in template expressions no longer silently ignores
+ exceptions other than `AttributeError` raised in the attribute accessor.
+
+
+Version 0.4.2
+http://svn.edgewall.org/repos/genshi/tags/0.4.2/
+(Jun 20 2007, from branches/stable/0.4.x)
+
+ * The `doctype` parameter of the markup serializers now also accepts the "name"
+ of the doctype as string, in addition to the `(name, pubid, sysid)` tuple.
+ * The I18n filter was not replacing the original attributes with the
+ translation, but instead adding a second attribute with the same name.
+ * `TextTemplate` can now handle unicode source (ticket #125).
+ * A `<?python ?>` processing instruction containing trailing whitespace no
+ longer causes a syntax error (ticket #127).
+ * The I18n filter now skips the content of elements that have an `xml:lang`
+ attribute with a fixed string value. Basically, `xml:lang` can now be used
+ as a flag to mark specific sections as not needing localization.
+ * Added plugin for message extraction via Babel (http://babel.edgewall.org/).
+
+
+Version 0.4.1
+http://svn.edgewall.org/repos/genshi/tags/0.4.1/
+(May 21 2007, from branches/stable/0.4.x)
+
+ * Fix incorrect reference to translation function in the I18N filter.
+ * The `ET()` function now correctly handles attributes with a namespace.
+ * XML declarations are now processed internally, as well as written to the
+ output when XML serialization is used (ticket #111).
+ * Added the functions `encode()` and `get_serializer()` to the `genshi.output`
+ module, which provide a lower-level API to the functionality previously only
+ available through `Stream.render()` and `Stream.serialize()`.
+ * The `DocType` class now has a `get(name)` function that returns a `DOCTYPE`
+ tuple for a given string.
+ * Added frameset variants to the `DocType` constants for HTML 4.01 and XHTML
+ 1.0.
+ * Improved I18n extraction for pluralizable messages: for any translation
+ function with multiple string arguments (such as ``ngettext``), a single
+ item with a tuple of strings is yielded, instead an item for each string
+ argument.
+ * The `HTMLFormFiller` stream filter no longer alters form elements for which
+ the data element contains no corresponding item.
+ * Code in `<?python ?>` processing instructions no longer gets the special
+ treatment as Python code in template expressions, i.e. item and attribute
+ access are no longer interchangeable (which was broken in a number of ways
+ anyway, see ticket #113). This change does not affect expressions.
+ * Numerous fixes for the execution of Python code in `<?python ?>` processing
+ instructions (tickets #113 and #114).
+ * The `py:def` (and `#def`) directive now supports "star args" (i.e. `*args`
+ and `**kwargs`) in the function declaration (ticket #116).
+
+
+Version 0.4
+http://svn.edgewall.org/repos/genshi/tags/0.4.0/
+(Apr 16 2007, from branches/stable/0.4.x)
+
+ * New example applications for CherryPy and web.py.
+ * The template loader now uses a LRU cache to limit the number of cached
+ templates to a configurable maximum. Also, a race condition in the template
+ loader was fixed by adding locking.
+ * A new filter (genshi.filters.HTMLFormFiller) was added, which can populate
+ HTML forms based on a dictionary of values.
+ * The set of permitted tag and attribute names for the HTMLSanitizer can now
+ be configured per instance.
+ * The template engine plugin now supports a range of options for
+ configuration, for example to set the default serialization method, the
+ default output encoding, or the default DOCTYPE.
+ * The ElementTree adaptation function `ET()` has moved into the `genshi.input`
+ module.
+ * Allow `when` directives to omit the test expression as long as the
+ associated choose directive does have one. In that case, the when branch is
+ followed if the expression of the choose directive evaluates to a truth
+ value.
+ * Unsuccessful attribute or item lookups now return `Undefined` objects for
+ nicer error messages.
+ * Split up the `genshi.template` module into multiple modules inside the new
+ `genshi.template` package.
+ * Results of expression evaluation are no longer implicitly called if they
+ are callable.
+ * Instances of the `genshi.core.Attrs` class are now immutable (they are
+ subclasses of `tuple` instead of `list`).
+ * `MarkupTemplate`s can now be instantiated from markup streams, in addition
+ to strings and file-like objects (ticket #69).
+ * Improve handling of incorrectly nested tags in the HTML parser.
+ * Template includes can now be nested inside fallback content.
+ * Expressions can now contain dict literals (ticket #37).
+ * It is now possible to have one or more escaped dollar signs in front of a
+ full expression (ticket #92).
+ * The `Markup` class is now available by default in template expressions
+ (ticket #67).
+ * The handling of namespace declarations in XML/XHTML output has been improved.
+ * The `Attrs` class no longer automatically wraps all attribute names in
+ `QName` objects. This is now the responsibility of whoever is instantiating
+ `Attrs` objects (for example, stream filters and generators).
+ * Python code blocks are now supported using the `<?python ?>` processing
+ instruction (ticket #84).
+ * The way errors in template expressions are handled can now be configured. The
+ option `LenientLookup` provides the same forgiving mode used in previous
+ Genshi versions, while `StrictLookup` raises exceptions when undefined
+ variables or members are accessed. The lenient mode is still the default in
+ this version, but that may change in the future. (ticket #88)
+ * If a variable is not necessarily defined at the top level of the template
+ data, the new built-in functions `defined(key)` and `value_of(key, default)`
+ can be used so that the template also works in strict lookup mode. These
+ functions were previously only available when using Genshi via the template
+ engine plugin (for compatibility with Kid).
+ * `style` attributes are no longer allowed by the `HTMLSanitizer` by default.
+ If they are explicitly added to the set of safe attributes, any unicode
+ escapes in the attribute value are now handled properly.
+ * Namespace declarations on conditional elements (for example using a `py:if`
+ directive`) are no longer moved to the following element when the element
+ originally carrying the declaration is removed from the stream (ticket #107).
+ * Added basic built-in support for internationalizing templates by providing
+ a new `Translator` class that can both extract localizable strings from a
+ stream, and replace those strings with their localizations at render time.
+ The code for this was largely taken from previous work done by Matt Good
+ and David Fraser.
+
+
+Version 0.3.6
+http://svn.edgewall.org/repos/genshi/tags/0.3.6/
+(Dec 11 2006, from branches/stable/0.3.x)
+
+ * The builder API now accepts streams as children of elements and fragments.
+
+
+Version 0.3.5
+http://svn.edgewall.org/repos/genshi/tags/0.3.5/
+(Nov 22 2006, from branches/stable/0.3.x)
+
+ * Fix XPath traversal in match templates. Previously, `div/p` would be treated
+ the same as `div//p`, i.e. it would match all descendants and not just the
+ immediate children.
+ * Preserve whitespace in HTML `<pre>` elements also when they contain child
+ elements.
+ * Match templates no longer match their own output (ticket #77).
+ * Blank lines before directives in text templates are now preserved as
+ expected (ticket #62).
+
+
+Version 0.3.4
+http://svn.edgewall.org/repos/genshi/tags/0.3.4/
+(Nov 2 2006, from branches/stable/0.3.x)
+
+ * The encoding of HTML and XML files, as well as markup and text templates,
+ can now be specified. Also, the encoding specified in XML declarations is
+ now respected unless an expiclit encoding is requested.
+ * Expressions used as arguments for `py:with`, `py:def`, and `py:for`
+ directives can now contain non-ASCII strings.
+
+
+Version 0.3.3
+http://svn.edgewall.org/repos/genshi/tags/0.3.3/
+(Oct 16 2006, from branches/stable/0.3.x)
+
+ * Fixed bug introduced in 0.3.2 that broke the parsing of templates which
+ declare the same namespace more than once in a nested fashion.
+ * Fixed the parsing of HTML entity references inside attribute values, both
+ in the `XMLParser` and the `HTMLParser` classes.
+ * Some changes to usage of absolute vs. relative template paths to ensure that
+ the filenamed-keyed cache employed by the TemplateLoader doesn't mix up
+ templates with the same name, but from different subdirectories.
+
+
+Version 0.3.2
+http://svn.edgewall.org/repos/genshi/tags/0.3.2/
+(Oct 12 2006, from branches/stable/0.3.x)
+
+ * Exceptions from templates now contain the absolute path to the template file
+ when a search path is used. This enables tracebacks to display the line in
+ question.
+ * The template engine plugin now provides three different types: "genshi" and
+ "genshi-markup" map to markup templates, while "genshi-text" maps to text
+ templates.
+ * Fixed the namespace context used by XPath patterns in py:match templates.
+ The were erroneously using the namespace context of the elements being
+ matched, where they should rather use the context in which they were
+ defined.
+ * The contents of `<script>` and `<style>` elements are no longer escaped when
+ serializing to HTML but declaring the XHTML namespace in the template.
+ * Improved error message raised when using the `py:for` directive on an object
+ that is not iterable (ticket #60).
+ * Fixed the XPath function `starts-with()` which was always returning true
+ (ticket #61).
+
+
+Version 0.3.1
+http://svn.edgewall.org/repos/genshi/tags/0.3.1/
+(Sep 22 2006, from branches/stable/0.3.x)
+
+ * Includes and user-defined filters were not getting the correct context data
+ when used inside a match template (ticket #56).
+ * XPath patterns using the union operator (`|`) were returning only partial
+ results in some cases.
+
+
+Version 0.3
+http://svn.edgewall.org/repos/genshi/tags/0.3.0/
+(Sep 17 2006, from branches/stable/0.3.x)
+
+ * The project name was changed from "Markup" to "Genshi". See UPGRADE.txt
+ for upgrade instructions.
+ * Expression evaluation now differentiates between undefined variables and
+ variables that are defined but set to `None`. This also means that local
+ variables can override built-ins even if the local variable are set to
+ `None` (ticket #36).
+ * The plugin implementation now makes more functions available for use inside
+ templates: XML(), HTML(), defined(), and value-of(). These are mostly for
+ migration from Kid.
+ * The parsing of `py:with` directives has been improved: you can now assign
+ to multiple names, and semicolons inside string literals are treated as
+ expected.
+ * Generator expressions can now be used in template expressions (ticket #16).
+ * Added serialization to plain text (ticket #41).
+ * Calling functions from template expressions with *args and/or **kwargs
+ now works correctly (ticket #42).
+ * The `TemplateLoader` class and the `Template` instances that it manages are
+ now thread-safe, as they no longer keep any state related to the current
+ processing context.
+ * Stream filters and serializers can now be applied using the "pipe" (bitwise
+ or) operator "|".
+ * The execution of named template functions (`py:def`) no longer silently
+ swallows TypeError exceptions (ticket #44).
+ * The XML Parser now correctly handles unicode input (ticket #43).
+ * HTML entities can now be used in templates without having to declare an HTML
+ document type.
+ * Error reporting on undefined entities has been fixed.
+ * Fix traversal of substreams by XPath expressions. For example, the path
+ '*/*' no longer matches non-immediate child elements, and '@*' no longer
+ matches the attributes of descendants.
+ * Fixes for `py:match` which would get confused when it should be applied
+ to multiple elements (ticket #49).
+ * Using namespace prefixes in XPath expressions is now supported.
+ * Expressions now correctly handle slices (ticket #51).
+ * A simple text-based template language is now included for generating
+ plain text output (ticket #47).
+
+
+Version 0.2
+http://svn.edgewall.org/repos/genshi/tags/0.2.0/
+(Aug 22 2006, from branches/stable/0.2.x)
+
+ * XPath syntax errors now contain position info (ticket #20).
+ * Errors in expression evaluation now contain the correct line number in the
+ template (ticket #22).
+ * <script> and <style> element contents are no longer escaped when using HTML
+ serialization (ticket #24).
+ * In some cases expressions in templates did not get interpolated (ticket
+ #26).
+ * CDATA sections are now passed through the pipeline and serialized correctly.
+ That allows using <script> or <style> elements in XHTML output that is still
+ compatible with HTML user agents.
+ * The XHTML serializer now correctly handles elements in foreign namespaces
+ (such as SVG or MathML).
+ * Fixed relative includes in templates on Windows (ticket #27).
+ * Output can be encoded using legacy codecs such as ISO-8859-1. Any character
+ not representable in the chosen encoding gets replaced by the corresponding
+ XML character reference.
+ * String literals in XPath expressions that contain spaces are now parsed
+ as expected.
+ * Added support for the XPath functions boolean(), ceiling(), concat(),
+ contains(), false(), floor(), normalize-space(), number(), round(),
+ starts-with(), string-length(), substring(), substring-after(),
+ substring-before(), translate(), and true().
+ * Non-ASCII characters in expressions should now be handled correctly (ticket
+ #29).
+ * Default values for arguments of template functions now also work with
+ constants and complex expressions (they only worked for string or number
+ literals before).
+ * XPath expressions in now support XPath variables ($var) in predicates
+ (ticket #31).
+ * Expressions in templates can now span multiple lines if they are enclosed
+ in curly braces.
+ * py:def macros can now be invoked from within expressions inside attribute
+ values (ticket #34).
+
+
+Version 0.1
+http://svn.edgewall.org/repos/genshi/tags/0.1.0/
+(Aug 3 2006, from branches/stable/0.1.x)
+
+ * First public release
diff --git a/lib/genshi/MANIFEST.in b/lib/genshi/MANIFEST.in
new file mode 100644
--- /dev/null
+++ b/lib/genshi/MANIFEST.in
@@ -0,0 +1,5 @@
+exclude doc/2000ft.graffle
+recursive-exclude doc/logo.lineform *
+include doc/api/*.*
+include doc/*.html
+recursive-include genshi/template/tests/templates *.html *.txt
diff --git a/lib/genshi/README.txt b/lib/genshi/README.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/README.txt
@@ -0,0 +1,12 @@
+About Genshi
+============
+
+Genshi is a Python library that provides an integrated set of
+components for parsing, generating, and processing HTML, XML or other
+textual content for output generation on the web. The major feature is
+a template language, which is heavily inspired by Kid.
+
+For more information please see the documentation in the `doc`
+directory, and visit the Genshi web site:
+
+ <http://genshi.edgewall.org/>
diff --git a/lib/genshi/doc/2000ft.graffle b/lib/genshi/doc/2000ft.graffle
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/2000ft.graffle
@@ -0,0 +1,1333 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ActiveLayerIndex</key>
+ <integer>0</integer>
+ <key>AutoAdjust</key>
+ <false/>
+ <key>CanvasColor</key>
+ <dict>
+ <key>w</key>
+ <string>1</string>
+ </dict>
+ <key>CanvasOrigin</key>
+ <string>{0, 0}</string>
+ <key>CanvasScale</key>
+ <real>1</real>
+ <key>ColumnAlign</key>
+ <integer>1</integer>
+ <key>ColumnSpacing</key>
+ <real>36</real>
+ <key>CreationDate</key>
+ <string>2006-05-08 14:33:49 +0200</string>
+ <key>Creator</key>
+ <string>chris</string>
+ <key>DisplayScale</key>
+ <string>1 cm = 1 cm</string>
+ <key>GraphDocumentVersion</key>
+ <integer>5</integer>
+ <key>GraphicsList</key>
+ <array>
+ <dict>
+ <key>Bounds</key>
+ <string>{{58.1159, 157}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>54</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 Fragment}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{387.348, 157}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>53</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 TextTemplate}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{241.464, 376}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>52</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 Plain Text Serializer}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{126, 376}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>51</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 XHTML Serializer}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{241.464, 258}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>50</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 Transformer}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>29</integer>
+ </dict>
+ <key>ID</key>
+ <integer>48</integer>
+ <key>OrthogonalBarAutomatic</key>
+ <true/>
+ <key>OrthogonalBarPosition</key>
+ <real>-1</real>
+ <key>Points</key>
+ <array>
+ <string>{372, 148.88}</string>
+ <string>{317.768, 149.21}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>LineType</key>
+ <integer>2</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>46</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{387.348, 119}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>47</integer>
+ <key>Link</key>
+ <dict>
+ <key>url</key>
+ <string>/wiki/Documentation/xml-templates.html</string>
+ </dict>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 MarkupTemplate}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{372, 99}, {125.232, 99}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>1</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>46</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.701961</string>
+ <key>g</key>
+ <string>0.701961</string>
+ <key>r</key>
+ <string>0.701961</string>
+ </dict>
+ <key>GradientAngle</key>
+ <real>114</real>
+ <key>GradientColor</key>
+ <dict>
+ <key>b</key>
+ <string>0.701961</string>
+ <key>g</key>
+ <string>0.701961</string>
+ <key>r</key>
+ <string>0.701961</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>CornerRadius</key>
+ <real>5</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-BoldOblique;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\i\b\fs24 \cf1 templating}</string>
+ <key>VerticalPad</key>
+ <integer>2</integer>
+ </dict>
+ <key>TextPlacement</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>29</integer>
+ </dict>
+ <key>ID</key>
+ <integer>45</integer>
+ <key>OrthogonalBarAutomatic</key>
+ <true/>
+ <key>OrthogonalBarPosition</key>
+ <real>-1</real>
+ <key>Points</key>
+ <array>
+ <string>{168, 149.5}</string>
+ <string>{222.232, 149.5}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>LineType</key>
+ <integer>2</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>44</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{58.1159, 119}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>43</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 Element}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>23</integer>
+ </dict>
+ <key>ID</key>
+ <integer>41</integer>
+ <key>OrthogonalBarAutomatic</key>
+ <true/>
+ <key>OrthogonalBarPosition</key>
+ <real>-1</real>
+ <key>Points</key>
+ <array>
+ <string>{270, 299}</string>
+ <string>{270, 328}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>LineType</key>
+ <integer>2</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>34</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>29</integer>
+ </dict>
+ <key>ID</key>
+ <integer>40</integer>
+ <key>OrthogonalBarAutomatic</key>
+ <true/>
+ <key>OrthogonalBarPosition</key>
+ <real>-1</real>
+ <key>Points</key>
+ <array>
+ <string>{270, 83}</string>
+ <string>{270, 133.5}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>LineType</key>
+ <integer>2</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>26</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>34</integer>
+ </dict>
+ <key>ID</key>
+ <integer>39</integer>
+ <key>OrthogonalBarAutomatic</key>
+ <true/>
+ <key>OrthogonalBarPosition</key>
+ <real>-1</real>
+ <key>Points</key>
+ <array>
+ <string>{270, 165.5}</string>
+ <string>{270, 210}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>LineType</key>
+ <integer>2</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>29</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{241.464, 220}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>37</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 HTML Sanitizer}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{126, 220}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>36</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 HTML Form Filler}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{126, 258}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>35</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 I18N Translator}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{116, 210}, {308, 89}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>1</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>34</integer>
+ <key>Link</key>
+ <dict>
+ <key>url</key>
+ <string>/browser/trunk/markup/filters.py</string>
+ </dict>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.701961</string>
+ <key>g</key>
+ <string>0.701961</string>
+ <key>r</key>
+ <string>0.701961</string>
+ </dict>
+ <key>GradientAngle</key>
+ <real>114</real>
+ <key>GradientColor</key>
+ <dict>
+ <key>b</key>
+ <string>0.701961</string>
+ <key>g</key>
+ <string>0.701961</string>
+ <key>r</key>
+ <string>0.701961</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>CornerRadius</key>
+ <real>5</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Align</key>
+ <integer>2</integer>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-BoldOblique;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qr\pardirnatural
+
+\f0\i\b\fs24 \cf1 filtering}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{222.732, 134}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>11</real>
+ </dict>
+ <key>ID</key>
+ <integer>29</integer>
+ <key>Link</key>
+ <dict>
+ <key>url</key>
+ <string>/wiki/Documentation/streams.html</string>
+ </dict>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 Stream}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{240.464, 42}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>28</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 XML Parser}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{126, 42}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>27</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 HTML Parser}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{116, 32}, {308, 51}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>1</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>26</integer>
+ <key>Link</key>
+ <dict>
+ <key>url</key>
+ <string>/browser/trunk/markup/input.py</string>
+ </dict>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.701961</string>
+ <key>g</key>
+ <string>0.701961</string>
+ <key>r</key>
+ <string>0.701961</string>
+ </dict>
+ <key>GradientAngle</key>
+ <real>114</real>
+ <key>GradientColor</key>
+ <dict>
+ <key>b</key>
+ <string>0.701961</string>
+ <key>g</key>
+ <string>0.701961</string>
+ <key>r</key>
+ <string>0.701961</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>CornerRadius</key>
+ <real>5</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Align</key>
+ <integer>2</integer>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-BoldOblique;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qr\pardirnatural
+
+\f0\i\b\fs24 \cf1 parsing}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{241.464, 338}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>25</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 XML Serializer}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{126, 338}, {94.5364, 31}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>24</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs22 \cf0 HTML Serializer}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{116, 328}, {308, 89}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>1</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>23</integer>
+ <key>Link</key>
+ <dict>
+ <key>url</key>
+ <string>/browser/trunk/markup/output.py</string>
+ </dict>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.701961</string>
+ <key>g</key>
+ <string>0.701961</string>
+ <key>r</key>
+ <string>0.701961</string>
+ </dict>
+ <key>GradientAngle</key>
+ <real>114</real>
+ <key>GradientColor</key>
+ <dict>
+ <key>b</key>
+ <string>0.701961</string>
+ <key>g</key>
+ <string>0.701961</string>
+ <key>r</key>
+ <string>0.701961</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>CornerRadius</key>
+ <real>5</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Align</key>
+ <integer>2</integer>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-BoldOblique;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qr\pardirnatural
+
+\f0\i\b\fs24 \cf1 serialization}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{42.7682, 100}, {125.232, 99}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>1</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>44</integer>
+ <key>Link</key>
+ <dict>
+ <key>url</key>
+ <string>/wiki/Documentation/builder.html</string>
+ </dict>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.701961</string>
+ <key>g</key>
+ <string>0.701961</string>
+ <key>r</key>
+ <string>0.701961</string>
+ </dict>
+ <key>GradientAngle</key>
+ <real>114</real>
+ <key>GradientColor</key>
+ <dict>
+ <key>b</key>
+ <string>0.701961</string>
+ <key>g</key>
+ <string>0.701961</string>
+ <key>r</key>
+ <string>0.701961</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>CornerRadius</key>
+ <real>5</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf420
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-BoldOblique;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\i\b\fs24 \cf1 building}</string>
+ <key>VerticalPad</key>
+ <integer>2</integer>
+ </dict>
+ <key>TextPlacement</key>
+ <integer>0</integer>
+ </dict>
+ </array>
+ <key>GridInfo</key>
+ <dict/>
+ <key>GuidesLocked</key>
+ <string>NO</string>
+ <key>GuidesVisible</key>
+ <string>YES</string>
+ <key>HPages</key>
+ <integer>1</integer>
+ <key>ImageCounter</key>
+ <integer>1</integer>
+ <key>IsPalette</key>
+ <string>NO</string>
+ <key>KeepToScale</key>
+ <false/>
+ <key>Layers</key>
+ <array>
+ <dict>
+ <key>Lock</key>
+ <string>NO</string>
+ <key>Name</key>
+ <string>Ebene 1</string>
+ <key>Print</key>
+ <string>YES</string>
+ <key>View</key>
+ <string>YES</string>
+ </dict>
+ </array>
+ <key>LayoutInfo</key>
+ <dict/>
+ <key>LinksVisible</key>
+ <string>NO</string>
+ <key>MagnetsVisible</key>
+ <string>NO</string>
+ <key>MasterSheets</key>
+ <array>
+ <dict>
+ <key>ActiveLayerIndex</key>
+ <integer>0</integer>
+ <key>AutoAdjust</key>
+ <false/>
+ <key>CanvasColor</key>
+ <dict>
+ <key>w</key>
+ <string>1</string>
+ </dict>
+ <key>CanvasOrigin</key>
+ <string>{0, 0}</string>
+ <key>CanvasScale</key>
+ <real>1</real>
+ <key>ColumnAlign</key>
+ <integer>1</integer>
+ <key>ColumnSpacing</key>
+ <real>36</real>
+ <key>DisplayScale</key>
+ <string>1 cm = 1 cm</string>
+ <key>GraphicsList</key>
+ <array/>
+ <key>GridInfo</key>
+ <dict/>
+ <key>HPages</key>
+ <integer>1</integer>
+ <key>IsPalette</key>
+ <string>NO</string>
+ <key>KeepToScale</key>
+ <false/>
+ <key>Layers</key>
+ <array>
+ <dict>
+ <key>Lock</key>
+ <string>NO</string>
+ <key>Name</key>
+ <string>Ebene 1</string>
+ <key>Print</key>
+ <string>YES</string>
+ <key>View</key>
+ <string>YES</string>
+ </dict>
+ </array>
+ <key>LayoutInfo</key>
+ <dict/>
+ <key>Orientation</key>
+ <integer>2</integer>
+ <key>RowAlign</key>
+ <integer>1</integer>
+ <key>RowSpacing</key>
+ <real>36</real>
+ <key>SheetTitle</key>
+ <string>Master 1</string>
+ <key>UniqueID</key>
+ <integer>1</integer>
+ <key>VPages</key>
+ <integer>1</integer>
+ </dict>
+ </array>
+ <key>ModificationDate</key>
+ <string>2007-06-17 13:12:07 +0200</string>
+ <key>Modifier</key>
+ <string>Christopher Lenz</string>
+ <key>NotesVisible</key>
+ <string>NO</string>
+ <key>Orientation</key>
+ <integer>2</integer>
+ <key>OriginVisible</key>
+ <string>NO</string>
+ <key>PageBreaks</key>
+ <string>YES</string>
+ <key>PrintInfo</key>
+ <dict>
+ <key>NSBottomMargin</key>
+ <array>
+ <string>float</string>
+ <string>36</string>
+ </array>
+ <key>NSLeftMargin</key>
+ <array>
+ <string>float</string>
+ <string>36</string>
+ </array>
+ <key>NSPaperSize</key>
+ <array>
+ <string>size</string>
+ <string>{595, 842}</string>
+ </array>
+ <key>NSRightMargin</key>
+ <array>
+ <string>float</string>
+ <string>36</string>
+ </array>
+ <key>NSTopMargin</key>
+ <array>
+ <string>float</string>
+ <string>36</string>
+ </array>
+ </dict>
+ <key>ReadOnly</key>
+ <string>NO</string>
+ <key>RowAlign</key>
+ <integer>1</integer>
+ <key>RowSpacing</key>
+ <real>36</real>
+ <key>SheetTitle</key>
+ <string>Arbeitsfläche 1</string>
+ <key>SmartAlignmentGuidesActive</key>
+ <string>YES</string>
+ <key>SmartDistanceGuidesActive</key>
+ <string>NO</string>
+ <key>UniqueID</key>
+ <integer>1</integer>
+ <key>UseEntirePage</key>
+ <false/>
+ <key>VPages</key>
+ <integer>1</integer>
+ <key>WindowInfo</key>
+ <dict>
+ <key>CurrentSheet</key>
+ <string>0</string>
+ <key>DrawerOpen</key>
+ <false/>
+ <key>DrawerTab</key>
+ <string>Outline</string>
+ <key>DrawerWidth</key>
+ <real>209</real>
+ <key>FitInWindow</key>
+ <false/>
+ <key>Frame</key>
+ <string>{{64, 119}, {780, 560}}</string>
+ <key>ShowRuler</key>
+ <false/>
+ <key>ShowStatusBar</key>
+ <true/>
+ <key>VisibleRegion</key>
+ <string>{{-121, 0}, {765, 446}}</string>
+ <key>Zoom</key>
+ <string>1</string>
+ </dict>
+</dict>
+</plist>
diff --git a/lib/genshi/doc/2000ft.png b/lib/genshi/doc/2000ft.png
new file mode 100644
index 0000000000000000000000000000000000000000..b4ce041211763686822d58697d549d3a48137327
GIT binary patch
[cut]
diff --git a/lib/genshi/doc/common/COPYING b/lib/genshi/doc/common/COPYING
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/common/COPYING
@@ -0,0 +1,28 @@
+Copyright (C) 2007 Edgewall Software
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/genshi/doc/common/README.txt b/lib/genshi/doc/common/README.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/common/README.txt
@@ -0,0 +1,7 @@
+Edgewall Documentation Utilities
+================================
+
+This repository contains distutils commands for generating offline documentation
+from reStructuredText files and Python source code. These tools are shared among
+a couple of different Edgewall projects to ensure common style and
+functionality.
diff --git a/lib/genshi/doc/common/conf/docutils.ini b/lib/genshi/doc/common/conf/docutils.ini
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/common/conf/docutils.ini
@@ -0,0 +1,9 @@
+[general]
+input_encoding = utf-8
+strip_comments = yes
+toc_backlinks = none
+
+[html4css1 writer]
+embed_stylesheet = no
+stylesheet = common/style/edgewall.css
+xml_declaration = no
diff --git a/lib/genshi/doc/common/conf/epydoc.ini b/lib/genshi/doc/common/conf/epydoc.ini
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/common/conf/epydoc.ini
@@ -0,0 +1,22 @@
+[epydoc]
+
+name: Documentation Index
+url: ../index.html
+verbosity: 1
+
+# Extraction
+docformat: restructuredtext
+parse: yes
+introspect: yes
+exclude: .*\.tests.*
+inheritance: listed
+private: no
+imports: no
+include-log: no
+
+# HTML output
+output: html
+target: doc/api/
+css: doc/common/style/epydoc.css
+frames: no
+sourcecode: no
diff --git a/lib/genshi/doc/common/doctools.py b/lib/genshi/doc/common/doctools.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/common/doctools.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://babel.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://babel.edgewall.org/log/.
+
+from distutils.cmd import Command
+import doctest
+from glob import glob
+import os
+import sys
+
+TOOLS_DIR = os.path.dirname(__file__)
+
+
+class build_doc(Command):
+ description = 'generate the documentation'
+ user_options = [
+ ('force', None,
+ "force regeneration even if no reStructuredText files have changed"),
+ ('without-apidocs', None,
+ "whether to skip the generation of API documentaton"),
+ ]
+ boolean_options = ['force', 'without-apidocs']
+
+ def initialize_options(self):
+ self.force = False
+ self.without_apidocs = False
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ from docutils.core import publish_cmdline
+ from docutils.nodes import raw
+ from docutils.parsers import rst
+ from genshi.input import HTMLParser
+ from genshi.template import TemplateLoader
+
+ docutils_conf = os.path.join(TOOLS_DIR, 'conf', 'docutils.ini')
+ epydoc_conf = os.path.join(TOOLS_DIR, 'conf', 'epydoc.ini')
+
+ try:
+ from pygments import highlight
+ from pygments.lexers import get_lexer_by_name
+ from pygments.formatters import HtmlFormatter
+
+ def code_block(name, arguments, options, content, lineno,
+ content_offset, block_text, state, state_machine):
+ lexer = get_lexer_by_name(arguments[0])
+ html = highlight('\n'.join(content), lexer, HtmlFormatter())
+ return [raw('', html, format='html')]
+ code_block.arguments = (1, 0, 0)
+ code_block.options = {'language' : rst.directives.unchanged}
+ code_block.content = 1
+ rst.directives.register_directive('code-block', code_block)
+ except ImportError:
+ print('Pygments not installed, syntax highlighting disabled')
+
+ loader = TemplateLoader(['doc', 'doc/common'], variable_lookup='strict')
+ for source in glob('doc/*.txt'):
+ dest = os.path.splitext(source)[0] + '.html'
+ if self.force or not os.path.exists(dest) or \
+ os.path.getmtime(dest) < os.path.getmtime(source):
+ print('building documentation file %s' % dest)
+ publish_cmdline(writer_name='html',
+ argv=['--config=%s' % docutils_conf, source,
+ dest])
+ fileobj = open(dest)
+ try:
+ html = HTMLParser(fileobj, encoding='utf-8')
+ template = loader.load('template.html')
+ output = template.generate(
+ html=html,
+ project=self.distribution
+ ).render('html', encoding='utf-8')
+ finally:
+ fileobj.close()
+ fileobj = open(dest, 'w')
+ try:
+ fileobj.write(output)
+ finally:
+ fileobj.close()
+
+ if not self.without_apidocs:
+ try:
+ from epydoc import cli
+ old_argv = sys.argv[1:]
+ sys.argv[1:] = [
+ '--config=%s' % epydoc_conf,
+ '--top=%s' % self.distribution.packages[0],
+ '--no-private', # epydoc bug, not read from config
+ '--simple-term',
+ '--verbose'
+ ] + self.distribution.packages
+ cli.cli()
+ sys.argv[1:] = old_argv
+
+ except ImportError:
+ print('epydoc not installed, skipping API documentation.')
+
+
+class test_doc(Command):
+ description = 'test the code examples in the documentation'
+ user_options = []
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ for filename in glob('doc/*.txt'):
+ print('testing documentation file %s' % filename)
+ doctest.testfile(filename, False, optionflags=doctest.ELLIPSIS)
diff --git a/lib/genshi/doc/common/style/bkgnd_pattern.png b/lib/genshi/doc/common/style/bkgnd_pattern.png
new file mode 100644
index 0000000000000000000000000000000000000000..90e92682135d3f7213332f870f973bd06d6d57ee
GIT binary patch
[cut]
diff --git a/lib/genshi/doc/common/style/docutils.css b/lib/genshi/doc/common/style/docutils.css
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/common/style/docutils.css
@@ -0,0 +1,277 @@
+/*
+:Author: David Goodger
+:Contact: goodger at 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 }
+
+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 }
diff --git a/lib/genshi/doc/common/style/edgewall.css b/lib/genshi/doc/common/style/edgewall.css
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/common/style/edgewall.css
@@ -0,0 +1,109 @@
+ at import url(docutils.css);
+ at import url(pygments.css);
+
+html, body { height: 100%; margin: 0; padding: 0; }
+body { background: #4b4d4d url(bkgnd_pattern.png); color: #000; }
+body, th, td {
+ font: normal small Verdana,Arial,'Bitstream Vera Sans',Helvetica,sans-serif;
+}
+pre, code, tt { font-size: medium; }
+h1, h2, h3, h4 {
+ border-bottom: 1px solid #ccc;
+ font-family: Arial,Verdana,'Bitstream Vera Sans',Helvetica,sans-serif;
+ font-weight: bold; letter-spacing: -0.018em;
+}
+h1 { font-size: 19px; margin: 2em 0 .5em; }
+h2 { font-size: 16px; margin: 1.5em 0 .5em; }
+h3 { color: #333; font-size: 14px; margin: 1.2em 0 .5em; }
+hr { border: none; border-top: 1px solid #ccb; margin: 2em 0; }
+p { margin: 0 0 1em; }
+
+table { border: 1px solid #999; border-width: 0 1px 0 0;
+ border-collapse: separate; border-spacing: 0;
+}
+table thead th { background: #999; border: 1px solid #999;; color: #fff;
+ font-weight: bold;
+}
+table td { border: 1px solid #ccc; border-width: 0 0 1px 1px; padding: .3em; }
+
+:link, :visited { text-decoration: none; border-bottom: 1px dotted #bbb;
+ color: #b00;
+}
+: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;
+}
+
+#navigation { background: #b00; color: #fff; font-size: 90%; margin: 0 -20px;
+ padding: .2em .5em; text-align: right;
+}
+#navigation :link, #navigation :visited { border: none; color: #fff; }
+#navigation :link:hover, #navigation :visited:hover { background: #fff;
+ color: #b00;
+}
+#navigation span.projinfo { float: left; }
+#footer { border-top: 1px solid #d7d7d7; color: #666; font-size: 90%;
+ margin: 8em -20px 0; padding: .5em 0 2em; text-align: center;
+}
+#footer :link, #footer :visited { color: #666; }
+
+div.document { background: #fff url(shadow.gif) right top repeat-y;
+ border-left: 1px solid #000; margin: 0 auto 0 40px; min-height: 100%;
+ padding: 0 180px 1px 20px; width: 700px;
+}
+
+div.contents { font-size: 90%; position: absolute; position: fixed;
+ margin-left: 81px; left: 60em; top: 30px; right: 0; bottom: 0;
+ overflow: auto; overflow-x: hidden;
+}
+div.contents .topic-title { display: none; }
+div.contents ul { list-style: none; padding-left: 0; }
+div.contents .auto-toc :link, div.contents .auto-toc :visited {
+ color: #e9e9e9; border: none; display: block; font-weight: bold;
+ padding: 1px 5px 2px 10px;
+}
+div.contents .auto-toc :link:hover, div.contents .auto-toc :visited:hover {
+ background: #000; color: #fff;
+}
+div.contents .auto-toc .auto-toc :link,
+div.contents .auto-toc .auto-toc :visited { color: #c6c6c6;
+ font-weight: normal;
+}
+
+h1.title, div.document#preface h1 { border: none; color: #666;
+ font-size: x-large; margin: 0 -20px 1em; padding: 2em 20px 0;
+}
+h1.title { background: url(vertbars.png) repeat-x; }
+div.document#preface h1.title { text-indent: -4000px; }
+div.document#preface h1 { text-align: center; }
+pre.literal-block, div.highlight pre { background: #f4f4f4;
+ border: 1px solid #e6e6e6; color: #000; margin: 1em 1em; padding: .25em;
+ overflow: auto;
+}
+
+table.field-list { border-collapse: collapse; }
+table.field-list th.field-name { color: #333; font-weight: normal;
+ text-align: right;
+}
+table.field-list td { border-width: 1px; }
+
+div.admonition, div.attention, div.caution, div.danger, div.error, div.hint,
+div.important, div.note, div.tip, div.warning {
+ border: none; color: #333; font-style: italic; margin: 1em 2em;
+}
+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: #b00;
+}
+p.admonition-title { margin-bottom: 0; text-transform: uppercase; }
+tt.docutils { background-color: transparent; }
+span.pre { white-space: normal; }
+
+div.section { margin-left: 1.5em; }
+div.section h1 { margin-left: -1em; }
+div.section div.section { margin-left: 0; }
+div.section div.section { margin-bottom: 4em; }
+div.section div.section div.section { margin-bottom: 0; }
diff --git a/lib/genshi/doc/common/style/epydoc.css b/lib/genshi/doc/common/style/epydoc.css
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/common/style/epydoc.css
@@ -0,0 +1,136 @@
+html { background: #4b4d4d url(../style/bkgnd_pattern.png); margin: 0;
+ padding: 1em 1em 3em;
+}
+body { background: #fff url(../style/vertbars.png) repeat-x;
+ border: 1px solid #000; color: #000; margin: 1em 0; padding: 0 1em 1em;
+}
+body, th, td {
+ font: normal small 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: 2em 0 .5em; }
+h2 { font-size: 16px; margin: 1.5em 0 .5em; }
+h3 { font-size: 14px; margin: 1.2em 0 .5em; }
+hr { border: none; border-top: 1px solid #ccb; margin: 2em 0; }
+p { margin: 0 0 1em; }
+:link, :visited { text-decoration: none; border-bottom: 1px dotted #bbb;
+ color: #b00;
+}
+:link:hover, :visited:hover { background-color: #eee; color: #555; }
+
+table { border: none; border-collapse: collapse; }
+
+table.navbar { background: #000; color: #fff; margin: 2em 0 .33em; }
+table.navbar th { border: 1px solid #000; font-weight: bold; padding: 1px; }
+table.navbar :link, table.navbar :visited { border: none; color: #fff; }
+table.navbar :link:hover, table.navbar :visited:hover { background: none;
+ text-decoration: underline overline;
+}
+table.navbar th.navbar-select { background: #fff; color: #000; }
+span.breadcrumbs { color: #666; font-size: 95%; }
+h1.epydoc { border: none; color: #666;
+ font-size: x-large; margin: 1em 0 0; padding: 0;
+}
+pre.base-tree { color: #666; margin: 0; padding: 0; }
+pre.base-tree :link, pre.base-tree :visited { border: none; }
+pre.py-doctest, pre.variable, pre.rst-literal-block { background: #eee;
+ border: 1px solid #e6e6e6; color: #000; margin: 1em; padding: .25em;
+ overflow: auto;
+}
+pre.variable { margin: 0; }
+
+/* Summary tables */
+
+table.summary { margin: .5em 0; }
+table.summary tr.table-header { background: #f7f7f0; }
+table.summary td.table-header { color: #666; font-weight: bold; }
+table.summary th.group-header { background: #f7f7f0; color: #666;
+ font-size: 90%; font-weight: bold; text-align: left;
+}
+table.summary th, table.summary td { border: 1px solid #d7d7d7; }
+table.summary th th, table.summary td td { border: none; }
+table.summary td.summary table td { color: #666; font-size: 90%; }
+table.summary td.summary table br { display: none; }
+table.summary td.summary span.summary-type { font-family: monospace;
+ font-size: 90%;
+}
+table.summary td.summary span.summary-type code { font-size: 110%; }
+p.indent-wrapped-lines { color: #999; font-size: 85%; margin: 0;
+ padding: 0 0 0 7em; text-indent: -7em;
+}
+p.indent-wrapped-lines code { color: #999; font-size: 115%; }
+p.indent-wrapped-lines :link, p.indent-wrapped-lines :visited { border: none; }
+.summary-sig { display: block; font-family: monospace; font-size: 120%;
+ margin-bottom: .5em;
+}
+.summary-sig-name { font-weight: bold; }
+.summary-sig-arg { color: #333; }
+.summary-sig :link, .summary-sig :visited { border: none; }
+.summary-name { font-family: monospace; font-weight: bold; }
+
+/* Details tables */
+
+table.details { margin: 2em 0 0; }
+div table.details { margin-top: 0; }
+table.details tr.table-header { background: transparent; }
+table.details td.table-header { border-bottom: 1px solid #ccc; padding: 2em 0 0; }
+table.details span.table-header {
+ font: bold 140% Arial,Verdana,'Bitstream Vera Sans',Helvetica,sans-serif;
+ letter-spacing: -0.018em;
+}
+table.details th, table.details td { border: none; }
+table.details th th, table.details td td { border: none; }
+table.details td { padding-left: 2em; }
+table.details td td { padding-left: 0; }
+table.details h3.epydoc { margin-left: -2em; }
+table.details h3.epydoc .sig { color: #999; font-family: monospace; }
+table.details h3.epydoc .sig-name { color: #000; }
+table.details h3.epydoc .sig-arg { color: #666; }
+table.details h3.epydoc .sig-default { font-size: 95%; font-weight: normal; }
+table.details h3.epydoc .sig-default code { font-weight: normal; }
+table.details h3.epydoc .fname { color: #999; font-size: 90%;
+ font-style: italic; font-weight: normal; line-height: 1.6em;
+}
+
+dl dt { color: #666; margin-top: 1em; }
+dl dd { margin: 0; padding-left: 2em; }
+dl.fields { margin: 1em 0; padding: 0; }
+dl.fields dt { color: #666; margin-top: 1em; }
+dl.fields dd ul { margin: 0; padding: 0; }
+div.fields { font-size: 90%; margin: 0 0 2em 2em; }
+div.fields p { margin-bottom: 0.5em; }
+
+table td.footer { color: #999; font-size: 85%; margin-top: 3em;
+ padding: 0 3em 1em; position: absolute; width: 80%; }
+table td.footer :link, table td.footer :visited { border: none; color: #999; }
+table td.footer :link:hover, table td.footer :visited:hover {
+ background: transparent; text-decoration: underline;
+}
+
+/* Syntax highlighting */
+
+.py-prompt, .py-more, .variable-ellipsis, .variable-op { color: #999; }
+.variable-group { color: #666; font-weight: bold; }
+.py-string, .variable-string, .variable-quote { color: #093; }
+.py-comment { color: #06f; font-style: italic; }
+.py-keyword { color: #00f; }
+.py-output { background: #f6f6f0; color: #666; font-weight: bold; }
+
+/* Index */
+
+table.link-index { background: #f6f6f0; border: none; margin-top: 1em; }
+table.link-index td.link-index { border: none; font-family: monospace;
+ font-weight: bold; padding: .5em 1em;
+}
+table.link-index td table, table.link-index td td { border: none; }
+table.link-index .index-where { color: #999;
+ font-family: Verdana,Arial,'Bitstream Vera Sans',Helvetica,sans-serif;
+ font-size: 90%; font-weight: normal; line-height: 1.6em;
+}
+table.link-index .index-where :link, table.link-index .index-where :visited {
+ border: none; color: #666;
+}
+h2.epydoc { color: #999; font-size: 200%; line-height: 10px; }
diff --git a/lib/genshi/doc/common/style/pygments.css b/lib/genshi/doc/common/style/pygments.css
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/common/style/pygments.css
@@ -0,0 +1,57 @@
+div.highlight { background: #ffffff; }
+div.highlight .c { color: #999988; font-style: italic }
+div.highlight .err { color: #a61717; background-color: #e3d2d2 }
+div.highlight .k { font-weight: bold }
+div.highlight .o { font-weight: bold }
+div.highlight .cm { color: #999988; font-style: italic }
+div.highlight .cp { color: #999999; font-weight: bold }
+div.highlight .c1 { color: #999988; font-style: italic }
+div.highlight .cs { color: #999999; font-weight: bold; font-style: italic }
+div.highlight .gd { color: #000000; background-color: #ffdddd }
+div.highlight .ge { font-style: italic }
+div.highlight .gr { color: #aa0000 }
+div.highlight .gh { color: #999999 }
+div.highlight .gi { color: #000000; background-color: #ddffdd }
+div.highlight .go { color: #888888 }
+div.highlight .gp { color: #555555 }
+div.highlight .gs { font-weight: bold }
+div.highlight .gu { color: #aaaaaa }
+div.highlight .gt { color: #aa0000 }
+div.highlight .kc { font-weight: bold }
+div.highlight .kd { font-weight: bold }
+div.highlight .kp { font-weight: bold }
+div.highlight .kr { font-weight: bold }
+div.highlight .kt { color: #445588; font-weight: bold }
+div.highlight .m { color: #009999 }
+div.highlight .s { color: #bb8844 }
+div.highlight .na { color: #008080 }
+div.highlight .nb { color: #999999 }
+div.highlight .nc { color: #445588; font-weight: bold }
+div.highlight .no { color: #008080 }
+div.highlight .ni { color: #800080 }
+div.highlight .ne { color: #990000; font-weight: bold }
+div.highlight .nf { color: #990000; font-weight: bold }
+div.highlight .nn { color: #555555 }
+div.highlight .nt { color: #000080 }
+div.highlight .nv { color: #008080 }
+div.highlight .ow { font-weight: bold }
+div.highlight .mf { color: #009999 }
+div.highlight .mh { color: #009999 }
+div.highlight .mi { color: #009999 }
+div.highlight .mo { color: #009999 }
+div.highlight .sb { color: #bb8844 }
+div.highlight .sc { color: #bb8844 }
+div.highlight .sd { color: #bb8844 }
+div.highlight .s2 { color: #bb8844 }
+div.highlight .se { color: #bb8844 }
+div.highlight .sh { color: #bb8844 }
+div.highlight .si { color: #bb8844 }
+div.highlight .sx { color: #bb8844 }
+div.highlight .sr { color: #808000 }
+div.highlight .s1 { color: #bb8844 }
+div.highlight .ss { color: #bb8844 }
+div.highlight .bp { color: #999999 }
+div.highlight .vc { color: #008080 }
+div.highlight .vg { color: #008080 }
+div.highlight .vi { color: #008080 }
+div.highlight .il { color: #009999 }
diff --git a/lib/genshi/doc/common/style/shadow.gif b/lib/genshi/doc/common/style/shadow.gif
new file mode 100644
index 0000000000000000000000000000000000000000..98d093f87be7d122f6baf4ab06aff856a8452ae1
GIT binary patch
[cut]
diff --git a/lib/genshi/doc/common/style/vertbars.png b/lib/genshi/doc/common/style/vertbars.png
new file mode 100644
index 0000000000000000000000000000000000000000..42ae3f86d7f1513fd585ad02959afbb37ad476cb
GIT binary patch
[cut]
diff --git a/lib/genshi/doc/common/template.html b/lib/genshi/doc/common/template.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/common/template.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html xmlns:py="http://genshi.edgewall.org/" py:strip="">
+
+ <title py:match="head/title">${project.get_name()}: ${select('text()')}</title>
+
+ <!--! Add navigation and footer around content -->
+ <div py:match="div[@class='document']" py:attrs="select('@*')">
+ <div id="navigation">
+ <span class="projinfo">${project.get_name()} ${project.get_version()}</span>
+ <a href="index.html">Documentation Index</a>
+ </div>
+ ${select('*|text()')}
+ <div id="footer">
+ Visit the ${project.get_name()} open source project at
+ <a href="${project.get_url()}">${project.get_url()}</a>
+ </div>
+ </div>
+
+ $html
+
+</html>
diff --git a/lib/genshi/doc/filters.txt b/lib/genshi/doc/filters.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/filters.txt
@@ -0,0 +1,243 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+==============
+Stream Filters
+==============
+
+`Markup Streams`_ showed how to write filters and how they are applied to
+markup streams. This page describes the features of the various filters that
+come with Genshi itself.
+
+.. _`Markup Streams`: streams.html
+
+.. contents:: Contents
+ :depth: 1
+.. sectnum::
+
+
+HTML Form Filler
+================
+
+The filter ``genshi.filters.html.HTMLFormFiller`` can automatically populate an
+HTML form from values provided as a simple dictionary. When using this filter,
+you can basically omit any ``value``, ``selected``, or ``checked`` attributes
+from form controls in your templates, and let the filter do all that work for
+you.
+
+``HTMLFormFiller`` takes a dictionary of data to populate the form with, where
+the keys should match the names of form elements, and the values determine the
+values of those controls. For example:
+
+.. code-block:: pycon
+
+ >>> from genshi.filters import HTMLFormFiller
+ >>> from genshi.template import MarkupTemplate
+
+ >>> template = MarkupTemplate("""<form>
+ ... <p>
+ ... <label>User name:
+ ... <input type="text" name="username" />
+ ... </label><br />
+ ... <label>Password:
+ ... <input type="password" name="password" />
+ ... </label><br />
+ ... <label>
+ ... <input type="checkbox" name="remember" /> Remember me
+ ... </label>
+ ... </p>
+ ... </form>""")
+ >>> filler = HTMLFormFiller(data=dict(username='john', remember=True))
+ >>> print(template.generate() | filler)
+ <form>
+ <p>
+ <label>User name:
+ <input type="text" name="username" value="john"/>
+ </label><br/>
+ <label>Password:
+ <input type="password" name="password"/>
+ </label><br/>
+ <label>
+ <input type="checkbox" name="remember" checked="checked"/> Remember me
+ </label>
+ </p>
+ </form>
+
+.. note:: This processing is done without in any way reparsing the template
+ output. As any stream filter it operates after the template output is
+ generated but *before* that output is actually serialized.
+
+The filter will of course also handle radio buttons as well as ``<select>`` and
+``<textarea>`` elements. For radio buttons to be marked as checked, the value in
+the data dictionary needs to match the ``value`` attribute of the ``<input>``
+element, or evaluate to a truth value if the element has no such attribute. For
+options in a ``<select>`` box to be marked as selected, the value in the data
+dictionary needs to match the ``value`` attribute of the ``<option>`` element,
+or the text content of the option if it has no ``value`` attribute. Password and
+file input fields are not populated, as most browsers would ignore that anyway
+for security reasons.
+
+You'll want to make sure that the values in the data dictionary have already
+been converted to strings. While the filter may be able to deal with non-string
+data in some cases (such as check boxes), in most cases it will either not
+attempt any conversion or not produce the desired results.
+
+You can restrict the form filler to operate only on a specific ``<form>`` by
+passing either the ``id`` or the ``name`` keyword argument to the initializer.
+If either of those is specified, the filter will only apply to form tags with
+an attribute matching the specified value.
+
+
+HTML Sanitizer
+==============
+
+The filter ``genshi.filters.html.HTMLSanitizer`` filter can be used to clean up
+user-submitted HTML markup, removing potentially dangerous constructs that could
+be used for various kinds of abuse, such as cross-site scripting (XSS) attacks:
+
+.. code-block:: pycon
+
+ >>> from genshi.filters import HTMLSanitizer
+ >>> from genshi.input import HTML
+
+ >>> html = HTML("""<div>
+ ... <p>Innocent looking text.</p>
+ ... <script>alert("Danger: " + document.cookie)</script>
+ ... </div>""")
+ >>> sanitize = HTMLSanitizer()
+ >>> print(html | sanitize)
+ <div>
+ <p>Innocent looking text.</p>
+ </div>
+
+In this example, the ``<script>`` tag was removed from the output.
+
+You can determine which tags and attributes should be allowed by initializing
+the filter with corresponding sets. See the API documentation for more
+information.
+
+Inline ``style`` attributes are forbidden by default. If you allow them, the
+filter will still perform sanitization on the contents any encountered inline
+styles: the proprietary ``expression()`` function (supported only by Internet
+Explorer) is removed, and any property using an ``url()`` which a potentially
+dangerous URL scheme (such as ``javascript:``) are also stripped out:
+
+.. code-block:: pycon
+
+ >>> from genshi.filters import HTMLSanitizer
+ >>> from genshi.input import HTML
+
+ >>> html = HTML("""<div>
+ ... <br style="background: url(javascript:alert(document.cookie); color: #000" />
+ ... </div>""")
+ >>> sanitize = HTMLSanitizer(safe_attrs=HTMLSanitizer.SAFE_ATTRS | set(['style']))
+ >>> print(html | sanitize)
+ <div>
+ <br style="color: #000"/>
+ </div>
+
+.. warning:: You should probably not rely on the ``style`` filtering, as
+ sanitizing mixed HTML, CSS, and Javascript is very complicated and
+ suspect to various browser bugs. If you can somehow get away with
+ not allowing inline styles in user-submitted content, that would
+ definitely be the safer route to follow.
+
+
+Transformer
+===========
+
+The filter ``genshi.filters.transform.Transformer`` provides a convenient way to
+transform or otherwise work with markup event streams. It allows you to specify
+which parts of the stream you're interested in with XPath expressions, and then
+attach a variety of transformations to the parts that match:
+
+.. code-block:: pycon
+
+ >>> from genshi.builder import tag
+ >>> from genshi.core import TEXT
+ >>> from genshi.filters import Transformer
+ >>> from genshi.input import HTML
+
+ >>> html = HTML('''<html>
+ ... <head><title>Some Title</title></head>
+ ... <body>
+ ... Some <em>body</em> text.
+ ... </body>
+ ... </html>''')
+
+ >>> print(html | Transformer('body/em').map(unicode.upper, TEXT)
+ ... .unwrap().wrap(tag.u).end()
+ ... .select('body/u')
+ ... .prepend('underlined '))
+ <html>
+ <head><title>Some Title</title></head>
+ <body>
+ Some <u>underlined BODY</u> text.
+ </body>
+ </html>
+
+This example sets up a transformation that:
+
+ 1. matches any `<em>` element anywhere in the body,
+ 2. uppercases any text nodes in the element,
+ 3. strips off the `<em>` start and close tags,
+ 4. wraps the content in a `<u>` tag, and
+ 5. inserts the text `underlined` inside the `<u>` tag.
+
+A number of commonly useful transformations are available for this filter.
+Please consult the API documentation a complete list.
+
+In addition, you can also perform custom transformations. For example, the
+following defines a transformation that changes the name of a tag:
+
+.. code-block:: pycon
+
+ >>> from genshi import QName
+ >>> from genshi.filters.transform import ENTER, EXIT
+
+ >>> class RenameTransformation(object):
+ ... def __init__(self, name):
+ ... self.name = QName(name)
+ ... def __call__(self, stream):
+ ... for mark, (kind, data, pos) in stream:
+ ... if mark is ENTER:
+ ... data = self.name, data[1]
+ ... elif mark is EXIT:
+ ... data = self.name
+ ... yield mark, (kind, data, pos)
+
+A transformation can be any callable object that accepts an augmented event
+stream. In this case we define a class, so that we can initialize it with the
+tag name.
+
+Custom transformations can be applied using the `apply()` method of a
+transformer instance:
+
+.. code-block:: pycon
+
+ >>> xform = Transformer('body//em').map(unicode.upper, TEXT) \
+ >>> xform = xform.apply(RenameTransformation('u'))
+ >>> print(html | xform)
+ <html>
+ <head><title>Some Title</title></head>
+ <body>
+ Some <u>BODY</u> text.
+ </body>
+ </html>
+
+.. note:: The transformation filter was added in Genshi 0.5.
+
+
+Translator
+==========
+
+The ``genshi.filters.i18n.Translator`` filter implements basic support for
+internationalizing and localizing templates. When used as a filter, it
+translates a configurable set of text nodes and attribute values using a
+``gettext``-style translation function.
+
+The ``Translator`` class also defines the ``extract`` class method, which can
+be used to extract localizable messages from a template.
+
+Please refer to the API documentation for more information on this filter.
+
+.. note:: The translation filter was added in Genshi 0.4.
diff --git a/lib/genshi/doc/i18n.txt b/lib/genshi/doc/i18n.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/i18n.txt
@@ -0,0 +1,507 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+=====================================
+Internationalization and Localization
+=====================================
+
+Genshi provides comprehensive supporting infrastructure for internationalizing
+and localizing templates. That includes functionality for extracting
+localizable strings from templates, as well as a template filter and special
+directives that can apply translations to templates as they get rendered.
+
+This support is based on `gettext`_ message catalogs and the `gettext Python
+module`_. The extraction process can be used from the API level, or through
+the front-ends implemented by the `Babel`_ project, for which Genshi provides
+a plugin.
+
+.. _`gettext`: http://www.gnu.org/software/gettext/
+.. _`gettext python module`: http://docs.python.org/lib/module-gettext.html
+.. _`babel`: http://babel.edgewall.org/
+
+
+.. contents:: Contents
+ :depth: 2
+.. sectnum::
+
+
+Basics
+======
+
+The simplest way to internationalize and translate templates would be to wrap
+all localizable strings in a ``gettext()`` function call (which is often
+aliased to ``_()`` for brevity). In that case, no extra template filter is
+required.
+
+.. code-block:: genshi
+
+ <p>${_("Hello, world!")}</p>
+
+However, this approach results in significant “character noise” in templates,
+making them harder to read and preview.
+
+The ``genshi.filters.Translator`` filter allows you to get rid of the
+explicit `gettext`_ function calls, so you can (often) just continue to write:
+
+.. code-block:: genshi
+
+ <p>Hello, world!</p>
+
+This text will still be extracted and translated as if you had wrapped it in a
+``_()`` call.
+
+.. note:: For parameterized or pluralizable messages, you need to use the
+ special `template directives`_ described below, or use the
+ corresponding ``gettext`` function in embedded Python expressions.
+
+You can control which tags should be ignored by this process; for example, it
+doesn't really make sense to translate the content of the HTML
+``<script></script>`` element. Both ``<script>`` and ``<style>`` are excluded
+by default.
+
+Attribute values can also be automatically translated. The default is to
+consider the attributes ``abbr``, ``alt``, ``label``, ``prompt``, ``standby``,
+``summary``, and ``title``, which is a list that makes sense for HTML
+documents. Of course, you can tell the translator to use a different set of
+attribute names, or none at all.
+
+----------------
+Language Tagging
+----------------
+
+You can control automatic translation in your templates using the ``xml:lang``
+attribute. If the value of that attribute is a literal string, the contents and
+attributes of the element will be ignored:
+
+.. code-block:: genshi
+
+ <p xml:lang="en">Hello, world!</p>
+
+On the other hand, if the value of the ``xml:lang`` attribute contains a Python
+expression, the element contents and attributes are still considered for
+automatic translation:
+
+.. code-block:: genshi
+
+ <html xml:lang="$locale">
+ ...
+ </html>
+
+
+.. _`template directives`:
+
+Template Directives
+===================
+
+Sometimes localizable strings in templates may contain dynamic parameters, or
+they may depend on the numeric value of some variable to choose a proper
+plural form. Sometimes the strings contain embedded markup, such as tags for
+emphasis or hyperlinks, and you don't want to rely on the people doing the
+translations to know the syntax and escaping rules of HTML and XML.
+
+In those cases the simple text extraction and translation process described
+above is not sufficient. You could just use ``gettext`` API functions in
+embedded Python expressions for parameters and pluralization, but that does
+not help when messages contain embedded markup. Genshi provides special
+template directives for internationalization that attempt to provide a
+comprehensive solution for this problem space.
+
+To enable these directives, you'll need to register them with the templates
+they are used in. You can do this by adding them manually via the
+``Template.add_directives(namespace, factory)`` (where ``namespace`` would be
+“http://genshi.edgewall.org/i18n” and ``factory`` would be an instance of the
+``Translator`` class). Or you can just call the ``Translator.setup(template)``
+class method, which both registers the directives and adds the translation
+filter.
+
+After the directives have been registered with the template engine on the
+Python side of your application, you need to declare the corresponding
+directive namespace in all markup templates that use them. For example:
+
+.. code-block:: genshi
+
+ <html xmlns:py="http://genshi.edgewall.org/"
+ xmlns:i18n="http://genshi.edgewall.org/i18n">
+ …
+ </html>
+
+These directives only make sense in the context of `markup templates`_. For
+`text templates`_, you can just use the corresponding ``gettext`` API calls as needed.
+
+.. note:: The internationalization directives are still somewhat experimental
+ and have some known issues. However, the attribute language they
+ implement should be stable and is not subject to change
+ substantially in future versions.
+
+.. _`markup templates`: xml-templates.html
+.. _`text templates`: text-templates.html
+
+--------
+Messages
+--------
+
+``i18n:msg``
+------------
+
+This is the basic directive for defining localizable text passages that
+contain parameters and/or markup.
+
+For example, consider the following template snippet:
+
+.. code-block:: genshi
+
+ <p>
+ Please visit <a href="${site.url}">${site.name}</a> for help.
+ </p>
+
+Without further annotation, the translation filter would treat this sentence
+as two separate messages (“Please visit” and “for help”), and the translator
+would have no control over the position of the link in the sentence.
+
+However, when you use the Genshi internationalization directives, you simply
+add an ``i18n:msg`` attribute to the enclosing ``<p>`` element:
+
+.. code-block:: genshi
+
+ <p i18n:msg="name">
+ Please visit <a href="${site.url}">${site.name}</a> for help.
+ </p>
+
+Genshi is then able to identify the text in the ``<p>`` element as a single
+message for translation purposes. You'll see the following string in your
+message catalog::
+
+ Please visit [1:%(name)s] for help.
+
+The `<a>` element with its attribute has been replaced by a part in square
+brackets, which does not include the tag name or the attributes of the element.
+
+The value of the ``i18n:msg`` attribute is a comma-separated list of parameter
+names, which serve as simplified aliases for the actual Python expressions the
+message contains. The order of the paramer names in the list must correspond
+to the order of the expressions in the text. In this example, there is only
+one parameter: its alias for translation is “name”, while the corresponding
+expression is ``${site.name}``.
+
+The translator now has complete control over the structure of the sentence. He
+or she certainly does need to make sure that any bracketed parts are not
+removed, and that the ``name`` parameter is preserved correctly. But those are
+things that can be easily checked by validating the message catalogs. The
+important thing is that the translator can change the sentence structure, and
+has no way to break the application by forgetting to close a tag, for example.
+
+So if the German translator of this snippet decided to translate it to::
+
+ Um Hilfe zu erhalten, besuchen Sie bitte [1:%(name)s]
+
+The resulting output might be:
+
+.. code-block:: xml
+
+ <p>
+ Um Hilfe zu erhalten, besuchen Sie bitte
+ <a href="http://example.com/">Example</a>
+ </p>
+
+Messages may contain multiple tags, and they may also be nested. For example:
+
+.. code-block:: genshi
+
+ <p i18n:msg="name">
+ <i>Please</i> visit <b>the site <a href="${site.url}">${site.name}</a></b>
+ for help.
+ </p>
+
+This would result in the following message ID::
+
+ [1:Please] visit [2:the site [3:%(name)s]] for help.
+
+Again, the translator has full control over the structure of the sentence. So
+the German translation could actually look like this::
+
+ Um Hilfe zu erhalten besuchen Sie [1:bitte]
+ [3:%(name)s], [2:das ist eine Web-Site]
+
+Which Genshi would recompose into the following outout:
+
+.. code-block:: xml
+
+ <p>
+ Um Hilfe zu erhalten besuchen Sie <i>bitte</i>
+ <a href="http://example.com/">Example</a>, <b>das ist eine Web-Site</b>
+ </p>
+
+Note how the translation has changed the order and even the nesting of the
+tags.
+
+.. warning:: Please note that ``i18n:msg`` directives do not support other
+ nested directives. Directives commonly change the structure of
+ the generated markup dynamically, which often would result in the
+ structure of the text changing, thus making translation as a
+ single message ineffective.
+
+``i18n:choose``, ``i18n:singular``, ``i18n:plural``
+---------------------------------------------------
+
+Translatable strings that vary based on some number of objects, such as “You
+have 1 new message” or “You have 3 new messages”, present their own challenge,
+in particular when you consider that different languages have different rules
+for pluralization. For example, while English and most western languages have
+two plural forms (one for ``n=1`` and an other for ``n<>1``), Welsh has five
+different plural forms, while Hungarian only has one.
+
+The ``gettext`` framework has long supported this via the ``ngettext()``
+family of functions. You specify two default messages, one singular and one
+plural, and the number of items. The translations however may contain any
+number of plural forms for the message, depending on how many are commonly
+used in the language. ``ngettext`` will choose the correct plural form of the
+translated message based on the specified number of items.
+
+Genshi provides a variant of the ``i18n:msg`` directive described above that
+allows choosing the proper plural form based on the numeric value of a given
+variable. The pluralization support is implemented in a set of three
+directives that must be used together: ``i18n:choose``, ``i18n:singular``, and
+``i18n:plural``.
+
+The ``i18n:choose`` directive is used to set up the context of the message: it
+simply wraps the singular and plural variants.
+
+The value of this directive is split into two parts: the first is the
+*numeral*, a Python expression that evaluates to a number to determine which
+plural form should be chosen. The second part, separated by a semicolon, lists
+the parameter names. This part is equivalent to the value of the ``i18n:msg``
+directive.
+
+For example:
+
+.. code-block:: genshi
+
+ <p i18n:choose="len(messages); num">
+ <i18n:singular>You have <b>${len(messages)}</b> new message.</i18n:singular>
+ <i18n:plural>You have <b>${len(messages)}</b> new messages.</i18n:plural>
+ </p>
+
+All three directives can be used either as elements or attribute. So the above
+example could also be written as follows:
+
+.. code-block:: genshi
+
+ <i18n:choose numeral="len(messages)" params="num">
+ <p i18n:singular="">You have <b>${len(messages)}</b> new message.</p>
+ <p i18n:plural="">You have <b>${len(messages)}</b> new messages.</p>
+ </i18n:choose>
+
+When used as an element, the two parts of the ``i18n:choose`` value are split
+into two different attributes: ``numeral`` and ``params``. The
+``i18n:singular`` and ``i18n:plural`` directives do not require or support any
+value (or any extra attributes).
+
+--------------------
+Comments and Domains
+--------------------
+
+``i18n:comment``
+----------------
+
+The ``i18n:comment`` directive can be used to supply a comment for the
+translator. For example, if a template snippet is not easily understood
+outside of its context, you can add a translator comment to help the
+translator understand in what context the message will be used:
+
+.. code-block:: genshi
+
+ <p i18n:msg="name" i18n:comment="Link to the relevant support site">
+ Please visit <a href="${site.url}">${site.name}</a> for help.
+ </p>
+
+This comment will be extracted together with the message itself, and will
+commonly be placed along the message in the message catalog, so that it is
+easily visible to the person doing the translation.
+
+This directive has no impact on how the template is rendered, and is ignored
+outside of the extraction process.
+
+``i18n:domain``
+---------------
+
+In larger projects, message catalogs are commonly split up into different
+*domains*. For example, you might have a core application domain, and then
+separate domains for extensions or libraries.
+
+Genshi provides a directive called ``i18n:domain`` that lets you choose the
+translation domain for a particular scope. For example:
+
+.. code-block:: genshi
+
+ <div i18n:domain="examples">
+ <p>Hello, world!</p>
+ </div>
+
+
+Extraction
+==========
+
+The ``Translator`` class provides a class method called ``extract``, which is
+a generator yielding all localizable strings found in a template or markup
+stream. This includes both literal strings in text nodes and attribute values,
+as well as strings in ``gettext()`` calls in embedded Python code. See the API
+documentation for details on how to use this method directly.
+
+-----------------
+Babel Integration
+-----------------
+
+This functionality is integrated with the message extraction framework provided
+by the `Babel`_ project. Babel provides a command-line interface as well as
+commands that can be used from ``setup.py`` scripts using `Setuptools`_ or
+`Distutils`_.
+
+.. _`setuptools`: http://peak.telecommunity.com/DevCenter/setuptools
+.. _`distutils`: http://docs.python.org/dist/dist.html
+
+The first thing you need to do to make Babel extract messages from Genshi
+templates is to let Babel know which files are Genshi templates. This is done
+using a “mapping configuration”, which can be stored in a configuration file,
+or specified directly in your ``setup.py``.
+
+In a configuration file, the mapping may look like this:
+
+.. code-block:: ini
+
+ # Python souce
+ [python:**.py]
+
+ # Genshi templates
+ [genshi:**/templates/**.html]
+ include_attrs = title
+
+ [genshi:**/templates/**.txt]
+ template_class = genshi.template.TextTemplate
+ encoding = latin-1
+
+Please consult the Babel documentation for details on configuration.
+
+If all goes well, running the extraction with Babel should create a POT file
+containing the strings from your Genshi templates and your Python source files.
+
+
+---------------------
+Configuration Options
+---------------------
+
+The Genshi extraction plugin for Babel supports the following options:
+
+``template_class``
+------------------
+The concrete ``Template`` class that the file should be loaded with. Specify
+the package/module name and the class name, separated by a colon.
+
+The default is to use ``genshi.template:MarkupTemplate``, and you'll want to
+set it to ``genshi.template:TextTemplate`` for `text templates`_.
+
+.. _`text templates`: text-templates.html
+
+``encoding``
+------------
+The encoding of the template file. This is only used for text templates. The
+default is to assume “utf-8”.
+
+``include_attrs``
+-----------------
+Comma-separated list of attribute names that should be considered to have
+localizable values. Only used for markup templates.
+
+``ignore_tags``
+---------------
+Comma-separated list of tag names that should be ignored. Only used for markup
+templates.
+
+``extract_text``
+----------------
+Whether text outside explicit ``gettext`` function calls should be extracted.
+By default, any text nodes not inside ignored tags, and values of attribute in
+the ``include_attrs`` list are extracted. If this option is disabled, only
+strings in ``gettext`` function calls are extracted.
+
+.. note:: If you disable this option, and do not make use of the
+ internationalization directives, it's not necessary to add the
+ translation filter as described above. You only need to make sure
+ that the template has access to the ``gettext`` functions it uses.
+
+
+Translation
+===========
+
+If you have prepared MO files for use with Genshi using the appropriate tools,
+you can access the message catalogs with the `gettext Python module`_. You'll
+probably want to create a ``gettext.GNUTranslations`` instance, and make the
+translation functions it provides available to your templates by putting them
+in the template context.
+
+The ``Translator`` filter needs to be added to the filters of the template
+(applying it as a stream filter will likely not have the desired effect).
+Furthermore it needs to be the first filter in the list, including the internal
+filters that Genshi adds itself:
+
+.. code-block:: python
+
+ from genshi.filters import Translator
+ from genshi.template import MarkupTemplate
+
+ template = MarkupTemplate("...")
+ template.filters.insert(0, Translator(translations.ugettext))
+
+The ``Translator`` class also provides the convenience method ``setup()``,
+which will both add the filter and register the i18n directives:
+
+.. code-block:: python
+
+ from genshi.filters import Translator
+ from genshi.template import MarkupTemplate
+
+ template = MarkupTemplate("...")
+ translator = Translator(translations.ugettext)
+ translator.setup(template)
+
+.. warning:: If you're using ``TemplateLoader``, you should specify a
+ `callback function`_ in which you add the filter. That ensures
+ that the filter is not added everytime the template is rendered,
+ thereby being applied multiple times.
+
+.. _`callback function`: loader.html#callback-interface
+
+
+Related Considerations
+======================
+
+If you intend to produce an application that is fully prepared for an
+international audience, there are a couple of other things to keep in mind:
+
+-------
+Unicode
+-------
+
+Use ``unicode`` internally, not encoded bytestrings. Only encode/decode where
+data enters or exits the system. This means that your code works with characters
+and not just with bytes, which is an important distinction for example when
+calculating the length of a piece of text. When you need to decode/encode, it's
+probably a good idea to use UTF-8.
+
+-------------
+Date and Time
+-------------
+
+If your application uses datetime information that should be displayed to users
+in different timezones, you should try to work with UTC (universal time)
+internally. Do the conversion from and to "local time" when the data enters or
+exits the system. Make use the Python `datetime`_ module and the third-party
+`pytz`_ package.
+
+--------------------------
+Formatting and Locale Data
+--------------------------
+
+Make sure you check out the functionality provided by the `Babel`_ project for
+things like number and date formatting, locale display strings, etc.
+
+.. _`datetime`: http://docs.python.org/lib/module-datetime.html
+.. _`pytz`: http://pytz.sourceforge.net/
diff --git a/lib/genshi/doc/index.txt b/lib/genshi/doc/index.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/index.txt
@@ -0,0 +1,45 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+=======
+Preface
+=======
+
+.. image:: logo.png
+ :width: 225
+ :height: 81
+ :align: center
+ :alt: Genshi - Generate output for the web
+ :class: logo
+
+--------------------------------------------
+Toolkit for generation of output for the web
+--------------------------------------------
+
+Genshi is a Python library that provides an integrated set of components
+for parsing, generating, and processing HTML, XML or other textual content
+for output generation on the web. The major feature is a template language,
+which is heavily inspired by Kid.
+
+Installation
+------------
+
+* `Installing Genshi <install.html>`_
+* `Upgrading from Previous Versions <upgrade.html>`_
+
+Usage
+-----
+
+* `Markup Streams <streams.html>`_
+* `Templating Basics <templates.html>`_
+* `XML Template Language <xml-templates.html>`_
+* `Text Template Language <text-templates.html>`_
+* `Loading Templates <loader.html>`_
+* `Using Stream Filters <filters.html>`_
+* `Using XPath <xpath.html>`_
+* `Internationalization and Localization <i18n.html>`_
+* `Using the Templating Plugin <plugin.html>`_
+
+API Documentation
+-----------------
+
+* `Generated API Documentation <api/index.html>`_
diff --git a/lib/genshi/doc/install.txt b/lib/genshi/doc/install.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/install.txt
@@ -0,0 +1,88 @@
+Installing Genshi
+=================
+
+
+.. contents:: Contents
+ :depth: 2
+.. sectnum::
+
+
+Prerequisites
+-------------
+
+* Python_ 2.4 or later
+* Optional: Setuptools_ 0.6c3 or later
+
+.. _python: http://www.python.org/
+.. _setuptools: http://cheeseshop.python.org/pypi/setuptools
+
+Setuptools is only required for the `template engine plugin`_, which can be
+used to integrate Genshi with Python web application frameworks such as Pylons
+or TurboGears. Genshi also provides a Setuptools-based plugin that integrates
+its `internationalization support`_ with the Babel_ library, but that support
+can also be used without Setuptools being available (although in a slightly
+less convenient fashion).
+
+.. _`template engine plugin`: plugin.html
+.. _`internationalization support`: i18n.html
+.. _babel: http://babel.edgewall.org/
+
+
+Installing via ``easy_install``
+-------------------------------
+
+If you have a recent version of Setuptools_ installed, you can directly install
+Genshi using the easy_install command-line tool::
+
+ $ easy_install Genshi
+
+This downloads and installs the latest version of the Genshi package.
+
+If you have an older Genshi release installed and would like to upgrade, add
+the ``-U`` option to the above command.
+
+
+Installing from a Binary Installer
+----------------------------------
+
+Binary packages for Windows and Mac OS X are provided for Genshi. To install
+from such a package, simply download and open it.
+
+
+Installing from a Source Tarball
+--------------------------------
+
+Once you've downloaded and unpacked a Genshi source release, enter the
+directory where the archive was unpacked, and run::
+
+ $ python setup.py install
+
+Note that you may need administrator/root privileges for this step, as this
+command will by default attempt to install Genshi to the Python
+``site-packages`` directory on your system.
+
+Genshi comes with an optional extension module written in C that is used to
+improve performance in some areas. This extension is automatically compiled
+when you run the ``setup.py`` script as shown above. In the case that the
+extension can not be compiled, possibly due to a missing or incompatible C
+compiler, the compilation is skipped. If you'd prefer Genshi to not use this
+native extension module, you can explicitly bypass the compilation using the
+``--without-speedups`` option::
+
+ $ python setup.py --without-speedups install
+
+For other build and installation options, please consult the easy_install_
+and/or the Python distutils_ documentation.
+
+.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall
+.. _distutils: http://docs.python.org/inst/inst.html
+
+
+Support
+-------
+
+If you encounter any problems with Genshi, please don't hesitate to ask
+questions on the Genshi `mailing list`_ or `IRC channel`_.
+
+.. _`mailing list`: http://genshi.edgewall.org/wiki/MailingList
+.. _`irc channel`: http://genshi.edgewall.org/wiki/IrcChannel
diff --git a/lib/genshi/doc/loader.txt b/lib/genshi/doc/loader.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/loader.txt
@@ -0,0 +1,255 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+=================
+Loading Templates
+=================
+
+Genshi comes with a simple but flexible implementation of a template loader in
+the ``genshi.template.loader`` module. The loader provides caching of
+templates so they do not need to be reparsed when used, support for multiple
+template directories that together form a virtual search path, as well as
+support for different template loading strategies.
+
+.. contents:: Contents
+ :depth: 3
+.. sectnum::
+
+
+-----
+Usage
+-----
+
+The basic usage pattern is simple: instantiate one ``TemplateLoader`` object
+and keep it around, then ask it to load a template whenever you need to load
+one:
+
+.. code-block:: python
+
+ from genshi.template import TemplateLoader
+
+ loader = TemplateLoader(['/path/to/dir1', '/path/to/dir2'],
+ auto_reload=True)
+ tmpl = loader.load('test.html')
+
+When you try to load a template that can't be found, you get a
+``TemplateNotFound`` error.
+
+The default template class used by the loader is ``MarkupTemplate``, but that
+can be overridden both with a different default (as a keyword argument to the
+``TemplateLoader`` constructor), as well as on invocation of the ``load()``
+method:
+
+.. code-block:: python
+
+ from genshi.template.text import NewTextTemplate
+
+ tmpl = loader.load('mail.txt', cls=NewTextTemplate)
+
+
+-------
+Caching
+-------
+
+The ``TemplateLoader`` class provides a simple in-memory cache for parsed
+template objects. This improves performance, because templates do not need to
+be reparsed every time they are rendered.
+
+The size of this cache can be adjusted using the `max_cache_size` option on
+the ``TemplateLoader`` constructor. The value of that option determines the
+maximum number of template objects kept in the cache. When this limit is
+reached, any templates that haven't been used in a while get purged.
+Technically, this is a least-recently-used (LRU) cache, the default limit is
+set to 25 templates.
+
+Automatic Reloading
+===================
+
+Once a template has been cached, it will normally not get reparsed until it
+has been purged from the cache. This means that any changes to the template
+file are not taken into consideration as long as it is still found in the
+cache. As this is inconvenient in development scenarios, the ``auto_reload``
+option allows for automatic cache invalidation based on whether the template
+source has changed.
+
+.. code-block:: python
+
+ from genshi.template import TemplateLoader
+
+ loader = TemplateLoader('templates', auto_reload=True, max_cache_size=100)
+
+In production environments, automatic reloading should be disabled, as it does
+affect performance negatively.
+
+Callback Interface
+==================
+
+Sometimes you need to make sure that templates get properly configured after
+they have been loaded, but you only want to do that when the template is
+actually loaded and parsed, not when it is returned from the cache.
+
+For such cases, the ``TemplateLoader`` provides a way to specify a callback
+function that gets invoked whenever a template is loaded. You can specify that
+callback by passing it into the loader constructor via the ``callback``
+keyword argument, or later by setting the attribute of the same name. The
+callback function should expect a single argument, the template object.
+
+For example, to properly inject the `translation filter`_ into any loaded
+template, you'd use code similar to this:
+
+.. code-block:: python
+
+ from genshi.filters import Translator
+ from genshi.template import TemplateLoader
+
+ def template_loaded(template):
+ Translator(translations.ugettext).setup(template)
+
+ loader = TemplateLoader('templates', callback=template_loaded)
+
+.. _`translation filter`: i18n.html
+
+--------------------
+Template Search Path
+--------------------
+
+The template loader can be configured with a list of multiple directories to
+search for templates. The loader maps these directories to a single logical
+directory for locating templates by file name.
+
+The order of the directories making up the search path is significant: the
+loader will first try to locate a requested template in the first directory on
+the path, then in the second, and so on. If there are two templates with the
+same file name in multiple directories on the search path, whatever file is
+found first gets used.
+
+Based on this design, an application could, for example, configure a search
+path consisting of a directory containing the default templates, as well as a
+directory where site-specific templates can be stored that will override the
+default templates.
+
+
+Load Functions
+==============
+
+Usually the search path consists of strings representing directory paths, but
+it may also contain “load functions”: functions that are basically invoked
+with the file name, and return the template content.
+
+Genshi comes with three builtin load functions:
+
+``directory(path)``
+-------------------
+
+The equivalent of just using a string containing the directory path: looks up
+the file name in a specific directory.
+
+.. code-block:: python
+
+ from genshi.template import TemplateLoader, loader
+ tl = TemplateLoader([loader.directory('/path/to/dir/')])
+
+That is the same as:
+
+.. code-block:: python
+
+ tl = TemplateLoader(['/path/to/dir/'])
+
+
+``package(name, path)``
+-----------------------
+
+Uses the ``pkg_resources`` API to locate files in Python package data (which
+may be inside a ZIP archive).
+
+.. code-block:: python
+
+ from genshi.template import TemplateLoader, loader
+ tl = TemplateLoader([loader.package('myapp', 'templates')])
+
+This will look for templates in the ``templates`` directory of the Python
+package ``myapp``.
+
+``prefixed(**delegates)``
+-------------------------
+
+Delegates load requests to different load functions based on the path prefix.
+
+.. code-block:: python
+
+ from genshi.template import TemplateLoader, loader
+ tl = TemplateLoader(loader.prefixed(
+ core = '/tmp/dir1',
+ plugin1 = loader.package('plugin1', 'templates'),
+ plugin2 = loader.package('plugin2', 'templates'),
+ ))
+ tmpl = tl.load('core/index.html')
+
+This example sets up a loader with three delegates, under the prefixes “core”,
+“plugin1”, and “plugin2”. When a template is requested, the ``prefixed`` load
+function looks for a delegate with a corresponding prefix, removes the prefix
+from the path and asks the delegate to load the template.
+
+In this case, assuming the directory ``/path/to/dir`` contains a file named
+``index.html``, that file will be used when we load ``core/index.html``. The
+other delegates are not checked as their prefix does not match.
+
+
+.. note:: These builtin load functions are available both as class methods
+ of the ``TemplateLoader`` class as well as on the module level
+
+
+Custom Load Functions
+---------------------
+
+You can easily use your own load function with the template loader, for
+example to load templates from a database. All that is needed is a callable
+object that accepts a ``filename`` (a string) and returns a tuple of the form
+``(filepath, filename, fileobj, uptodate_fun)``, where:
+
+``filepath``
+ is the absolute path to the template. This is primarily used for output in
+ tracebacks, and does not need to map to an actual path on the file system.
+``filename``
+ is the base name of the template file
+``fileobj``
+ is a readable file-like object that provides the content of the template
+``uptodate_fun``
+ is a function that the loader can invoke to check whether the cached version
+ of the template is still up-to-date, or ``None`` if the load function is not
+ able to provide such a check. If provided, the function should not expect
+ any parameters (so you'll definitely want to use a closure here), and should
+ return ``True`` if the template has not changed since it was last loaded.
+
+When the requested template can not be found, the function should raise an
+``IOError`` or ``TemplateNotFound`` exception.
+
+
+------------------
+Customized Loading
+------------------
+
+If you require a completely different implementation of template loading, you
+can extend or even replace the builtin ``TemplateLoader`` class.
+
+Protocol
+========
+
+The protocol between the template loader and the ``Template`` class is simple
+and only used for processing includes. The only required part of that protocol
+is that the object assigned to ``Template.loader`` implements a ``load``
+method compatible to that of the ``TemplateLoader`` class, at the minimum with
+the signature ``load(filename, relative_to=None, cls=None)``.
+
+In addition, templates currently check for the existence and value of a boolean
+``auto_reload`` property. If the property does not exist or evaluates to a
+non-truth value, inlining of included templates is disabled. Inlining is a
+small optimization that removes some overhead in the processing of includes.
+
+Subclassing ``TemplateLoader``
+==============================
+
+You can also adjust the behavior of the ``TemplateLoader`` class by subclassing
+it. You can of course override anything needed, but the class also provides the
+``_instantiate()`` hook, which is intended for use by subclasses to customize
+the creation of the template object from the file name and content. Please
+consult the code and the API documentation for more detail.
diff --git a/lib/genshi/doc/logo.gif b/lib/genshi/doc/logo.gif
new file mode 100644
index 0000000000000000000000000000000000000000..05edacd14f1ae6f2cdf29559104655b815fba1bd
GIT binary patch
[cut]
diff --git a/lib/genshi/doc/logo.lineform/data b/lib/genshi/doc/logo.lineform/data
new file mode 100644
index 0000000000000000000000000000000000000000..f836e7144ec22c73b80c41d7a346dae4d66307e9
GIT binary patch
[cut]
diff --git a/lib/genshi/doc/logo.png b/lib/genshi/doc/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..7b290a6dac09aa4be51289e983fc33240d45f812
GIT binary patch
[cut]
diff --git a/lib/genshi/doc/plugin.txt b/lib/genshi/doc/plugin.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/plugin.txt
@@ -0,0 +1,260 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+===========================
+Using the Templating Plugin
+===========================
+
+While you can easily use Genshi templating through the APIs provided directly
+by Genshi, in some situations you may want to use Genshi through the template
+engine plugin API. Note though that this considerably limits the power and
+flexibility of Genshi templates (for example, there's no good way to use filters
+such as Genshi's `HTMLFormFiller`_ when the plugin
+API is sitting between your code and Genshi).
+
+.. _`HTMLFormFiller`: filters.html>#html-form-filler
+
+
+.. contents:: Contents
+ :depth: 2
+.. sectnum::
+
+
+Introduction
+============
+
+Some Python web frameworks support a variety of different templating engines
+through the `Template Engine Plugin API`_, which was first developed by the
+Buffet_ and TurboGears_ projects.
+
+.. _`Template Engine Plugin API`: http://docs.turbogears.org/1.0/TemplatePlugins
+.. _`Buffet`: http://projects.dowski.com/projects/buffet
+.. _`TurboGears`: http://www.turbogears.org/
+
+Genshi supports this API out of the box, so you can use it in frameworks like
+TurboGears or `Pylons`_ without installing any additional packages. A small
+example TurboGears application is included in the ``examples`` directory of
+source distributions of Genshi.
+
+.. _`Pylons`: http://pylonshq.com/
+
+
+Usage
+=====
+
+The way you use Genshi through the plugin API depends very much on the framework
+you're using. In general, the approach will look something like the following:
+
+(1) Configure Genshi as the default (or an additional) template engine
+(2) Optionally specify Genshi-specific `configuration options`_
+(3) For any given *view* or *controller* (or whatever those are called in your
+ framework of choice), specify the name of the template to use and which data
+ should be made available to it.
+
+For point 1, you'll have to specify the *name* of the template engine plugin.
+For Genshi, this is **"genshi"**. However, because Genshi supports both markup
+and text templates, it also provides two separate plugins, namely
+**"genshi-markup"** and **"genshi-text"** (the "genshi" name is just an
+alias for "genshi-markup").
+
+Usually, you can choose a default template engine, but also use a different
+engine on a per-request basis. So to use markup templates in general, but a text
+template in a specific controller, you'd configure "genshi" as the default
+template engine, and specify "genshi-text" for the controllers that should use
+text templates. How exactly this works depends on the framework in use.
+
+When rendering a specific template in a controller (point 3 above), you may also
+be able to pass additional options to the plugin. This includes the ``format``
+keyword argument, which Genshi will use to override the configured default
+serialization method. In combination with specifying the "genshi-text" engine
+name as explained above, you would use this to specify the "text" serialization
+method when you want to use a text template. Or you'd specify "xml" for the
+format when you want to produce an Atom feed or other XML content.
+
+
+Template Paths
+--------------
+
+How you specify template paths depends on whether you have a `search path`_ set
+up or not. The search path is a list of directories that Genshi should load
+templates from. Now when you request a template using a relative path such as
+``mytmpl.html`` or ``foo/mytmpl.html``, Genshi will look for that file in the
+directories on the search path.
+
+For mostly historical reasons, the Genshi template engine plugin uses a
+different approach when you **haven't** configured the template search path:
+you now load templates using *dotted notation*, for example ``mytmpl`` or
+``foo.mytmpl``. Note how you've lost the ability to explicitly specify the
+file extension: you now have to use ``.html`` for markup templates, and
+``.txt`` for text templates.
+
+Using the search path is recommended for a number of reasons: First, it's
+the native Genshi model and is thus more robust and better supported.
+Second, a search path gives you much more flexibility for organizing your
+application templates. And as noted above, you aren't forced to use hardcoded
+filename extensions for your template files.
+
+
+Extra Implicit Objects
+----------------------
+
+The "genshi-markup" template engine plugin adds some extra functions that are
+made available to all templates implicitly, namely:
+
+``HTML(string)``
+ Parses the given string as HTML and returns a markup stream.
+``XML(string)``
+ Parses the given string as XML and returns a markup stream.
+``ET(tree)``
+ Adapts the given `ElementTree`_ object to a markup stream.
+
+The framework may make additional objects available by default. Consult the
+documentation of your framework for more information.
+
+.. _elementtree: http://effbot.org/zone/element-index.htm
+
+
+.. _`configuration options`:
+
+Configuration Options
+=====================
+
+The plugin API allows plugins to be configured using a dictionary of strings.
+The following is a list of configuration options that Genshi supports. These may
+or may not be made available by your framework. TurboGears 1.0, for example,
+only passes a fixed set of options to all plugins.
+
+``genshi.allow_exec``
+--------------------------
+Whether the Python code blocks should be permitted in templates. Specify "yes"
+to allow code blocks (which is the default), or "no" otherwise. Please note
+that disallowing code blocks in templates does not turn Genshi into a
+sandboxable template engine; there are sufficient ways to do harm even using
+plain expressions.
+
+``genshi.auto_reload``
+----------------------
+Whether the template loader should check the last modification time of template
+files, and automatically reload them if they have been changed. Specify "yes"
+to enable this reloading (which is the default), or "no" to turn it off.
+
+You probably want to disable reloading in a production environment to improve
+performance of both templating loading and the processing of includes. But
+remember that you'll then have to manually restart the server process anytime
+the templates are updated.
+
+``genshi.default_doctype``
+--------------------------
+The default ``DOCTYPE`` declaration to use in generated markup. Valid values
+are:
+
+**html-strict** (or just **html**)
+ HTML 4.01 Strict
+**html-transitional**
+ HTML 4.01 Transitional
+**xhtml-strict** (or just **xhtml**)
+ XHTML 1.0 Strict
+**xhtml-transitional**
+ XHTML 1.0 Transitional
+**html5**
+ HTML5 (as `proposed`_ by the WHAT-WG)
+
+.. _proposed: http://www.whatwg.org/specs/web-apps/current-work/
+
+.. note:: While using the Genshi API directly allows you to specify document
+ types not in that list, the *dictionary-of-strings* based
+ configuration utilized by the plugin API unfortunately limits your
+ choices to those listed above.
+
+The default behavior is to not do any prepending/replacing of a ``DOCTYPE``, but
+rather pass through those defined in the templates (if any). If this option is
+set, however, any ``DOCTYPE`` declarations in the templates are replaced by the
+specified document type.
+
+Note that with (X)HTML, the presence and choice of the ``DOCTYPE`` can have a
+more or less dramatic impact on how modern browsers render pages that use CSS
+style sheets. In particular, browsers may switch to *quirks rendering mode* for
+certain document types, or when the ``DOCTYPE`` declaration is missing
+completely.
+
+For more information on the choice of the appropriate ``DOCTYPE``, see:
+
+* `Recommended DTDs to use in your Web document <http://www.w3.org/QA/2002/04/valid-dtd-list.html>`_
+* `Choosing a DOCTYPE <http://htmlhelp.com/tools/validator/doctype.html>`_
+
+``genshi.default_encoding``
+---------------------------
+The default output encoding to use when serializing a template. By default,
+Genshi uses UTF-8. If you need to, you can choose a different charset by
+specifying this option, although that rarely makes sense.
+
+As Genshi is not in control over what HTTP headers are being sent together with
+the template output, make sure that you (or the framework you're using)
+specify the chosen encoding as part of the outgoing ``Content-Type`` header.
+For example::
+
+ Content-Type: text/html; charset=utf-8
+
+.. note:: Browsers commonly use ISO-8859-1 by default for ``text/html``, so even
+ if you use Genshi's default UTF-8 encoding, you'll have to let the
+ browser know about that explicitly
+
+``genshi.default_format``
+-------------------------
+Determines the default serialization method to use. Valid options are:
+
+**xml**
+ Serialization to XML
+**xhtml**
+ Serialization to XHTML in a way that should be compatible with HTML (i.e. the
+ result can be sent using the ``text/html`` MIME type, but can also be handled
+ by XML parsers if you're careful).
+**html**
+ Serialization to HTML
+**text**
+ Plain text serialization
+
+See `Understanding HTML, XML and XHTML`_ for an excellent description of the
+subtle differences between the three different markup serialization options. As
+a general recommendation, if you don't have a special requirement to produce
+well-formed XML, you should probably use the **html** option for your web sites.
+
+.. _`Understanding HTML, XML and XHTML`: http://webkit.org/blog/?p=68
+
+``genshi.loader_callback``
+--------------------------
+The callback function that should be invoked whenever the template loader loads
+a new template.
+
+.. note:: Unlike the other options, this option can **not** be passed as
+ a string value, but rather must be a reference to the actual function.
+ That effectively means it can not be set from (non-Python)
+ configuration files.
+
+``genshi.lookup_errors``
+------------------------
+The error handling style to use in template expressions. Can be either
+**lenient** (the default) or **strict**. See the `Error Handling`_ section for
+detailled information on the differences between these two modes.
+
+.. _`Error Handling`: templates.html#template-expressions-and-code-blocks
+
+``genshi.max_cache_size``
+-------------------------
+The maximum number of templates that the template loader will cache in memory.
+The default value is **25**. You may want to choose a higher value if your web
+site uses a larger number of templates, and you have enough memory to spare.
+
+``genshi.new_text_syntax``
+--------------------------
+Whether the new syntax for text templates should be used. Specify "yes" to
+enable the new syntax, or "no" to use the old syntax.
+
+In the version of Genshi, the default is to use the old syntax for
+backwards-compatibility, but that will change in a future release.
+
+.. _`search path`:
+
+``genshi.search_path``
+----------------------
+A colon-separated list of file-system path names that the template loader should
+use to search for templates.
diff --git a/lib/genshi/doc/streams.txt b/lib/genshi/doc/streams.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/streams.txt
@@ -0,0 +1,485 @@
+.. -*- 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
+* the result of selecting a subset of another stream using XPath, or
+* programmatically generated.
+
+For example, the functions ``XML()`` and ``HTML()`` can be used to convert
+literal XML or HTML text to a markup stream:
+
+.. code-block:: pycon
+
+ >>> from genshi import XML
+ >>> stream = XML('<p class="intro">Some text and '
+ ... '<a href="http://example.org/">a link</a>.'
+ ... '<br/></p>')
+ >>> stream
+ <genshi.core.Stream object at ...>
+
+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 (see `event kinds`_)
+* ``pos`` is a ``(filename, lineno, column)`` tuple that describes where the
+ event “comes from”.
+
+.. code-block:: pycon
+
+ >>> for kind, data, pos in stream:
+ ... print('%s %r %r' % (kind, data, pos))
+ ...
+ START (QName('p'), Attrs([(QName('class'), u'intro')])) (None, 1, 0)
+ TEXT u'Some text and ' (None, 1, 17)
+ START (QName('a'), Attrs([(QName('href'), u'http://example.org/')])) (None, 1, 31)
+ TEXT u'a link' (None, 1, 61)
+ END QName('a') (None, 1, 67)
+ TEXT u'.' (None, 1, 71)
+ START (QName('br'), Attrs()) (None, 1, 72)
+ END QName('br') (None, 1, 77)
+ END QName('p') (None, 1, 77)
+
+
+Filtering
+=========
+
+One important feature of markup streams is that you can apply *filters* to the
+stream, either filters that come with Genshi, or your own custom filters.
+
+A filter is simply a callable that accepts the stream as parameter, and returns
+the filtered stream:
+
+.. code-block:: python
+
+ 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:
+
+.. code-block:: python
+
+ stream = noop(stream)
+
+The ``Stream`` class also provides a ``filter()`` method, which takes an
+arbitrary number of filter callables and applies them all:
+
+.. code-block:: python
+
+ 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:
+
+.. code-block:: python
+
+ stream = stream | noop
+
+One example of a filter included with Genshi is the ``HTMLSanitizer`` in
+``genshi.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:
+
+.. code-block:: python
+
+ stream = stream | HTMLSanitizer()
+
+Both the ``filter()`` method and the pipe operator allow easy chaining of
+filters:
+
+.. code-block:: python
+
+ from genshi.filters import HTMLSanitizer
+ stream = stream.filter(noop, HTMLSanitizer())
+
+That is equivalent to:
+
+.. code-block:: python
+
+ stream = stream | noop | HTMLSanitizer()
+
+For more information about the built-in filters, see `Stream Filters`_.
+
+.. _`Stream Filters`: filters.html
+
+
+Serialization
+=============
+
+Serialization means producing some kind of textual output from a stream of
+events, which you'll need when you want to transmit or store the results of
+generating or otherwise processing markup.
+
+The ``Stream`` class provides two methods for serialization: ``serialize()``
+and ``render()``. The former is a generator that yields chunks of ``Markup``
+objects (which are basically unicode strings that are considered safe for
+output on the web). The latter returns a single string, by default UTF-8
+encoded.
+
+Here's the output from ``serialize()``:
+
+.. code-block:: pycon
+
+ >>> for output in stream.serialize():
+ ... print(repr(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()``:
+
+.. code-block:: pycon
+
+ >>> 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 serialized to text. This parameter can be either a string or a
+custom serializer class:
+
+.. code-block:: pycon
+
+ >>> 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. See `serialization methods`_ for more details.
+
+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 ``genshi.output`` can also be used
+directly:
+
+.. code-block:: pycon
+
+ >>> from genshi.filters import HTMLSanitizer
+ >>> from genshi.output import TextSerializer
+ >>> print(''.join(TextSerializer()(HTMLSanitizer()(stream))))
+ Some text and a link.
+
+The pipe operator allows a nicer syntax:
+
+.. code-block:: pycon
+
+ >>> print(stream | HTMLSanitizer() | TextSerializer())
+ Some text and a link.
+
+
+.. _`serialization methods`:
+
+Serialization Methods
+---------------------
+
+Genshi supports the use of different serialization methods to use for creating
+a text representation of a markup stream.
+
+``xml``
+ The ``XMLSerializer`` is the default serialization method and results in
+ proper XML output including namespace support, the XML declaration, CDATA
+ sections, and so on. It is not generally not suitable for serving HTML or
+ XHTML web pages (unless you want to use true XHTML 1.1), for which the
+ ``xhtml`` and ``html`` serializers described below should be preferred.
+
+``xhtml``
+ The ``XHTMLSerializer`` is a specialization of the generic ``XMLSerializer``
+ that understands the pecularities of producing XML-compliant output that can
+ also be parsed without problems by the HTML parsers found in modern web
+ browsers. Thus, the output by this serializer should be usable whether sent
+ as "text/html" or "application/xhtml+html" (although there are a lot of
+ subtle issues to pay attention to when switching between the two, in
+ particular with respect to differences in the DOM and CSS).
+
+ For example, instead of rendering a script tag as ``<script/>`` (which
+ confuses the HTML parser in many browsers), it will produce
+ ``<script></script>``. Also, it will normalize any boolean attributes values
+ that are minimized in HTML, so that for example ``<hr noshade="1"/>``
+ becomes ``<hr noshade="noshade" />``.
+
+ This serializer supports the use of namespaces for compound documents, for
+ example to use inline SVG inside an XHTML document.
+
+``html``
+ The ``HTMLSerializer`` produces proper HTML markup. The main differences
+ compared to ``xhtml`` serialization are that boolean attributes are
+ minimized, empty tags are not self-closing (so it's ``<br>`` instead of
+ ``<br />``), and that the contents of ``<script>`` and ``<style>`` elements
+ are not escaped.
+
+``text``
+ The ``TextSerializer`` produces plain text from markup streams. This is
+ useful primarily for `text templates`_, but can also be used to produce
+ plain text output from markup templates or other sources.
+
+.. _`text templates`: text-templates.html
+
+
+Serialization Options
+---------------------
+
+Both ``serialize()`` and ``render()`` support additional keyword arguments that
+are passed through to the initializer of the serializer class. The following
+options are supported by the built-in serializers:
+
+``strip_whitespace``
+ Whether the serializer should remove trailing spaces and empty lines.
+ Defaults to ``True``.
+
+ (This option is not available for serialization to plain text.)
+
+``doctype``
+ A ``(name, pubid, sysid)`` tuple defining the name, publid identifier, and
+ system identifier of a ``DOCTYPE`` declaration to prepend to the generated
+ output. If provided, this declaration will override any ``DOCTYPE``
+ declaration in the stream.
+
+ The parameter can also be specified as a string to refer to commonly used
+ doctypes:
+
+ +-----------------------------+-------------------------------------------+
+ | Shorthand | DOCTYPE |
+ +=============================+===========================================+
+ | ``html`` or | HTML 4.01 Strict |
+ | ``html-strict`` | |
+ +-----------------------------+-------------------------------------------+
+ | ``html-transitional`` | HTML 4.01 Transitional |
+ +-----------------------------+-------------------------------------------+
+ | ``html-frameset`` | HTML 4.01 Frameset |
+ +-----------------------------+-------------------------------------------+
+ | ``html5`` | DOCTYPE proposed for the work-in-progress |
+ | | HTML5 standard |
+ +-----------------------------+-------------------------------------------+
+ | ``xhtml`` or | XHTML 1.0 Strict |
+ | ``xhtml-strict`` | |
+ +-----------------------------+-------------------------------------------+
+ | ``xhtml-transitional`` | XHTML 1.0 Transitional |
+ +-----------------------------+-------------------------------------------+
+ | ``xhtml-frameset`` | XHTML 1.0 Frameset |
+ +-----------------------------+-------------------------------------------+
+ | ``xhtml11`` | XHTML 1.1 |
+ +-----------------------------+-------------------------------------------+
+ | ``svg`` or ``svg-full`` | SVG 1.1 |
+ +-----------------------------+-------------------------------------------+
+ | ``svg-basic`` | SVG 1.1 Basic |
+ +-----------------------------+-------------------------------------------+
+ | ``svg-tiny`` | SVG 1.1 Tiny |
+ +-----------------------------+-------------------------------------------+
+
+ (This option is not available for serialization to plain text.)
+
+``namespace_prefixes``
+ The namespace prefixes to use for namespace that are not bound to a prefix
+ in the stream itself.
+
+ (This option is not available for serialization to HTML or plain text.)
+
+``drop_xml_decl``
+ Whether to remove the XML declaration (the ``<?xml ?>`` part at the
+ beginning of a document) when serializing. This defaults to ``True`` as an
+ XML declaration throws some older browsers into "Quirks" rendering mode.
+
+ (This option is only available for serialization to XHTML.)
+
+``strip_markup``
+ Whether the text serializer should detect and remove any tags or entity
+ encoded characters in the text.
+
+ (This option is only available for serialization to plain text.)
+
+
+
+Using XPath
+===========
+
+XPath can be used to extract a specific subset of the stream via the
+``select()`` method:
+
+.. code-block:: pycon
+
+ >>> substream = stream.select('a')
+ >>> substream
+ <genshi.core.Stream object at ...>
+ >>> 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``:
+
+.. code-block:: pycon
+
+ >>> from genshi import Stream
+ >>> substream = Stream(list(stream.select('a')))
+ >>> substream
+ <genshi.core.Stream object at ...>
+ >>> print(substream)
+ <a href="http://example.org/">a link</a>
+ >>> print(substream.select('@href'))
+ http://example.org/
+ >>> print(substream.select('text()'))
+ a link
+
+See `Using XPath in Genshi`_ for more information about the XPath support in
+Genshi.
+
+.. _`Using XPath in Genshi`: xpath.html
+
+
+.. _`event kinds`:
+
+Event Kinds
+===========
+
+Every event in a stream is of one of several *kinds*, which also determines
+what the ``data`` item of the event tuple looks like. The different kinds of
+events are documented below.
+
+.. note:: The ``data`` item is generally immutable. If the data is to be
+ modified when processing a stream, it must be replaced by a new tuple.
+ Effectively, this means the entire event tuple is immutable.
+
+START
+-----
+The opening tag of an element.
+
+For this kind of event, the ``data`` item is a tuple of the form
+``(tagname, attrs)``, where ``tagname`` is a ``QName`` instance describing the
+qualified name of the tag, and ``attrs`` is an ``Attrs`` instance containing
+the attribute names and values associated with the tag (excluding namespace
+declarations):
+
+.. code-block:: python
+
+ START, (QName('p'), Attrs([(QName('class'), u'intro')])), pos
+
+END
+---
+The closing tag of an element.
+
+The ``data`` item of end events consists of just a ``QName`` instance
+describing the qualified name of the tag:
+
+.. code-block:: python
+
+ END, QName('p'), pos
+
+TEXT
+----
+Character data outside of elements and comments.
+
+For text events, the ``data`` item should be a unicode object:
+
+.. code-block:: python
+
+ TEXT, u'Hello, world!', pos
+
+START_NS
+--------
+The start of a namespace mapping, binding a namespace prefix to a URI.
+
+The ``data`` item of this kind of event is a tuple of the form
+``(prefix, uri)``, where ``prefix`` is the namespace prefix and ``uri`` is the
+full URI to which the prefix is bound. Both should be unicode objects. If the
+namespace is not bound to any prefix, the ``prefix`` item is an empty string:
+
+.. code-block:: python
+
+ START_NS, (u'svg', u'http://www.w3.org/2000/svg'), pos
+
+END_NS
+------
+The end of a namespace mapping.
+
+The ``data`` item of such events consists of only the namespace prefix (a
+unicode object):
+
+.. code-block:: python
+
+ END_NS, u'svg', pos
+
+DOCTYPE
+-------
+A document type declaration.
+
+For this type of event, the ``data`` item is a tuple of the form
+``(name, pubid, sysid)``, where ``name`` is the name of the root element,
+``pubid`` is the public identifier of the DTD (or ``None``), and ``sysid`` is
+the system identifier of the DTD (or ``None``):
+
+.. code-block:: python
+
+ DOCTYPE, (u'html', u'-//W3C//DTD XHTML 1.0 Transitional//EN', \
+ u'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'), pos
+
+COMMENT
+-------
+A comment.
+
+For such events, the ``data`` item is a unicode object containing all character
+data between the comment delimiters:
+
+.. code-block:: python
+
+ COMMENT, u'Commented out', pos
+
+PI
+--
+A processing instruction.
+
+The ``data`` item is a tuple of the form ``(target, data)`` for processing
+instructions, where ``target`` is the target of the PI (used to identify the
+application by which the instruction should be processed), and ``data`` is text
+following the target (excluding the terminating question mark):
+
+.. code-block:: python
+
+ PI, (u'php', u'echo "Yo" '), pos
+
+START_CDATA
+-----------
+Marks the beginning of a ``CDATA`` section.
+
+The ``data`` item for such events is always ``None``:
+
+.. code-block:: python
+
+ START_CDATA, None, pos
+
+END_CDATA
+---------
+Marks the end of a ``CDATA`` section.
+
+The ``data`` item for such events is always ``None``:
+
+.. code-block:: python
+
+ END_CDATA, None, pos
diff --git a/lib/genshi/doc/templates.txt b/lib/genshi/doc/templates.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/templates.txt
@@ -0,0 +1,458 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+========================
+Genshi Templating Basics
+========================
+
+Genshi provides a template engine that can be used for generating either
+markup (such as HTML_ or XML_) or plain text. While both share some of the
+syntax (and much of the underlying implementation) they are essentially
+separate languages.
+
+.. _html: http://www.w3.org/html/
+.. _xml: http://www.w3.org/XML/
+
+This document describes the common parts of the template engine and will be most
+useful as reference to those developing Genshi templates. Templates are XML or
+plain text files that include processing directives_ that affect how the
+template is rendered, and template expressions_ that are dynamically substituted
+by variable data.
+
+
+.. contents:: Contents
+ :depth: 3
+.. sectnum::
+
+--------
+Synopsis
+--------
+
+A Genshi *markup template* is a well-formed XML document with embedded Python
+used for control flow and variable substitution. Markup templates should be
+used to generate any kind of HTML or XML output, as they provide a number of
+advantages over simple text-based templates (such as automatic escaping of
+variable data).
+
+The following is a simple Genshi markup template:
+
+.. code-block:: genshi
+
+ <?python
+ title = "A Genshi Template"
+ fruits = ["apple", "orange", "kiwi"]
+ ?>
+ <html xmlns:py="http://genshi.edgewall.org/">
+ <head>
+ <title py:content="title">This is replaced.</title>
+ </head>
+
+ <body>
+ <p>These are some of my favorite fruits:</p>
+ <ul>
+ <li py:for="fruit in fruits">
+ I like ${fruit}s
+ </li>
+ </ul>
+ </body>
+ </html>
+
+This example shows:
+
+(a) a Python code block in a processing instruction
+(b) the Genshi namespace declaration
+(c) usage of templates directives (``py:content`` and ``py:for``)
+(d) an inline Python expression (``${fruit}``).
+
+The template would generate output similar to this:
+
+.. code-block:: genshi
+
+ <html>
+ <head>
+ <title>A Genshi Template</title>
+ </head>
+
+ <body>
+ <p>These are some of my favorite fruits:</p>
+ <ul>
+ <li>I like apples</li>
+ <li>I like oranges</li>
+ <li>I like kiwis</li>
+ </ul>
+ </body>
+ </html>
+
+A *text template* is a simple plain text document that can also contain
+embedded Python code. Text templates are intended to be used for simple
+*non-markup* text formats, such as the body of an plain text email. For
+example:
+
+.. code-block:: genshitext
+
+ Dear $name,
+
+ These are some of my favorite fruits:
+ #for fruit in fruits
+ * $fruit
+ #end
+
+
+----------
+Python API
+----------
+
+The Python code required for templating with Genshi is generally based on the
+following pattern:
+
+* Attain a ``MarkupTemplate`` or ``TextTemplate`` object from a string or
+ file-like object containing the template 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:
+
+.. code-block:: pycon
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('<h1>Hello, $name!</h1>')
+ >>> stream = tmpl.generate(name='world')
+ >>> print(stream.render('xhtml'))
+ <h1>Hello, world!</h1>
+
+.. note:: See the Serialization_ section of the `Markup Streams`_ page for
+ information on configuring template output options.
+
+Using a text template is similar:
+
+.. code-block:: pycon
+
+ >>> from genshi.template import TextTemplate
+ >>> tmpl = TextTemplate('Hello, $name!')
+ >>> stream = tmpl.generate(name='world')
+ >>> print(stream)
+ Hello, world!
+
+.. note:: If you want to use text templates, you should consider using the
+ ``NewTextTemplate`` class instead of simply ``TextTemplate``. See
+ the `Text Template Language`_ page.
+
+.. _serialization: streams.html#serialization
+.. _`Text Template Language`: text-templates.html
+.. _`Markup Streams`: streams.html
+
+Using a `template loader`_ provides the advantage that “compiled” templates are
+automatically cached, and only parsed again when the template file changes. In
+addition, it enables the use of a *template search path*, allowing template
+directories to be spread across different file-system locations. Using a
+template loader would generally look as follows:
+
+.. code-block:: python
+
+ from genshi.template import TemplateLoader
+ loader = TemplateLoader([templates_dir1, templates_dir2])
+ tmpl = loader.load('test.html')
+ stream = tmpl.generate(title='Hello, world!')
+ print(stream.render())
+
+See the `API documentation <api/index.html>`_ for details on using Genshi via
+the Python API.
+
+.. _`template loader`: loader.html
+
+.. _`expressions`:
+
+------------------------------------
+Template Expressions and Code Blocks
+------------------------------------
+
+Python_ expressions can be used in text and directive arguments. An expression
+is substituted with the result of its evaluation against the template data.
+Expressions in text (which includes the values of non-directive attributes) need
+to prefixed with a dollar sign (``$``) and usually enclosed in curly braces
+(``{…}``).
+
+.. _python: http://www.python.org/
+
+If the expression starts with a letter and contains only letters, digits, dots,
+and underscores, the curly braces may be omitted. In all other cases, the
+braces are required so that the template processor knows where the expression
+ends:
+
+.. code-block:: pycon
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('<em>${items[0].capitalize()} item</em>')
+ >>> print(tmpl.generate(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):
+
+.. code-block:: pycon
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('<em>${dict.foo}</em>')
+ >>> print(tmpl.generate(dict={'foo': 'bar'}))
+ <em>bar</em>
+
+Because there are two ways to access either attributes or items, expressions
+do not raise the standard ``AttributeError`` or ``IndexError`` exceptions, but
+rather an exception of the type ``UndefinedError``. The same kind of error is
+raised when you try to use a top-level variable that is not in the context data.
+See `Error Handling`_ below for details on how such errors are handled.
+
+
+Escaping
+========
+
+If you need to include a literal dollar sign in the output where Genshi would
+normally detect an expression, you can simply add another dollar sign:
+
+.. code-block:: pycon
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('<em>$foo</em>') # Wanted "$foo" as literal output
+ >>> print(tmpl.generate())
+ Traceback (most recent call last):
+ ...
+ UndefinedError: "foo" not defined
+ >>> tmpl = MarkupTemplate('<em>$$foo</em>')
+ >>> print(tmpl.generate())
+ <em>$foo</em>
+
+But note that this is not necessary if the characters following the dollar sign
+do not qualify as an expression. For example, the following needs no escaping:
+
+.. code-block:: pycon
+
+ >>> tmpl = MarkupTemplate('<script>$(function() {})</script>')
+ >>> print(tmpl.generate())
+ <script>$(function() {})</script>
+
+On the other hand, Genshi will always replace two dollar signs in text with a
+single dollar sign, so you'll need to use three dollar signs to get two in the
+output:
+
+.. code-block:: pycon
+
+ >>> tmpl = MarkupTemplate('<script>$$$("div")</script>')
+ >>> print(tmpl.generate())
+ <script>$$("div")</script>
+
+
+.. _`code blocks`:
+
+Code Blocks
+===========
+
+Templates also support full Python code blocks, using the ``<?python ?>``
+processing instruction in XML templates:
+
+.. code-block:: genshi
+
+ <div>
+ <?python
+ from genshi.builder import tag
+ def greeting(name):
+ return tag.b('Hello, %s!' % name) ?>
+ ${greeting('world')}
+ </div>
+
+This will produce the following output:
+
+.. code-block:: xml
+
+ <div>
+ <b>Hello, world!</b>
+ </div>
+
+In text templates (although only those using the new syntax introduced in
+Genshi 0.5), code blocks use the special ``{% python %}`` directive:
+
+.. code-block:: genshitext
+
+ {% python
+ from genshi.builder import tag
+ def greeting(name):
+ return 'Hello, %s!' % name
+ %}
+ ${greeting('world')}
+
+This will produce the following output::
+
+ Hello, world!
+
+
+Code blocks can import modules, define classes and functions, and basically do
+anything you can do in normal Python code. What code blocks can *not* do is to
+produce content that is emitted directly tp the generated output.
+
+.. note:: Using the ``print`` statement will print to the standard output
+ stream, just as it does for other Python code in your application.
+
+Unlike expressions, Python code in ``<?python ?>`` processing instructions can
+not use item and attribute access in an interchangeable manner. That means that
+“dotted notation” is always attribute access, and vice-versa.
+
+The support for Python code blocks in templates is not supposed to encourage
+mixing application code into templates, which is generally considered bad
+design. If you're using many code blocks, that may be a sign that you should
+move such code into separate Python modules.
+
+If you'd rather not allow the use of Python code blocks in templates, you can
+simply set the ``allow_exec`` parameter (available on the ``Template`` and the
+``TemplateLoader`` initializers) to ``False``. In that case Genshi will raise
+a syntax error when a ``<?python ?>`` processing instruction is encountered.
+But please note that disallowing code blocks in templates does not turn Genshi
+into a sandboxable template engine; there are sufficient ways to do harm even
+using plain expressions.
+
+
+.. _`error handling`:
+
+Error Handling
+==============
+
+By default, Genshi raises an ``UndefinedError`` if a template expression
+attempts to access a variable that is not defined:
+
+.. code-block:: pycon
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('<p>${doh}</p>')
+ >>> tmpl.generate().render('xhtml')
+ Traceback (most recent call last):
+ ...
+ UndefinedError: "doh" not defined
+
+You can change this behavior by setting the variable lookup mode to "lenient".
+In that case, accessing undefined variables returns an `Undefined` object,
+meaning that the expression does not fail immediately. See below for details.
+
+If you need to check whether a variable exists in the template context, use the
+defined_ or the value_of_ function described below. To check for existence of
+attributes on an object, or keys in a dictionary, use the ``hasattr()``,
+``getattr()`` or ``get()`` functions, or the ``in`` operator, just as you would
+in regular Python code:
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('<p>${defined("doh")}</p>')
+ >>> print(tmpl.generate().render('xhtml'))
+ <p>False</p>
+
+.. note:: Lenient error handling was the default in Genshi prior to version 0.5.
+ Strict mode was introduced in version 0.4, and became the default in
+ 0.5. The reason for this change was that the lenient error handling
+ was masking actual errors in templates, thereby also making it harder
+ to debug some problems.
+
+
+.. _`lenient`:
+
+Lenient Mode
+------------
+
+If you instruct Genshi to use the lenient variable lookup mode, it allows you
+to access variables that are not defined, without raising an ``UndefinedError``.
+
+This mode can be chosen by passing the ``lookup='lenient'`` keyword argument to
+the template initializer, or by passing the ``variable_lookup='lenient'``
+keyword argument to the ``TemplateLoader`` initializer:
+
+.. code-block:: pycon
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('<p>${doh}</p>', lookup='lenient')
+ >>> print(tmpl.generate().render('xhtml'))
+ <p></p>
+
+You *will* however get an exception if you try to call an undefined variable, or
+do anything else with it, such as accessing its attributes:
+
+.. code-block:: pycon
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('<p>${doh.oops}</p>', lookup='lenient')
+ >>> print(tmpl.generate().render('xhtml'))
+ Traceback (most recent call last):
+ ...
+ UndefinedError: "doh" not defined
+
+If you need to know whether a variable is defined, you can check its type
+against the ``Undefined`` class, for example in a conditional directive:
+
+.. code-block:: pycon
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('<p>${type(doh) is not Undefined}</p>',
+ ... lookup='lenient')
+ >>> print(tmpl.generate().render('xhtml'))
+ <p>False</p>
+
+Alternatively, the built-in functions defined_ or value_of_ can be used in this
+case.
+
+Custom Modes
+------------
+
+In addition to the built-in "lenient" and "strict" modes, it is also possible to
+use a custom error handling mode. For example, you could use lenient error
+handling in a production environment, while also logging a warning when an
+undefined variable is referenced.
+
+See the API documentation of the ``genshi.template.eval`` module for details.
+
+
+Built-in Functions & Types
+==========================
+
+The following functions and types are available by default in template code, in
+addition to the standard built-ins that are available to all Python code.
+
+.. _`defined`:
+
+``defined(name)``
+-----------------
+This function determines whether a variable of the specified name exists in
+the context data, and returns ``True`` if it does.
+
+.. _`value_of`:
+
+``value_of(name, default=None)``
+--------------------------------
+This function returns the value of the variable with the specified name if
+such a variable is defined, and returns the value of the ``default``
+parameter if no such variable is defined.
+
+.. _`Markup`:
+
+``Markup(text)``
+----------------
+The ``Markup`` type marks a given string as being safe for inclusion in markup,
+meaning it will *not* be escaped in the serialization stage. Use this with care,
+as not escaping a user-provided string may allow malicious users to open your
+web site to cross-site scripting attacks.
+
+.. _`Undefined`:
+
+``Undefined``
+----------------
+The ``Undefined`` type can be used to check whether a reference variable is
+defined, as explained in `error handling`_.
+
+
+.. _`directives`:
+
+-------------------
+Template Directives
+-------------------
+
+Directives provide control flow functionality for templates, such as conditions
+or iteration. As the syntax for directives depends on whether you're using
+markup or text templates, refer to the
+`XML Template Language <xml-templates.html>`_ or
+`Text Template Language <text-templates.html>`_ pages for information.
diff --git a/lib/genshi/doc/text-templates.txt b/lib/genshi/doc/text-templates.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/text-templates.txt
@@ -0,0 +1,360 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+=============================
+Genshi Text Template Language
+=============================
+
+In addition to the XML-based template language, Genshi provides a simple
+text-based template language, intended for basic plain text generation needs.
+The language is similar to the Django_ template language.
+
+This document describes the template language and will be most useful as
+reference to those developing Genshi text templates. Templates are text files of
+some kind that include processing directives_ that affect how the template is
+rendered, and template expressions that are dynamically substituted by
+variable data.
+
+See `Genshi Templating Basics <templates.html>`_ for general information on
+embedding Python code in templates.
+
+.. note:: Actually, Genshi currently has two different syntaxes for text
+ templates languages: One implemented by the class ``OldTextTemplate``
+ and another implemented by ``NewTextTemplate``. This documentation
+ concentrates on the latter, which is planned to completely replace the
+ older syntax. The older syntax is briefly described under legacy_.
+
+.. _django: http://www.djangoproject.com/
+
+.. contents:: Contents
+ :depth: 3
+.. sectnum::
+
+
+.. _`directives`:
+
+-------------------
+Template Directives
+-------------------
+
+Directives are template commands enclosed by ``{% ... %}`` characters. They can
+affect how the template is rendered in a number of ways: Genshi provides
+directives for conditionals and looping, among others.
+
+Each directive must be terminated using an ``{% end %}`` marker. You can add
+a string inside the ``{% end %}`` marker, for example to document which
+directive is being closed, or even the expression associated with that
+directive. Any text after ``end`` inside the delimiters is ignored, and
+effectively treated as a comment.
+
+If you want to include a literal delimiter in the output, you need to escape it
+by prepending a backslash character (``\``).
+
+
+Conditional Sections
+====================
+
+.. _`if`:
+
+``{% if %}``
+------------
+
+The content is only rendered if the expression evaluates to a truth value:
+
+.. code-block:: genshitext
+
+ {% if foo %}
+ ${bar}
+ {% end %}
+
+Given the data ``foo=True`` and ``bar='Hello'`` in the template context, this
+would produce::
+
+ Hello
+
+
+.. _`choose`:
+.. _`when`:
+.. _`otherwise`:
+
+``{% choose %}``
+----------------
+
+The ``choose`` directive, in combination with the directives ``when`` and
+``otherwise``, provides advanced contional processing for rendering one of
+several alternatives. The first matching ``when`` branch is rendered, or, if
+no ``when`` branch matches, the ``otherwise`` branch is be rendered.
+
+If the ``choose`` directive has no argument the nested ``when`` directives will
+be tested for truth:
+
+.. code-block:: genshitext
+
+ The answer is:
+ {% choose %}
+ {% when 0 == 1 %}0{% end %}
+ {% when 1 == 1 %}1{% end %}
+ {% otherwise %}2{% end %}
+ {% end %}
+
+This would produce the following output::
+
+ The answer is:
+ 1
+
+If the ``choose`` does have an argument, the nested ``when`` directives will
+be tested for equality to the parent ``choose`` value:
+
+.. code-block:: genshitext
+
+ The answer is:
+ {% choose 1 %}\
+ {% when 0 %}0{% end %}\
+ {% when 1 %}1{% end %}\
+ {% otherwise %}2{% end %}\
+ {% end %}
+
+This would produce the following output::
+
+ The answer is:
+ 1
+
+
+Looping
+=======
+
+.. _`for`:
+
+``{% for %}``
+-------------
+
+The content is repeated for every item in an iterable:
+
+.. code-block:: genshitext
+
+ Your items:
+ {% for item in items %}\
+ * ${item}
+ {% end %}
+
+Given ``items=[1, 2, 3]`` in the context data, this would produce::
+
+ Your items
+ * 1
+ * 2
+ * 3
+
+
+Snippet Reuse
+=============
+
+.. _`def`:
+.. _`macros`:
+
+``{% def %}``
+-------------
+
+The ``def`` directive can be used to create macros, i.e. snippets of template
+text that have a name and optionally some parameters, and that can be inserted
+in other places:
+
+.. code-block:: genshitext
+
+ {% def greeting(name) %}
+ Hello, ${name}!
+ {% end %}
+ ${greeting('world')}
+ ${greeting('everyone else')}
+
+The above would be rendered to::
+
+ Hello, world!
+ Hello, everyone else!
+
+If a macro doesn't require parameters, it can be defined without the
+parenthesis. For example:
+
+.. code-block:: genshitext
+
+ {% def greeting %}
+ Hello, world!
+ {% end %}
+ ${greeting()}
+
+The above would be rendered to::
+
+ Hello, world!
+
+
+.. _includes:
+.. _`include`:
+
+``{% include %}``
+-----------------
+
+To reuse common parts of template text across template files, you can include
+other files using the ``include`` directive:
+
+.. code-block:: genshitext
+
+ {% include base.txt %}
+
+Any content included this way is inserted into the generated output. The
+included template sees the context data as it exists at the point of the
+include. `Macros`_ in the included template are also available to the including
+template after the point it was included.
+
+Include paths are relative to the filename of the template currently being
+processed. So if the example above was in the file "``myapp/mail.txt``"
+(relative to the template search path), the include directive would look for
+the included file at "``myapp/base.txt``". You can also use Unix-style
+relative paths, for example "``../base.txt``" to look in the parent directory.
+
+Just like other directives, the argument to the ``include`` directive accepts
+any Python expression, so the path to the included template can be determined
+dynamically:
+
+.. code-block:: genshitext
+
+ {% include ${'%s.txt' % filename} %}
+
+Note that a ``TemplateNotFound`` exception is raised if an included file can't
+be found.
+
+.. note:: The include directive for text templates was added in Genshi 0.5.
+
+
+Variable Binding
+================
+
+.. _`with`:
+
+``{% with %}``
+--------------
+
+The ``{% 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:
+
+.. code-block:: genshitext
+
+ Magic numbers!
+ {% with y=7; z=x+10 %}
+ $x $y $z
+ {% end %}
+
+Given ``x=42`` in the context data, this would produce::
+
+ Magic numbers!
+ 42 7 52
+
+Note that if a variable of the same name already existed outside of the scope
+of the ``with`` directive, it will **not** be overwritten. Instead, it will
+have the same value it had prior to the ``with`` assignment. Effectively,
+this means that variables are immutable in Genshi.
+
+
+.. _whitespace:
+
+---------------------------
+White-space and Line Breaks
+---------------------------
+
+Note that space or line breaks around directives is never automatically removed.
+Consider the following example:
+
+.. code-block:: genshitext
+
+ {% for item in items %}
+ {% if item.visible %}
+ ${item}
+ {% end %}
+ {% end %}
+
+This will result in two empty lines above and beneath every item, plus the
+spaces used for indentation. If you want to supress a line break, simply end
+the line with a backslash:
+
+.. code-block:: genshitext
+
+ {% for item in items %}\
+ {% if item.visible %}\
+ ${item}
+ {% end %}\
+ {% end %}\
+
+Now there would be no empty lines between the items in the output. But you still
+get the spaces used for indentation, and because the line breaks are removed,
+they actually continue and add up between lines. There are numerous ways to
+control white-space in the output while keeping the template readable, such as
+moving the indentation into the delimiters, or moving the end delimiter on the
+next line, and so on.
+
+
+.. _comments:
+
+--------
+Comments
+--------
+
+Parts in templates can be commented out using the delimiters ``{# ... #}``.
+Any content in comments are removed from the output.
+
+.. code-block:: genshitext
+
+ {# This won't end up in the output #}
+ This will.
+
+Just like directive delimiters, these can be escaped by prefixing with a
+backslash.
+
+.. code-block:: genshitext
+
+ \{# This *will* end up in the output, including delimiters #}
+ This too.
+
+
+.. _legacy:
+
+---------------------------
+Legacy Text Template Syntax
+---------------------------
+
+The syntax for text templates was redesigned in version 0.5 of Genshi to make
+the language more flexible and powerful. The older syntax is based on line
+starting with dollar signs, similar to e.g. Cheetah_ or Velocity_.
+
+.. _cheetah: http://cheetahtemplate.org/
+.. _velocity: http://jakarta.apache.org/velocity/
+
+A simple template using the old syntax looked like this:
+
+.. code-block:: genshitext
+
+ Dear $name,
+
+ We have the following items for you:
+ #for item in items
+ * $item
+ #end
+
+ All the best,
+ Foobar
+
+Beyond the requirement of putting directives on separate lines prefixed with
+dollar signs, the language itself is very similar to the new one. Except that
+comments are lines that start with two ``#`` characters, and a line-break at the
+end of a directive is removed automatically.
+
+.. note:: If you're using this old syntax, it is strongly recommended to
+ migrate to the new syntax. Simply replace any references to
+ ``TextTemplate`` by ``NewTextTemplate`` (and also change the
+ text templates, of course). On the other hand, if you want to stick
+ with the old syntax for a while longer, replace references to
+ ``TextTemplate`` by ``OldTextTemplate``; while ``TextTemplate`` is
+ still an alias for the old language at this point, that will change
+ in a future release. But also note that the old syntax may be
+ dropped entirely in a future release.
diff --git a/lib/genshi/doc/upgrade.txt b/lib/genshi/doc/upgrade.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/upgrade.txt
@@ -0,0 +1,176 @@
+================
+Upgrading Genshi
+================
+
+
+.. contents:: Contents
+ :depth: 2
+.. sectnum::
+
+------------------------------------------------------
+Upgrading from Genshi 0.6.x to the development version
+------------------------------------------------------
+
+The Genshi development version now supports both Python 2 and Python 3.
+
+The most noticable API change in the Genshi development version is that the
+default encoding in numerous places is now None (i.e. unicode) instead
+of UTF-8. This change was made in order to ease the transition to Python 3
+where strings are unicode strings by default.
+
+If your application relies on the default UTF-8 encoding a simple way to
+have it work both with Genshi 0.6.x and the development version is to specify the
+encoding explicitly in calls to the following classes, methods and functions:
+
+* genshi.HTML
+* genshi.Stream.render
+* genshi.input.HTMLParser
+* genshi.template.MarkupTemplate
+* genshi.template.TemplateLoader
+* genshi.template.TextTemplate (and genshi.template.NewTextTemplate)
+* genshi.template.OldTextTemplate
+
+Whether you explicitly specify UTF-8 or explicitly specify None (unicode) is
+a matter of personal taste, although working with unicode may make porting
+your own application to Python 3 easier.
+
+
+------------------------------------
+Upgrading from Genshi 0.5.x to 0.6.x
+------------------------------------
+
+Required Python Version
+-----------------------
+
+Support for Python 2.3 has been dropped in this release. Python 2.4 is
+now the minimum version of Python required to run Genshi.
+
+The XPath engine has been completely overhauled for this version. Some
+patterns that previously matched incorrectly will no longer match, and
+the other way around. In such cases, the XPath expressions need to be
+fixed in your application and templates.
+
+
+------------------------------------
+Upgrading from Genshi 0.4.x to 0.5.x
+------------------------------------
+
+Error Handling
+--------------
+
+The default error handling mode has been changed to "strict". This
+means that accessing variables not defined in the template data will
+now generate an immediate exception, as will accessing object
+attributes or dictionary keys that don't exist. If your templates rely
+on the old lenient behavior, you can configure Genshi to use that
+instead. See the documentation for details on how to do that. But be
+warned that lenient error handling may be removed completely in a
+future release.
+
+Match Template Processing
+-------------------------
+
+There has also been a subtle change to how ``py:match`` templates are
+processed: in previous versions, all match templates would be applied
+to the content generated by the matching template, and only the
+matching template itself was applied recursively to the original
+content. This behavior resulted in problems with many kinds of
+recursive matching, and hence was changed for 0.5: now, all match
+templates declared before the matching template are applied to the
+original content, and match templates declared after the matching
+template are applied to the generated content. This change should not
+have any effect on most applications, but you may want to check your
+use of match templates to make sure.
+
+Text Templates
+--------------
+
+Genshi 0.5 introduces a new, alternative syntax for text templates,
+which is more flexible and powerful compared to the old syntax. For
+backwards compatibility, this new syntax is not used by default,
+though it will be in a future version. It is recommended that you
+migrate to using this new syntax. To do so, simply rename any
+references in your code to ``TextTemplate`` to ``NewTextTemplate``. To
+explicitly use the old syntax, use ``OldTextTemplate`` instead, so
+that you can be sure you'll be using the same language when the
+default in Genshi is changed (at least until the old implementation is
+completely removed).
+
+``Markup`` Constructor
+----------------------
+
+The ``Markup`` class no longer has a specialized constructor. The old
+(undocumented) constructor provided a shorthand for doing positional
+substitutions. If you have code like this:
+
+.. code-block:: python
+
+ Markup('<b>%s</b>', name)
+
+You must replace it by the more explicit:
+
+.. code-block:: python
+
+ Markup('<b>%s</b>') % name
+
+``Template`` Constructor
+------------------------
+
+The constructor of the ``Template`` class and its subclasses has changed
+slightly: instead of the optional ``basedir`` parameter, it now expects
+an (also optional) ``filepath`` parameter, which specifies the absolute
+path to the template. You probably aren't using those constructors
+directly, anyway, but using the ``TemplateLoader`` API instead.
+
+
+------------------------------------
+Upgrading from Genshi 0.3.x to 0.4.x
+------------------------------------
+
+The modules ``genshi.filters`` and ``genshi.template`` have been
+refactored into packages containing multiple modules. While code using
+the regular APIs should continue to work without problems, you should
+make sure to remove any leftover traces of the files ``filters.py``
+and ``template.py`` in the ``genshi`` package on the installation
+path (including the corresponding ``.pyc`` files). This is not
+necessary when Genshi was installed as a Python egg.
+
+Results of evaluating template expressions are no longer implicitly
+called if they are callable. If you have been using that feature, you
+will need to add the parenthesis to actually call the function.
+
+Instances of ``genshi.core.Attrs`` are now immutable. Filters
+manipulating the attributes in a stream may need to be updated. Also,
+the ``Attrs`` class no longer automatically wraps all attribute names
+in ``QName`` objects, so users of the ``Attrs`` class need to do this
+themselves. See the documentation of the ``Attrs`` class for more
+information.
+
+
+---------------------
+Upgrading from Markup
+---------------------
+
+Prior to version 0.3, the name of the Genshi project was "Markup". The
+name change means that you will have to adjust your import statements
+and the namespace URI of XML templates, among other things:
+
+* The package name was changed from "markup" to "genshi". Please
+ adjust any import statements referring to the old package name.
+* The namespace URI for directives in Genshi XML templates has changed
+ from ``http://markup.edgewall.org/`` to
+ ``http://genshi.edgewall.org/``. Please update the ``xmlns:py``
+ declaration in your template files accordingly.
+
+Furthermore, due to the inclusion of a text-based template language,
+the class::
+
+ markup.template.Template
+
+has been renamed to::
+
+ genshi.template.MarkupTemplate
+
+If you've been using the Template class directly, you'll need to
+update your code (a simple find/replace should do—the API itself
+did not change).
diff --git a/lib/genshi/doc/xml-templates.txt b/lib/genshi/doc/xml-templates.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/xml-templates.txt
@@ -0,0 +1,702 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+============================
+Genshi XML Template Language
+============================
+
+Genshi provides a 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 Genshi XML 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.
+
+See `Genshi Templating Basics <templates.html>`_ for general information on
+embedding Python code in templates.
+
+
+.. contents:: Contents
+ :depth: 3
+.. sectnum::
+
+
+.. _`directives`:
+
+-------------------
+Template Directives
+-------------------
+
+Directives are elements and/or attributes in the template that are identified
+by the namespace ``http://genshi.edgewall.org/``. They can affect how the
+template is rendered in a number of ways: Genshi provides directives for
+conditionals and looping, among others.
+
+To use directives in a template, the namespace must be declared, which is
+usually done on the root element:
+
+.. code-block:: genshi
+
+ <html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ lang="en">
+ ...
+ </html>
+
+In this example, the default namespace is set to the XHTML namespace, and the
+namespace for Genshi 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:
+
+.. code-block:: genshi
+
+ <html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ lang="en">
+ ...
+ <div py:if="foo">
+ <p>Bar</p>
+ </div>
+ ...
+ </html>
+
+This is basically equivalent to the following:
+
+.. code-block:: genshi
+
+ <html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.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.
+
+
+Conditional Sections
+====================
+
+.. _`py:if`:
+
+``py:if``
+---------
+
+The element and its content is only rendered if the expression evaluates to a
+truth value:
+
+.. code-block:: genshi
+
+ <div>
+ <b py:if="foo">${bar}</b>
+ </div>
+
+Given the data ``foo=True`` and ``bar='Hello'`` in the template context, this
+would produce:
+
+.. code-block:: xml
+
+ <div>
+ <b>Hello</b>
+ </div>
+
+But setting ``foo=False`` would result in the following output:
+
+.. code-block:: xml
+
+ <div>
+ </div>
+
+This directive can also be used as an element:
+
+.. code-block:: genshi
+
+ <div>
+ <py:if test="foo">
+ <b>${bar}</b>
+ </py:if>
+ </div>
+
+.. _`py:choose`:
+.. _`py:when`:
+.. _`py:otherwise`:
+
+``py:choose``
+-------------
+
+The ``py:choose`` directive, in combination with the directives ``py:when``
+and ``py:otherwise`` provides advanced conditional 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 rendered.
+
+If the ``py:choose`` directive is empty the nested ``py:when`` directives will
+be tested for truth:
+
+.. code-block:: genshi
+
+ <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:
+
+.. code-block:: xml
+
+ <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:
+
+.. code-block:: genshi
+
+ <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:
+
+.. code-block:: xml
+
+ <div>
+ <span>1</span>
+ </div>
+
+These directives can also be used as elements:
+
+.. code-block:: genshi
+
+ <py:choose test="1">
+ <py:when test="0">0</py:when>
+ <py:when test="1">1</py:when>
+ <py:otherwise>2</py:otherwise>
+ </py:choose>
+
+Looping
+=======
+
+.. _`py:for`:
+
+``py:for``
+----------
+
+The element is repeated for every item in an iterable:
+
+.. code-block:: genshi
+
+ <ul>
+ <li py:for="item in items">${item}</li>
+ </ul>
+
+Given ``items=[1, 2, 3]`` in the context data, this would produce:
+
+.. code-block:: xml
+
+ <ul>
+ <li>1</li><li>2</li><li>3</li>
+ </ul>
+
+This directive can also be used as an element:
+
+.. code-block:: genshi
+
+ <ul>
+ <py:for each="item in items">
+ <li>${item}</li>
+ </py:for>
+ </ul>
+
+
+Snippet Reuse
+=============
+
+.. _`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:
+
+.. code-block:: genshi
+
+ <div>
+ <p py:def="greeting(name)" class="greeting">
+ Hello, ${name}!
+ </p>
+ ${greeting('world')}
+ ${greeting('everyone else')}
+ </div>
+
+The above would be rendered to:
+
+.. code-block:: xml
+
+ <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 without the
+parenthesis. For example:
+
+.. code-block:: genshi
+
+ <div>
+ <p py:def="greeting" class="greeting">
+ Hello, world!
+ </p>
+ ${greeting()}
+ </div>
+
+The above would be rendered to:
+
+.. code-block:: xml
+
+ <div>
+ <p class="greeting">
+ Hello, world!
+ </p>
+ </div>
+
+This directive can also be used as an element:
+
+.. code-block:: genshi
+
+ <div>
+ <py:def function="greeting(name)">
+ <p class="greeting">Hello, ${name}!</p>
+ </py:def>
+ </div>
+
+
+.. _Match Templates:
+.. _`py:match`:
+
+``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”:
+
+.. code-block:: genshi
+
+ <div>
+ <span py:match="greeting">
+ Hello ${select('@name')}
+ </span>
+ <greeting name="Dude" />
+ </div>
+
+This would result in the following output:
+
+.. code-block:: xml
+
+ <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 `Using XPath`_ for more information
+about this function.
+
+.. _`Using XPath`: streams.html#using-xpath
+
+Match templates are applied both to the original markup as well to the
+generated markup. The order in which they are applied depends on the order
+they are declared in the template source: a match template defined after
+another match template is applied to the output generated by the first match
+template. The match templates basically form a pipeline.
+
+This directive can also be used as an element:
+
+.. code-block:: genshi
+
+ <div>
+ <py:match path="greeting">
+ <span>Hello ${select('@name')}</span>
+ </py:match>
+ <greeting name="Dude" />
+ </div>
+
+When used this way, the ``py:match`` directive can also be annotated with a
+couple of optimization hints. For example, the following informs the matching
+engine that the match should only be applied once:
+
+.. code-block:: genshi
+
+ <py:match path="body" once="true">
+ <body py:attrs="select('@*')">
+ <div id="header">...</div>
+ ${select("*|text()")}
+ <div id="footer">...</div>
+ </body>
+ </py:match>
+
+The following optimization hints are recognized:
+
++---------------+-----------+-----------------------------------------------+
+| Attribute | Default | Description |
++===============+===========+===============================================+
+| ``buffer`` | ``true`` | Whether the matched content should be |
+| | | buffered in memory. Buffering can improve |
+| | | performance a bit at the cost of needing more |
+| | | memory during rendering. Buffering is |
+| | | ''required'' for match templates that contain |
+| | | more than one invocation of the ``select()`` |
+| | | function. If there is only one call, and the |
+| | | matched content can potentially be very long, |
+| | | consider disabling buffering to avoid |
+| | | excessive memory use. |
++---------------+-----------+-----------------------------------------------+
+| ``once`` | ``false`` | Whether the engine should stop looking for |
+| | | more matching elements after the first match. |
+| | | Use this on match templates that match |
+| | | elements that can only occur once in the |
+| | | stream, such as the ``<head>`` or ``<body>`` |
+| | | elements in an HTML template, or elements |
+| | | with a specific ID. |
++---------------+-----------+-----------------------------------------------+
+| ``recursive`` | ``true`` | Whether the match template should be applied |
+| | | to its own output. Note that ``once`` implies |
+| | | non-recursive behavior, so this attribute |
+| | | only needs to be set for match templates that |
+| | | don't also have ``once`` set. |
++---------------+-----------+-----------------------------------------------+
+
+.. note:: The ``py:match`` optimization hints were added in the 0.5 release. In
+ earlier versions, the attributes have no effect.
+
+
+Variable Binding
+================
+
+.. _`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:
+
+.. code-block:: genshi
+
+ <div>
+ <span py:with="y=7; z=x+10">$x $y $z</span>
+ </div>
+
+Given ``x=42`` in the context data, this would produce:
+
+.. code-block:: xml
+
+ <div>
+ <span>42 7 52</span>
+ </div>
+
+This directive can also be used as an element:
+
+.. code-block:: genshi
+
+ <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 Genshi.
+
+
+Structure Manipulation
+======================
+
+.. _`py:attrs`:
+
+``py:attrs``
+------------
+
+This directive adds, modifies or removes attributes from the element:
+
+.. code-block:: genshi
+
+ <ul>
+ <li py:attrs="foo">Bar</li>
+ </ul>
+
+Given ``foo={'class': 'collapse'}`` in the template context, this would
+produce:
+
+.. code-block:: xml
+
+ <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:
+
+.. code-block:: xml
+
+ <ul>
+ <li>Bar</li>
+ </ul>
+
+This directive can only be used as an attribute.
+
+
+.. _`py:content`:
+
+``py:content``
+--------------
+
+This directive replaces any nested content with the result of evaluating the
+expression:
+
+.. code-block:: genshi
+
+ <ul>
+ <li py:content="bar">Hello</li>
+ </ul>
+
+Given ``bar='Bye'`` in the context data, this would produce:
+
+.. code-block:: xml
+
+ <ul>
+ <li>Bye</li>
+ </ul>
+
+This directive can only be used as an attribute.
+
+
+.. _`py:replace`:
+
+``py:replace``
+--------------
+
+This directive replaces the element itself with the result of evaluating the
+expression:
+
+.. code-block:: genshi
+
+ <div>
+ <span py:replace="bar">Hello</span>
+ </div>
+
+Given ``bar='Bye'`` in the context data, this would produce:
+
+.. code-block:: xml
+
+ <div>
+ Bye
+ </div>
+
+This directive can also be used as an element (since version 0.5):
+
+.. code-block:: genshi
+
+ <div>
+ <py:replace value="title">Placeholder</py:replace>
+ </div>
+
+
+
+.. _`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:
+
+.. code-block:: genshi
+
+ <div>
+ <div py:strip="True"><b>foo</b></div>
+ </div>
+
+This would be rendered as:
+
+.. code-block:: xml
+
+ <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).
+
+
+.. _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:
+
+.. code-block:: genshi
+
+ <html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.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:
+
+.. code-block:: genshi
+
+ <xi:include href="base.html"><xi:fallback /></xi:include>
+
+See the `XInclude specification`_ for more about fallback content. Note though
+that Genshi currently only supports a small subset of XInclude.
+
+.. _`xinclude specification`: http://www.w3.org/TR/xinclude/
+
+
+Dynamic Includes
+================
+
+Incudes in Genshi 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:
+
+.. code-block:: genshi
+
+ <xi:include href="${name}.html" py:if="not in_popup"
+ py:for="name in ('foo', 'bar', 'baz')" />
+
+
+Including Text Templates
+========================
+
+The ``parse`` attribute of the ``<xi:include>`` element can be used to specify
+whether the included template is an XML template or a text template (using the
+new syntax added in Genshi 0.5):
+
+.. code-block:: genshi
+
+ <xi:include href="myscript.js" parse="text" />
+
+This example would load the ``myscript.js`` file as a ``NewTextTemplate``. See
+`text templates`_ for details on the syntax of text templates.
+
+.. _`text templates`: text-templates.html
+
+
+.. _comments:
+
+--------
+Comments
+--------
+
+Normal XML/HTML comment syntax can be used in templates:
+
+.. code-block:: genshi
+
+ <!-- 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:
+
+.. code-block:: genshi
+
+ <!-- !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:
+
+.. code-block:: genshi
+
+ <!--! this is a comment too, but one that will be stripped from the output -->
diff --git a/lib/genshi/doc/xpath.txt b/lib/genshi/doc/xpath.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/doc/xpath.txt
@@ -0,0 +1,101 @@
+.. -*- mode: rst; encoding: utf-8 -*-
+
+=====================
+Using XPath in Genshi
+=====================
+
+Genshi 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, Genshi 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 Genshi
+(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 sub-expressions and the various comparison and logical
+operators should work as expected.
+
+You can also use XPath variable references (``$var``) inside predicates.
+
+
+----------------
+Querying Streams
+----------------
+
+The ``Stream`` class provides a ``select(path)`` function that can be used to
+retrieve subsets of the stream:
+
+.. code-block:: pycon
+
+ >>> from genshi.input import XML
+
+ >>> doc = XML('''<doc>
+ ... <items count="4">
+ ... <item status="new">
+ ... <summary>Foo</summary>
+ ... </item>
+ ... <item status="closed">
+ ... <summary>Bar</summary>
+ ... </item>
+ ... <item status="closed" resolution="invalid">
+ ... <summary>Baz</summary>
+ ... </item>
+ ... <item status="closed" resolution="fixed">
+ ... <summary>Waz</summary>
+ ... </item>
+ ... </items>
+ ... </doc>''')
+
+ >>> print(doc.select('items/item[@status="closed" and '
+ ... '(@resolution="invalid" or not(@resolution))]/summary/text()'))
+ BarBaz
+
+
+
+---------------------
+Matching in Templates
+---------------------
+
+See the directive ``py:match`` in the `XML Template Language Specification`_.
+
+.. _`XML Template Language Specification`: xml-templates.html
diff --git a/lib/genshi/examples/basic/kidrun.py b/lib/genshi/examples/basic/kidrun.py
new file mode 100755
--- /dev/null
+++ b/lib/genshi/examples/basic/kidrun.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+import time
+
+import kid
+
+def test():
+ base_path = os.path.dirname(os.path.abspath(__file__))
+ kid.path = kid.TemplatePath([base_path])
+
+ ctxt = dict(hello='<world>', hey='ZYX', bozz=None,
+ items=['Number %d' % num for num in range(1, 15)],
+ prefix='#')
+
+ start = time.clock()
+ template = kid.Template(file='test.kid', **ctxt)
+ print ' --> parse stage: %.4f ms' % ((time.clock() - start) * 1000)
+
+ for output in template.generate():
+ sys.stdout.write(output)
+ print
+
+ times = []
+ for i in range(1000):
+ start = time.clock()
+ list(template.generate())
+ times.append(time.clock() - start)
+ sys.stdout.write('.')
+ sys.stdout.flush()
+ print
+
+ print ' --> render stage: %s ms (average)' % (
+ (sum(times) / len(times) * 1000))
+
+if __name__ == '__main__':
+ if '-p' in sys.argv:
+ import hotshot, hotshot.stats
+ prof = hotshot.Profile("template.prof")
+ benchtime = prof.runcall(test)
+ stats = hotshot.stats.load("template.prof")
+ stats.strip_dirs()
+ stats.sort_stats('time', 'calls')
+ stats.print_stats()
+ else:
+ test()
diff --git a/lib/genshi/examples/basic/layout.html b/lib/genshi/examples/basic/layout.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/basic/layout.html
@@ -0,0 +1,15 @@
+<div xmlns:py="http://genshi.edgewall.org/" py:strip="">
+ <head>
+ <title>Hello ${hello}</title>
+ <style type="text/css">@import(style.css)</style>
+ </head>
+ <div py:def="macro1">reference me, please</div>
+ <div py:def="macro2(name, classname='expanded')" class="${classname}">
+ Hello ${name.title()}
+ </div>
+ <span py:match="greeting" class="greeting">
+ Hello ${select('@name')}
+ </span>
+ <span py:match="span[@class='greeting']" style="text-decoration: underline"
+ py:content="select('text()')"/>
+</div>
diff --git a/lib/genshi/examples/basic/layout.kid b/lib/genshi/examples/basic/layout.kid
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/basic/layout.kid
@@ -0,0 +1,15 @@
+<div xmlns:py="http://purl.org/kid/ns#" py:strip="">
+ <head>
+ <title>Hello ${hello}</title>
+ <style type="text/css">@import(style.css)</style>
+ </head>
+ <div py:def="macro1">reference me, please</div>
+ <div py:def="macro2(name, classname='expanded')" class="${classname}">
+ Hello ${name.title()}
+ </div>
+ <span py:match="item.tag == '{http://www.w3.org/1999/xhtml}greeting'" class="greeting">
+ Hello ${item.get('name')}
+ </span>
+ <span py:match="item.tag == '{http://www.w3.org/1999/xhtml}span' and item.get('class') == 'greeting'"
+ py:content="item.text" style="text-decoration: underline" />
+</div>
diff --git a/lib/genshi/examples/basic/run.py b/lib/genshi/examples/basic/run.py
new file mode 100755
--- /dev/null
+++ b/lib/genshi/examples/basic/run.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+import time
+
+from genshi.template import TemplateLoader
+
+def test():
+ base_path = os.path.dirname(os.path.abspath(__file__))
+ loader = TemplateLoader([base_path], auto_reload=True)
+
+ start = time.clock()
+ tmpl = loader.load('test.html')
+ print ' --> parse stage: %.4f ms' % ((time.clock() - start) * 1000)
+
+ data = dict(hello='<world>', skin='default', hey='ZYX', bozz=None,
+ items=['Number %d' % num for num in range(1, 15)],
+ prefix='#')
+
+ print tmpl.generate(**data).render(method='html')
+
+ times = []
+ for i in range(1000):
+ start = time.clock()
+ list(tmpl.generate(**data))
+ times.append(time.clock() - start)
+ sys.stdout.write('.')
+ sys.stdout.flush()
+ print
+
+ print ' --> render stage: %s ms (average)' % (
+ (sum(times) / len(times) * 1000))
+
+if __name__ == '__main__':
+ if '-p' in sys.argv:
+ import hotshot, hotshot.stats
+ prof = hotshot.Profile("template.prof")
+ benchtime = prof.runcall(test)
+ stats = hotshot.stats.load("template.prof")
+ stats.strip_dirs()
+ stats.sort_stats('time', 'calls')
+ stats.print_stats()
+ else:
+ test()
diff --git a/lib/genshi/examples/basic/test.html b/lib/genshi/examples/basic/test.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/basic/test.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ lang="en">
+ <xi:include href="layout.html" />
+ <xi:include href="custom_stuff.html"><xi:fallback/></xi:include>
+ <body class="$bozz">
+ <ul py:attrs="{'id': 'second', 'class': None}" py:if="len(items) > 0">
+ <li py:for="item in items">Item $prefix${item.split()[-1]}</li>
+ </ul>
+ ${macro1()} ${macro1()} ${macro1()}
+ ${macro2('john')}
+ ${macro2('kate', classname='collapsed')}
+ <div py:content="macro2('helmut')" py:strip="">Replace me</div>
+ <greeting name="Dude" />
+ <greeting name="King" />
+ <span class="greeting">Hello Silicon</span>
+ </body>
+</html>
diff --git a/lib/genshi/examples/basic/test.kid b/lib/genshi/examples/basic/test.kid
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/basic/test.kid
@@ -0,0 +1,21 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'layout.kid'"
+ lang="en">
+ <body class="${bozz}">
+ <ul py:attrs="{'id': 'second', 'class': None}" py:if="len(items) > 0">
+ <li py:for="item in items">Item $prefix${item.split()[-1]}</li>
+ XYZ ${hey}
+ </ul>
+ ${macro1()} ${macro1()} ${macro1()}
+ ${macro2('john')}
+ ${macro2('kate', classname='collapsed')}
+ <div py:content="macro2('helmut')" py:strip="">Replace me</div>
+ <greeting name="Dude" />
+ <greeting name="King" />
+ <span class="greeting">Hello Silicon</span>
+ </body>
+</html>
diff --git a/lib/genshi/examples/bench/basic.py b/lib/genshi/examples/bench/basic.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/basic.py
@@ -0,0 +1,204 @@
+# -*- encoding: utf-8 -*-
+# Template language benchmarks
+#
+# Objective: Test general templating features using a small template
+
+from cgi import escape
+import os
+from StringIO import StringIO
+import sys
+import timeit
+
+__all__ = ['clearsilver', 'mako', 'django', 'kid', 'genshi', 'genshi_text',
+ 'simpletal']
+
+def genshi(dirname, verbose=False):
+ from genshi.template import TemplateLoader
+ loader = TemplateLoader([dirname], auto_reload=False)
+ template = loader.load('template.html')
+ def render():
+ data = dict(title='Just a test', user='joe',
+ items=['Number %d' % num for num in range(1, 15)])
+ return template.generate(**data).render('xhtml')
+
+ if verbose:
+ print render()
+ return render
+
+def genshi_text(dirname, verbose=False):
+ from genshi.core import escape
+ from genshi.template import TemplateLoader, NewTextTemplate
+ loader = TemplateLoader([dirname], auto_reload=False)
+ template = loader.load('template.txt', cls=NewTextTemplate)
+ def render():
+ data = dict(escape=escape, title='Just a test', user='joe',
+ items=['Number %d' % num for num in range(1, 15)])
+ return template.generate(**data).render('text')
+
+ if verbose:
+ print render()
+ return render
+
+def mako(dirname, verbose=False):
+ from mako.lookup import TemplateLookup
+ lookup = TemplateLookup(directories=[dirname], filesystem_checks=False)
+ template = lookup.get_template('template.html')
+ def render():
+ data = dict(title='Just a test', user='joe',
+ list_items=['Number %d' % num for num in range(1, 15)])
+ return template.render(**data)
+ if verbose:
+ print render()
+ return render
+
+def cheetah(dirname, verbose=False):
+ # FIXME: infinite recursion somewhere... WTF?
+ try:
+ from Cheetah.Template import Template
+ except ImportError:
+ print>>sys.stderr, 'Cheetah not installed, skipping'
+ return lambda: None
+ class MyTemplate(Template):
+ def serverSidePath(self, path): return os.path.join(dirname, path)
+ filename = os.path.join(dirname, 'template.tmpl')
+ template = MyTemplate(file=filename)
+
+ def render():
+ template = MyTemplate(file=filename,
+ searchList=[{'title': 'Just a test', 'user': 'joe',
+ 'items': [u'Number %d' % num for num in range(1, 15)]}])
+ return template.respond()
+
+ if verbose:
+ print render()
+ return render
+
+def clearsilver(dirname, verbose=False):
+ try:
+ import neo_cgi
+ except ImportError:
+ print>>sys.stderr, 'ClearSilver not installed, skipping'
+ return lambda: None
+ neo_cgi.update()
+ import neo_util
+ import neo_cs
+ def render():
+ hdf = neo_util.HDF()
+ hdf.setValue('hdf.loadpaths.0', dirname)
+ hdf.setValue('title', escape('Just a test'))
+ hdf.setValue('user', escape('joe'))
+ for num in range(1, 15):
+ hdf.setValue('items.%d' % (num - 1), escape('Number %d' % num))
+ cs = neo_cs.CS(hdf)
+ cs.parseFile('template.cs')
+ return cs.render()
+
+ if verbose:
+ print render()
+ return render
+
+def django(dirname, verbose=False):
+ try:
+ from django.conf import settings
+ settings.configure(TEMPLATE_DIRS=[os.path.join(dirname, 'templates')])
+ except ImportError:
+ print>>sys.stderr, 'Django not installed, skipping'
+ return lambda: None
+ from django import template, templatetags
+ from django.template import loader
+ templatetags.__path__.append(os.path.join(dirname, 'templatetags'))
+ tmpl = loader.get_template('template.html')
+
+ def render():
+ data = {'title': 'Just a test', 'user': 'joe',
+ 'items': ['Number %d' % num for num in range(1, 15)]}
+ return tmpl.render(template.Context(data))
+
+ if verbose:
+ print render()
+ return render
+
+def kid(dirname, verbose=False):
+ try:
+ import kid
+ except ImportError:
+ print>>sys.stderr, "Kid not installed, skipping"
+ return lambda: None
+ kid.path = kid.TemplatePath([dirname])
+ template = kid.load_template('template.kid').Template
+ def render():
+ return template(
+ title='Just a test', user='joe',
+ items=['Number %d' % num for num in range(1, 15)]
+ ).serialize(output='xhtml')
+
+ if verbose:
+ print render()
+ return render
+
+def simpletal(dirname, verbose=False):
+ try:
+ from simpletal import simpleTAL, simpleTALES
+ except ImportError:
+ print>>sys.stderr, "SimpleTAL not installed, skipping"
+ return lambda: None
+ fileobj = open(os.path.join(dirname, 'base.html'))
+ base = simpleTAL.compileHTMLTemplate(fileobj)
+ fileobj.close()
+ fileobj = open(os.path.join(dirname, 'template.html'))
+ template = simpleTAL.compileHTMLTemplate(fileobj)
+ fileobj.close()
+ def render():
+ ctxt = simpleTALES.Context(allowPythonPath=1)
+ ctxt.addGlobal('base', base)
+ ctxt.addGlobal('title', 'Just a test')
+ ctxt.addGlobal('user', 'joe')
+ ctxt.addGlobal('items', ['Number %d' % num for num in range(1, 15)])
+ buf = StringIO()
+ template.expand(ctxt, buf)
+ return buf.getvalue()
+
+ if verbose:
+ print render()
+ return render
+
+def run(engines, number=2000, verbose=False):
+ basepath = os.path.abspath(os.path.dirname(__file__))
+ for engine in engines:
+ dirname = os.path.join(basepath, engine)
+ if verbose:
+ print '%s:' % engine.capitalize()
+ print '--------------------------------------------------------'
+ else:
+ print '%s:' % engine.capitalize(),
+ t = timeit.Timer(setup='from __main__ import %s; render = %s(r"%s", %s)'
+ % (engine, engine, dirname, verbose),
+ stmt='render()')
+ time = t.timeit(number=number) / number
+ if verbose:
+ print '--------------------------------------------------------'
+ print '%.2f ms' % (1000 * time)
+ if verbose:
+ print '--------------------------------------------------------'
+
+
+if __name__ == '__main__':
+ engines = [arg for arg in sys.argv[1:] if arg[0] != '-']
+ if not engines:
+ engines = __all__
+
+ verbose = '-v' in sys.argv
+
+ if '-p' in sys.argv:
+ import cProfile, pstats
+ prof = cProfile.Profile()
+ prof.run('run(%r, number=200, verbose=%r)' % (engines, verbose))
+ stats = pstats.Stats(prof)
+ stats.strip_dirs()
+ stats.sort_stats('calls')
+ stats.print_stats(25)
+ if verbose:
+ stats.print_callees()
+ stats.print_callers()
+ else:
+ run(engines, verbose=verbose)
diff --git a/lib/genshi/examples/bench/bigtable.py b/lib/genshi/examples/bench/bigtable.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/bigtable.py
@@ -0,0 +1,233 @@
+# -*- encoding: utf-8 -*-
+# Template language benchmarks
+#
+# Objective: Generate a 1000x10 HTML table as fast as possible.
+#
+# Author: Jonas Borgström <jonas at edgewall.com>
+
+import cgi
+import sys
+import timeit
+from StringIO import StringIO
+from genshi.builder import tag
+from genshi.template import MarkupTemplate, NewTextTemplate
+
+try:
+ from elementtree import ElementTree as et
+except ImportError:
+ et = None
+
+try:
+ import cElementTree as cet
+except ImportError:
+ cet = None
+
+try:
+ import neo_cgi, neo_cs, neo_util
+except ImportError:
+ neo_cgi = None
+
+try:
+ import kid
+except ImportError:
+ kid = None
+
+try:
+ from django.conf import settings
+ settings.configure()
+ from django.template import Context as DjangoContext
+ from django.template import Template as DjangoTemplate
+except ImportError:
+ DjangoContext = DjangoTemplate = None
+
+try:
+ from mako.template import Template as MakoTemplate
+except ImportError:
+ MakoTemplate = None
+
+table = [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10)
+ for x in range(1000)]
+
+genshi_tmpl = MarkupTemplate("""
+<table xmlns:py="http://genshi.edgewall.org/">
+<tr py:for="row in table">
+<td py:for="c in row.values()" py:content="c"/>
+</tr>
+</table>
+""")
+
+genshi_tmpl2 = MarkupTemplate("""
+<table xmlns:py="http://genshi.edgewall.org/">$table</table>
+""")
+
+genshi_text_tmpl = NewTextTemplate("""
+<table>
+{% for row in table %}<tr>
+{% for c in row.values() %}<td>$c</td>{% end %}
+</tr>{% end %}
+</table>
+""")
+
+if DjangoTemplate:
+ django_tmpl = DjangoTemplate("""
+ <table>
+ {% for row in table %}
+ <tr>{% for col in row.values %}{{ col|escape }}{% endfor %}</tr>
+ {% endfor %}
+ </table>
+ """)
+
+ def test_django():
+ """Djange template"""
+ context = DjangoContext({'table': table})
+ django_tmpl.render(context)
+
+if MakoTemplate:
+ mako_tmpl = MakoTemplate("""
+<table>
+ % for row in table:
+ <tr>
+ % for col in row.values():
+ <td>${ col | h }</td>
+ % endfor
+ </tr>
+ % endfor
+</table>
+""")
+ def test_mako():
+ """Mako Template"""
+ mako_tmpl.render(table=table)
+
+def test_genshi():
+ """Genshi template"""
+ stream = genshi_tmpl.generate(table=table)
+ stream.render('html', strip_whitespace=False)
+
+def test_genshi_text():
+ """Genshi text template"""
+ stream = genshi_text_tmpl.generate(table=table)
+ stream.render('text')
+
+def test_genshi_builder():
+ """Genshi template + tag builder"""
+ stream = tag.TABLE([
+ tag.tr([tag.td(c) for c in row.values()])
+ for row in table
+ ]).generate()
+ stream = genshi_tmpl2.generate(table=stream)
+ stream.render('html', strip_whitespace=False)
+
+def test_builder():
+ """Genshi tag builder"""
+ stream = tag.TABLE([
+ tag.tr([
+ tag.td(c) for c in row.values()
+ ])
+ for row in table
+ ]).generate()
+ stream.render('html', strip_whitespace=False)
+
+if kid:
+ kid_tmpl = kid.Template("""
+ <table xmlns:py="http://purl.org/kid/ns#">
+ <tr py:for="row in table">
+ <td py:for="c in row.values()" py:content="c"/>
+ </tr>
+ </table>
+ """)
+
+ kid_tmpl2 = kid.Template("""
+ <html xmlns:py="http://purl.org/kid/ns#">$table</html>
+ """)
+
+ def test_kid():
+ """Kid template"""
+ kid_tmpl.table = table
+ kid_tmpl.serialize(output='html')
+
+ if cet:
+ def test_kid_et():
+ """Kid template + cElementTree"""
+ _table = cet.Element('table')
+ for row in table:
+ td = cet.SubElement(_table, 'tr')
+ for c in row.values():
+ cet.SubElement(td, 'td').text=str(c)
+ kid_tmpl2.table = _table
+ kid_tmpl2.serialize(output='html')
+
+if et:
+ def test_et():
+ """ElementTree"""
+ _table = et.Element('table')
+ for row in table:
+ tr = et.SubElement(_table, 'tr')
+ for c in row.values():
+ et.SubElement(tr, 'td').text=str(c)
+ et.tostring(_table)
+
+if cet:
+ def test_cet():
+ """cElementTree"""
+ _table = cet.Element('table')
+ for row in table:
+ tr = cet.SubElement(_table, 'tr')
+ for c in row.values():
+ cet.SubElement(tr, 'td').text=str(c)
+ cet.tostring(_table)
+
+if neo_cgi:
+ def test_clearsilver():
+ """ClearSilver"""
+ hdf = neo_util.HDF()
+ for i, row in enumerate(table):
+ for j, c in enumerate(row.values()):
+ hdf.setValue("rows.%d.cell.%d" % (i, j), cgi.escape(str(c)))
+
+ cs = neo_cs.CS(hdf)
+ cs.parseStr("""
+<table><?cs
+ each:row=rows
+?><tr><?cs each:c=row.cell
+ ?><td><?cs var:c ?></td><?cs /each
+?></tr><?cs /each?>
+</table>""")
+ cs.render()
+
+
+def run(which=None, number=10):
+ tests = ['test_builder', 'test_genshi', 'test_genshi_text',
+ 'test_genshi_builder', 'test_mako', 'test_kid', 'test_kid_et',
+ 'test_et', 'test_cet', 'test_clearsilver', 'test_django']
+
+ if which:
+ tests = filter(lambda n: n[5:] in which, tests)
+
+ for test in [t for t in tests if hasattr(sys.modules[__name__], t)]:
+ t = timeit.Timer(setup='from __main__ import %s;' % test,
+ stmt='%s()' % test)
+ time = t.timeit(number=number) / number
+
+ if time < 0.00001:
+ result = ' (not installed?)'
+ else:
+ result = '%16.2f ms' % (1000 * time)
+ print '%-35s %s' % (getattr(sys.modules[__name__], test).__doc__, result)
+
+
+if __name__ == '__main__':
+ which = [arg for arg in sys.argv[1:] if arg[0] != '-']
+
+ if '-p' in sys.argv:
+ import cProfile, pstats
+ prof = cProfile.Profile()
+ prof.run('run(%r, number=1)' % which)
+ stats = pstats.Stats(prof)
+ stats.strip_dirs()
+ stats.sort_stats('time', 'calls')
+ stats.print_stats(25)
+ if '-v' in sys.argv:
+ stats.print_callees()
+ stats.print_callers()
+ else:
+ run(which)
diff --git a/lib/genshi/examples/bench/cheetah/footer.tmpl b/lib/genshi/examples/bench/cheetah/footer.tmpl
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/cheetah/footer.tmpl
@@ -0,0 +1,2 @@
+<div id="footer">
+</div>
diff --git a/lib/genshi/examples/bench/cheetah/header.tmpl b/lib/genshi/examples/bench/cheetah/header.tmpl
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/cheetah/header.tmpl
@@ -0,0 +1,3 @@
+<div id="header">
+ <h1>$title</h1>
+</div>
diff --git a/lib/genshi/examples/bench/cheetah/template.tmpl b/lib/genshi/examples/bench/cheetah/template.tmpl
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/cheetah/template.tmpl
@@ -0,0 +1,22 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+ <head>
+ <title>${title}</title>
+ </head>
+ <body>
+ #include "cheetah/header.tmpl"
+
+ <h2>Loop</h2>
+ #if $items
+ <ul>
+ #for $item in $items
+ <li>$item</li>
+ #end for
+ </ul>
+ #end if
+
+ #include "cheetah/footer.tmpl"
+ </body>
+</html>
diff --git a/lib/genshi/examples/bench/clearsilver/footer.cs b/lib/genshi/examples/bench/clearsilver/footer.cs
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/clearsilver/footer.cs
@@ -0,0 +1,2 @@
+<div id="footer">
+</div>
diff --git a/lib/genshi/examples/bench/clearsilver/header.cs b/lib/genshi/examples/bench/clearsilver/header.cs
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/clearsilver/header.cs
@@ -0,0 +1,7 @@
+<div id="header">
+ <h1><?cs var:title ?></h1>
+</div>
+
+<?cs def:greeting(name) ?>
+ <p>Hello, <?cs var:name ?>!</p>
+<?cs /def ?>
diff --git a/lib/genshi/examples/bench/clearsilver/template.cs b/lib/genshi/examples/bench/clearsilver/template.cs
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/clearsilver/template.cs
@@ -0,0 +1,25 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+ <head>
+ <title><?cs var:title ?></title>
+ </head>
+ <body>
+ <?cs include:"header.cs" ?>
+ <?cs call:greeting(user) ?>
+ <?cs call:greeting('me') ?>
+ <?cs call:greeting('world') ?>
+
+ <h2>Loop</h2>
+ <?cs if:len(items) ?>
+ <ul>
+ <?cs each:item = items ?>
+ <li<?cs if:name(item) == len(items) ?> class="last"<?cs /if ?>><?cs var:item ?></li>
+ <?cs /each ?>
+ </ul>
+ <?cs /if ?>
+
+ <?cs include:"footer.cs" ?>
+ </body>
+</html>
diff --git a/lib/genshi/examples/bench/django/templates/base.html b/lib/genshi/examples/bench/django/templates/base.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/django/templates/base.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+
+ {% block content %}
+ {% block header %}
+ <div id="header">
+ <h1>{{ title|escape }}</h1>
+ </div>
+ {% endblock %}
+
+ {% block footer %}
+ <div id="footer"></div>
+ {% endblock %}
+ {% endblock %}
+
+</html>
diff --git a/lib/genshi/examples/bench/django/templates/template.html b/lib/genshi/examples/bench/django/templates/template.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/django/templates/template.html
@@ -0,0 +1,27 @@
+{% extends "base.html" %}
+{% load bench %}
+
+{% block content %}
+ <head>
+ <title>{{title|escape}}</title>
+ </head>
+
+ <body>
+ {% block header %}{% endblock %}
+
+ <div>{% greeting user %}</div>
+ <div>{% greeting "me" %}</div>
+ <div>{% greeting "world" %}</div>
+
+ <h2>Loop</h2>
+ {% if items %}
+ <ul>
+ {% for item in items %}
+ <li{% if forloop.last %} class="last"{% endif %}>{{ item|escape }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+
+ {% block footer %}{% endblock %}
+ </body>
+{% endblock %}
diff --git a/lib/genshi/examples/bench/django/templatetags/__init__.py b/lib/genshi/examples/bench/django/templatetags/__init__.py
new file mode 100644
diff --git a/lib/genshi/examples/bench/django/templatetags/bench.py b/lib/genshi/examples/bench/django/templatetags/bench.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/django/templatetags/bench.py
@@ -0,0 +1,8 @@
+from django.template import Library, Node, resolve_variable
+from django.utils.html import escape
+
+register = Library()
+
+def greeting(name):
+ return 'Hello, %s!' % escape(name)
+greeting = register.simple_tag(greeting)
diff --git a/lib/genshi/examples/bench/genshi/base.html b/lib/genshi/examples/bench/genshi/base.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/genshi/base.html
@@ -0,0 +1,17 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ py:strip="">
+
+ <p py:def="greeting(name)">
+ Hello, ${name}!
+ </p>
+
+ <py:match path="body" once="true"><body>
+ <div id="header">
+ <h1>${title}</h1>
+ </div>
+ ${select('*')}
+ <div id="footer" />
+ </body></py:match>
+
+</html>
diff --git a/lib/genshi/examples/bench/genshi/template.html b/lib/genshi/examples/bench/genshi/template.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/genshi/template.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ lang="en">
+ <xi:include href="base.html" />
+ <head>
+ <title>${title}</title>
+ </head>
+ <body>
+ <div>${greeting(user)}</div>
+ <div>${greeting('me')}</div>
+ <div>${greeting('world')}</div>
+
+ <h2>Loop</h2>
+ <ul py:if="items">
+ <li py:for="idx, item in enumerate(items)" py:content="item"
+ class="${idx + 1 == len(items) and 'last' or None}" />
+ </ul>
+
+ </body>
+</html>
diff --git a/lib/genshi/examples/bench/genshi_text/footer.txt b/lib/genshi/examples/bench/genshi_text/footer.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/genshi_text/footer.txt
@@ -0,0 +1,1 @@
+<div id="footer"></div>
diff --git a/lib/genshi/examples/bench/genshi_text/header.txt b/lib/genshi/examples/bench/genshi_text/header.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/genshi_text/header.txt
@@ -0,0 +1,3 @@
+<div id="header">
+ <h1>${escape(title)}</h1>
+</div>
diff --git a/lib/genshi/examples/bench/genshi_text/template.txt b/lib/genshi/examples/bench/genshi_text/template.txt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/genshi_text/template.txt
@@ -0,0 +1,28 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+
+<head>
+ <title>${escape(title)}</title>
+</head>
+
+<body>
+ {% include header.txt %}
+ {% def greeting(name) %}
+ Hello, ${name}!
+ {% end %}
+
+ <div>${greeting(user)}</div>
+ <div>${greeting("me")}</div>
+ <div>${greeting("world")}</div>
+
+ <h2>Loop</h2>
+ {% if items %}<ul>
+ {% for idx, item in enumerate(items) %}\
+ <li{% if idx + 1 == len(items) %} class="last"{% end %}>${escape(item)}</li>
+ {% end %}
+ </ul>{% end %}
+
+ {% include footer.txt %}
+</body>
diff --git a/lib/genshi/examples/bench/kid/base.kid b/lib/genshi/examples/bench/kid/base.kid
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/kid/base.kid
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://purl.org/kid/ns#">
+
+ <p py:def="greeting(name)">
+ Hello, ${name}!
+ </p>
+
+ <body py:match="item.tag == '{http://www.w3.org/1999/xhtml}body'" py:strip="">
+ <div id="header">
+ <h1>${title}</h1>
+ </div>
+ ${item}
+ <div id="footer" />
+ </body>
+</html>
diff --git a/lib/genshi/examples/bench/kid/template.kid b/lib/genshi/examples/bench/kid/template.kid
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/kid/template.kid
@@ -0,0 +1,22 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://purl.org/kid/ns#"
+ py:extends="'base.kid'"
+ lang="en">
+ <head>
+ <title>${title}</title>
+ </head>
+ <body>
+ <div>${greeting(user)}</div>
+ <div>${greeting('me')}</div>
+ <div>${greeting('world')}</div>
+
+ <h2>Loop</h2>
+ <ul py:if="items">
+ <li py:for="idx, item in enumerate(items)" py:content="item"
+ class="${idx + 1 == len(items) and 'last' or None}" />
+ </ul>
+ </body>
+</html>
diff --git a/lib/genshi/examples/bench/mako/footer.html b/lib/genshi/examples/bench/mako/footer.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/mako/footer.html
@@ -0,0 +1,2 @@
+<div id="footer">
+</div>
diff --git a/lib/genshi/examples/bench/mako/header.html b/lib/genshi/examples/bench/mako/header.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/mako/header.html
@@ -0,0 +1,5 @@
+<div id="header">
+ <h1>${title | h}</h1>
+</div>
+
+
diff --git a/lib/genshi/examples/bench/mako/template.html b/lib/genshi/examples/bench/mako/template.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/mako/template.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+ <head>
+ <title>${title}</title>
+ </head>
+ <body>
+ <%def name="greeting(name)">
+ <p>Hello, ${name | h}!</p>
+ </%def>
+
+ <%include file="header.html"/>
+
+ ${greeting(user)}
+ ${greeting('me')}
+ ${greeting('world')}
+
+ <h2>Loop</h2>
+ % if items:
+ <ul>
+ % for idx, item in enumerate(items):
+ <li ${idx+1==len(items) and "class='last'" or ""}>${item | h}</li>
+ % endfor
+ </ul>
+ % endif
+ <%include file="footer.html"/>
+ </body>
+</html>
diff --git a/lib/genshi/examples/bench/myghty/base.myt b/lib/genshi/examples/bench/myghty/base.myt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/myghty/base.myt
@@ -0,0 +1,26 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+
+<%method wrap>
+<%args scope="request">
+ title
+</%args>
+<div id="header">
+ <h1><% title %></h1>
+</div>
+
+<% m.content() %>
+
+</%method>
+
+<div id="footer"></div>
+</html>
+
+<%method greeting>
+<%args>
+ name
+</%args>
+Hello, <% name | h %>
+</%method>
diff --git a/lib/genshi/examples/bench/myghty/template.myt b/lib/genshi/examples/bench/myghty/template.myt
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/myghty/template.myt
@@ -0,0 +1,25 @@
+<%args>
+ title
+ items
+ user
+</%args>
+
+<head>
+ <title><% title %></title>
+</head>
+
+ <&|base.myt:wrap &>
+ <div><& base.myt:greeting, name=user &></div>
+ <div><& base.myt:greeting, name="me"&></div>
+ <div><& base.myt:greeting, name="world" &></div>
+
+ <h2>Loop</h2>
+%if items:
+ <ul>
+% for item in items:
+ <li><% item %></li>
+%
+ </ul>
+%
+
+ </&>
diff --git a/lib/genshi/examples/bench/simpletal/base.html b/lib/genshi/examples/bench/simpletal/base.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/simpletal/base.html
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+
+ <p metal:define-macro="greeting">
+ Hello, <span metal:define-slot="name">name</span>
+ </p>
+
+ <body metal:define-macro="content">
+ <div id="header">
+ <h1 tal:content="title" />
+ </div>
+ <div metal:define-slot="content" tal:omit-tag="" />
+ <div id="footer"></div>
+ </body>
+
+</html>
diff --git a/lib/genshi/examples/bench/simpletal/template.html b/lib/genshi/examples/bench/simpletal/template.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/simpletal/template.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+ <head>
+ <title tal:content="title">Title</title>
+ </head>
+ <body metal:use-macro="base/macros/content">
+ <div metal:fill-slot="content">
+
+ <div metal:use-macro="base/macros/greeting">
+ <span metal:fill-slot="name" tal:content="user" tal:omit-tag=""></span>
+ </div>
+ <div metal:use-macro="base/macros/greeting">
+ <span metal:fill-slot="name" tal:omit-tag="">me</span>
+ </div>
+ <div metal:use-macro="base/macros/greeting">
+ <span metal:fill-slot="name" tal:omit-tag="">world</span>
+ </div>
+
+ <h2>Loop</h2>
+ <ul tal:condition="items">
+ <li tal:repeat="item items" tal:content="item"
+ tal:attributes="class python:repeat['item'].getEnd() and 'last' or None">Item</li>
+ </ul>
+
+ </div>
+ </body>
+</html>
diff --git a/lib/genshi/examples/bench/xpath.py b/lib/genshi/examples/bench/xpath.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/bench/xpath.py
@@ -0,0 +1,170 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2008 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+
+try:
+ from os import times
+ def time_func():
+ tup = times()
+ #just user time
+ return tup[0] # + tup[1]
+except ImportError:
+ from time import time as time_func
+
+from genshi.core import START, END
+from genshi.path import Path
+from genshi.input import XML
+
+def benchmark(f, acurate_time=1):
+ """Checks how much time does function f work. It runs it as
+ many times as needed for avoiding inaccuracy"""
+
+ runs = 1
+ while True:
+ start_time = time_func()
+ for _ in xrange(runs):
+ f()
+ dt = time_func() - start_time
+ if dt >= acurate_time:
+ break
+ runs *= 2
+ return dt / runs
+
+def spell(t):
+ """Returns spelled representation of time"""
+ units = [(0.000001, 'microsecond', 'microseconds'),
+ (0.001, 'milisecond', 'miliseconds'),
+ (1, 'second', 'seconds'),
+ (60, 'minute', 'minutes'),
+ (60*60, 'hour', 'hours'),
+ ]
+ i = 0
+ at = abs(t)
+ while i + 1 < len(units) and at >= units[i + 1][0]:
+ i += 1
+ t /= units[i][0]
+ if t >= 2:
+ name = units[i][2]
+ else:
+ name = units[i][1]
+ return "%f %s"%(t, name)
+
+def test_paths_in_streams(exprs, streams, test_strategies=False):
+ for expr in exprs:
+ print "Testing path %r" % expr
+ for stream, sname in streams:
+ print '\tRunning on "%s" example:' % sname
+
+ path = Path(expr)
+ def f():
+ for e in path.select(stream):
+ pass
+ t = spell(benchmark(f))
+ print "\t\tselect:\t\t%s" % t
+
+ def f():
+ path = Path(expr)
+ for e in path.select(stream):
+ pass
+ t = spell(benchmark(f))
+ print "\t\tinit + select:\t%s" % t
+
+ if test_strategies and len(path.paths) == 1:
+ from genshi.path import GenericStrategy, SingleStepStrategy, \
+ SimplePathStrategy
+ from genshi.tests.path import FakePath
+ strategies = (GenericStrategy, SingleStepStrategy,
+ SimplePathStrategy)
+ for strategy in strategies:
+ if not strategy.supports(path.paths[0]):
+ continue
+ print "\t\t%s Strategy"%strategy.__name__
+ fp = FakePath(strategy(path.paths[0]))
+ def f():
+ for e in fp.select(stream):
+ pass
+ t = spell(benchmark(f))
+ print "\t\t\tselect:\t\t%s"%t
+
+
+def test_documents(test_strategies=False):
+ streams = []
+
+ s = XML("""\
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl" xmlns:py="http://genshi.edgewall.org/" py:strip="" lang="en">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Foo</title>
+ </head>
+ <body>
+ <h1>Hello</h1>
+ </body>
+</html>
+""")
+ streams.append((s, "small document"))
+
+ s = XML("""\
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl" xmlns:py="http://genshi.edgewall.org/" py:strip="" lang="en">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Foo</title>
+ </head>
+ <body>
+ <h1>Hello</h1>
+ <div id="splash">
+ <ul>
+ <li><a class="b1" href="http://genshi.edgewall.org/">
+ <strong>Genshi</strong>
+ Python toolkit for generating output for the web</a></li>
+ <li><a class="b2" href="http://babel.edgewall.org/">
+ <strong>Babel</strong>
+ Python library for I18n/L10n in web applications</a></li>
+ <li><a class="b3" href="http://bitten.edgewall.org/">
+ <strong>Bitten</strong>
+ Continuous integration plugin for Trac</a></li>
+ <li><a class="b4" href="http://posterity.edgewall.org/">
+ <strong>Posterity</strong>
+ Web-based email system</a></li>
+ </ul>
+ <div id="trac-splash">
+ <a href="http://trac.edgewall.org/">
+ <strong>Trac</strong> Web-based lightweight project management
+ system
+ </a>
+ </div>
+ </div>
+ </body>
+</html>
+""")
+ streams.append((s, "big document"))
+
+ paths = [
+ '.',
+ '*|text()',
+ 'html',
+ 'html[@lang="en"]',
+ 'html/body/h1/text()',
+ 'html/body/div/a/@href',
+ 'html/body/div[@id="splash"]/a[@class="b4"]/strong/text()',
+ 'descendant-or-self::text()',
+ 'descendant-or-self::h1/text()',
+ ]
+ test_paths_in_streams(paths, streams, test_strategies)
+
+if __name__ == '__main__':
+ from sys import argv
+ if "--strategies" in argv:
+ test_strategies = True
+ else:
+ test_strategies = False
+ test_documents(test_strategies)
diff --git a/lib/genshi/examples/tutorial/geddit/__init__.py b/lib/genshi/examples/tutorial/geddit/__init__.py
new file mode 100644
diff --git a/lib/genshi/examples/tutorial/geddit/controller.py b/lib/genshi/examples/tutorial/geddit/controller.py
new file mode 100755
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/controller.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+
+import operator, os, pickle, sys
+
+import cherrypy
+from formencode import Invalid
+from genshi.input import HTML
+from genshi.filters import HTMLFormFiller, HTMLSanitizer
+
+from geddit.form import LinkForm, CommentForm
+from geddit.lib import ajax, template
+from geddit.model import Link, Comment
+
+
+class Root(object):
+
+ def __init__(self, data):
+ self.data = data
+
+ @cherrypy.expose
+ @template.output('index.html')
+ def index(self):
+ links = sorted(self.data.values(), key=operator.attrgetter('time'))
+ return template.render(links=links)
+
+ @cherrypy.expose
+ @template.output('submit.html')
+ def submit(self, cancel=False, **data):
+ if cherrypy.request.method == 'POST':
+ if cancel:
+ raise cherrypy.HTTPRedirect('/')
+ form = LinkForm()
+ try:
+ data = form.to_python(data)
+ link = Link(**data)
+ self.data[link.id] = link
+ raise cherrypy.HTTPRedirect('/')
+ except Invalid, e:
+ errors = e.unpack_errors()
+ else:
+ errors = {}
+
+ return template.render(errors=errors) | HTMLFormFiller(data=data)
+
+ @cherrypy.expose
+ @template.output('info.html')
+ def info(self, id):
+ link = self.data.get(id)
+ if not link:
+ raise cherrypy.NotFound()
+ return template.render(link=link)
+
+ @cherrypy.expose
+ @template.output('comment.html')
+ def comment(self, id, cancel=False, **data):
+ link = self.data.get(id)
+ if not link:
+ raise cherrypy.NotFound()
+ if cherrypy.request.method == 'POST':
+ if cancel:
+ raise cherrypy.HTTPRedirect('/info/%s' % link.id)
+ form = CommentForm()
+ try:
+ data = form.to_python(data)
+ markup = HTML(data['content']) | HTMLSanitizer()
+ data['content'] = markup.render('xhtml')
+ comment = link.add_comment(**data)
+ if not ajax.is_xhr():
+ raise cherrypy.HTTPRedirect('/info/%s' % link.id)
+ return template.render('_comment.html', comment=comment,
+ num=len(link.comments))
+ except Invalid, e:
+ errors = e.unpack_errors()
+ else:
+ errors = {}
+
+ if ajax.is_xhr():
+ stream = template.render('_form.html', link=link, errors=errors)
+ else:
+ stream = template.render(link=link, comment=None, errors=errors)
+ return stream | HTMLFormFiller(data=data)
+
+ @cherrypy.expose
+ @template.output('index.xml', method='xml')
+ def feed(self, id=None):
+ if id:
+ link = self.data.get(id)
+ if not link:
+ raise cherrypy.NotFound()
+ return template.render('info.xml', link=link)
+ else:
+ links = sorted(self.data.values(), key=operator.attrgetter('time'))
+ return template.render(links=links)
+
+
+def main(filename):
+ # load data from the pickle file, or initialize it to an empty list
+ if os.path.exists(filename):
+ fileobj = open(filename, 'rb')
+ try:
+ data = pickle.load(fileobj)
+ finally:
+ fileobj.close()
+ else:
+ data = {}
+
+ def _save_data():
+ # save data back to the pickle file
+ fileobj = open(filename, 'wb')
+ try:
+ pickle.dump(data, fileobj)
+ finally:
+ fileobj.close()
+ if hasattr(cherrypy.engine, 'subscribe'): # CherryPy >= 3.1
+ cherrypy.engine.subscribe('stop', _save_data)
+ else:
+ cherrypy.engine.on_stop_engine_list.append(_save_data)
+
+ # Some global configuration; note that this could be moved into a
+ # configuration file
+ cherrypy.config.update({
+ 'tools.encode.on': True, 'tools.encode.encoding': 'utf-8',
+ 'tools.decode.on': True,
+ 'tools.trailing_slash.on': True,
+ 'tools.staticdir.root': os.path.abspath(os.path.dirname(__file__)),
+ })
+
+ cherrypy.quickstart(Root(data), '/', {
+ '/media': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': 'static'
+ }
+ })
+
+if __name__ == '__main__':
+ import formencode
+ formencode.api.set_stdtranslation(languages=['en'])
+ main(sys.argv[1])
diff --git a/lib/genshi/examples/tutorial/geddit/form.py b/lib/genshi/examples/tutorial/geddit/form.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/form.py
@@ -0,0 +1,12 @@
+from formencode import Schema, validators
+
+
+class LinkForm(Schema):
+ username = validators.UnicodeString(not_empty=True)
+ url = validators.URL(not_empty=True, add_http=True, check_exists=False)
+ title = validators.UnicodeString(not_empty=True)
+
+
+class CommentForm(Schema):
+ username = validators.UnicodeString(not_empty=True)
+ content = validators.UnicodeString(not_empty=True)
diff --git a/lib/genshi/examples/tutorial/geddit/lib/__init__.py b/lib/genshi/examples/tutorial/geddit/lib/__init__.py
new file mode 100644
diff --git a/lib/genshi/examples/tutorial/geddit/lib/ajax.py b/lib/genshi/examples/tutorial/geddit/lib/ajax.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/lib/ajax.py
@@ -0,0 +1,5 @@
+import cherrypy
+
+def is_xhr():
+ requested_with = cherrypy.request.headers.get('X-Requested-With')
+ return requested_with and requested_with.lower() == 'xmlhttprequest'
diff --git a/lib/genshi/examples/tutorial/geddit/lib/template.py b/lib/genshi/examples/tutorial/geddit/lib/template.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/lib/template.py
@@ -0,0 +1,47 @@
+import os
+
+import cherrypy
+from genshi.core import Stream
+from genshi.output import encode, get_serializer
+from genshi.template import Context, TemplateLoader
+
+from geddit.lib import ajax
+
+loader = TemplateLoader(
+ os.path.join(os.path.dirname(__file__), '..', 'templates'),
+ auto_reload=True
+)
+
+def output(filename, method='html', encoding='utf-8', **options):
+ """Decorator for exposed methods to specify what template the should use
+ for rendering, and which serialization method and options should be
+ applied.
+ """
+ def decorate(func):
+ def wrapper(*args, **kwargs):
+ cherrypy.thread_data.template = loader.load(filename)
+ opt = options.copy()
+ if not ajax.is_xhr() and method == 'html':
+ opt.setdefault('doctype', 'html')
+ serializer = get_serializer(method, **opt)
+ stream = func(*args, **kwargs)
+ if not isinstance(stream, Stream):
+ return stream
+ return encode(serializer(stream), method=serializer,
+ encoding=encoding)
+ return wrapper
+ return decorate
+
+def render(*args, **kwargs):
+ """Function to render the given data to the template specified via the
+ ``@output`` decorator.
+ """
+ if args:
+ assert len(args) == 1, \
+ 'Expected exactly one argument, but got %r' % (args,)
+ template = loader.load(args[0])
+ else:
+ template = cherrypy.thread_data.template
+ ctxt = Context(url=cherrypy.url)
+ ctxt.push(kwargs)
+ return template.generate(ctxt)
diff --git a/lib/genshi/examples/tutorial/geddit/model.py b/lib/genshi/examples/tutorial/geddit/model.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/model.py
@@ -0,0 +1,31 @@
+from datetime import datetime
+
+
+class Link(object):
+
+ def __init__(self, username, url, title):
+ self.username = username
+ self.url = url
+ self.title = title
+ self.time = datetime.utcnow()
+ self.id = hex(hash(tuple([username, url, title, self.time])))[2:]
+ self.comments = []
+
+ def __repr__(self):
+ return '<%s %r>' % (type(self).__name__, self.title)
+
+ def add_comment(self, username, content):
+ comment = Comment(username, content)
+ self.comments.append(comment)
+ return comment
+
+
+class Comment(object):
+
+ def __init__(self, username, content):
+ self.username = username
+ self.content = content
+ self.time = datetime.utcnow()
+
+ def __repr__(self):
+ return '<%s by %r>' % (type(self).__name__, self.username)
diff --git a/lib/genshi/examples/tutorial/geddit/static/jquery.js b/lib/genshi/examples/tutorial/geddit/static/jquery.js
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/static/jquery.js
@@ -0,0 +1,11 @@
+/*
+ * jQuery 1.1.4 - New Wave Javascript
+ *
+ * Copyright (c) 2007 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2007-08-23 21:49:27 -0400 (Thu, 23 Aug 2007) $
+ * $Rev: 2862 $
+ */
+eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(9(){6(1f C!="Q")E v=C;E C=19.16=9(a,c){6(19==7||!7.4a)F 1s C(a,c);F 7.4a(a,c)};6(1f $!="Q")E B=$;19.$=C;E q=/^[^<]*(<(.|\\s)+>)[^>]*$|^#(\\w+)$/;C.15=C.3v={4a:9(a,c){a=a||R;6(1f a=="1E"){E m=q.2d(a);6(m&&(m[1]||!c)){6(m[1])a=C.3c([m[1]]);G{E b=R.37(m[3]);6(b)6(b.2j!=m[3])F C().1F(a);G{7[0]=b;7.H=1;F 7}G a=[]}}G F 1s C(c).1F(a)}G 6(C.1g(a))F 1s C(R)[C.15.1L?"1L":"2f"](a);F 7.5J(a.1b==1K&&a||(a.3w||a.H&&a!=19&&!a.1t&&a[0]!=Q&&a[0].1t)&&C.2V(a)||[a])},3w:"1.1.4",7K:9(){F 7.H},H:0,21:9(a){F a==Q?C.2V(7):7[a]},1O:9(a){E b=C(a);b.5c=7;F b},5J:9(a){7.H=0;1K.3v.Y.T(7,a);F 7},J:9(a,b){F C.J(7,a,b)},45:9(a){E b=-1;7.J(9(i){6(7==a)b=i});F b},1j:9(f,d,e){E c=f;6(f.1b==3n)6(d==Q)F 7.H&&C[e||"1j"](7[0],f)||Q;G{c={};c[f]=d}F 7.J(9(a){I(E b 17 c)C.1j(e?7.S:7,b,C.4Q(7,c[b],e,a,b))})},1h:9(b,a){F 7.1j(b,a,"34")},2Q:9(e){6(1f e!="4P"&&e!=K)F 7.3K().3H(R.60(e));E t="";C.J(e||7,9(){C.J(7.2Z,9(){6(7.1t!=8)t+=7.1t!=1?7.5S:C.15.2Q([7])})});F t},82:9(){E a,2e=1a;F 7.J(9(){6(!a)a=C.3c(2e,7.2I);E b=a[0].3B(O);7.P.2p(b,7);20(b.1k)b=b.1k;b.4p(7)})},3H:9(){F 7.2J(1a,O,1,9(a){7.4p(a)})},5v:9(){F 7.2J(1a,O,-1,9(a){7.2p(a,7.1k)})},5u:9(){F 7.2J(1a,M,1,9(a){7.P.2p(a,7)})},5t:9(){F 7.2J(1a,M,-1,9(a){7.P.2p(a,7.2a)})},3L:9(){F 7.5c||C([])},1F:9(t){E b=C.3M(7,9(a){F C.1F(t,a)});F 7.1O(/[^+>] [^+>]/.1d(t)||t.U("..")>-1?C.4d(b):b)},7o:9(e){e=e!=Q?e:O;E d=7.1r(7.1F("*"));6(C.N.12){d.J(9(){7.2l$1i={};I(E a 17 7.$1i)7.2l$1i[a]=C.14({},7.$1i[a])}).49()}E r=7.1O(C.3M(7,9(a){F a.3B(e)}));6(C.N.12){d.J(9(){E c=7.2l$1i;I(E a 17 c)I(E b 17 c[a])C.1c.1r(7,a,c[a][b],c[a][b].V);7.2l$1i=K})}6(e){E f=r.1r(r.1F(\'*\')).1l(\'2b,39[@L=3i]\');d.1l(\'2b,39[@L=3i]\').J(9(i){6(7.3j)f[i].3j=7.3j;6(7.27)f[i].27=O})}F r},1l:9(t){F 7.1O(C.1g(t)&&C.2B(7,9(b,a){F t.T(b,[a])})||C.2R(t,7))},5l:9(t){F 7.1O(t.1b==3n&&C.2R(t,7,O)||C.2B(7,9(a){F(t.1b==1K||t.3w)?C.4K(a,t)<0:a!=t}))},1r:9(t){F 7.1O(C.29(7.21(),t.1b==3n?C(t).21():t.H!=Q&&(!t.W||t.W=="6s")?t:[t]))},3y:9(a){F a?C.2R(a,7).H>0:M},2G:9(a){F a==Q?(7.H?7[0].2A:K):7.1j("2A",a)},5W:9(a){F a==Q?(7.H?7[0].2W:K):7.3K().3H(a)},3S:9(){F 7.1O(1K.3v.3S.T(7,1a))},2J:9(f,d,g,e){E c=7.H>1,a;F 7.J(9(){6(!a){a=C.3c(f,7.2I);6(g<0)a.8E()}E b=7;6(d&&C.W(7,"1A")&&C.W(a[0],"3O"))b=7.4L("1w")[0]||7.4p(R.6a("1w"));C.J(a,9(){6(C.W(7,"33")){6(7.32)C.31({1G:7.32,2w:M,3G:"33"});G C.4E(7.2Q||7.5Z||7.2W||"")}G e.T(b,[c?7.3B(O):7])})})}};C.14=C.15.14=9(){E c=1a[0]||{},a=1,1M=1a.H,4D=M;6(c.1b==8d){4D=c;c=1a[1]||{}}6(1M==1){c=7;a=0}E b;I(;a<1M;a++)6((b=1a[a])!=K)I(E i 17 b){6(c==b[i])5X;6(4D&&1f b[i]==\'4P\'&&c[i])C.14(c[i],b[i]);G 6(b[i]!=Q)c[i]=b[i]}F c};C.14({8a:9(a){19.$=B;6(a)19.16=v;F C},1g:9(a){F!!a&&1f a!="1E"&&!a.W&&a.1b!=1K&&/9/i.1d(a+"")},3E:9(a){F a.3D&&!a.4z||a.4y&&a.2I&&!a.2I.4z},4E:9(a){a=C.2s(a);6(a){6(19.5N)19.5N(a);G 6(C.N.1H)19.4x(a,0);G 2T.2S(19,a)}},W:9(b,a){F b.W&&b.W.1I()==a.1I()},J:9(a,b,c){6(c){6(a.H==Q)I(E i 17 a)b.T(a[i],c);G I(E i=0,3A=a.H;i<3A;i++)6(b.T(a[i],c)===M)1J}G{6(a.H==Q)I(E i 17 a)b.2S(a[i],i,a[i]);G I(E i=0,3A=a.H,2G=a[0];i<3A&&b.2S(2G,i,2G)!==M;2G=a[++i]){}}F a},4Q:9(c,b,d,e,a){6(C.1g(b))b=b.2S(c,[e]);E f=/z-?45|7S-?7Q|1e|5y|7O-?1u/i;F b&&b.1b==3x&&d=="34"&&!f.1d(a)?b+"4t":b},18:{1r:9(b,c){C.J((c||"").2M(/\\s+/),9(i,a){6(!C.18.2N(b.18,a))b.18+=(b.18?" ":"")+a})},23:9(b,c){b.18=c!=Q?C.2B(b.18.2M(/\\s+/),9(a){F!C.18.2N(c,a)}).5w(" "):""},2N:9(t,c){F C.4K(c,(t.18||t).3s().2M(/\\s+/))>-1}},1V:9(e,o,f){I(E i 17 o){e.S["2U"+i]=e.S[i];e.S[i]=o[i]}f.T(e,[]);I(E i 17 o)e.S[i]=e.S["2U"+i]},1h:9(e,p){6(p=="1u"||p=="24"){E b={},3p,3o,d=["7J","7G","7F","7B"];C.J(d,9(){b["7A"+7]=0;b["7x"+7+"7u"]=0});C.1V(e,b,9(){6(C(e).3y(\':4N\')){3p=e.7t;3o=e.7q}G{e=C(e.3B(O)).1F(":4e").5d("27").3L().1h({3V:"1C",3k:"7n",11:"2m",7h:"0",7e:"0"}).57(e.P)[0];E a=C.1h(e.P,"3k")||"3g";6(a=="3g")e.P.S.3k="76";3p=e.74;3o=e.71;6(a=="3g")e.P.S.3k="3g";e.P.3e(e)}});F p=="1u"?3p:3o}F C.34(e,p)},34:9(h,d,g){E i,1R=[],1V=[];9 2E(a){6(!C.N.1H)F M;E b=R.2L.3b(a,K);F!b||b.44("2E")==""}6(d=="1e"&&C.N.12){i=C.1j(h.S,"1e");F i==""?"1":i}6(d.2k(/3a/i))d=x;6(!g&&h.S[d])i=h.S[d];G 6(R.2L&&R.2L.3b){6(d.2k(/3a/i))d="3a";d=d.1v(/([A-Z])/g,"-$1").2D();E e=R.2L.3b(h,K);6(e&&!2E(h))i=e.44(d);G{I(E a=h;a&&2E(a);a=a.P)1R.42(a);I(a=0;a<1R.H;a++)6(2E(1R[a])){1V[a]=1R[a].S.11;1R[a].S.11="2m"}i=d=="11"&&1V[1R.H-1]!=K?"1T":R.2L.3b(h,K).44(d)||"";I(a=0;a<1V.H;a++)6(1V[a]!=K)1R[a].S.11=1V[a]}6(d=="1e"&&i=="")i="1"}G 6(h.41){E f=d.1v(/\\-(\\w)/g,9(m,c){F c.1I()});i=h.41[d]||h.41[f]}F i},3c:9(a,c){E r=[];c=c||R;C.J(a,9(i,b){6(!b)F;6(b.1b==3x)b=b.3s();6(1f b=="1E"){E s=C.2s(b).2D(),1m=c.6a("1m"),1P=[];E a=!s.U("<1Z")&&[1,"<2b>","</2b>"]||!s.U("<6L")&&[1,"<4V>","</4V>"]||s.2k(/^<(6I|1w|6H|6F|6D)/)&&[1,"<1A>","</1A>"]||!s.U("<3O")&&[2,"<1A><1w>","</1w></1A>"]||(!s.U("<6A")||!s.U("<6y"))&&[3,"<1A><1w><3O>","</3O></1w></1A>"]||!s.U("<6x")&&[2,"<1A><1w></1w><4T>","</4T></1A>"]||C.N.12&&[1,"1m<1m>","</1m>"]||[0,"",""];1m.2W=a[1]+b+a[2];20(a[0]--)1m=1m.3Y;6(C.N.12){6(!s.U("<1A")&&s.U("<1w")<0)1P=1m.1k&&1m.1k.2Z;G 6(a[1]=="<1A>"&&s.U("<1w")<0)1P=1m.2Z;I(E n=1P.H-1;n>=0;--n)6(C.W(1P[n],"1w")&&!1P[n].2Z.H)1P[n].P.3e(1P[n]);6(/^\\s/.1d(b))1m.2p(c.60(b.2k(/^\\s*/)[0]),1m.1k)}b=C.2V(1m.2Z)}6(0===b.H&&(!C.W(b,"38")&&!C.W(b,"2b")))F;6(b[0]==Q||C.W(b,"38")||b.6u)r.Y(b);G r=C.29(r,b)});F r},1j:9(c,d,a){E e=C.3E(c)?{}:C.4q;6(d=="28"&&C.N.1H)c.P.3j;6(e[d]){6(a!=Q)c[e[d]]=a;F c[e[d]]}G 6(C.N.12&&d=="S")F C.1j(c.S,"6p",a);G 6(a==Q&&C.N.12&&C.W(c,"38")&&(d=="6n"||d=="6m"))F c.6k(d).5S;G 6(c.4y){6(a!=Q)c.6j(d,a);6(C.N.12&&/5R|32/.1d(d)&&!C.3E(c))F c.3F(d,2);F c.3F(d)}G{6(d=="1e"&&C.N.12){6(a!=Q){c.5y=1;c.1l=(c.1l||"").1v(/5T\\([^)]*\\)/,"")+(3m(a).3s()=="6d"?"":"5T(1e="+a*6c+")")}F c.1l?(3m(c.1l.2k(/1e=([^)]*)/)[1])/6c).3s():""}d=d.1v(/-([a-z])/8I,9(z,b){F b.1I()});6(a!=Q)c[d]=a;F c[d]}},2s:9(t){F(t||"").1v(/^\\s+|\\s+$/g,"")},2V:9(a){E r=[];6(1f a!="8H")I(E i=0,1M=a.H;i<1M;i++)r.Y(a[i]);G r=a.3S(0);F r},4K:9(b,a){I(E i=0,1M=a.H;i<1M;i++)6(a[i]==b)F i;F-1},29:9(a,b){6(C.N.12){I(E i=0;b[i];i++)6(b[i].1t!=8)a.Y(b[i])}G I(E i=0;b[i];i++)a.Y(b[i]);F a},4d:9(a){E r=[],4O=C.1q++;2g{I(E i=0,69=a.H;i<69;i++)6(4O!=a[i].1q){a[i].1q=4O;r.Y(a[i])}}2h(e){r=a}F r},1q:0,2B:9(b,a,c){6(1f a=="1E")a=2T("M||9(a,i){F "+a+"}");E d=[];I(E i=0,3P=b.H;i<3P;i++)6(!c&&a(b[i],i)||c&&!a(b[i],i))d.Y(b[i]);F d},3M:9(c,b){6(1f b=="1E")b=2T("M||9(a){F "+b+"}");E d=[];I(E i=0,3P=c.H;i<3P;i++){E a=b(c[i],i);6(a!==K&&a!=Q){6(a.1b!=1K)a=[a];d=d.8x(a)}}F d}});E u=8w.8u.2D();C.N={6b:(u.2k(/.+(?:8s|8q|8p|8o)[\\/: ]([\\d.]+)/)||[])[1],1H:/61/.1d(u),2t:/2t/.1d(u),12:/12/.1d(u)&&!/2t/.1d(u),3J:/3J/.1d(u)&&!/(8n|61)/.1d(u)};E x=C.N.12?"3I":"4G";C.14({8m:!C.N.12||R.8l=="8k",3I:C.N.12?"3I":"4G",4q:{"I":"8j","8i":"18","3a":x,4G:x,3I:x,2W:"2W",18:"18",2A:"2A",30:"30",27:"27",8h:"8g",28:"28",8f:"8e"}});C.J({5Y:"a.P",4C:"16.4C(a)",8c:"16.25(a,2,\'2a\')",8b:"16.25(a,2,\'4B\')",88:"16.4A(a.P.1k,a)",87:"16.4A(a.1k)"},9(i,n){C.15[i]=9(a){E b=C.3M(7,n);6(a&&1f a=="1E")b=C.2R(a,b);F 7.1O(C.4d(b))}});C.J({57:"3H",86:"5v",2p:"5u",85:"5t"},9(i,n){C.15[i]=9(){E a=1a;F 7.J(9(){I(E j=0,1M=a.H;j<1M;j++)C(a[j])[n](7)})}});C.J({5d:9(a){C.1j(7,a,"");7.84(a)},83:9(c){C.18.1r(7,c)},81:9(c){C.18.23(7,c)},80:9(c){C.18[C.18.2N(7,c)?"23":"1r"](7,c)},23:9(a){6(!a||C.1l(a,[7]).r.H)7.P.3e(7)},3K:9(){20(7.1k)7.3e(7.1k)}},9(i,n){C.15[i]=9(){F 7.J(n,1a)}});C.J(["5Q","5P","5M","5L"],9(i,n){C.15[n]=9(a,b){F 7.1l(":"+n+"("+a+")",b)}});C.J(["1u","24"],9(i,n){C.15[n]=9(h){F h==Q?(7.H?C.1h(7[0],n):K):7.1h(n,h.1b==3n?h:h+"4t")}});E A=C.N.1H&&5K(C.N.6b)<7Z?"(?:[\\\\w*2l-]|\\\\\\\\.)":"(?:[\\\\w\\7Y-\\7V*2l-]|\\\\\\\\.)",5I=1s 3C("^[/>]\\\\s*("+A+"+)"),5H=1s 3C("^("+A+"+)(#)("+A+"+)"),5G=1s 3C("^([#.]?)("+A+"*)");C.14({4w:{"":"m[2]==\'*\'||16.W(a,m[2])","#":"a.3F(\'2j\')==m[2]",":":{5P:"i<m[3]-0",5M:"i>m[3]-0",25:"m[3]-0==i",5Q:"m[3]-0==i",2H:"i==0",2P:"i==r.H-1",5E:"i%2==0",5D:"i%2","2H-3z":"a.P.4L(\'*\')[0]==a","2P-3z":"16.25(a.P.3Y,1,\'4B\')==a","7U-3z":"!16.25(a.P.3Y,2,\'4B\')",5Y:"a.1k",3K:"!a.1k",5L:"(a.5Z||a.7T||\'\').U(m[3])>=0",4N:\'"1C"!=a.L&&16.1h(a,"11")!="1T"&&16.1h(a,"3V")!="1C"\',1C:\'"1C"==a.L||16.1h(a,"11")=="1T"||16.1h(a,"3V")=="1C"\',7R:"!a.30",30:"a.30",27:"a.27",28:"a.28||16.1j(a,\'28\')",2Q:"\'2Q\'==a.L",4e:"\'4e\'==a.L",3i:"\'3i\'==a.L",4v:"\'4v\'==a.L",5C:"\'5C\'==a.L",4u:"\'4u\'==a.L",5B:"\'5B\'==a.L",5A:"\'5A\'==a.L",1X:\'"1X"==a.L||16.W(a,"1X")\',39:"/39|2b|7P|1X/i.1d(a.W)",2N:"16.1F(m[3],a).H"},"[":"16.1F(m[2],a).H"},5x:[/^\\[ *(@)([\\w-]+) *([!*$^~=]*) *(\'?"?)(.*?)\\4 *\\]/,/^(\\[)\\s*(.*?(\\[.*?\\])?[^[]*?)\\s*\\]/,/^(:)([\\w-]+)\\("?\'?(.*?(\\(.*?\\))?[^(]*?)"?\'?\\)/,1s 3C("^([:.#]*)("+A+"+)")],2R:9(a,c,b){E d,1Y=[];20(a&&a!=d){d=a;E f=C.1l(a,c,b);a=f.t.1v(/^\\s*,\\s*/,"");1Y=b?c=f.r:C.29(1Y,f.r)}F 1Y},1F:9(t,l){6(1f t!="1E")F[t];6(l&&!l.1t)l=K;l=l||R;6(!t.U("//")){t=t.2K(2,t.H)}G 6(!t.U("/")&&!l.2I){l=l.3D;t=t.2K(1,t.H);6(t.U("/")>=1)t=t.2K(t.U("/"),t.H)}E d=[l],2q=[],2P;20(t&&2P!=t){E r=[];2P=t;t=C.2s(t).1v(/^\\/\\//,"");E k=M;E g=5I;E m=g.2d(t);6(m){E o=m[1].1I();I(E i=0;d[i];i++)I(E c=d[i].1k;c;c=c.2a)6(c.1t==1&&(o=="*"||c.W.1I()==o.1I()))r.Y(c);d=r;t=t.1v(g,"");6(t.U(" ")==0)5X;k=O}G{g=/^((\\/?\\.\\.)|([>\\/+~]))\\s*(\\w*)/i;6((m=g.2d(t))!=K){r=[];E o=m[4],1q=C.1q++;m=m[1];I(E j=0,2o=d.H;j<2o;j++)6(m.U("..")<0){E n=m=="~"||m=="+"?d[j].2a:d[j].1k;I(;n;n=n.2a)6(n.1t==1){6(m=="~"&&n.1q==1q)1J;6(!o||n.W.1I()==o.1I()){6(m=="~")n.1q=1q;r.Y(n)}6(m=="+")1J}}G r.Y(d[j].P);d=r;t=C.2s(t.1v(g,""));k=O}}6(t&&!k){6(!t.U(",")){6(l==d[0])d.4s();2q=C.29(2q,d);r=d=[l];t=" "+t.2K(1,t.H)}G{E h=5H;E m=h.2d(t);6(m){m=[0,m[2],m[3],m[1]]}G{h=5G;m=h.2d(t)}m[2]=m[2].1v(/\\\\/g,"");E f=d[d.H-1];6(m[1]=="#"&&f&&f.37&&!C.3E(f)){E p=f.37(m[2]);6((C.N.12||C.N.2t)&&p&&1f p.2j=="1E"&&p.2j!=m[2])p=C(\'[@2j="\'+m[2]+\'"]\',f)[0];d=r=p&&(!m[3]||C.W(p,m[3]))?[p]:[]}G{I(E i=0;d[i];i++){E a=m[1]!=""||m[0]==""?"*":m[2];6(a=="*"&&d[i].W.2D()=="4P")a="2O";r=C.29(r,d[i].4L(a))}6(m[1]==".")r=C.4r(r,m[2]);6(m[1]=="#"){E e=[];I(E i=0;r[i];i++)6(r[i].3F("2j")==m[2]){e=[r[i]];1J}r=e}d=r}t=t.1v(h,"")}}6(t){E b=C.1l(t,r);d=r=b.r;t=C.2s(b.t)}}6(t)d=[];6(d&&l==d[0])d.4s();2q=C.29(2q,d);F 2q},4r:9(r,m,a){m=" "+m+" ";E c=[];I(E i=0;r[i];i++){E b=(" "+r[i].18+" ").U(m)>=0;6(!a&&b||a&&!b)c.Y(r[i])}F c},1l:9(t,r,h){E d;20(t&&t!=d){d=t;E p=C.5x,m;I(E i=0;p[i];i++){m=p[i].2d(t);6(m){t=t.7N(m[0].H);m[2]=m[2].1v(/\\\\/g,"");1J}}6(!m)1J;6(m[1]==":"&&m[2]=="5l")r=C.1l(m[3],r,O).r;G 6(m[1]==".")r=C.4r(r,m[2],h);G 6(m[1]=="@"){E g=[],L=m[3];I(E i=0,2o=r.H;i<2o;i++){E a=r[i],z=a[C.4q[m[2]]||m[2]];6(z==K||/5R|32|28/.1d(m[2]))z=C.1j(a,m[2])||\'\';6((L==""&&!!z||L=="="&&z==m[5]||L=="!="&&z!=m[5]||L=="^="&&z&&!z.U(m[5])||L=="$="&&z.2K(z.H-m[5].H)==m[5]||(L=="*="||L=="~=")&&z.U(m[5])>=0)^h)g.Y(a)}r=g}G 6(m[1]==":"&&m[2]=="25-3z"){E e=C.1q++,g=[],1d=/(\\d*)n\\+?(\\d*)/.2d(m[3]=="5E"&&"2n"||m[3]=="5D"&&"2n+1"||!/\\D/.1d(m[3])&&"n+"+m[3]||m[3]),2H=(1d[1]||1)-0,d=1d[2]-0;I(E i=0,2o=r.H;i<2o;i++){E j=r[i],P=j.P;6(e!=P.1q){E c=1;I(E n=P.1k;n;n=n.2a)6(n.1t==1)n.4o=c++;P.1q=e}E b=M;6(2H==1){6(d==0||j.4o==d)b=O}G 6((j.4o+d)%2H==0)b=O;6(b^h)g.Y(j)}r=g}G{E f=C.4w[m[1]];6(1f f!="1E")f=C.4w[m[1]][m[2]];f=2T("M||9(a,i){F "+f+"}");r=C.2B(r,f,h)}}F{r:r,t:t}},4C:9(c){E b=[];E a=c.P;20(a&&a!=R){b.Y(a);a=a.P}F b},25:9(a,e,c,b){e=e||1;E d=0;I(;a;a=a[c])6(a.1t==1&&++d==e)1J;F a},4A:9(n,a){E r=[];I(;n;n=n.2a){6(n.1t==1&&(!a||n!=a))r.Y(n)}F r}});C.1c={1r:9(f,d,c,b){6(C.N.12&&f.3t!=Q)f=19;6(!c.22)c.22=7.22++;6(b!=Q){E e=c;c=9(){F e.T(7,1a)};c.V=b;c.22=e.22}6(!f.$1i)f.$1i={};6(!f.$1y)f.$1y=9(){E a;6(1f C=="Q"||C.1c.4n)F a;a=C.1c.1y.T(f,1a);F a};E g=f.$1i[d];6(!g){g=f.$1i[d]={};6(f.4m)f.4m(d,f.$1y,M);G f.7M("3r"+d,f.$1y)}g[c.22]=c;7.1D[d]=O},22:1,1D:{},23:9(c,b,a){E d=c.$1i,2c,45;6(d){6(b&&b.L){a=b.4l;b=b.L}6(!b){I(b 17 d)7.23(c,b)}G 6(d[b]){6(a)4k d[b][a.22];G I(a 17 c.$1i[b])4k d[b][a];I(2c 17 d[b])1J;6(!2c){6(c.4j)c.4j(b,c.$1y,M);G c.7L("3r"+b,c.$1y);2c=K;4k d[b]}}I(2c 17 d)1J;6(!2c)c.$1y=c.$1i=K}},1z:9(c,b,d){b=C.2V(b||[]);6(!d){6(7.1D[c])C("*").1r([19,R]).1z(c,b)}G{E a,2c,15=C.1g(d[c]||K);b.42(7.4i({L:c,1S:d}));6(C.1g(d.$1y))a=d.$1y.T(d,b);6(!15&&d["3r"+c]&&d["3r"+c].T(d,b)===M)a=M;6(15&&a!==M&&!(C.W(d,\'a\')&&c=="4h")){7.4n=O;d[c]()}7.4n=M}},1y:9(b){E a;b=C.1c.4i(b||19.1c||{});E c=7.$1i&&7.$1i[b.L],2e=1K.3v.3S.2S(1a,1);2e.42(b);I(E j 17 c){2e[0].4l=c[j];2e[0].V=c[j].V;6(c[j].T(7,2e)===M){b.2u();b.2X();a=M}}6(C.N.12)b.1S=b.2u=b.2X=b.4l=b.V=K;F a},4i:9(c){E a=c;c=C.14({},a);c.2u=9(){6(a.2u)a.2u();a.7I=M};c.2X=9(){6(a.2X)a.2X();a.7H=O};6(!c.1S&&c.5r)c.1S=c.5r;6(C.N.1H&&c.1S.1t==3)c.1S=a.1S.P;6(!c.4g&&c.4F)c.4g=c.4F==c.1S?c.7C:c.4F;6(c.5p==K&&c.66!=K){E e=R.3D,b=R.4z;c.5p=c.66+(e&&e.5o||b.5o||0);c.7z=c.7v+(e&&e.5m||b.5m||0)}6(!c.3Q&&(c.5k||c.5j))c.3Q=c.5k||c.5j;6(!c.5i&&c.5g)c.5i=c.5g;6(!c.3Q&&c.1X)c.3Q=(c.1X&1?1:(c.1X&2?3:(c.1X&4?2:0)));F c}};C.15.14({3l:9(c,a,b){F c=="5f"?7.5e(c,a,b):7.J(9(){C.1c.1r(7,c,b||a,b&&a)})},5e:9(d,b,c){F 7.J(9(){C.1c.1r(7,d,9(a){C(7).49(a);F(c||b).T(7,1a)},c&&b)})},49:9(a,b){F 7.J(9(){C.1c.23(7,a,b)})},1z:9(a,b){F 7.J(9(){C.1c.1z(a,b,7)})},1W:9(){E a=1a;F 7.4h(9(e){7.3T=0==7.3T?1:0;e.2u();F a[7.3T].T(7,[e])||M})},7p:9(f,g){9 3U(e){E p=e.4g;20(p&&p!=7)2g{p=p.P}2h(e){p=7};6(p==7)F M;F(e.L=="3W"?f:g).T(7,[e])}F 7.3W(3U).5b(3U)},1L:9(f){5a();6(C.36)f.T(R,[C]);G C.2C.Y(9(){F f.T(7,[C])});F 7}});C.14({36:M,2C:[],1L:9(){6(!C.36){C.36=O;6(C.2C){C.J(C.2C,9(){7.T(R)});C.2C=K}6(C.N.3J||C.N.2t)R.4j("59",C.1L,M);6(!19.7m.H)C(19).2f(9(){C("#4b").23()})}}});C.J(("7l,7k,2f,7j,7i,5f,4h,7g,"+"7f,7d,7c,3W,5b,7b,2b,"+"4u,7a,79,78,3f").2M(","),9(i,o){C.15[o]=9(f){F f?7.3l(o,f):7.1z(o)}});E w=M;9 5a(){6(w)F;w=O;6(C.N.3J||C.N.2t)R.4m("59",C.1L,M);G 6(C.N.12){R.75("<73"+"72 2j=4b 70=O "+"32=//:><\\/33>");E a=R.37("4b");6(a)a.6Z=9(){6(R.3d!="1x")F;C.1L()};a=K}G 6(C.N.1H)C.48=3t(9(){6(R.3d=="6Y"||R.3d=="1x"){47(C.48);C.48=K;C.1L()}},10);C.1c.1r(19,"2f",C.1L)}C.15.14({6X:9(c,b,a){7.2f(c,b,a,1)},2f:9(g,e,c,d){6(C.1g(g))F 7.3l("2f",g);c=c||9(){};E f="46";6(e)6(C.1g(e)){c=e;e=K}G{e=C.2O(e);f="55"}E h=7;C.31({1G:g,L:f,V:e,2F:d,1x:9(a,b){6(b=="1U"||!d&&b=="54")h.5W(a.43);4x(9(){h.J(c,[a.43,b,a])},13)}});F 7},6W:9(){F C.2O(7)},6V:9(){}});C.J("53,52,51,50,4Z,5h".2M(","),9(i,o){C.15[o]=9(f){F 7.3l(o,f)}});C.14({21:9(e,c,a,d,b){6(C.1g(c)){a=c;c=K}F C.31({L:"46",1G:e,V:c,1U:a,3G:d,2F:b})},6U:9(d,b,a,c){F C.21(d,b,a,c,1)},6T:9(b,a){F C.21(b,K,a,"33")},77:9(c,b,a){F C.21(c,b,a,"56")},6S:9(d,b,a,c){6(C.1g(b)){a=b;b={}}F C.31({L:"55",1G:d,V:b,1U:a,3G:c})},6R:9(a){C.3u.1Q=a},6Q:9(a){C.14(C.3u,a)},3u:{1D:O,L:"46",1Q:0,4Y:"6P/x-6O-38-6N",4X:O,2w:O,V:K},3h:{},31:9(s){s=C.14(O,s,C.14(O,{},C.3u,s));6(s.V){6(s.4X&&1f s.V!="1E")s.V=C.2O(s.V);6(s.L.2D()=="21"){s.1G+=(s.1G.U("?")>-1?"&":"?")+s.V;s.V=K}}6(s.1D&&!C.40++)C.1c.1z("53");E f=M;E h=19.4W?1s 4W("6M.6K"):1s 58();h.6J(s.L,s.1G,s.2w);6(s.V)h.4c("7r-7s",s.4Y);6(s.2F)h.4c("6G-3Z-6E",C.3h[s.1G]||"7w, 6C 7y 6B 4J:4J:4J 6z");h.4c("X-7D-7E","58");6(s.4U)s.4U(h);6(s.1D)C.1c.1z("5h",[h,s]);E g=9(d){6(!f&&h&&(h.3d==4||d=="1Q")){f=O;6(i){47(i);i=K}E c=d=="1Q"&&"1Q"||!C.5n(h)&&"3f"||s.2F&&C.5s(h,s.1G)&&"54"||"1U";6(c=="1U"){2g{E a=C.5q(h,s.3G)}2h(e){c="4I"}}6(c=="1U"){E b;2g{b=h.4f("4S-3Z")}2h(e){}6(s.2F&&b)C.3h[s.1G]=b;6(s.1U)s.1U(a,c);6(s.1D)C.1c.1z("4Z",[h,s])}G C.3X(s,h,c);6(s.1D)C.1c.1z("51",[h,s]);6(s.1D&&!--C.40)C.1c.1z("52");6(s.1x)s.1x(h,c);6(s.2w)h=K}};6(s.2w){E i=3t(g,13);6(s.1Q>0)4x(9(){6(h){h.6w();6(!f)g("1Q")}},s.1Q)}2g{h.6v(s.V)}2h(e){C.3X(s,h,K,e)}6(!s.2w)g();F h},3X:9(s,a,b,e){6(s.3f)s.3f(a,b,e);6(s.1D)C.1c.1z("50",[a,s,e])},40:0,5n:9(r){2g{F!r.26&&6t.6r=="4v:"||(r.26>=4R&&r.26<6q)||r.26==5z||C.N.1H&&r.26==Q}2h(e){}F M},5s:9(a,c){2g{E b=a.4f("4S-3Z");F a.26==5z||b==C.3h[c]||C.N.1H&&a.26==Q}2h(e){}F M},5q:9(r,a){E b=r.4f("6o-L");E c=a=="5F"||!a&&b&&b.U("5F")>=0;V=c?r.7W:r.43;6(c&&V.3D.4y=="4I")7X"4I";6(a=="33")C.4E(V);6(a=="56")V=2T("("+V+")");F V},2O:9(a){E s=[];6(a.1b==1K||a.3w)C.J(a,9(){s.Y(2y(7.6l)+"="+2y(7.2A))});G I(E j 17 a)6(a[j]&&a[j].1b==1K)C.J(a[j],9(){s.Y(2y(j)+"="+2y(7))});G s.Y(2y(j)+"="+2y(a[j]));F s.5w("&")}});C.15.14({1o:9(b,a){F b?7.1B({1u:"1o",24:"1o",1e:"1o"},b,a):7.1l(":1C").J(9(){7.S.11=7.2r?7.2r:"";6(C.1h(7,"11")=="1T")7.S.11="2m"}).3L()},1p:9(b,a){F b?7.1B({1u:"1p",24:"1p",1e:"1p"},b,a):7.1l(":4N").J(9(){7.2r=7.2r||C.1h(7,"11");6(7.2r=="1T")7.2r="2m";7.S.11="1T"}).3L()},5O:C.15.1W,1W:9(a,b){F C.1g(a)&&C.1g(b)?7.5O(a,b):a?7.1B({1u:"1W",24:"1W",1e:"1W"},a,b):7.J(9(){C(7)[C(7).3y(":1C")?"1o":"1p"]()})},6i:9(b,a){F 7.1B({1u:"1o"},b,a)},6h:9(b,a){F 7.1B({1u:"1p"},b,a)},6g:9(b,a){F 7.1B({1u:"1W"},b,a)},6f:9(b,a){F 7.1B({1e:"1o"},b,a)},89:9(b,a){F 7.1B({1e:"1p"},b,a)},6e:9(c,a,b){F 7.1B({1e:a},c,b)},1B:9(d,h,f,g){F 7.1n(9(){E c=C(7).3y(":1C"),1Z=C.5V(h,f,g),5U=7;I(E p 17 d){6(d[p]=="1p"&&c||d[p]=="1o"&&!c)F C.1g(1Z.1x)&&1Z.1x.T(7);6(p=="1u"||p=="24"){1Z.11=C.1h(7,"11");1Z.2z=7.S.2z}}6(1Z.2z!=K)7.S.2z="1C";7.2v=C.14({},d);C.J(d,9(a,b){E e=1s C.2Y(5U,1Z,a);6(b.1b==3x)e.3R(e.1Y()||0,b);G e[b=="1W"?c?"1o":"1p":b](d)});F O})},1n:9(a,b){6(!b){b=a;a="2Y"}F 7.J(9(){6(!7.1n)7.1n={};6(!7.1n[a])7.1n[a]=[];7.1n[a].Y(b);6(7.1n[a].H==1)b.T(7)})}});C.14({5V:9(b,a,c){E d=b&&b.1b==8G?b:{1x:c||!c&&a||C.1g(b)&&b,1N:b,35:c&&a||a&&a.1b!=8F&&a};d.1N=(d.1N&&d.1N.1b==3x?d.1N:{8D:8C,8B:4R}[d.1N])||8A;d.2U=d.1x;d.1x=9(){C.68(7,"2Y");6(C.1g(d.2U))d.2U.T(7)};F d},35:{62:9(p,n,b,a){F b+a*p},4H:9(p,n,b,a){F((-67.8z(p*67.8y)/2)+0.5)*a+b}},1n:{},68:9(b,a){a=a||"2Y";6(b.1n&&b.1n[a]){b.1n[a].4s();E f=b.1n[a][0];6(f)f.T(b)}},3N:[],2Y:9(f,e,g){E z=7;E y=f.S;z.a=9(){6(e.3q)e.3q.T(f,[z.2x]);6(g=="1e")C.1j(y,"1e",z.2x);G{y[g]=5K(z.2x)+"4t";6(g=="1u"||g=="24")y.11="2m"}};z.65=9(){F 3m(C.1h(f,g))};z.1Y=9(){E r=3m(C.34(f,g));F r&&r>-8v?r:z.65()};z.3R=9(c,b){z.4M=(1s 64()).63();z.2x=c;z.a();C.3N.Y(9(){F z.3q(c,b)});6(C.3N.H==1){E d=3t(9(){E a=C.3N;I(E i=0;i<a.H;i++)6(!a[i]())a.8t(i--,1);6(!a.H)47(d)},13)}};z.1o=9(){6(!f.2i)f.2i={};f.2i[g]=C.1j(f.S,g);e.1o=O;z.3R(0,7.1Y());6(g!="1e")y[g]="8r";C(f).1o()};z.1p=9(){6(!f.2i)f.2i={};f.2i[g]=C.1j(f.S,g);e.1p=O;z.3R(7.1Y(),0)};z.3q=9(a,c){E t=(1s 64()).63();6(t>e.1N+z.4M){z.2x=c;z.a();6(f.2v)f.2v[g]=O;E b=O;I(E i 17 f.2v)6(f.2v[i]!==O)b=M;6(b){6(e.11!=K){y.2z=e.2z;y.11=e.11;6(C.1h(f,"11")=="1T")y.11="2m"}6(e.1p)y.11="1T";6(e.1p||e.1o)I(E p 17 f.2v)C.1j(y,p,f.2i[p])}6(b&&C.1g(e.1x))e.1x.T(f);F M}G{E n=t-7.4M;E p=n/e.1N;z.2x=C.35[e.35||(C.35.4H?"4H":"62")](p,n,a,(c-a),e.1N);z.a()}F O}}})})();',62,541,'||||||if|this||function|||||||||||||||||||||||||||||||var|return|else|length|for|each|null|type|false|browser|true|parentNode|undefined|document|style|apply|indexOf|data|nodeName||push|||display|msie||extend|fn|jQuery|in|className|window|arguments|constructor|event|test|opacity|typeof|isFunction|css|events|attr|firstChild|filter|div|queue|show|hide|mergeNum|add|new|nodeType|height|replace|tbody|complete|handle|trigger|table|animate|hidden|global|string|find|url|safari|toUpperCase|break|Array|ready|al|duration|pushStack|tb|timeout|stack|target|none|success|swap|toggle|button|cur|opt|while|get|guid|remove|width|nth|status|checked|selected|merge|nextSibling|select|ret|exec|args|load|try|catch|orig|id|match|_|block||rl|insertBefore|done|oldblock|trim|opera|preventDefault|curAnim|async|now|encodeURIComponent|overflow|value|grep|readyList|toLowerCase|color|ifModified|val|first|ownerDocument|domManip|substr|defaultView|split|has|param|last|text|multiFilter|call|eval|old|makeArray|innerHTML|stopPropagation|fx|childNodes|disabled|ajax|src|script|curCSS|easing|isReady|getElementById|form|input|float|getComputedStyle|clean|readyState|removeChild|error|static|lastModified|checkbox|selectedIndex|position|bind|parseFloat|String|oWidth|oHeight|step|on|toString|setInterval|ajaxSettings|prototype|jquery|Number|is|child|ol|cloneNode|RegExp|documentElement|isXMLDoc|getAttribute|dataType|append|styleFloat|mozilla|empty|end|map|timers|tr|el|which|custom|slice|lastToggle|handleHover|visibility|mouseover|handleError|lastChild|Modified|active|currentStyle|unshift|responseText|getPropertyValue|index|GET|clearInterval|safariTimer|unbind|init|__ie_init|setRequestHeader|unique|radio|getResponseHeader|relatedTarget|click|fix|removeEventListener|delete|handler|addEventListener|triggered|nodeIndex|appendChild|props|classFilter|shift|px|submit|file|expr|setTimeout|tagName|body|sibling|previousSibling|parents|deep|globalEval|fromElement|cssFloat|swing|parsererror|00|inArray|getElementsByTagName|startTime|visible|num|object|prop|200|Last|colgroup|beforeSend|fieldset|ActiveXObject|processData|contentType|ajaxSuccess|ajaxError|ajaxComplete|ajaxStop|ajaxStart|notmodified|POST|json|appendTo|XMLHttpRequest|DOMContentLoaded|bindReady|mouseout|prevObject|removeAttr|one|unload|ctrlKey|ajaxSend|metaKey|keyCode|charCode|not|scrollTop|httpSuccess|scrollLeft|pageX|httpData|srcElement|httpNotModified|after|before|prepend|join|parse|zoom|304|reset|image|password|odd|even|xml|quickClass|quickID|quickChild|setArray|parseInt|contains|gt|execScript|_toggle|lt|eq|href|nodeValue|alpha|self|speed|html|continue|parent|textContent|createTextNode|webkit|linear|getTime|Date|max|clientX|Math|dequeue|fl|createElement|version|100|NaN|fadeTo|fadeIn|slideToggle|slideUp|slideDown|setAttribute|getAttributeNode|name|method|action|content|cssText|300|protocol|FORM|location|options|send|abort|col|th|GMT|td|1970|01|cap|Since|colg|If|tfoot|thead|open|XMLHTTP|leg|Microsoft|urlencoded|www|application|ajaxSetup|ajaxTimeout|post|getScript|getIfModified|evalScripts|serialize|loadIfModified|loaded|onreadystatechange|defer|clientWidth|ipt|scr|clientHeight|write|relative|getJSON|keyup|keypress|keydown|change|mousemove|mouseup|left|mousedown|dblclick|right|scroll|resize|focus|blur|frames|absolute|clone|hover|offsetWidth|Content|Type|offsetHeight|Width|clientY|Thu|border|Jan|pageY|padding|Left|toElement|Requested|With|Right|Bottom|cancelBubble|returnValue|Top|size|detachEvent|attachEvent|substring|line|textarea|weight|enabled|font|innerText|only|uFFFF|responseXML|throw|u0128|417|toggleClass|removeClass|wrap|addClass|removeAttribute|insertAfter|prependTo|children|siblings|fadeOut|noConflict|prev|next|Boolean|maxLength|maxlength|readOnly|readonly|class|htmlFor|CSS1Compat|compatMode|boxModel|compatible|ie|ra|it|1px|rv|splice|userAgent|10000|navigator|concat|PI|cos|400|fast|600|slow|reverse|Function|Object|array|ig'.split('|'),0,{}))
\ No newline at end of file
diff --git a/lib/genshi/examples/tutorial/geddit/static/layout.css b/lib/genshi/examples/tutorial/geddit/static/layout.css
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/static/layout.css
@@ -0,0 +1,42 @@
+html, body { margin: 0; padding: 0; }
+body { background: #ddd; color: #333; font: normal 90%/1.3 Arial,Helvetica,sans-serif; }
+:link, :visited { color: #c10000; text-decoration: none; }
+:link:hover, :visited:hover { text-decoration: underline; }
+:link img, :visited img { border: none; }
+h1 { color: #666;
+ font: normal xx-large/1.5 Georgia,serif; margin: 0 0 .5em;
+}
+blockquote { font-style: italic; }
+form table { margin-bottom: 1em; }
+form table tbody th { font-weight: normal; padding-top: .3em; text-align: right;
+ vertical-align: top;
+}
+form label { color: #666; font-size: 90%; }
+
+#wrap { background: #fff; width: 600px; margin: 30px auto; }
+#content { border-left: 10px solid #b00; min-height: 240px; padding: 10px; }
+#header img { float: right; margin-top: -21px; margin-right: -37px; }
+
+#footer { background: #4A4D4D; border-top: 2px solid #333; font-size: x-small;
+ padding: 3px; text-align: center;
+}
+#footer hr { display: none; }
+.legalese { color: #999; margin: 0; }
+
+ol.links li .info { font-size: 85%; }
+ol.links li .info :link, ol.links li .info :visited { color: #666; }
+
+ul.comments { list-style: none; margin: 1em 0; padding: 0 0 0 1em; }
+ul.comments li { color: #999; margin: 0 0 1em; }
+ul.comments blockquote { color: #333; font-style: normal; margin: 0;
+ padding: 0;
+}
+.action:link, .action:visited { background: #f3f3f3; border: 1px outset #ddd;
+ color: #666; font-size: 90%; padding: 0 .3em;
+}
+.action:link:hover, .action:visited:hover { background: #e8e8e8;
+ border-color: #aaa; color: #000; text-decoration: none;
+}
+
+form p.hint { color: #666; font-size: 90%; font-style: italic; margin: 0; }
+form .error { color: #b00; }
diff --git a/lib/genshi/examples/tutorial/geddit/static/logo.gif b/lib/genshi/examples/tutorial/geddit/static/logo.gif
new file mode 100644
index 0000000000000000000000000000000000000000..60267e548999abf7ddb818be82c0e9397ef51b7b
GIT binary patch
[cut]
diff --git a/lib/genshi/examples/tutorial/geddit/templates/_comment.html b/lib/genshi/examples/tutorial/geddit/templates/_comment.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/templates/_comment.html
@@ -0,0 +1,5 @@
+<?python from genshi import HTML ?>
+<li id="comment$num">
+ <strong>${comment.username}</strong> at ${comment.time.strftime('%x %X')}
+ <blockquote>${HTML(comment.content)}</blockquote>
+</li>
diff --git a/lib/genshi/examples/tutorial/geddit/templates/_form.html b/lib/genshi/examples/tutorial/geddit/templates/_form.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/templates/_form.html
@@ -0,0 +1,24 @@
+<form xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ class="comment" action="${url('/comment/%s/' % link.id)}" method="post">
+ <table summary=""><tbody><tr>
+ <th><label for="username">Your name:</label></th>
+ <td>
+ <input type="text" id="username" name="username" />
+ <span py:if="'username' in errors" class="error">${errors.username}</span>
+ </td>
+ </tr><tr>
+ <th><label for="comment">Comment:</label></th>
+ <td>
+ <textarea id="comment" name="content" rows="6" cols="50"></textarea>
+ <span py:if="'content' in errors" class="error"><br />${errors.content}</span>
+ <p class="hint">You can use HTML tags here for formatting.</p>
+ </td>
+ </tr><tr>
+ <td></td>
+ <td>
+ <input type="submit" value="Submit" />
+ <input type="submit" name="cancel" value="Cancel" />
+ </td>
+ </tr></tbody></table>
+</form>
diff --git a/lib/genshi/examples/tutorial/geddit/templates/comment.html b/lib/genshi/examples/tutorial/geddit/templates/comment.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/templates/comment.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:py="http://genshi.edgewall.org/">
+ <xi:include href="layout.html" />
+ <head>
+ <title>Comment on “${link.title}”</title>
+ </head>
+ <body class="comment">
+ <h1>Comment on “${link.title}”</h1>
+ <p py:if="comment">
+ In reply to <strong>${comment.username}</strong>
+ at ${comment.time.strftime('%x %X')}:
+ <blockquote>${comment.content}</blockquote>
+ </p>
+ <xi:include href="_form.html" />
+ </body>
+</html>
diff --git a/lib/genshi/examples/tutorial/geddit/templates/index.html b/lib/genshi/examples/tutorial/geddit/templates/index.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/templates/index.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:py="http://genshi.edgewall.org/">
+ <xi:include href="layout.html" />
+ <head>
+ <title>News</title>
+ <link rel="alternate" type="application/atom+xml" title="Geddit"
+ href="${url('/feed/')}" />
+ </head>
+ <body class="index">
+ <h1>News</h1>
+
+ <ol py:if="links" class="links">
+ <li py:for="link in links">
+ <a href="${link.url}">${link.title}</a>
+ posted by ${link.username} at ${link.time.strftime('%x %X')}
+ <div class="info">
+ <a href="${url('/info/%s/' % link.id)}">
+ ${len(link.comments)} comments
+ </a>
+ </div>
+ </li>
+ </ol>
+
+ <p><a class="action" href="${url('/submit/')}">Submit new link</a></p>
+ </body>
+</html>
diff --git a/lib/genshi/examples/tutorial/geddit/templates/index.xml b/lib/genshi/examples/tutorial/geddit/templates/index.xml
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/templates/index.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom"
+ xmlns:py="http://genshi.edgewall.org/">
+
+ <title>Geddit News</title>
+ <id href="${url('/')}"/>
+ <link rel="alternate" href="${url('/')}" type="text/html"/>
+ <link rel="self" href="${url('/feed/')}" type="application/atom+xml"/>
+ <updated>${links[0].time.isoformat()}</updated>
+
+ <entry py:for="link in reversed(links)">
+ <title>${link.url}</title>
+ <link rel="alternate" href="${link.url}" type="text/html"/>
+ <link rel="via" href="${url('/info/%s/' % link.id)}" type="text/html"/>
+ <id>${url('/info/%s/' % link.id)}</id>
+ <author>
+ <name>${link.username}</name>
+ </author>
+ <updated>${link.time.isoformat()}</updated>
+ <summary>${link.title}</summary>
+ </entry>
+
+</feed>
diff --git a/lib/genshi/examples/tutorial/geddit/templates/info.html b/lib/genshi/examples/tutorial/geddit/templates/info.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/templates/info.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:py="http://genshi.edgewall.org/">
+ <xi:include href="layout.html" />
+ <head>
+ <title>${link.title}</title>
+ <link rel="alternate" type="application/atom+xml" title="Geddit: ${link.title}"
+ href="${url('/feed/%s/' % link.id)}" />
+ <script type="text/javascript">
+ function loadCommentForm(a) {
+ $.get("${url('/comment/%s/' % link.id)}", {}, function(html) {
+ var form = a.hide().parent().after(html).next();
+ function closeForm() {
+ form.slideUp("fast", function() { a.fadeIn(); form.remove() });
+ return false;
+ }
+ function initForm() {
+ form.find("input[@name='cancel']").click(closeForm);
+ form.submit(function() {
+ var data = form.find("input[@type='text'], textarea").serialize();
+ $.post("${url('/comment/%s/' % link.id)}", data, function(html) {
+ var elem = $(html).get(0);
+ if (/form/i.test(elem.tagName)) {
+ form.after(elem).remove();
+ form = $(elem);
+ initForm();
+ } else {
+ if ($("ul.comments").length == 0) {
+ a.parent().before('<ul class="comments"></ul>');
+ }
+ $("ul.comments").append($(elem));
+ closeForm();
+ }
+ });
+ return false;
+ });
+ }
+ initForm();
+ });
+ }
+ $(document).ready(function() {
+ $("a.action").click(function() {
+ loadCommentForm($(this));
+ return false;
+ });
+ });
+ </script>
+ </head>
+ <body class="info">
+ <h1>${link.title}</h1>
+ <a href="${link.url}">${link.url}</a><br />
+ posted by ${link.username} at ${link.time.strftime('%x %X')}<br />
+
+ <ul py:if="link.comments" class="comments">
+ <xi:include href="_comment.html"
+ py:for="num, comment in enumerate(link.comments)" />
+ </ul>
+
+ <p><a class="action" href="${url('/comment/%s/' % link.id)}">Add comment</a></p>
+ </body>
+</html>
diff --git a/lib/genshi/examples/tutorial/geddit/templates/info.xml b/lib/genshi/examples/tutorial/geddit/templates/info.xml
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/templates/info.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom"
+ xmlns:py="http://genshi.edgewall.org/">
+
+ <title>Geddit: ${link.title}</title>
+ <id href="${url('/info/%s/' % link.id)}"/>
+ <link rel="alternate" href="${url('/info/%s/' % link.id)}" type="text/html"/>
+ <link rel="self" href="${url('/feed/%s/' % link.id)}" type="application/atom+xml"/>
+ <updated py:with="time=link.comments and link.comments[-1].time or link.time">
+ ${time.isoformat()}
+ </updated>
+
+ <?python from genshi import HTML ?>
+ <entry py:for="idx, comment in enumerate(reversed(link.comments))">
+ <title>Comment ${len(link.comments) - idx} on “${link.title}”</title>
+ <link rel="alternate" href="${url('/info/%s/' % link.id)}#comment${idx}"
+ type="text/html"/>
+ <id>${url('/info/%s/' % link.id)}#comment${idx}</id>
+ <author>
+ <name>${comment.username}</name>
+ </author>
+ <updated>${comment.time.isoformat()}</updated>
+ <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml">
+ ${HTML(comment.content)}
+ </div></content>
+ </entry>
+
+</feed>
diff --git a/lib/genshi/examples/tutorial/geddit/templates/layout.html b/lib/genshi/examples/tutorial/geddit/templates/layout.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/templates/layout.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/" py:strip="">
+
+ <py:match path="head" once="true">
+ <head py:attrs="select('@*')">
+ <title py:with="title = list(select('title/text()'))">
+ Geddit<py:if test="title">: ${title}</py:if>
+ </title>
+ <link rel="stylesheet" href="${url('/media/layout.css')}" type="text/css" />
+ <script type="text/javascript" src="${url('/media/jquery.js')}"></script>
+ ${select('*[local-name()!="title"]')}
+ </head>
+ </py:match>
+
+ <py:match path="body" once="true">
+ <body py:attrs="select('@*')"><div id="wrap">
+ <div id="header">
+ <a href="/"><img src="${url('/media/logo.gif')}" width="201" height="79" alt="geddit?" /></a>
+ </div>
+ <div id="content">
+ ${select('*|text()')}
+ </div>
+ <div id="footer">
+ <hr />
+ <p class="legalese">© 2007 Edgewall Software</p>
+ </div>
+ </div></body>
+ </py:match>
+
+</html>
diff --git a/lib/genshi/examples/tutorial/geddit/templates/submit.html b/lib/genshi/examples/tutorial/geddit/templates/submit.html
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples/tutorial/geddit/templates/submit.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:py="http://genshi.edgewall.org/">
+ <xi:include href="layout.html" />
+ <head>
+ <title>Submit new link</title>
+ </head>
+ <body class="submit">
+ <h1>Submit new link</h1>
+
+ <form action="" method="post">
+ <table summary=""><tbody><tr>
+ <th><label for="username">Your name:</label></th>
+ <td>
+ <input type="text" id="username" name="username" />
+ <span py:if="'username' in errors" class="error">${errors.username}</span>
+ </td>
+ </tr><tr>
+ <th><label for="url">Link URL:</label></th>
+ <td>
+ <input type="text" id="url" name="url" />
+ <span py:if="'url' in errors" class="error">${errors.url}</span>
+ </td>
+ </tr><tr>
+ <th><label for="title">Title:</label></th>
+ <td>
+ <input type="text" name="title" />
+ <span py:if="'title' in errors" class="error">${errors.title}</span>
+ </td>
+ </tr><tr>
+ <td></td>
+ <td>
+ <input type="submit" value="Submit" />
+ <input type="submit" name="cancel" value="Cancel" />
+ </td>
+ </tr></tbody></table>
+ </form>
+
+ </body>
+</html>
diff --git a/lib/genshi/examples_to_py3k.sh b/lib/genshi/examples_to_py3k.sh
new file mode 100644
--- /dev/null
+++ b/lib/genshi/examples_to_py3k.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# Script to run 2to3 on files not covered by setup.py
+#
+export PYTHONIOENCODING=utf8
+
+# General 2to3 run
+2to3 -w --no-diffs examples/
diff --git a/lib/genshi/fixes/__init__.py b/lib/genshi/fixes/__init__.py
new file mode 100644
diff --git a/lib/genshi/fixes/fix_unicode_in_strings.py b/lib/genshi/fixes/fix_unicode_in_strings.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/fixes/fix_unicode_in_strings.py
@@ -0,0 +1,17 @@
+"""Fixer that changes expressions inside strings literals from u"..." to "...".
+
+"""
+
+import re
+from lib2to3 import fixer_base
+
+_literal_re = re.compile(r"(.+?)\b[uU]([rR]?[\'\"])")
+
+class FixUnicodeInStrings(fixer_base.BaseFix):
+
+ PATTERN = "STRING"
+
+ def transform(self, node, results):
+ new = node.clone()
+ new.value = _literal_re.sub(r"\1\2", new.value)
+ return new
diff --git a/lib/genshi/genshi/__init__.py b/lib/genshi/genshi/__init__.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/genshi/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""This package provides various means for generating and processing web markup
+(XML or HTML).
+
+The design is centered around the concept of streams of markup events (similar
+in concept to SAX parsing events) which can be processed in a uniform manner
+independently of where or how they are produced.
+"""
+
+__docformat__ = 'restructuredtext en'
+__version__ = '0.7'
+
+from genshi.core import *
+from genshi.input import ParseError, XML, HTML
diff --git a/lib/genshi/genshi/_speedups.c b/lib/genshi/genshi/_speedups.c
new file mode 100644
--- /dev/null
+++ b/lib/genshi/genshi/_speedups.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2006-2008 Edgewall Software
+ * All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://genshi.edgewall.org/wiki/License.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals. For the exact contribution history, see the revision
+ * history and logs, available at http://genshi.edgewall.org/log/.
+ */
+
+#include <Python.h>
+#include <structmember.h>
+
+#if PY_MAJOR_VERSION > 2
+# define IS_PY3K
+#elif PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+ typedef int Py_ssize_t;
+# define PY_SSIZE_T_MAX INT_MAX
+# define PY_SSIZE_T_MIN INT_MIN
+#endif
+
+/* We only use Unicode Strings in this module */
+#ifndef IS_PY3K
+# define PyObject_Str PyObject_Unicode
+#endif
+
+static PyObject *amp1, *amp2, *lt1, *lt2, *gt1, *gt2, *qt1, *qt2;
+static PyObject *stripentities, *striptags;
+
+static void
+init_constants(void)
+{
+ PyObject *util = PyImport_ImportModule("genshi.util");
+ stripentities = PyObject_GetAttrString(util, "stripentities");
+ striptags = PyObject_GetAttrString(util, "striptags");
+ Py_DECREF(util);
+
+ amp1 = PyUnicode_DecodeASCII("&", 1, NULL);
+ amp2 = PyUnicode_DecodeASCII("&", 5, NULL);
+ lt1 = PyUnicode_DecodeASCII("<", 1, NULL);
+ lt2 = PyUnicode_DecodeASCII("<", 4, NULL);
+ gt1 = PyUnicode_DecodeASCII(">", 1, NULL);
+ gt2 = PyUnicode_DecodeASCII(">", 4, NULL);
+ qt1 = PyUnicode_DecodeASCII("\"", 1, NULL);
+ qt2 = PyUnicode_DecodeASCII(""", 5, NULL);
+}
+
+/* Markup class */
+
+PyTypeObject MarkupType; /* declared later */
+
+PyDoc_STRVAR(Markup__doc__,
+"Marks a string as being safe for inclusion in HTML/XML output without\n\
+needing to be escaped.");
+
+static PyObject *
+escape(PyObject *text, int quotes)
+{
+ PyObject *args, *ret;
+ PyUnicodeObject *in, *out;
+ Py_UNICODE *inp, *outp;
+ int len, inn, outn;
+
+ if (PyObject_TypeCheck(text, &MarkupType)) {
+ Py_INCREF(text);
+ return text;
+ }
+ if (PyObject_HasAttrString(text, "__html__")) {
+ ret = PyObject_CallMethod(text, "__html__", NULL);
+ args = PyTuple_New(1);
+ if (args == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args, 0, ret);
+ ret = MarkupType.tp_new(&MarkupType, args, NULL);
+ Py_DECREF(args);
+ return ret;
+ }
+ in = (PyUnicodeObject *) PyObject_Str(text);
+ if (in == NULL) {
+ return NULL;
+ }
+ /* First we need to figure out how long the escaped string will be */
+ len = inn = 0;
+ inp = in->str;
+ while (*(inp) || in->length > inp - in->str) {
+ switch (*inp++) {
+ case '&': len += 5; inn++; break;
+ case '"': len += quotes ? 5 : 1; inn += quotes ? 1 : 0; break;
+ case '<':
+ case '>': len += 4; inn++; break;
+ default: len++;
+ }
+ }
+
+ /* Do we need to escape anything at all? */
+ if (!inn) {
+ args = PyTuple_New(1);
+ if (args == NULL) {
+ Py_DECREF((PyObject *) in);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args, 0, (PyObject *) in);
+ ret = MarkupType.tp_new(&MarkupType, args, NULL);
+ Py_DECREF(args);
+ return ret;
+ }
+
+ out = (PyUnicodeObject*) PyUnicode_FromUnicode(NULL, len);
+ if (out == NULL) {
+ Py_DECREF((PyObject *) in);
+ return NULL;
+ }
+
+ outn = 0;
+ inp = in->str;
+ outp = out->str;
+ while (*(inp) || in->length > inp - in->str) {
+ if (outn == inn) {
+ /* copy rest of string if we have already replaced everything */
+ Py_UNICODE_COPY(outp, inp, in->length - (inp - in->str));
+ break;
+ }
+ switch (*inp) {
+ case '&':
+ Py_UNICODE_COPY(outp, ((PyUnicodeObject *) amp2)->str, 5);
+ outp += 5;
+ outn++;
+ break;
+ case '"':
+ if (quotes) {
+ Py_UNICODE_COPY(outp, ((PyUnicodeObject *) qt2)->str, 5);
+ outp += 5;
+ outn++;
+ } else {
+ *outp++ = *inp;
+ }
+ break;
+ case '<':
+ Py_UNICODE_COPY(outp, ((PyUnicodeObject *) lt2)->str, 4);
+ outp += 4;
+ outn++;
+ break;
+ case '>':
+ Py_UNICODE_COPY(outp, ((PyUnicodeObject *) gt2)->str, 4);
+ outp += 4;
+ outn++;
+ break;
+ default:
+ *outp++ = *inp;
+ }
+ inp++;
+ }
+
+ Py_DECREF((PyObject *) in);
+
+ args = PyTuple_New(1);
+ if (args == NULL) {
+ Py_DECREF((PyObject *) out);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args, 0, (PyObject *) out);
+ ret = MarkupType.tp_new(&MarkupType, args, NULL);
+ Py_DECREF(args);
+ return ret;
+}
+
+PyDoc_STRVAR(escape__doc__,
+"Create a Markup instance from a string and escape special characters\n\
+it may contain (<, >, & and \").\n\
+\n\
+>>> escape('\"1 < 2\"')\n\
+<Markup u'"1 < 2"'>\n\
+\n\
+If the `quotes` parameter is set to `False`, the \" character is left\n\
+as is. Escaping quotes is generally only required for strings that are\n\
+to be used in attribute values.\n\
+\n\
+>>> escape('\"1 < 2\"', quotes=False)\n\
+<Markup u'\"1 < 2\"'>\n\
+\n\
+:param text: the text to escape\n\
+:param quotes: if ``True``, double quote characters are escaped in\n\
+ addition to the other special characters\n\
+:return: the escaped `Markup` string\n\
+:rtype: `Markup`\n\
+");
+
+static PyObject *
+Markup_escape(PyTypeObject* type, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"text", "quotes", 0};
+ PyObject *text = NULL;
+ char quotes = 1;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|b", kwlist, &text, "es)) {
+ return NULL;
+ }
+ if (PyObject_Not(text)) {
+ args = PyTuple_New(0);
+ if (args == NULL)
+ return NULL;
+ text = type->tp_new(type, args, NULL);
+ Py_DECREF(args);
+ return text;
+ }
+ if (PyObject_TypeCheck(text, type)) {
+ Py_INCREF(text);
+ return text;
+ }
+ return escape(text, quotes);
+}
+
+static PyObject *
+Markup_html(PyObject *self)
+{
+ Py_INCREF(self);
+ return self;
+}
+
+PyDoc_STRVAR(join__doc__,
+"Return a `Markup` object which is the concatenation of the strings\n\
+in the given sequence, where this `Markup` object is the separator\n\
+between the joined elements.\n\
+\n\
+Any element in the sequence that is not a `Markup` instance is\n\
+automatically escaped.\n\
+\n\
+:param seq: the sequence of strings to join\n\
+:param escape_quotes: whether double quote characters in the elements\n\
+ should be escaped\n\
+:return: the joined `Markup` object\n\
+:rtype: `Markup`\n\
+:see: `escape`\n\
+");
+
+static PyObject *
+Markup_join(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"seq", "escape_quotes", 0};
+ PyObject *seq = NULL, *seq2, *tmp, *tmp2;
+ char quotes = 1;
+ Py_ssize_t n;
+ int i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|b", kwlist, &seq, "es)) {
+ return NULL;
+ }
+ if (!PySequence_Check(seq)) {
+ return NULL;
+ }
+ n = PySequence_Size(seq);
+ if (n < 0) {
+ return NULL;
+ }
+ seq2 = PyTuple_New(n);
+ if (seq2 == NULL) {
+ return NULL;
+ }
+ for (i = 0; i < n; i++) {
+ tmp = PySequence_GetItem(seq, i);
+ if (tmp == NULL) {
+ Py_DECREF(seq2);
+ return NULL;
+ }
+ tmp2 = escape(tmp, quotes);
+ if (tmp2 == NULL) {
+ Py_DECREF(seq2);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(seq2, i, tmp2);
+ Py_DECREF(tmp);
+ }
+ tmp = PyUnicode_Join(self, seq2);
+ Py_DECREF(seq2);
+ if (tmp == NULL)
+ return NULL;
+ args = PyTuple_New(1);
+ if (args == NULL) {
+ Py_DECREF(tmp);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args, 0, tmp);
+ tmp = MarkupType.tp_new(&MarkupType, args, NULL);
+ Py_DECREF(args);
+ return tmp;
+}
+
+static PyObject *
+Markup_add(PyObject *self, PyObject *other)
+{
+ PyObject *tmp, *tmp2, *args, *ret;
+ if (PyObject_TypeCheck(self, &MarkupType)) {
+ tmp = escape(other, 1);
+ if (tmp == NULL)
+ return NULL;
+ tmp2 = PyUnicode_Concat(self, tmp);
+ } else { // __radd__
+ tmp = escape(self, 1);
+ if (tmp == NULL)
+ return NULL;
+ tmp2 = PyUnicode_Concat(tmp, other);
+ }
+ Py_DECREF(tmp);
+ if (tmp2 == NULL)
+ return NULL;
+ args = PyTuple_New(1);
+ if (args == NULL) {
+ Py_DECREF(tmp2);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args, 0, tmp2);
+ ret = MarkupType.tp_new(&MarkupType, args, NULL);
+ Py_DECREF(args);
+ return ret;
+}
+
+static PyObject *
+Markup_mod(PyObject *self, PyObject *args)
+{
+ PyObject *tmp, *tmp2, *ret, *args2;
+ int i;
+ Py_ssize_t nargs = 0;
+ PyObject *kwds = NULL;
+
+ if (PyDict_Check(args)) {
+ kwds = args;
+ }
+ if (kwds && PyDict_Size(kwds)) {
+ PyObject *kwcopy, *key, *value;
+ Py_ssize_t pos = 0;
+
+ kwcopy = PyDict_Copy( kwds );
+ if (kwcopy == NULL) {
+ return NULL;
+ }
+ while (PyDict_Next(kwcopy, &pos, &key, &value)) {
+ tmp = escape(value, 1);
+ if (tmp == NULL) {
+ Py_DECREF(kwcopy);
+ return NULL;
+ }
+ if (PyDict_SetItem(kwcopy, key, tmp) < 0) {
+ Py_DECREF(tmp);
+ Py_DECREF(kwcopy);
+ return NULL;
+ }
+ }
+ tmp = PyUnicode_Format(self, kwcopy);
+ Py_DECREF(kwcopy);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ } else if (PyTuple_Check(args)) {
+ nargs = PyTuple_GET_SIZE(args);
+ args2 = PyTuple_New(nargs);
+ if (args2 == NULL) {
+ return NULL;
+ }
+ for (i = 0; i < nargs; i++) {
+ tmp = escape(PyTuple_GET_ITEM(args, i), 1);
+ if (tmp == NULL) {
+ Py_DECREF(args2);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args2, i, tmp);
+ }
+ tmp = PyUnicode_Format(self, args2);
+ Py_DECREF(args2);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ } else {
+ tmp2 = escape(args, 1);
+ if (tmp2 == NULL) {
+ return NULL;
+ }
+ tmp = PyUnicode_Format(self, tmp2);
+ Py_DECREF(tmp2);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ }
+ args = PyTuple_New(1);
+ if (args == NULL) {
+ Py_DECREF(tmp);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args, 0, tmp);
+ ret = PyUnicode_Type.tp_new(&MarkupType, args, NULL);
+ Py_DECREF(args);
+ return ret;
+}
+
+static PyObject *
+Markup_mul(PyObject *self, PyObject *num)
+{
+ PyObject *unicode, *result, *args;
+
+ if (PyObject_TypeCheck(self, &MarkupType)) {
+ unicode = PyObject_Str(self);
+ if (unicode == NULL) return NULL;
+ result = PyNumber_Multiply(unicode, num);
+ } else { // __rmul__
+ unicode = PyObject_Str(num);
+ if (unicode == NULL) return NULL;
+ result = PyNumber_Multiply(unicode, self);
+ }
+ Py_DECREF(unicode);
+
+ if (result == NULL) return NULL;
+ args = PyTuple_New(1);
+ if (args == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args, 0, result);
+ result = PyUnicode_Type.tp_new(&MarkupType, args, NULL);
+ Py_DECREF(args);
+
+ return result;
+}
+
+static PyObject *
+Markup_repr(PyObject *self)
+{
+ PyObject *format, *result, *args;
+
+#ifdef IS_PY3K
+ format = PyUnicode_FromString("<Markup %r>");
+#else
+ format = PyString_FromString("<Markup %r>");
+#endif
+ if (format == NULL) return NULL;
+ result = PyObject_Str(self);
+ if (result == NULL) {
+ Py_DECREF(format);
+ return NULL;
+ }
+ args = PyTuple_New(1);
+ if (args == NULL) {
+ Py_DECREF(format);
+ Py_DECREF(result);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args, 0, result);
+#ifdef IS_PY3K
+ result = PyUnicode_Format(format, args);
+#else
+ result = PyString_Format(format, args);
+#endif
+ Py_DECREF(format);
+ Py_DECREF(args);
+ return result;
+}
+
+PyDoc_STRVAR(unescape__doc__,
+"Reverse-escapes &, <, >, and \" and returns a `unicode` object.\n\
+\n\
+>>> Markup('1 < 2').unescape()\n\
+u'1 < 2'\n\
+\n\
+:return: the unescaped string\n\
+:rtype: `unicode`\n\
+:see: `genshi.core.unescape`\n\
+");
+
+static PyObject *
+Markup_unescape(PyObject* self)
+{
+ PyObject *tmp, *tmp2;
+
+ tmp = PyUnicode_Replace(self, qt2, qt1, -1);
+ if (tmp == NULL) return NULL;
+ tmp2 = PyUnicode_Replace(tmp, gt2, gt1, -1);
+ Py_DECREF(tmp);
+ if (tmp2 == NULL) return NULL;
+ tmp = PyUnicode_Replace(tmp2, lt2, lt1, -1);
+ Py_DECREF(tmp2);
+ if (tmp == NULL) return NULL;
+ tmp2 = PyUnicode_Replace(tmp, amp2, amp1, -1);
+ Py_DECREF(tmp);
+ return tmp2;
+}
+
+PyDoc_STRVAR(stripentities__doc__,
+"Return a copy of the text with any character or numeric entities\n\
+replaced by the equivalent UTF-8 characters.\n\
+\n\
+If the `keepxmlentities` parameter is provided and evaluates to `True`,\n\
+the core XML entities (``&``, ``'``, ``>``, ``<`` and\n\
+``"``) are not stripped.\n\
+\n\
+:return: a `Markup` instance with entities removed\n\
+:rtype: `Markup`\n\
+:see: `genshi.util.stripentities`\n\
+");
+
+static PyObject *
+Markup_stripentities(PyObject* self, PyObject *args, PyObject *kwds)
+{
+ static char *kwlist[] = {"keepxmlentities", 0};
+ PyObject *result, *args2;
+ char keepxml = 0;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|b", kwlist, &keepxml)) {
+ return NULL;
+ }
+
+ if (stripentities == NULL) return NULL;
+ result = PyObject_CallFunction(stripentities, "Ob", self, keepxml);
+ if (result == NULL) return NULL;
+ args2 = PyTuple_New(1);
+ if (args2 == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args2, 0, result);
+ result = MarkupType.tp_new(&MarkupType, args2, NULL);
+ Py_DECREF(args2);
+ return result;
+}
+
+PyDoc_STRVAR(striptags__doc__,
+"""Return a copy of the text with all XML/HTML tags removed.\n\
+\n\
+:return: a `Markup` instance with all tags removed\n\
+:rtype: `Markup`\n\
+:see: `genshi.util.striptags`\n\
+");
+
+static PyObject *
+Markup_striptags(PyObject* self)
+{
+ PyObject *result, *args;
+
+ if (striptags == NULL) return NULL;
+ result = PyObject_CallFunction(striptags, "O", self);
+ if (result == NULL) return NULL;
+ args = PyTuple_New(1);
+ if (args == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(args, 0, result);
+ result = MarkupType.tp_new(&MarkupType, args, NULL);
+ Py_DECREF(args);
+ return result;
+}
+
+typedef struct {
+ PyUnicodeObject HEAD;
+} MarkupObject;
+
+static PyMethodDef Markup_methods[] = {
+ {"__html__", (PyCFunction) Markup_html, METH_NOARGS, NULL},
+ {"escape", (PyCFunction) Markup_escape,
+ METH_VARARGS|METH_CLASS|METH_KEYWORDS, escape__doc__},
+ {"join", (PyCFunction)Markup_join, METH_VARARGS|METH_KEYWORDS, join__doc__},
+ {"unescape", (PyCFunction)Markup_unescape, METH_NOARGS, unescape__doc__},
+ {"stripentities", (PyCFunction) Markup_stripentities,
+ METH_VARARGS|METH_KEYWORDS, stripentities__doc__},
+ {"striptags", (PyCFunction) Markup_striptags, METH_NOARGS,
+ striptags__doc__},
+ {NULL} /* Sentinel */
+};
+
+static PyNumberMethods Markup_as_number = {
+ Markup_add, /*nb_add*/
+ 0, /*nb_subtract*/
+ Markup_mul, /*nb_multiply*/
+#ifndef IS_PY3K
+ 0, /*nb_divide*/
+#endif
+ Markup_mod, /*nb_remainder*/
+};
+
+PyTypeObject MarkupType = {
+#ifdef IS_PY3K
+ PyVarObject_HEAD_INIT(NULL, 0)
+#else
+ PyObject_HEAD_INIT(NULL)
+ 0,
+#endif
+ "genshi._speedups.Markup",
+ sizeof(MarkupObject),
+ 0,
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+#ifdef IS_PY3K
+ 0, /*tp_reserved*/
+#else
+ 0, /*tp_compare*/
+#endif
+ Markup_repr, /*tp_repr*/
+ &Markup_as_number, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+
+#ifdef IS_PY3K
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_UNICODE_SUBCLASS, /*tp_flags*/
+#elif defined(Py_TPFLAGS_UNICODE_SUBCLASS)
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_UNICODE_SUBCLASS, /*tp_flags*/
+#else
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES, /*tp_flags*/
+#endif
+
+ Markup__doc__,/*tp_doc*/
+
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+
+ /* Attribute descriptor and subclassing stuff */
+
+ Markup_methods,/*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+
+ 0, /*tp_init*/
+ 0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
+ 0, /*tp_new*/
+ 0, /*tp_free Low-level free-memory routine */
+ 0, /*tp_is_gc For PyObject_IS_GC */
+ 0, /*tp_bases*/
+ 0, /*tp_mro method resolution order */
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0 /*tp_weaklist*/
+};
+
+#ifdef IS_PY3K
+struct PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT, /*m_base*/
+ "_speedups", /*m_name*/
+ NULL, /*m_doc*/
+ -1, /*m_size*/
+ NULL, /*m_methods*/
+ NULL, /*m_reload*/
+ NULL, /*m_traverse*/
+ NULL, /*m_clear*/
+ NULL /*m_free*/
+};
+
+PyObject *
+PyInit__speedups(void)
+#else
+PyMODINIT_FUNC
+init_speedups(void)
+#endif
+{
+ PyObject *module;
+
+ /* Workaround for quirk in Visual Studio, see
+ <http://www.python.it/faq/faq-3.html#3.24> */
+ MarkupType.tp_base = &PyUnicode_Type;
+
+ if (PyType_Ready(&MarkupType) < 0)
+#ifdef IS_PY3K
+ return NULL;
+#else
+ return;
+#endif
+
+ init_constants();
+
+#ifdef IS_PY3K
+ module = PyModule_Create(&module_def);
+#else
+ module = Py_InitModule("_speedups", NULL);
+#endif
+ Py_INCREF(&MarkupType);
+ PyModule_AddObject(module, "Markup", (PyObject *) &MarkupType);
+
+#ifdef IS_PY3K
+ return module;
+#endif
+}
diff --git a/lib/genshi/genshi/builder.py b/lib/genshi/genshi/builder.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/genshi/builder.py
@@ -0,0 +1,359 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Support for programmatically generating markup streams from Python code using
+a very simple syntax. The main entry point to this 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
+this module.
+
+Elements can be created using the `tag` object using attribute access. For
+example:
+
+>>> 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 `Element.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 #doctest: +ELLIPSIS
+<genshi.core.Stream object at ...>
+>>> print(stream)
+<p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p>
+
+
+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, which
+returns an object of type `Fragment`:
+
+>>> fragment = tag('Hello, ', tag.em('world'), '!')
+>>> fragment
+<Fragment>
+>>> print(fragment)
+Hello, <em>world</em>!
+"""
+
+from genshi.core import Attrs, Markup, Namespace, QName, Stream, \
+ START, END, TEXT
+
+__all__ = ['Fragment', 'Element', 'ElementFactory', 'tag']
+__docformat__ = 'restructuredtext en'
+
+
+class Fragment(object):
+ """Represents a markup fragment, which is basically just a list of element
+ or text nodes.
+ """
+ __slots__ = ['children']
+
+ def __init__(self):
+ """Create a new fragment."""
+ self.children = []
+
+ def __add__(self, other):
+ return Fragment()(self, other)
+
+ def __call__(self, *args):
+ """Append any positional arguments as child nodes.
+
+ :see: `append`
+ """
+ for arg in args:
+ self.append(arg)
+ return self
+
+ def __iter__(self):
+ return self._generate()
+
+ def __repr__(self):
+ return '<%s>' % type(self).__name__
+
+ def __str__(self):
+ return str(self.generate())
+
+ def __unicode__(self):
+ return unicode(self.generate())
+
+ def __html__(self):
+ return Markup(self.generate())
+
+ def append(self, node):
+ """Append an element or string as child node.
+
+ :param node: the node to append; can be an `Element`, `Fragment`, or a
+ `Stream`, or a Python string or number
+ """
+ if isinstance(node, (Stream, Element, basestring, int, float, long)):
+ # For objects of a known/primitive type, we avoid the check for
+ # whether it is iterable for better performance
+ self.children.append(node)
+ elif isinstance(node, Fragment):
+ self.children.extend(node.children)
+ elif node is not None:
+ try:
+ for child in node:
+ self.append(child)
+ except TypeError:
+ self.children.append(node)
+
+ def _generate(self):
+ for child in self.children:
+ if isinstance(child, Fragment):
+ for event in child._generate():
+ yield event
+ elif isinstance(child, Stream):
+ for event in child:
+ yield event
+ else:
+ if not isinstance(child, basestring):
+ child = unicode(child)
+ yield TEXT, child, (None, -1, -1)
+
+ def generate(self):
+ """Return a markup event stream for the fragment.
+
+ :rtype: `Stream`
+ """
+ return Stream(self._generate())
+
+
+def _kwargs_to_attrs(kwargs):
+ attrs = []
+ names = set()
+ for name, value in kwargs.items():
+ name = name.rstrip('_').replace('_', '-')
+ if value is not None and name not in names:
+ attrs.append((QName(name), unicode(value)))
+ names.add(name)
+ return Attrs(attrs)
+
+
+class Element(Fragment):
+ """Simple XML output generator based on the builder pattern.
+
+ Construct XML elements by passing the tag name to the constructor:
+
+ >>> print(Element('strong'))
+ <strong/>
+
+ Attributes can be specified using keyword arguments. The values of the
+ arguments will be converted to strings and any special XML characters
+ escaped:
+
+ >>> print(Element('textarea', rows=10, cols=60))
+ <textarea rows="10" cols="60"/>
+ >>> print(Element('span', title='1 < 2'))
+ <span title="1 < 2"/>
+ >>> print(Element('span', title='"baz"'))
+ <span title=""baz""/>
+
+ The " character is escaped using a numerical entity.
+ The order in which attributes are rendered is undefined.
+
+ If an attribute value evaluates to `None`, that attribute is not included
+ in the output:
+
+ >>> print(Element('a', name=None))
+ <a/>
+
+ Attribute names that conflict with Python keywords can be specified by
+ appending an underscore:
+
+ >>> print(Element('div', class_='warning'))
+ <div class="warning"/>
+
+ Nested elements can be added to an element using item access notation.
+ The call notation can also be used for this and for adding attributes
+ using keyword arguments, as one would do in the constructor.
+
+ >>> print(Element('ul')(Element('li'), Element('li')))
+ <ul><li/><li/></ul>
+ >>> print(Element('a')('Label'))
+ <a>Label</a>
+ >>> print(Element('a')('Label', href="target"))
+ <a href="target">Label</a>
+
+ Text nodes can be nested in an element by adding strings instead of
+ elements. Any special characters in the strings are escaped automatically:
+
+ >>> print(Element('em')('Hello world'))
+ <em>Hello world</em>
+ >>> print(Element('em')(42))
+ <em>42</em>
+ >>> print(Element('em')('1 < 2'))
+ <em>1 < 2</em>
+
+ This technique also allows mixed content:
+
+ >>> print(Element('p')('Hello ', Element('b')('world')))
+ <p>Hello <b>world</b></p>
+
+ Quotes are not escaped inside text nodes:
+ >>> print(Element('p')('"Hello"'))
+ <p>"Hello"</p>
+
+ Elements can also be combined with other elements or strings using the
+ addition operator, which results in a `Fragment` object that contains the
+ operands:
+
+ >>> print(Element('br') + 'some text' + Element('br'))
+ <br/>some text<br/>
+
+ Elements with a namespace can be generated using the `Namespace` and/or
+ `QName` classes:
+
+ >>> from genshi.core import Namespace
+ >>> xhtml = Namespace('http://www.w3.org/1999/xhtml')
+ >>> print(Element(xhtml.html, lang='en'))
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en"/>
+ """
+ __slots__ = ['tag', 'attrib']
+
+ def __init__(self, tag_, **attrib):
+ Fragment.__init__(self)
+ self.tag = QName(tag_)
+ self.attrib = _kwargs_to_attrs(attrib)
+
+ def __call__(self, *args, **kwargs):
+ """Append any positional arguments as child nodes, and keyword arguments
+ as attributes.
+
+ :return: the element itself so that calls can be chained
+ :rtype: `Element`
+ :see: `Fragment.append`
+ """
+ self.attrib |= _kwargs_to_attrs(kwargs)
+ Fragment.__call__(self, *args)
+ return self
+
+ def __repr__(self):
+ return '<%s "%s">' % (type(self).__name__, self.tag)
+
+ def _generate(self):
+ yield START, (self.tag, self.attrib), (None, -1, -1)
+ for kind, data, pos in Fragment._generate(self):
+ yield kind, data, pos
+ yield END, self.tag, (None, -1, -1)
+
+ def generate(self):
+ """Return a markup event stream for the fragment.
+
+ :rtype: `Stream`
+ """
+ return Stream(self._generate())
+
+
+class ElementFactory(object):
+ """Factory for `Element` objects.
+
+ A new element is created simply by accessing a correspondingly named
+ attribute of the factory object:
+
+ >>> factory = ElementFactory()
+ >>> print(factory.foo)
+ <foo/>
+ >>> print(factory.foo(id=2))
+ <foo id="2"/>
+
+ Markup fragments (lists of nodes without a parent element) can be created
+ by calling the factory:
+
+ >>> print(factory('Hello, ', factory.em('world'), '!'))
+ Hello, <em>world</em>!
+
+ A factory can also be bound to a specific namespace:
+
+ >>> factory = ElementFactory('http://www.w3.org/1999/xhtml')
+ >>> print(factory.html(lang="en"))
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en"/>
+
+ The namespace for a specific element can be altered on an existing factory
+ by specifying the new namespace using item access:
+
+ >>> factory = ElementFactory()
+ >>> print(factory.html(factory['http://www.w3.org/2000/svg'].g(id=3)))
+ <html><g xmlns="http://www.w3.org/2000/svg" id="3"/></html>
+
+ Usually, the `ElementFactory` class is not be used directly. Rather, the
+ `tag` instance should be used to create elements.
+ """
+
+ def __init__(self, namespace=None):
+ """Create the factory, optionally bound to the given namespace.
+
+ :param namespace: the namespace URI for any created elements, or `None`
+ for no namespace
+ """
+ if namespace and not isinstance(namespace, Namespace):
+ namespace = Namespace(namespace)
+ self.namespace = namespace
+
+ def __call__(self, *args):
+ """Create a fragment that has the given positional arguments as child
+ nodes.
+
+ :return: the created `Fragment`
+ :rtype: `Fragment`
+ """
+ return Fragment()(*args)
+
+ def __getitem__(self, namespace):
+ """Return a new factory that is bound to the specified namespace.
+
+ :param namespace: the namespace URI or `Namespace` object
+ :return: an `ElementFactory` that produces elements bound to the given
+ namespace
+ :rtype: `ElementFactory`
+ """
+ return ElementFactory(namespace)
+
+ def __getattr__(self, name):
+ """Create an `Element` with the given name.
+
+ :param name: the tag name of the element to create
+ :return: an `Element` with the specified name
+ :rtype: `Element`
+ """
+ return Element(self.namespace and self.namespace[name] or name)
+
+
+tag = ElementFactory()
+"""Global `ElementFactory` bound to the default namespace.
+
+:type: `ElementFactory`
+"""
diff --git a/lib/genshi/genshi/compat.py b/lib/genshi/genshi/compat.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/genshi/compat.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Various Python version compatibility classes and functions."""
+
+import sys
+from types import CodeType
+
+
+IS_PYTHON2 = (sys.version_info[0] == 2)
+
+
+# This function should only be called in Python 2, and will fail in Python 3
+
+if IS_PYTHON2:
+ def stringrepr(string):
+ ascii = string.encode('ascii', 'backslashreplace')
+ quoted = "'" + ascii.replace("'", "\\'") + "'"
+ if len(ascii) > len(string):
+ return 'u' + quoted
+ return quoted
+else:
+ def stringrepr(string):
+ raise RuntimeError(
+ 'Python 2 compatibility function. Not usable in Python 3.')
+
+
+# We need to differentiate between StringIO and BytesIO in places
+
+if IS_PYTHON2:
+ from StringIO import StringIO
+ try:
+ from cStringIO import StringIO as BytesIO
+ except ImportError:
+ BytesIO = StringIO
+else:
+ from io import StringIO, BytesIO
+
+
+# We want to test bytestring input to some stuff.
+
+if IS_PYTHON2:
+ def wrapped_bytes(bstr):
+ assert bstr.startswith('b')
+ return bstr[1:]
+else:
+ def wrapped_bytes(bstr):
+ assert bstr.startswith('b')
+ return bstr
+
+
+# We do some scary stuff with CodeType() in template/eval.py
+
+if IS_PYTHON2:
+ def get_code_params(code):
+ return (code.co_nlocals, code.co_stacksize, code.co_flags,
+ code.co_code, code.co_consts, code.co_names, code.co_varnames,
+ code.co_filename, code.co_name, code.co_firstlineno,
+ code.co_lnotab, (), ())
+
+ def build_code_chunk(code, filename, name, lineno):
+ return CodeType(0, code.co_nlocals, code.co_stacksize,
+ code.co_flags | 0x0040, code.co_code, code.co_consts,
+ code.co_names, code.co_varnames, filename, name,
+ lineno, code.co_lnotab, (), ())
+else:
+ def get_code_params(code):
+ return (code.co_nlocals, code.co_kwonlyargcount, code.co_stacksize,
+ code.co_flags, code.co_code, code.co_consts, code.co_names,
+ code.co_varnames, code.co_filename, code.co_name,
+ code.co_firstlineno, code.co_lnotab, (), ())
+
+ def build_code_chunk(code, filename, name, lineno):
+ return CodeType(0, code.co_nlocals, code.co_kwonlyargcount,
+ code.co_stacksize, code.co_flags | 0x0040,
+ code.co_code, code.co_consts, code.co_names,
+ code.co_varnames, filename, name, lineno,
+ code.co_lnotab, (), ())
+
+# Compatibility fallback implementations for Python < 2.6
+
+try:
+ next = next
+except NameError:
+ def next(iterator):
+ return iterator.next()
+
+# Compatibility fallback implementations for Python < 2.5
+
+try:
+ all = all
+ any = any
+except NameError:
+ def any(S):
+ for x in S:
+ if x:
+ return True
+ return False
+
+ def all(S):
+ for x in S:
+ if not x:
+ return False
+ return True
+
diff --git a/lib/genshi/genshi/core.py b/lib/genshi/genshi/core.py
new file mode 100644
--- /dev/null
+++ b/lib/genshi/genshi/core.py
@@ -0,0 +1,745 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Core classes for markup processing."""
+
+try:
+ reduce # builtin in Python < 3
+except NameError:
+ from functools import reduce
+import sys
+from itertools import chain
+import operator
+
+from genshi.util import plaintext, stripentities, striptags, stringrepr
+
+__all__ = ['Stream', 'Markup', 'escape', 'unescape', 'Attrs', 'Namespace',
+ 'QName']
+__docformat__ = 'restructuredtext en'
+
+
+class StreamEventKind(str):
+ """A kind of event on a markup stream."""
+ __slots__ = []
+ _instances = {}
+
+ def __new__(cls, val):
+ return cls._instances.setdefault(val, str.__new__(cls, val))
+
+
+class Stream(object):
+ """Represents a stream of markup events.
+
+ This class is basically an iterator over the events.
+
+ Stream events are tuples of the form::
+
+ (kind, data, position)
+
+ where ``kind`` is the event kind (such as `START`, `END`, `TEXT`, etc),
+ ``data`` depends on the kind of event, and ``position`` is a
+ ``(filename, line, offset)`` tuple that contains the location of the
+ original element or text in the input. If the original location is unknown,
+ ``position`` is ``(None, -1, -1)``.
+
+ Also provided are ways to serialize the stream to text. The `serialize()`
+ method will return an iterator over generated strings, while `render()`
+ returns the complete generated text at once. Both accept various parameters
+ that impact the way the stream is serialized.
+ """
+ __slots__ = ['events', 'serializer']
+
+ START = StreamEventKind('START') #: a start tag
+ END = StreamEventKind('END') #: an end tag
+ TEXT = StreamEventKind('TEXT') #: literal text
+ XML_DECL = StreamEventKind('XML_DECL') #: XML declaration
+ DOCTYPE = StreamEventKind('DOCTYPE') #: doctype declaration
+ START_NS = StreamEventKind('START_NS') #: start namespace mapping
+ END_NS = StreamEventKind('END_NS') #: end namespace mapping
+ START_CDATA = StreamEventKind('START_CDATA') #: start CDATA section
+ END_CDATA = StreamEventKind('END_CDATA') #: end CDATA section
+ PI = StreamEventKind('PI') #: processing instruction
+ COMMENT = StreamEventKind('COMMENT') #: comment
+
+ def __init__(self, events, serializer=None):
+ """Initialize the stream with a sequence of markup events.
+
+ :param events: a sequence or iterable providing the events
+ :param serializer: the default serialization method to use for this
+ stream
+
+ :note: Changed in 0.5: added the `serializer` argument
+ """
+ self.events = events #: The underlying iterable producing the events
+ self.serializer = serializer #: The default serializion method
+
+ def __iter__(self):
+ return iter(self.events)
+
+ def __or__(self, function):
+ """Override the "bitwise or" operator to apply filters or serializers
+ to the stream, providing a syntax similar to pipes on Unix shells.
+
+ Assume the following stream produced by the `HTML` function:
+
+ >>> from genshi.input import HTML
+ >>> html = HTML('''<p onclick="alert('Whoa')">Hello, world!</p>''', encoding='utf-8')
+ >>> print(html)
+ <p onclick="alert('Whoa')">Hello, world!</p>
+
+ A filter such as the HTML sanitizer can be applied to that stream using
+ the pipe notation as follows:
+
+ >>> from genshi.filters import HTMLSanitizer
+ >>> sanitizer = HTMLSanitizer()
+ >>> print(html | sanitizer)
+ <p>Hello, world!</p>
+
+ Filters can be any function that accepts and produces a stream (where
+ a stream is anything that iterates over events):
+
+ >>> def uppercase(stream):
+ ... for kind, data, pos in stream:
+ ... if kind is TEXT:
+ ... data = data.upper()
+ ... yield kind, data, pos
+ >>> print(html | sanitizer | uppercase)
+ <p>HELLO, WORLD!</p>
+
+ Serializers can also be used with this notation:
+
+ >>> from genshi.output import TextSerializer
+ >>> output = TextSerializer()
+ >>> print(html | sanitizer | uppercase | output)
+ HELLO, WORLD!
+
+ Commonly, serializers should be used at the end of the "pipeline";
+ using them somewhere in the middle may produce unexpected results.
+
+ :param function: the callable object that should be applied as a filter
+ :return: the filtered stream
+ :rtype: `Stream`
+ """
+ return Stream(_ensure(function(self)), serializer=self.serializer)
+
+ def filter(self, *filters):
+ """Apply filters to the stream.
+
+ This method returns a new stream with the given filters applied. The
+ filters must be callables that accept the stream object as parameter,
+ and return the filtered stream.
+
+ The call::
+
+ stream.filter(filter1, filter2)
+
+ is equivalent to::
+
+ stream | filter1 | filter2
+
+ :param filters: one or more callable objects that should be applied as
+ filters
+ :return: the filtered stream
+ :rtype: `Stream`
+ """
+ return reduce(operator.or_, (self,) + filters)
+
+ def render(self, method=None, encoding=None, out=None, **kwargs):
+ """Return a string representation of the stream.
+
+ Any additional keyword arguments are passed to the serializer, and thus
+ depend on the `method` parameter value.
+
+ :param method: determines how the stream is serialized; can be either
+ "xml", "xhtml", "html", "text", or a custom serializer
+ class; if `None`, the default serialization method of
+ the stream is used
+ :param encoding: how the output string should be encoded; if set to
+ `None`, this method returns a `unicode` object
+ :param out: a file-like object that the output should be written to
+ instead of being returned as one big string; note that if
+ this is a file or socket (or similar), the `encoding` must
+ not be `None` (that is, the output must be encoded)
+ :return: a `str` or `unicode` object (depending on the `encoding`
+ parameter), or `None` if the `out` parameter is provided
+ :rtype: `basestring`
+
+ :see: XMLSerializer, XHTMLSerializer, HTMLSerializer, TextSerializer
+ :note: Changed in 0.5: added the `out` parameter
+ """
+ from genshi.output import encode
+ if method is None:
+ method = self.serializer or 'xml'
+ generator = self.serialize(method=method, **kwargs)
+ return encode(generator, method=method, encoding=encoding, out=out)
+
+ def select(self, path, namespaces=None, variables=None):
+ """Return a new stream that contains the events matching the given
+ XPath expression.
+
+ >>> from genshi import HTML
+ >>> stream = HTML('<doc><elem>foo</elem><elem>bar</elem></doc>', encoding='utf-8')
+ >>> print(stream.select('elem'))
+ <elem>foo</elem><elem>bar</elem>
+ >>> print(stream.select('elem/text()'))
+ foobar
+
+ Note that the outermost element of the stream becomes the *context
+ node* for the XPath test. That means that the expression "doc" would
+ not match anything in the example above, because it only tests against
+ child elements of the outermost element:
+
+ >>> print(stream.select('doc'))
+ <BLANKLINE>
+
+ You can use the "." expression to match the context node itself
+ (although that usually makes little sense):
+
+ >>> print(stream.select('.'))
+ <doc><elem>foo</elem><elem>bar</elem></doc>
+
+ :param path: a string containing the XPath expression
+ :param namespaces: mapping of namespace prefixes used in the path
+ :param variables: mapping of variable names to values
+ :return: the selected substream
+ :rtype: `Stream`
+ :raises PathSyntaxError: if the given path expression is invalid or not
+ supported
+ """
+ from genshi.path import Path
+ return Path(path).select(self, namespaces, variables)
+
+ def serialize(self, method='xml', **kwargs):
+ """Generate strings corresponding to a specific serialization of the
+ stream.
+
+ Unlike the `render()` method, this method is a generator that returns
+ the serialized output incrementally, as opposed to returning a single
+ string.
+
+ Any additional keyword arguments are passed to the serializer, and thus
+ depend on the `method` parameter value.
+
+ :param method: determines how the stream is serialized; can be either
+ "xml", "xhtml", "html", "text", or a custom serializer
+ class; if `None`, the default serialization method of
+ the stream is used
+ :return: an iterator over the serialization results (`Markup` or
+ `unicode` objects, depending on the serialization method)
+ :rtype: ``iterator``
+ :see: XMLSerializer, XHTMLSerializer, HTMLSerializer, TextSerializer
+ """
+ from genshi.output import get_serializer
+ if method is None:
+ method = self.serializer or 'xml'
+ return get_serializer(method, **kwargs)(_ensure(self))
+
+ def __str__(self):
+ return self.render()
+
+ def __unicode__(self):
+ return self.render(encoding=None)
+
+ def __html__(self):
+ return self
+
+
+START = Stream.START
+END = Stream.END
+TEXT = Stream.TEXT
+XML_DECL = Stream.XML_DECL
+DOCTYPE = Stream.DOCTYPE
+START_NS = Stream.START_NS
+END_NS = Stream.END_NS
+START_CDATA = Stream.START_CDATA
+END_CDATA = Stream.END_CDATA
+PI = Stream.PI
+COMMENT = Stream.COMMENT
+
+
+def _ensure(stream):
+ """Ensure that every item on the stream is actually a markup event."""
+ stream = iter(stream)
+ event = stream.next()
+
+ # Check whether the iterable is a real markup event stream by examining the
+ # first item it yields; if it's not we'll need to do some conversion
+ if type(event) is not tuple or len(event) != 3:
+ for event in chain([event], stream):
+ if hasattr(event, 'totuple'):
+ event = event.totuple()
+ else:
+ event = TEXT, unicode(event), (None, -1, -1)
+ yield event
+ return
+
+ # This looks like a markup event stream, so we'll just pass it through
+ # unchanged
+ yield event
+ for event in stream:
+ yield event
+
+
+class Attrs(tuple):
+ """Immutable sequence type that stores the attributes of an element.
+
+ Ordering of the attributes is preserved, while access by name is also
+ supported.
More information about the pypy-commit
mailing list