[py-svn] r34597 - in py/dist/py/rst: . testing

guido at codespeak.net guido at codespeak.net
Tue Nov 14 14:49:06 CET 2006


Author: guido
Date: Tue Nov 14 14:49:04 2006
New Revision: 34597

Added:
   py/dist/py/rst/testing/test_transform.py
   py/dist/py/rst/transform.py
Modified:
   py/dist/py/rst/rst.py
   py/dist/py/rst/testing/test_rst.py
Log:
Added first bits of an event based Rest tree transformation thingie (a la SAX
but then for converting an rst.Rest tree to e.g. HTML), fixed support for
nested dlist items.


Modified: py/dist/py/rst/rst.py
==============================================================================
--- py/dist/py/rst/rst.py	(original)
+++ py/dist/py/rst/rst.py	Tue Nov 14 14:49:04 2006
@@ -268,6 +268,15 @@
     def text(self):
         idepth = self.get_indent_depth()
         indent = self.indent + (idepth + 1) * '  '
+        txt = '\n'.join(self.render_children(indent))
+        ret = []
+        if idepth:
+            ret.append('\n')
+        item_char = self.item_chars[idepth]
+        ret += [indent[len(item_char)+1:], item_char, ' ', txt[len(indent):]]
+        return ''.join(ret)
+    
+    def render_children(self, indent):
         txt = []
         for child in self.children:
             if isinstance(child, AbstractText):
@@ -276,13 +285,7 @@
                 txt.append(p.text())
             else:
                 txt.append(child.text())
-        txt = '\n'.join(txt)
-        ret = []
-        if idepth:
-            ret.append('\n')
-        item_char = self.item_chars[idepth]
-        ret += [indent[2:], item_char, ' ', txt[len(indent):]]
-        return ''.join(ret)
+        return txt
 
     def get_indent_depth(self):
         depth = 0
@@ -293,14 +296,24 @@
         return depth - 1
 
 class OrderedListItem(ListItem):
-    item_chars = ("#.",)
+    item_chars = ["#."] * 5
 
 class DListItem(ListItem):
     item_chars = None
     def __init__(self, term, definition, *args, **kwargs):
-        self.item_chars = ('%s\n ' % (term,),)
+        self.term = term
         super(DListItem, self).__init__(definition, *args, **kwargs)
 
+    def text(self):
+        idepth = self.get_indent_depth()
+        indent = self.indent + (idepth + 1) * '  '
+        txt = '\n'.join(self.render_children(indent))
+        ret = []
+        if idepth:
+            ret.append('\n')
+        ret += [indent[2:], self.term, '\n', txt]
+        return ''.join(ret)
+
 class Link(AbstractText):
     start = '`'
     end = '`_'

Modified: py/dist/py/rst/testing/test_rst.py
==============================================================================
--- py/dist/py/rst/testing/test_rst.py	(original)
+++ py/dist/py/rst/testing/test_rst.py	Tue Nov 14 14:49:04 2006
@@ -284,21 +284,6 @@
     assert txt == expected
     checkrest(txt)
 
-def test_title_following_links_empty_line():
-    expected = """\
-Foo, bar and `baz`_.
-
-.. _`baz`: http://www.baz.com
-
-Spam
-----
-
-Spam, eggs and spam.
-"""
-    txt = Rest(Paragraph("Foo, bar and ", Link("baz", "http://www.baz.com")),
-               Title('Spam'), Paragraph('Spam, eggs and spam.'))
-    checkrest(txt)
-
 def test_definition_list():
     expected = """\
 foo
@@ -313,6 +298,27 @@
     assert txt == expected
     checkrest(txt)
 
+def test_nested_dlists():
+    expected = """\
+foo
+  bar baz
+
+  qux
+    quux
+"""
+    txt = Rest(DListItem('foo', 'bar baz', DListItem('qux', 'quux'))).text()
+    assert txt == expected
+
+def test_nested_list_dlist():
+    expected = """\
+* foo
+
+  foobar
+    baz
+"""
+    txt = Rest(ListItem('foo', DListItem('foobar', 'baz'))).text()
+    assert txt == expected
+
 def test_transition():
     txt = Rest(Paragraph('foo'), Transition(), Paragraph('bar')).text()
     assert txt == 'foo\n\n%s\n\nbar\n' % ('-' * 79,)
@@ -354,3 +360,18 @@
    Some paragraph content.
 """
 
