[py-svn] r21129 - in py/dist/py/rest: . testing

jan at codespeak.net jan at codespeak.net
Tue Dec 13 14:07:52 CET 2005


Author: jan
Date: Tue Dec 13 14:07:50 2005
New Revision: 21129

Added:
   py/dist/py/rest/rst.py
   py/dist/py/rest/testing/test_rst.py
Log:
some stuff to generate docutils ReST files
not yet exported


Added: py/dist/py/rest/rst.py
==============================================================================
--- (empty file)
+++ py/dist/py/rest/rst.py	Tue Dec 13 14:07:50 2005
@@ -0,0 +1,230 @@
+import py
+from py.xml import Namespace, Tag
+from py.__.xmlobj.visit import SimpleUnicodeVisitor
+from py.__.xmlobj.html import HtmlVisitor
+
+import textwrap
+
+def itertext(text):
+    """ Generator: like string.split, but ''.join(itertext(t)) == t
+
+    >>> list(itertext('word\n  second word')) 
+    ['word', '\n', '  ', 'second', ' ', 'word']
+    """
+    state_word = 'not isspace()' 
+    state_whitespace = '\t\n\x0b\x0c\r'
+    state_space = ' '
+    def compute_state(char):
+        if char in state_whitespace:
+            return state_whitespace
+        if char == ' ':
+            return state_space
+        return state_word
+
+    word = ''
+    state = None 
+    for char in text:
+        if state is None:
+            # init
+            state = compute_state(char)
+            word = char
+            continue
+        
+        next_state = compute_state(char)
+        if state != state_whitespace and state == next_state:
+            word += char
+            continue
+        yield word
+        word = char
+            
+        state = next_state
+    yield word
+
+            
+class Out:
+    """ wrap text like textwarp.wrap, but preseves \n
+
+    all lines are left aligned
+    arguments to write must be strings or iterable and
+    return strings of length 1 (chars)
+    """
+
+    def __init__(self, width=70):
+        self.width = width
+        self.lines = ['']
+
+    def copy(self):
+        ' like copy, except self.lines = ['']'
+        return self.__class__(width = self.width)
+
+    def write(self, arg, literal=False):
+        line = self.lines.pop()
+        for w in itertext(arg):
+            if w == '\n':
+                self.lines.append(line)
+                line = ''
+                continue
+            if literal or self.width is None or \
+                   line and not line[-1].isspace():
+                line += w
+                continue
+            if len(line) + len(w) > self.width:
+                if line != '':
+                    self.lines.append(line)
+                line = w.lstrip()
+                continue
+            line += w
+                
+        self.lines.append(line)
+        return self   
+    
+    def writeln(self, arg='', literal=False):
+        self.write(arg, literal)
+        self.write('\n')
+
+    def write_literal(self, arg):
+        self.write(arg, literal=True)
+
+    def writeln_literal(self, arg):
+        self.writeln(arg, literal=True)
+
+    def append(self, out, indent = '', join=''):
+        keepends = True
+        self.write_literal(join.join(
+            [indent + line
+             for line in out.render().splitlines(keepends)]))
+        #self.write_literal(indent + line)
+
+    def extend(self, out_list, indent = '', infix = ' ',
+               join = '', literal = False):
+        l = list(out_list)
+        for out in l[:-1]:
+            self.append(out, indent, join=join)
+            self.write(infix, literal=literal)
+        self.append(l[-1], indent, join=join)
+
+    def max_length(self):
+        return max([len(l) for l in self.lines])
+
+    def render(self):
+        return '\n'.join(self.lines)
+
+class RestTag(Tag):
+    start_string = ''
+    end_string = ''
+    sep = ''
+    
+    def __init__(self, *args, **kwargs):
+        super(RestTag, self).__init__(*args, **kwargs)
+        self.parse_options(self.attr)
+
+    def parse_options(self, attr):
+        pass
+
+    def text(self, width = 70):
+        out = Out(width = width)
+        self.__rest__(out)
+        return out.render()
+    
+    def __rest__(self, out):
+        out.write(self.sep)
+        out.write(self.start_string)
+        self.write_children(out, self.render_children(out))
+        out.write(self.end_string)
+        out.write(self.sep)
+
+    def write_children(self, out, child_outs):
+        out.extend(child_outs)
+        
+    def render_children(self, out):
+        outs = []
+        for child in self:
+            child_out = out.copy()
+            if isinstance(child, RestTag):
+                child.__rest__(child_out)
+            else:
+                child_out.write(child)
+            outs.append(child_out)
+        return outs
+
+
+class rest(Namespace):
+    __tagclass__ = RestTag
+    __stickname__ = True
+
+    class paragraph(RestTag):
+        sep = '\n\n'
+
+    class emph(RestTag):
+        start_string = '*'
+        end_string = start_string
+
+    class strongemph(RestTag):
+        start_string = '**'
+        end_string = start_string
+
+    class inline_literal(RestTag):
+        start_string = "``"
+        end_string = "``"
+
+    class interpreted_text(RestTag):
+        start_string ='`'
+        end_string = '`'
+        role = ''
+
+        def parse_options(self, attr):
+            self.role = getattr(attr, 'role', self.role)
+            if self.role:
+                self.start_string = ':%s:%s' % (self.role, self.start_string)
+
+    class explicit_markup(RestTag):
+        sep = '\n\n'
+        start_string = '.. '
+
+        def write_children(self, out, child_outs):
+            out.extend(child_outs, join = ' ' * len(self.start_string))
+
+    class title(RestTag):
+        sep = '\n'
+        start_string = '#'
+        end_string = '#'
+        quotes = """! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~""".split()
+
+        def parse_options(self, attr):
+            self.start_string = getattr(attr, 'overline', '#')
+            self.end_string = getattr(attr, 'underline', '#')
+        
+        def __rest__(self, out):
+            child_outs = self.render_children(out)
+            max_length = max([o.max_length() for o in child_outs])
+            out.write(self.sep)
+            out.writeln_literal(self.start_string * max_length)
+            out.extend(child_outs)
+            out.writeln()
+            out.writeln_literal(self.end_string * max_length)
+            out.write(self.sep)
+
+    class list_item(RestTag):
+        sep = '\n\n'
+        start_string = '* '
+
+        def parse_options(self, attr):
+            self.start_string = getattr(attr, 'bullet', '*') + ' '
+            if  getattr(attr, 'enumerate', False):
+                self.start_string = '#. '
+            
+        def write_children(self, out, child_outs):
+            out.extend(child_outs, join = ' ' * len(self.start_string))
+            
+    class literal_block(RestTag):
+        sep = '\n\n'
+        start_string = '::\n\n'
+        indent = '  '
+        quote = '  '
+        quotes = """! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~""".split() + [' ']
+        
+        def parse_options(self, attr):
+            self.quote = getattr(attr, 'quote', '  ')
+
+        def write_children(self, out, child_outs):
+            out.extend(child_outs, indent = self.quote)

