pvl/web/html.py
author Tero Marttila <terom@paivola.fi>
Sun, 22 Dec 2013 19:03:57 +0200
changeset 336 edaa5d0aa57d
parent 178 f9f5e669bace
child 346 ae164470ceea
permissions -rw-r--r--
version 0.6.1: pvl.hosts forward/reverse delegation, and include= support
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
     1
"""
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
     2
    Generate XHTML output from python code.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
     3
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
     4
    >>> from html import tags
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
     5
    >>> unicode(tags.a(href="http://www.google.com")("Google <this>!"))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
     6
    u'<a href="http://www.google.com">\\n\\tGoogle &lt;this&gt;!\\n</a>'
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
     7
"""
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
     8
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
     9
# XXX: needs some refactoring for Text vs Tag now
178
f9f5e669bace pvl.verkko: refactor into dhcp -> hosts -> web+db modules, reworking index page
Tero Marttila <terom@paivola.fi>
parents: 162
diff changeset
    10
# XXX: not all tags work in self-closing form, e.g. empty html.title() breaks badly
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    11
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    12
import itertools as itertools
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    13
import types as types
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    14
from xml.sax import saxutils
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    15
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    16
class Renderable (object) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    17
    """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    18
        Structured data that's flattened into indented lines of text.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    19
    """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    20
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    21
    # types of nested items to flatten
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    22
    CONTAINER_TYPES = (types.TupleType, types.ListType, types.GeneratorType)
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    23
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    24
    @classmethod
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    25
    def process_contents (cls, *args) :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    26
        """
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    27
            Yield the HTML tag's contents from the given sequence of positional arguments as a series of flattened
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    28
            items, eagerly converting them to unicode.
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    29
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    30
            If no arguments are given, we don't have any children:
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    31
            
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    32
            >>> bool(list(Tag.process_contents()))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    33
            False
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    34
            
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    35
            Items that are None will be ignored:
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    36
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    37
            >>> list(Tag.process_contents(None))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    38
            []
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    39
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    40
            Various Python container types are recursively flattened:
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    41
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    42
            >>> list(Tag.process_contents([1, 2]))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    43
            [u'1', u'2']
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    44
            >>> list(Tag.process_contents([1], [2]))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    45
            [u'1', u'2']
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    46
            >>> list(Tag.process_contents([1, [2]]))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    47
            [u'1', u'2']
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    48
            >>> list(Tag.process_contents(n + 1 for n in xrange(2)))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    49
            [u'1', u'2']
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    50
            >>> list(Tag.process_contents((1, 2)))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    51
            [u'1', u'2']
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    52
            >>> list(Tag.process_contents((1), (2, )))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    53
            [u'1', u'2']
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    54
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    55
            Our own HTML-aware objects are returned as-is:
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    56
            
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    57
            >>> list(Tag.process_contents(Tag.build('foo')))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    58
            [tag('foo')]
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    59
            >>> list(Tag.process_contents(Text(u'bar')))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    60
            [Text(u'bar')]
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    61
            
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    62
            All other objects are converted to unicode:
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    63
            
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    64
            >>> list(Tag.process_contents('foo', u'bar', 0.123, False))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    65
            [u'foo', u'bar', u'0.123', u'False']
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    66
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    67
        """
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    68
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    69
        for arg in args :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    70
            if arg is None :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    71
                # skip null: None
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    72
                continue
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    73
            
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    74
            elif isinstance(arg, cls.CONTAINER_TYPES) :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    75
                # flatten nested container: tuple/list/generator
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    76
                for node in arg :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    77
                    # recurse
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    78
                    for item in cls.process_contents(node) :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    79
                        yield item
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    80
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    81
            elif isinstance(arg, Renderable) :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    82
                # yield item: Renderable
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    83
                yield arg
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    84
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    85
            else :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    86
                # as unicode
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    87
                yield unicode(arg)
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    88
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
    89
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    90
    def flatten (self) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    91
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    92
            Flatten this object into a series of (identlevel, line) tuples.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    93
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    94
        
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    95
        raise NotImplementedError()
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    96
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    97
    def iter (self, indent='\t') :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    98
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
    99
            Yield a series of lines for this render.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   100
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   101
        
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   102
        for indent_level, line in self.flatten() :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   103
            yield (indent * indent_level) + line
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   104
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   105
    def unicode (self, newline=u'\n', **opts) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   106
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   107
            Render as a single unicode string.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   108
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   109
            No newline is returned at the end of the string.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   110
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   111
            >>> Tag.build('a', 'b').unicode(newline='X', indent='Y')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   112
            u'<a>XYbX</a>'
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   113
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   114
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   115
        return newline.join(self.iter(**opts))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   116
    
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   117
    # required for print
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   118
    def str (self, newline='\n', encoding='ascii', **opts) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   119
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   120
            Render as a single string.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   121
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   122
        
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   123
        # XXX: try and render as non-unicode, i.e. binary data in the tree?
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   124
        return newline.join(line.encode(encoding) for line in self.iter(**opts))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   125
    
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   126
    # formal interface using defaults
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   127
    __iter__ = iter
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   128
    __unicode__ = unicode
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   129
    __str__ = str
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   130
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   131
class Text (Renderable) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   132
    """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   133
        Plain un-structured/un-processed HTML text for output
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   134
        
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   135
        >>> Text(u'foo')
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   136
        Text(u'foo')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   137
        >>> list(Text('<foo>'))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   138
        [u'<foo>']
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   139
        >>> list(Text('<foo>', tag('p', 'test')))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   140
        [u'<foo>', u'<p>', u'\\ttest', u'</p>']
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   141
        >>> list(tag('a', Text('<foo>')))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   142
        [u'<a>', u'\\t<foo>', u'</a>']
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   143
        >>> list(Text(range(2)))
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   144
        [u'0', u'1']
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   145
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   146
    """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   147
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   148
    def __init__ (self, *contents) :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   149
        self.contents = self.process_contents(*contents)
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   150
    
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   151
    def flatten (self, indent=0) :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   152
        for item in self.contents :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   153
            if isinstance(item, Renderable) :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   154
                # recursively flatten items
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   155
                for line_indent, line in item.flatten() :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   156
                    # indented
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   157
                    yield indent + line_indent, line
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   158
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   159
            else :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   160
                # render raw value
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   161
                yield indent, unicode(item)
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   162
 
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   163
    def __repr__ (self) :
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   164
        return "Text(%s)" % (', '.join(repr(item) for item in self.contents))
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   165
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   166
class Tag (Renderable) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   167
    """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   168
        An immutable HTML tag structure, with the tag's name, attributes and contents.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   169
    """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   170
    
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   171
    @classmethod
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   172
    def process_attrs (cls, **kwargs) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   173
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   174
            Yield the HTML tag attributes from the given set of keyword arguments as a series of (name, value) tuples.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   175
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   176
            Keyword-only options (`_key=value`) are filtered out:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   177
                
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   178
            >>> dict(Tag.process_attrs(_opt=True))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   179
            {}
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   180
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   181
            Attributes with a value of None/False are filtered out:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   182
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   183
            >>> dict(Tag.process_attrs(foo=None, bar=False))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   184
            {}
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   185
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   186
            A value given as True is returned as the key's value:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   187
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   188
            >>> dict(Tag.process_attrs(quux=True))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   189
            {'quux': u'quux'}
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   190
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   191
            A (single) trailing underscore in the attribute name is removed:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   192
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   193
            >>> dict(Tag.process_attrs(class_='foo'))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   194
            {'class': u'foo'}
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   195
            >>> dict(Tag.process_attrs(data__='foo'))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   196
            {'data_': u'foo'}
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   197
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   198
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   199
        for key, value in kwargs.iteritems() :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   200
            # keyword arguments are always pure strings
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   201
            assert type(key) is str
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   202
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   203
            if value is None or value is False:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   204
                # omit
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   205
                continue
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   206
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   207
            if key.startswith('_') :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   208
                # option
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   209
                continue
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   210
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   211
            if key.endswith('_') :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   212
                # strip underscore
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   213
                key = key[:-1]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   214
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   215
            if value is True :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   216
                # flag attr
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   217
                value = key
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   218
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   219
            yield key, unicode(value)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   220
        
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   221
    @classmethod
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   222
    def process_opts (cls, **kwargs) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   223
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   224
            Return a series of of the keyword-only _options, extracted from the given dict of keyword arguments, as 
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   225
            (k, v) tuples.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   226
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   227
            >>> Tag.process_opts(foo='bar', _bar=False)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   228
            (('bar', False),)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   229
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   230
        
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   231
        return tuple((k.lstrip('_'), v) for k, v in kwargs.iteritems() if k.startswith('_'))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   232
    
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   233
    @classmethod
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   234
    def build (cls, _name, *args, **kwargs) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   235
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   236
            Factory function for constructing Tags by directly passing in contents/attributes/options as Python function
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   237
            arguments/keyword arguments.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   238
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   239
            The first positional argument is the tag's name:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   240
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   241
            >>> Tag.build('foo')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   242
            tag('foo')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   243
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   244
            Further positional arguments are the tag's contents:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   245
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   246
            >>> Tag.build('foo', 'quux', 'bar')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   247
            tag('foo', u'quux', u'bar')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   248
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   249
            All the rules used by process_contents() are available:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   250
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   251
            >>> Tag.build('foo', [1, None], None, (n for n in xrange(2)))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   252
            tag('foo', u'1', u'0', u'1')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   253
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   254
            The special-case for a genexp as the only argument works:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   255
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   256
            >>> f = lambda *args: Tag.build('foo', *args)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   257
            >>> f('hi' for n in xrange(2))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   258
            tag('foo', u'hi', u'hi')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   259
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   260
            Attributes are passed as keyword arguments, with or without contents:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   261
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   262
            >>> Tag.build('foo', id=1)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   263
            tag('foo', id=u'1')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   264
            >>> Tag.build('foo', 'quux', bar=5)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   265
            tag('foo', u'quux', bar=u'5')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   266
            >>> Tag.build('foo', class_='ten')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   267
            tag('foo', class=u'ten')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   268
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   269
            The attribute names don't conflict with positional argument names:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   270
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   271
            >>> Tag.build('bar', name='foo')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   272
            tag('bar', name=u'foo')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   273
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   274
            Options are handled as the 'real' keyword arguments:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   275
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   276
            >>> print Tag.build('foo', _selfclosing=False)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   277
            <foo></foo>
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   278
            >>> print Tag.build('foo', _foo='bar')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   279
            Traceback (most recent call last):
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   280
                ...
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   281
            TypeError: __init__() got an unexpected keyword argument 'foo'
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   282
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   283
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   284
        # pre-process incoming user values
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   285
        contents = list(cls.process_contents(*args))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   286
        attrs = dict(cls.process_attrs(**kwargs))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   287
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   288
        # XXX: use Python 2.6 keyword-only arguments instead?
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   289
        options = dict(cls.process_opts(**kwargs))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   290
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   291
        return cls(_name, contents, attrs, **options)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   292
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   293
    def __init__ (self, name, contents, attrs, selfclosing=None, whitespace_sensitive=None, escape=True) :
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   294
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   295
            Initialize internal Tag state with the given tag identifier, flattened list of content items, dict of
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   296
            attributes and dict of options.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   297
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   298
                selfclosing             - set to False to render empty tags as <foo></foo> instead of <foo /> 
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   299
                                          (for XHTML -> HTML compatibility)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   300
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   301
                whitespace_sensitive    - do not indent tag content onto separate rows, render the full tag as a single
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   302
                                          row
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   303
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   304
                escape                  - html-escape non-Renderable's (text)
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   305
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   306
            Use the build() factory function to build Tag objects using Python's function call argument semantics.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   307
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   308
            The tag name is used a pure string identifier:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   309
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   310
            >>> Tag(u'foo', [], {})
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   311
            tag('foo')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   312
            >>> Tag(u'\\xE4', [], {})
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   313
            Traceback (most recent call last):
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   314
                ...
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   315
            UnicodeEncodeError: 'ascii' codec can't encode character u'\\xe4' in position 0: ordinal not in range(128)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   316
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   317
            Contents have their order preserved:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   318
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   319
            >>> Tag('foo', [1, 2], {})
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   320
            tag('foo', 1, 2)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   321
            >>> Tag('foo', [2, 1], {})
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   322
            tag('foo', 2, 1)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   323
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   324
            Attributes can be given:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   325
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   326
            >>> Tag('foo', [], dict(foo='bar'))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   327
            tag('foo', foo='bar')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   328
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   329
            Options can be given:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   330
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   331
            >>> print Tag('foo', [], {}, selfclosing=False)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   332
            <foo></foo>
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   333
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   334
        
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   335
        self.name = str(name)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   336
        self.contents = contents
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   337
        self.attrs = attrs
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   338
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   339
        # options
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   340
        self.selfclosing = selfclosing
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   341
        self.whitespace_sensitive = whitespace_sensitive
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   342
        self.escape = escape
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   343
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   344
    def __call__ (self, *args, **kwargs) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   345
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   346
            Return a new Tag as a copy of this tag, but with the given additional attributes/contents.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   347
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   348
            The same rules for function positional/keyword arguments apply as for build()
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   349
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   350
            >>> Tag.build('foo')('bar')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   351
            tag('foo', u'bar')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   352
            >>> Tag.build('a', href='index.html')("Home")
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   353
            tag('a', u'Home', href=u'index.html')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   354
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   355
            New contents and attributes can be given freely, using the same rules as for Tag.build:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   356
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   357
            >>> Tag.build('bar', None)(5, foo=None, class_='bar')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   358
            tag('bar', u'5', class=u'bar')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   359
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   360
            Tag contents accumulate in order:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   361
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   362
            >>> Tag.build('a')('b', ['c'])('d')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   363
            tag('a', u'b', u'c', u'd')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   364
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   365
            Each Tag is immutable, so the called Tag isn't changed, but rather a copy is returned:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   366
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   367
            >>> t1 = Tag.build('a'); t2 = t1('b'); t1
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   368
            tag('a')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   369
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   370
            Attribute values are replaced:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   371
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   372
            >>> Tag.build('foo', a=2)(a=3)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   373
            tag('foo', a=u'3')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   374
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   375
            Options are also supported:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   376
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   377
            >>> list(Tag.build('foo')(bar='quux', _selfclosing=False))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   378
            [u'<foo bar="quux"></foo>']
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   379
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   380
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   381
        # accumulate contents
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   382
        contents = self.contents + list(self.process_contents(*args))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   383
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   384
        # merge attrs
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   385
        attrs = dict(self.attrs)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   386
        attrs.update(self.process_attrs(**kwargs))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   387
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   388
        # options
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   389
        opts = dict(
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   390
            selfclosing = self.selfclosing,
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   391
            whitespace_sensitive = self.whitespace_sensitive,
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   392
        )
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   393
        opts.update(self.process_opts(**kwargs))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   394
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   395
        # build updated tag
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   396
        return Tag(self.name, contents, attrs, **opts)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   397
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   398
    def render_attrs (self) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   399
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   400
            Return the HTML attributes string
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   401
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   402
            >>> Tag.build('x', foo=5, bar='<', quux=None).render_attrs()
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   403
            u'foo="5" bar="&lt;"'
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   404
            >>> Tag.build('x', foo='a"b').render_attrs()
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   405
            u'foo=\\'a"b\\''
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   406
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   407
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   408
        return " ".join(
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   409
            (
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   410
                u'%s=%s' % (name, saxutils.quoteattr(value))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   411
            ) for name, value in self.attrs.iteritems()
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   412
        )
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   413
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   414
    def flatten_items (self, indent=1) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   415
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   416
            Flatten our content into a series of indented lines.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   417
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   418
            >>> list(Tag.build('tag', 5).flatten_items())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   419
            [(1, u'5')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   420
            >>> list(Tag.build('tag', 'line1', 'line2').flatten_items())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   421
            [(1, u'line1'), (1, u'line2')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   422
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   423
            Nested :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   424
            >>> list(Tag.build('tag', 'a', Tag.build('b', 'bb'), 'c').flatten_items())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   425
            [(1, u'a'), (1, u'<b>'), (2, u'bb'), (1, u'</b>'), (1, u'c')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   426
            >>> list(Tag.build('tag', Tag.build('hr'), Tag.build('foo')('bar')).flatten_items())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   427
            [(1, u'<hr />'), (1, u'<foo>'), (2, u'bar'), (1, u'</foo>')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   428
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   429
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   430
        for item in self.contents :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   431
            if isinstance(item, Renderable) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   432
                # recursively flatten items
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   433
                for line_indent, line in item.flatten() :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   434
                    # indented
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   435
                    yield indent + line_indent, line
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   436
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   437
            elif self.escape :
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   438
                # render HTML-escaped raw value
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   439
                # escape raw values
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   440
                yield indent, saxutils.escape(item)
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   441
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   442
            else :
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   443
                # render raw value
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   444
                yield indent, unicode(item)
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   445
   
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   446
    def flatten (self) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   447
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   448
            Render the tag and all content as a flattened series of indented lines.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   449
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   450
            Empty tags collapse per default:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   451
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   452
            >>> list(Tag.build('foo').flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   453
            [(0, u'<foo />')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   454
            >>> list(Tag.build('bar', id=5).flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   455
            [(0, u'<bar id="5" />')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   456
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   457
            Values are indented inside the start tag:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   458
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   459
            >>> list(Tag.build('foo', 'bar', a=5).flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   460
            [(0, u'<foo a="5">'), (1, u'bar'), (0, u'</foo>')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   461
            
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   462
            Nested tags are further indented:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   463
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   464
            >>> list(Tag.build('1', '1.1', Tag.build('1.2', '1.2.1'), '1.3', a=5).flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   465
            [(0, u'<1 a="5">'), (1, u'1.1'), (1, u'<1.2>'), (2, u'1.2.1'), (1, u'</1.2>'), (1, u'1.3'), (0, u'</1>')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   466
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   467
            Empty tags are rendered with a separate closing tag on the same line, if desired:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   468
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   469
            >>> list(Tag.build('foo', _selfclosing=False).flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   470
            [(0, u'<foo></foo>')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   471
            >>> list(Tag.build('foo', src='asdf', _selfclosing=False).flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   472
            [(0, u'<foo src="asdf"></foo>')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   473
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   474
            Tags that are declared as whitespace-sensitive are collapsed onto the same line:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   475
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   476
            >>> list(Tag.build('foo', _whitespace_sensitive=True).flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   477
            [(0, u'<foo />')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   478
            >>> list(Tag.build('foo', _whitespace_sensitive=True, _selfclosing=False).flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   479
            [(0, u'<foo></foo>')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   480
            >>> list(Tag.build('foo', 'bar', _whitespace_sensitive=True).flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   481
            [(0, u'<foo>bar</foo>')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   482
            >>> list(Tag.build('foo', 'bar\\nasdf\\tx', _whitespace_sensitive=True).flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   483
            [(0, u'<foo>bar\\nasdf\\tx</foo>')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   484
            >>> list(Tag.build('foo', 'bar', Tag.build('quux', 'asdf'), 'asdf', _whitespace_sensitive=True).flatten())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   485
            [(0, u'<foo>bar<quux>asdf</quux>asdf</foo>')]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   486
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   487
            Embedded HTML given as string values is escaped:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   488
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   489
            >>> list(Tag.build('foo', '<asdf>'))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   490
            [u'<foo>', u'\\t&lt;asdf&gt;', u'</foo>']
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   491
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   492
            Embedded quotes in attribute values are esacaped:
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   493
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   494
            >>> list(Tag.build('foo', style='ok;" onload="...'))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   495
            [u'<foo style=\\'ok;" onload="...\\' />']
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   496
            >>> list(Tag.build('foo', style='ok;\\'" onload=..."\\''))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   497
            [u'<foo style="ok;\\'&quot; onload=...&quot;\\'" />']
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   498
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   499
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   500
        # optional attr spec
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   501
        if self.attrs :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   502
            attrs = " " + self.render_attrs()
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   503
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   504
        else :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   505
            attrs = ""
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   506
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   507
        if not self.contents and self.selfclosing is False :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   508
            # empty tag, but don't use the self-closing syntax..
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   509
            yield 0, u"<%s%s></%s>" % (self.name, attrs, self.name)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   510
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   511
        elif not self.contents  :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   512
            # self-closing xml tag
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   513
            # do note that this is invalid HTML, and the space before the / is relevant for parsing it as HTML
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   514
            yield 0, u"<%s%s />" % (self.name, attrs)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   515
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   516
        elif self.whitespace_sensitive :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   517
            # join together each line for each child, discarding the indent
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   518
            content = u''.join(line for indent, line in self.flatten_items())
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   519
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   520
            # render full tag on a single line
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   521
            yield 0, u"<%s%s>%s</%s>" % (self.name, attrs, content, self.name)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   522
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   523
        else :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   524
            # start tag
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   525
            yield 0, u"<%s%s>" % (self.name, attrs)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   526
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   527
            # contents, indented one level below the start tag
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   528
            for indent, line in self.flatten_items(indent=1) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   529
                yield indent, line
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   530
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   531
            # close tag
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   532
            yield 0, u"</%s>" % (self.name, )
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   533
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   534
    def __repr__ (self) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   535
        return 'tag(%s)' % ', '.join(
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   536
            [
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   537
                repr(self.name)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   538
            ] + [
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   539
                repr(c) for c in self.contents
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   540
            ] + [
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   541
                '%s=%r' % (name, value) for name, value in self.attrs.iteritems()
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   542
            ]
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   543
        )
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   544
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   545
# factory function for Tag
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   546
tag = Tag.build
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   547
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   548
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   549
class Document (Renderable) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   550
    """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   551
        A full XHTML 1.0 document with optional XML header, doctype, html[@xmlns].
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   552
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   553
        >>> list(Document(tags.html('...')))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   554
        [u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">', u'<html xmlns="http://www.w3.org/1999/xhtml">', u'\\t...', u'</html>']
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   555
    """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   556
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   557
    def __init__ (self, root,
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   558
        doctype='html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"',
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   559
        html_xmlns='http://www.w3.org/1999/xhtml',
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   560
        xml_version=None, xml_encoding=None, 
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   561
    ) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   562
        # add xmlns attr to root node
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   563
        self.root = root(xmlns=html_xmlns)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   564
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   565
        # store
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   566
        self.doctype = doctype
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   567
        self.xml_declaration = {}
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   568
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   569
        if xml_version :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   570
            self.xml_declaration['version'] = xml_version
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   571
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   572
        if xml_encoding :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   573
            self.xml_declaration['encoding'] = xml_encoding
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   574
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   575
    def flatten (self) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   576
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   577
            Return the header lines along with the normally formatted <html> tag
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   578
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   579
        
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   580
        if self.xml_declaration :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   581
            yield 0, u'<?xml %s ?>' % (' '.join('%s="%s"' % kv for kv in self.xml_declaration.iteritems()))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   582
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   583
        if self.doctype :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   584
            yield 0, u'<!DOCTYPE %s>' % (self.doctype)
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   585
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   586
        # <html>
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   587
        for indent, line in self.root.flatten() :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   588
            yield indent, line
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   589
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   590
class TagFactory (object) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   591
    """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   592
        Build Tags with names give as attribute names
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   593
        
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   594
        >>> list(TagFactory().a(href='#')('Yay'))
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   595
        [u'<a href="#">', u'\\tYay', u'</a>']
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   596
23
b1e28f1efd70 html: fix doctest for changed html(...) api
Tero Marttila <terom@paivola.fi>
parents: 0
diff changeset
   597
        >>> list(TagFactory()("><"))
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   598
        [u'><']
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   599
    """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   600
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   601
    # full XHTML document
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   602
    document = Document
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   603
    
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   604
    def __getattr__ (self, name) :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   605
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   606
            Get a Tag object with the given name, but no contents
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   607
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   608
            >>> TagFactory().a
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   609
            tag('a')
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   610
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   611
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   612
        return Tag(name, [], {})
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   613
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   614
    def __call__ (self, *values) :
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   615
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   616
            Raw HTML.
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   617
        """
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   618
162
80c91d019a13 pvl.web.html: support arbitrary contents for Text
Tero Marttila <terom@paivola.fi>
parents: 151
diff changeset
   619
        return Text(*values)
0
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   620
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   621
# static instance
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   622
tags = TagFactory()
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   623
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   624
# testing
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   625
if __name__ == '__main__' :
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   626
    import doctest
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   627
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   628
    doctest.testmod()
91c739202f06 initial code for dhcp hosts
Tero Marttila <terom@paivola.fi>
parents:
diff changeset
   629