+def test_title_following_links_empty_line():
+    expected = """\
+Foo, bar and `baz`_.
+
+.. _`baz`: http://www.baz.com
+
+Spam
+----
+
+Spam, eggs and spam.
+"""
+    txt = Rest(Paragraph("Foo, bar and ", Link("baz", "http://www.baz.com")),
+               Title('Spam'), Paragraph('Spam, eggs and spam.'))
+    checkrest(txt)
+

Added: py/dist/py/rst/testing/test_transform.py
==============================================================================
--- (empty file)
+++ py/dist/py/rst/testing/test_transform.py	Tue Nov 14 14:49:04 2006
@@ -0,0 +1,38 @@
+import py
+from py.__.rst.rst import *
+from py.__.rst.transform import *
+
+def convert_to_html(tree):
+    handler = HTMLHandler()
+    t = RestTransformer(tree)
+    t.parse(handler)
+    return handler.html
+
+class HTMLHandler(py.__.rst.transform.HTMLHandler):
+    def startDocument(self):
+        pass
+    endDocument = startDocument
+
+def test_transform_basic_html():
+    for rest, expected in ((Rest(Title('foo')), '<h1>foo</h1>'),
+                           (Rest(Paragraph('foo')), '<p>foo</p>'),
+                           (Rest(SubParagraph('foo')),
+                            '<p class="sub">foo</p>'),
+                           (Rest(LiteralBlock('foo\nbar')),
+                            '<pre>foo\nbar</pre>'),
+                           (Rest(Paragraph(Link('foo',
+                                                'http://www.foo.com/'))),
+                            '<p><a href="http://www.foo.com/">foo</a></p>')):
+        html = convert_to_html(rest)
+        assert html == expected
+
+def test_transform_list_simple():
+    rest = Rest(ListItem('foo'), ListItem('bar'))
+    html = convert_to_html(rest)
+    assert html == '<ul><li>foo</li><li>bar</li></ul>'
+
+def test_transform_list_nested():
+    rest = Rest(ListItem('foo'), ListItem('bar', ListItem('baz')))
+    html = convert_to_html(rest)
+    assert html == '<ul><li>foo</li><li>bar<ul><li>baz</li></ul></li></ul>'
+

