--- a/pvl/web/html.py Mon Jan 21 18:13:06 2013 +0200
+++ b/pvl/web/html.py Mon Jan 21 18:39:23 2013 +0200
@@ -6,6 +6,8 @@
u'<a href="http://www.google.com">\\n\\tGoogle <this>!\\n</a>'
"""
+# XXX: needs some refactoring for Text vs Tag now
+
import itertools as itertools
import types as types
from xml.sax import saxutils
@@ -15,6 +17,75 @@
Structured data that's flattened into indented lines of text.
"""
+ # types of nested items to flatten
+ CONTAINER_TYPES = (types.TupleType, types.ListType, types.GeneratorType)
+
+ @classmethod
+ def process_contents (cls, *args) :
+ """
+ Yield the HTML tag's contents from the given sequence of positional arguments as a series of flattened
+ items, eagerly converting them to unicode.
+
+ If no arguments are given, we don't have any children:
+
+ >>> bool(list(Tag.process_contents()))
+ False
+
+ Items that are None will be ignored:
+
+ >>> list(Tag.process_contents(None))
+ []
+
+ Various Python container types are recursively flattened:
+
+ >>> list(Tag.process_contents([1, 2]))
+ [u'1', u'2']
+ >>> list(Tag.process_contents([1], [2]))
+ [u'1', u'2']
+ >>> list(Tag.process_contents([1, [2]]))
+ [u'1', u'2']
+ >>> list(Tag.process_contents(n + 1 for n in xrange(2)))
+ [u'1', u'2']
+ >>> list(Tag.process_contents((1, 2)))
+ [u'1', u'2']
+ >>> list(Tag.process_contents((1), (2, )))
+ [u'1', u'2']
+
+ Our own HTML-aware objects are returned as-is:
+
+ >>> list(Tag.process_contents(Tag.build('foo')))
+ [tag('foo')]
+ >>> list(Tag.process_contents(Text(u'bar')))
+ [Text(u'bar')]
+
+ All other objects are converted to unicode:
+
+ >>> list(Tag.process_contents('foo', u'bar', 0.123, False))
+ [u'foo', u'bar', u'0.123', u'False']
+
+ """
+
+ for arg in args :
+ if arg is None :
+ # skip null: None
+ continue
+
+ elif isinstance(arg, cls.CONTAINER_TYPES) :
+ # flatten nested container: tuple/list/generator
+ for node in arg :
+ # recurse
+ for item in cls.process_contents(node) :
+ yield item
+
+ elif isinstance(arg, Renderable) :
+ # yield item: Renderable
+ yield arg
+
+ else :
+ # as unicode
+ yield unicode(arg)
+
+
def flatten (self) :
"""
Flatten this object into a series of (identlevel, line) tuples.
@@ -60,99 +131,42 @@
"""
Plain un-structured/un-processed HTML text for output
- >>> Text('foo')
+ >>> Text(u'foo')
Text(u'foo')
>>> list(Text('<foo>'))
[u'<foo>']
+ >>> list(Text('<foo>', tag('p', 'test')))
+ [u'<foo>', u'<p>', u'\\ttest', u'</p>']
>>> list(tag('a', Text('<foo>')))
[u'<a>', u'\\t<foo>', u'</a>']
+ >>> list(Text(range(2)))
+ [u'0', u'1']
+
"""
- def __init__ (self, text) :
- self.text = unicode(text)
+ def __init__ (self, *contents) :
+ self.contents = self.process_contents(*contents)
- def flatten (self) :
- yield (0, self.text)
+ def flatten (self, indent=0) :
+ for item in self.contents :
+ if isinstance(item, Renderable) :
+ # recursively flatten items
+ for line_indent, line in item.flatten() :
+ # indented
+ yield indent + line_indent, line
+ else :
+ # render raw value
+ yield indent, unicode(item)
+
def __repr__ (self) :
- return "Text(%r)" % (self.text, )
+ return "Text(%s)" % (', '.join(repr(item) for item in self.contents))
class Tag (Renderable) :
"""
An immutable HTML tag structure, with the tag's name, attributes and contents.
"""
- # types of nested items to flatten
- CONTAINER_TYPES = (types.TupleType, types.ListType, types.GeneratorType)
-
- # types of items to keep as-is
- ITEM_TYPES = (Renderable, )
-
- @classmethod
- def process_contents (cls, *args) :
- """
- Yield the HTML tag's contents from the given sequence of positional arguments as a series of flattened
- items, eagerly converting them to unicode.
-
- If no arguments are given, we don't have any children:
-
- >>> bool(list(Tag.process_contents()))
- False
-
- Items that are None will be ignored:
-
- >>> list(Tag.process_contents(None))
- []
-
- Various Python container types are recursively flattened:
-
- >>> list(Tag.process_contents([1, 2]))
- [u'1', u'2']
- >>> list(Tag.process_contents([1], [2]))
- [u'1', u'2']
- >>> list(Tag.process_contents([1, [2]]))
- [u'1', u'2']
- >>> list(Tag.process_contents(n + 1 for n in xrange(2)))
- [u'1', u'2']
- >>> list(Tag.process_contents((1, 2)))
- [u'1', u'2']
- >>> list(Tag.process_contents((1), (2, )))
- [u'1', u'2']
-
- Our own HTML-aware objects are returned as-is:
-
- >>> list(Tag.process_contents(Tag.build('foo')))
- [tag('foo')]
- >>> list(Tag.process_contents(Text('bar')))
- [Text(u'bar')]
-
- All other objects are converted to unicode:
-
- >>> list(Tag.process_contents('foo', u'bar', 0.123, False))
- [u'foo', u'bar', u'0.123', u'False']
-
- """
-
- for arg in args :
- if arg is None :
- # skip null: None
- continue
-
- elif isinstance(arg, cls.CONTAINER_TYPES) :
- # flatten nested container: tuple/list/generator
- for node in arg :
- # recurse
- for item in cls.process_contents(node) :
- yield item
-
- elif isinstance(arg, cls.ITEM_TYPES) :
- # yield item: Renderable
- yield arg
-
- else :
- # as unicode
- yield unicode(arg)
-
@classmethod
def process_attrs (cls, **kwargs) :
"""
@@ -275,7 +289,7 @@
return cls(_name, contents, attrs, **options)
- def __init__ (self, name, contents, attrs, selfclosing=None, whitespace_sensitive=None) :
+ def __init__ (self, name, contents, attrs, selfclosing=None, whitespace_sensitive=None, escape=True) :
"""
Initialize internal Tag state with the given tag identifier, flattened list of content items, dict of
attributes and dict of options.
@@ -286,6 +300,8 @@
whitespace_sensitive - do not indent tag content onto separate rows, render the full tag as a single
row
+ escape - html-escape non-Renderable's (text)
+
Use the build() factory function to build Tag objects using Python's function call argument semantics.
The tag name is used a pure string identifier:
@@ -322,6 +338,7 @@
# options
self.selfclosing = selfclosing
self.whitespace_sensitive = whitespace_sensitive
+ self.escape = escape
def __call__ (self, *args, **kwargs) :
"""
@@ -416,10 +433,14 @@
# indented
yield indent + line_indent, line
- else :
+ elif self.escape :
# render HTML-escaped raw value
# escape raw values
yield indent, saxutils.escape(item)
+
+ else :
+ # render raw value
+ yield indent, unicode(item)
def flatten (self) :
"""
@@ -589,12 +610,12 @@
return Tag(name, [], {})
- def __call__ (self, value) :
+ def __call__ (self, *values) :
"""
Raw HTML.
"""
- return Text(value)
+ return Text(*values)
# static instance
tags = TagFactory()