Added: py/dist/py/rest/testing/test_rst.py
==============================================================================
--- (empty file)
+++ py/dist/py/rest/testing/test_rst.py	Tue Dec 13 14:07:50 2005
@@ -0,0 +1,288 @@
+import py
+from py.__.rest.rst import rest, Out, itertext, RestTag
+from py.__.misc import rest as pyrest
+
+#temp = py.test.ensuretemp('check_rest')
+temp = py.path.local.mkdtemp()
+
+def check_rest(content, include_dir = None):
+    if isinstance(content, RestTag):
+        content = content.text()
+    content = unicode(content)
+    print content
+    tempdir = py.path.local.make_numbered_dir(rootdir=temp)
+    if include_dir is None:
+        include_dir = tempdir
+    #logging
+    tempdir.ensure('input.txt').write(content)
+    try:
+        output = pyrest.convert_rest_html(content, include_dir)
+    except:
+        fail_msg = '''
+        failed to convert %s to html, probably not valid reStructuredText
+        see recorded stderr for error message'''
+        
+        py.test.fail(fail_msg % tempdir.join('input.txt') + '\n\n' + str(py.code.ExceptionInfo()))
+    tempdir.ensure('output.html').write(output)
+    return True
+
+def render_xml(arg):
+    return arg.unicode()
+
+class TestSplit:
+
+    def test_empyt_string(self):
+        assert list(itertext('')) == ['']
+
+    def test_whitespace(self):
+        assert list(itertext('  ')) == ['  ']
+
+    def test_single_word(self):
+        assert list(itertext('word')) == ['word']
+        
+    def test_word_with_whitespace(self):
+        assert list(itertext('word ')) == ['word', ' ']
+        
+    def test_two_words(self):
+        assert list(itertext('first second')) == ['first', ' ', 'second']
+
+    def test_trailing_newline(self):
+        assert list(itertext('\nfirst word')) == ['\n', 'first', ' ', 'word']
+    def test_newline_and_space_are_seperated(self):
+        assert list(itertext('\n third_item')) == ['\n', ' ', 'third_item']
+
+class TestOut:
+    
+    def test_write_nothing(self):
+        assert Out().write('').render() == ''
+
+    def test_write_returns_self(self):
+        out = Out()
+        assert out.write('') is out
+        assert out.write('') is not Out().write('')
+
+    def test_write_newline(self):
+        out = Out()
+        out.write('\n')
+        assert len(out.lines) == 2
+        assert out.render() == '\n'
+
+    def test_write_one_line(self):
+        text = "'[B]ut assume that I have some other use case' isn't a valid use case. - Fredrik Lundh"
+        out = Out(width=None)
+        out.write(text)
+        assert out.lines == [text]
+        assert out.render() == text
+
+    def test_write_and_width(self):
+        text = "This sentence is 36 characters wide."
+        out = Out(width = 36)
+        out.write(text)
+        assert len(out.lines) == 1
+        out = Out(width = 35)
+        out.write(text)
+        assert len(out.lines) == 2
+
+    def test_write_and_newline(self):
+        text = "1234567890\n1234567890"
+        out = Out(width=30)
+        out.write(text)
+        assert len(out.lines) == 2
+        assert len(out.lines[0]) == 10
+        assert out.render() == text
+        out.write(text)
+        assert len(out.lines) == 3
+
+    def test_write_with_trailing_newline(self):
+        text = "0123456789\n"
+        out = Out()
+        out.write(text)
+        assert len(out.lines) == 2
+        assert out.render() == text
+
+    def test_write_long_word(self):
+        text = '12345678901234567890'
+        out = Out(width=19)
+        out.write(text)
+        assert len(out.lines) == 1
+        assert text == out.render()
+        out.write('1')
+        assert len(out.lines) == 1
+        out.write(' 2')
+        assert len(out.lines) == 2
+
+    def test_long_literal_and_newline(self):
+        text = '12345678901234567890'
+        out = Out(width=10)
+        out.write_literal(text)
+        assert len(out.lines) == 1
+        text += '\n1234567890'
+        out.write_literal(text)
+        assert len(out.lines) == 2
+
+    def test_append(self):
+        out = Out()
+        out.write('First line\n')
+        out.write('Second line')
+
+        root_out = Out()
+        root_out.write('Root')
+        root_out.append(out)
+
+        assert len(root_out.lines) == 2
+
+    def test_max_length(self):
+        out = Out()
+        out.write('1234567890')
+        out.writeln()
+        out.write('123456789')
+        assert out.max_length() == 10
+        
+
+class TestRest:
+    disabled = False
+    def setup_method(self, method):
+        self.text = {}
+
+        self.text['paragraph'] = "Paragraphs consist of blocks of left-aligned text with no markup indicating any other body element. Blank lines separate paragraphs from each other and from other body elements. Paragraphs may contain inline markup."
+       
+    def test_paragraph(self):
+        para = rest.paragraph(self.text['paragraph'])
+        print render_xml(para)
+        text = para.text()
+        check_rest(text)
+        assert text[0] == '\n'
+        assert text[-1] == '\n'
+        
+    def test_emph(self):
+        emph = rest.emph('strong')
+        assert emph.text() == '*strong*'
+        assert check_rest(emph.text())
+
+    def test_add_whitespace(self):
+        para = rest.paragraph('Starttext', rest.emph('EMPHASIS'), 'endtext')
+        assert para.text() == '\n\nStarttext *EMPHASIS* endtext\n\n'
+
+    def test_nested_emph(self):
+        "Nested Inline Markup not allowed in ReST"
+        emph = rest.emph('start', rest.emph('middle'), 'end')
+        check_rest(emph.text())
+        assert emph.text() == '*start *middle* end*'
+    
+
+    def test_strongemph(self):
+        phrase = 'failure is not an option'
+        emph = rest.strongemph(phrase)
+        assert emph.text() == '**' + phrase + '**'
+
+    def test_title(self):
+        phrase = 'Everything should be built top-down, except the first time.'
+        title = rest.title(phrase)
+        expected = title.sep + title.start_string * len(phrase) \
+                   +'\n' + phrase + '\n' + title.end_string *len(phrase)\
+                   + '\n' + title.sep
+        assert title.text() == expected
+        check_rest(title.text())
+        
+## def test_subtitle():
+##     phrase = 'If your computer speaks English it was probably made in Japan'
+##     subtitle = rest.subtitle(phrase)
+##     expected = phrase + '\n' + subtitle.underline * len(phrase) + '\n'
+##     assert unicode(subtitle) == expected
+##     assert check_rest(subtitle)
+    
+
+    def test_paragraph(self):
+        phrase =  "Perhaps if we wrote programs from childhood on, as adults we'd be able to read them."
+        para = rest.paragraph(phrase)
+        assert check_rest(para)
+
+    def test_list_item(self):
+        item_text = 'A short item.'
+        item = rest.list_item(item_text)
+        assert item.text() == item.sep + item.start_string + item_text \
+               + item.end_string + item.sep
+        check_rest(item)
+
+    def test_list_item_multiline(self):
+        item_text = '01234567890 1234567890'
+        item = rest.list_item(item_text)
+        assert len(item.text(width=15).splitlines()) == 5
+        check_rest(item.text(width=15))
+
+    def test_list_item_custom_bullet(self):
+        item_text = '12345678901234567890'
+        item = rest.list_item(item_text, bullet='+')
+        assert item.text().strip()[0] == '+'
+        check_rest(item)
+
+    def test_auto_enumerated_list(self):
+        item_text = '12345678901234567890'
+        item = rest.list_item(item_text, enumerate = True)
+        assert item.text().strip()[0:2] == '#.'
+        check_rest(item)
+
+    def test_literal_block(self):
+        block_text = '''\
+        This line is only 45 characters wide.
+          This one is even longer (two spaces).
+        '''
+
+        block = rest.literal_block(block_text)
+        assert block.text()[:6] == '\n\n::\n\n'
+        out = Out()
+        block.__rest__(out)
+        assert out.max_length() == len(block.quote) + max([len(l) for l in block_text.splitlines()])
+        check_rest(block)
+
+        block = rest.literal_block(block_text, quote= rest.literal_block.quotes[3])
+        assert block.text().strip()[4] == rest.literal_block.quotes[3]
+
+
+    def test_interpreted_text(self):
+
+        itext = rest.interpreted_text('just text')
+        assert itext.text() == '`just text`'
+        itext_role = rest.interpreted_text('just text with role', role = 'red')
+        assert itext_role.text().startswith(':red:`just')
+
+    def test_directive(self):
+        pass
+
+
+# create texts with rest objects
+
+    def test_block_quote(self):
+        block ="""\
+This is an ordinary paragraph, introducing a block quote.
+
+    "It is my business to know things.  That is my trade."
+
+    -- Sherlock Holmes
+    """
+        assert check_rest(block)
+
+
+    
+    def test_quoted_line_block(self):
+        text = """\
+Take it away, Eric the Orchestra Leader!
+
+    | A one, two, a one two three four
+    |
+    | Half a bee, philosophically,
+    |     must, *ipso facto*, half not be.
+    | But half the bee has got to be,
+    |     *vis a vis* its entity.  D'you see?
+    |
+    | But can a bee be said to be
+    |     or not to be an entire bee,
+    |         when half the bee is not a bee,
+    |             due to some ancient injury?
+    |
+    | Singing...
+    """
+        assert check_rest(text)
+
+#def test_temdir_output():
+#    py.test.skip('tempdir is %s' % temp)



More information about the pytest-commit mailing list