Added: py/dist/py/rst/transform.py
==============================================================================
--- (empty file)
+++ py/dist/py/rst/transform.py	Tue Nov 14 14:49:04 2006
@@ -0,0 +1,163 @@
+from py.__.rst import rst
+
+class RestTransformer(object):
+    def __init__(self, tree):
+        self.tree = tree
+        self._titledepths = {}
+        self._listmarkers = []
+
+    def parse(self, handler):
+        handler.startDocument()
+        self.parse_nodes(self.tree.children, handler)
+        handler.endDocument()
+
+    def parse_nodes(self, nodes, handler):
+        for node in nodes:
+            name = node.__class__.__name__
+            if name == 'Rest':
+                continue
+            try:
+                getattr(self, 'handle_%s' % (name,))(node, handler)
+            except AttributeError:
+                # caused by the handler not implementing something (well, let's
+                # assume that at least ;)
+                py.std.sys.stderr.write('Warning: error handling node %s\n' % (
+                                            node,))
+    def handle_Title(self, node, handler):
+        depthkey = (node.abovechar, node.belowchar)
+        if depthkey not in self._titledepths:
+            if not self._titledepths:
+                depth = 1
+            else:
+                depth = max(self._titledepths.values()) + 1
+            self._titledepths[depthkey] = depth
+        else:
+            depth = self._titledepths[depthkey]
+        handler.startTitle(depth)
+        self.parse_nodes(node.children, handler)
+        handler.endTitle(depth)
+
+    def handle_ListItem(self, node, handler):
+        # XXX oomph...
+        startlist = False
+        c = node.parent.children
+        nodeindex = c.index(node)
+        if nodeindex == 0:
+            startlist = True
+        else:
+            prev = c[nodeindex - 1]
+            if not isinstance(prev, rst.ListItem):
+                startlist = True
+            elif prev.indent < node.indent:
+                startlist = True
+        endlist = False
+        if nodeindex == len(c) - 1:
+            endlist = True
+        else:
+            next = c[nodeindex + 1]
+            if not isinstance(next, rst.ListItem):
+                endlist = True
+            elif next.indent < node.indent:
+                endlist = True
+        type = isinstance(node, rst.OrderedListItem) and 'o' or 'u'
+        handler.startListItem('u', startlist)
+        self.parse_nodes(node.children, handler)
+        handler.endListItem('u', endlist)
+
+    def handle_Transition(self, node, handler):
+        handler.handleTransition()
+
+    def handle_Paragraph(self, node, handler):
+        handler.startParagraph()
+        self.parse_nodes(node.children, handler)
+        handler.endParagraph()
+
+    def handle_SubParagraph(self, node, handler):
+        handler.startSubParagraph()
+        self.parse_nodes(node.children, handler)
+        handler.endSubParagraph()
+
+    def handle_LiteralBlock(self, node, handler):
+        handler.handleLiteralBlock(node._text)
+
+    def handle_Text(self, node, handler):
+        handler.handleText(node._text)
+
+    def handle_Em(self, node, handler):
+        handler.handleEm(node._text)
+
+    def handle_Strong(self, node, handler):
+        handler.handleStrong(node._text)
+
+    def handle_Quote(self, node, handler):
+        handler.handleQuote(node._text)
+
+    def handle_Link(self, node, handler):
+        handler.handleLink(node._text, node.target)
+
+def entitize(txt):
+    for char, repl in (('&', 'amp'), ('>', 'gt'), ('<', 'lt'), ('"', 'quot'),
+                       ("'", 'apos')):
+        txt = txt.replace(char, '&%s;' % (repl,))
+    return txt
+
+class HTMLHandler(object):
+    def __init__(self, title='untitled rest document'):
+        self.title = title
+        self._data = []
+        self._listdepth = 0
+
+    def startDocument(self):
+        self._data += ['<html>', '<head>', '<title>%s</title>' % (self.title,),
+                       '</head>', '<body>']
+
+    def endDocument(self):
+        self._data += ['</body>', '</html>']
+
+    def startTitle(self, depth):
+        self._data.append('<h%s>' % (depth,))
+    
+    def endTitle(self, depth):
+        self._data.append('</h%s>' % (depth,))
+
+    def startParagraph(self):
+        self._data.append('<p>')
+
+    def endParagraph(self):
+        self._data.append('</p>')
+
+    def startSubParagraph(self):
+        self._data.append('<p class="sub">')
+
+    def endSubParagraph(self):
+        self._data.append('</p>')
+
+    def handleLiteralBlock(self, text):
+        self._data.append('<pre>%s</pre>' % (entitize(text),))
+
+    def handleText(self, text):
+        self._data.append(entitize(text))
+
+    def handleEm(self, text):
+        self._data.append('<em>%s</em>' % (entitize(text),))
+
+    def startListItem(self, type, startlist):
+        if startlist:
+            nodename = type == 'o' and 'ol' or 'ul'
+            self._data.append('<%s>' % (nodename,))
+        self._data.append('<li>')
+
+    def endListItem(self, type, closelist):
+        self._data.append('</li>')
+        if closelist:
+            nodename = type == 'o' and 'ol' or 'ul'
+            self._data.append('</%s>' % (nodename,))
+
+    def handleLink(self, text, target):
+        self._data.append('<a href="%s">%s</a>' % (entitize(target),
+                                                   entitize(text)))
+
+    def _html(self):
+        return ''.join(self._data)
+    html = property(_html)
+



More information about the pytest-commit mailing list