cmlenz@1: # -*- coding: utf-8 -*- cmlenz@1: # cmlenz@1: # Copyright (C) 2006 Christopher Lenz cmlenz@1: # All rights reserved. cmlenz@1: # cmlenz@1: # This software is licensed as described in the file COPYING, which cmlenz@1: # you should have received as part of this distribution. The terms cmlenz@1: # are also available at http://trac.edgewall.com/license.html. cmlenz@1: # cmlenz@1: # This software consists of voluntary contributions made by many cmlenz@1: # individuals. For the exact contribution history, see the revision cmlenz@1: # history and logs, available at http://projects.edgewall.com/trac/. cmlenz@1: cmlenz@1: from markup.core import Attributes, QName, Stream cmlenz@1: cmlenz@1: __all__ = ['Fragment', 'Element', 'tag'] cmlenz@1: cmlenz@1: cmlenz@1: class Fragment(object): cmlenz@1: __slots__ = ['children'] cmlenz@1: cmlenz@1: def __init__(self): cmlenz@1: self.children = [] cmlenz@1: cmlenz@1: def append(self, node): cmlenz@1: """Append an element or string as child node.""" cmlenz@1: if isinstance(node, (Element, basestring, int, float, long)): cmlenz@1: # For objects of a known/primitive type, we avoid the check for cmlenz@1: # whether it is iterable for better performance cmlenz@1: self.children.append(node) cmlenz@1: elif isinstance(node, Fragment): cmlenz@1: self.children += node.children cmlenz@1: elif node is not None: cmlenz@1: try: cmlenz@1: children = iter(node) cmlenz@1: except TypeError: cmlenz@1: self.children.append(node) cmlenz@1: else: cmlenz@1: for child in node: cmlenz@1: self.append(children) cmlenz@1: cmlenz@1: def __add__(self, other): cmlenz@1: return Fragment()(self, other) cmlenz@1: cmlenz@1: def __call__(self, *args): cmlenz@1: for arg in args: cmlenz@1: self.append(arg) cmlenz@1: return self cmlenz@1: cmlenz@1: def generate(self): cmlenz@1: """Generator that yield tags and text nodes as strings.""" cmlenz@1: def _generate(): cmlenz@1: for child in self.children: cmlenz@1: if isinstance(child, Fragment): cmlenz@1: for event in child.generate(): cmlenz@1: yield event cmlenz@1: else: cmlenz@1: yield Stream.TEXT, child, (-1, -1) cmlenz@1: return Stream(_generate()) cmlenz@1: cmlenz@1: def __iter__(self): cmlenz@1: return iter(self.generate()) cmlenz@1: cmlenz@1: def __str__(self): cmlenz@1: return str(self.generate()) cmlenz@1: cmlenz@1: def __unicode__(self): cmlenz@1: return unicode(self.generate()) cmlenz@1: cmlenz@1: cmlenz@1: class Element(Fragment): cmlenz@1: """Simple XML output generator based on the builder pattern. cmlenz@1: cmlenz@1: Construct XML elements by passing the tag name to the constructor: cmlenz@1: cmlenz@1: >>> print Element('strong') cmlenz@1: cmlenz@1: cmlenz@1: Attributes can be specified using keyword arguments. The values of the cmlenz@1: arguments will be converted to strings and any special XML characters cmlenz@1: escaped: cmlenz@1: cmlenz@1: >>> print Element('textarea', rows=10, cols=60) cmlenz@1: cmlenz@1: >>> print Element('span', title='1 < 2') cmlenz@1: cmlenz@1: >>> print Element('span', title='"baz"') cmlenz@1: cmlenz@1: cmlenz@1: The " character is escaped using a numerical entity. cmlenz@1: The order in which attributes are rendered is undefined. cmlenz@1: cmlenz@1: If an attribute value evaluates to `None`, that attribute is not included cmlenz@1: in the output: cmlenz@1: cmlenz@1: >>> print Element('a', name=None) cmlenz@1: cmlenz@1: cmlenz@1: Attribute names that conflict with Python keywords can be specified by cmlenz@1: appending an underscore: cmlenz@1: cmlenz@1: >>> print Element('div', class_='warning') cmlenz@1:
cmlenz@1: cmlenz@1: Nested elements can be added to an element using item access notation. cmlenz@1: The call notation can also be used for this and for adding attributes cmlenz@1: using keyword arguments, as one would do in the constructor. cmlenz@1: cmlenz@1: >>> print Element('ul')(Element('li'), Element('li')) cmlenz@1:Hello world
cmlenz@1: cmlenz@1: Elements can also be combined with other elements or strings using the cmlenz@1: addition operator, which results in a `Fragment` object that contains the cmlenz@1: operands: cmlenz@1: cmlenz@1: >>> print Element('br') + 'some text' + Element('br') cmlenz@1: