# -*- 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 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) >>> print(doc)

Some text and a link.

If an attribute name collides with a Python keyword, simply append an underscore to the name: >>> doc(class_='intro') >>> print(doc)

Some text and a link.

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 >>> print(stream)

Some text and a link.

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 >>> print(fragment) Hello, world! """ 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')) 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))