Commits

Matt Chaput committed 4753c1c

Added PyConsole block matcher.
Added expression argument to block matcher initializers.

  • Participants
  • Parent commits 8a0af86

Comments (0)

Files changed (2)

File tests/test_matches.py

 import re
 
 from wikiparser.markup import BREAK, StreamTree, tostring
-from wikiparser.match.blocks import LineStart, BlockStart, BreakBefore, BreakAfter
+from wikiparser.match.blocks import LineStart, BlockStart, BreakBefore, BreakAfter, PyConsole
 from wikiparser.match.core import Suppress, OneOrMore, ZeroOrMore, StringEnd, Not, FollowedBy
 from wikiparser.match.data import Element
 from wikiparser.match.text import Mixed, Regex, Until
         target = re.sub(">[ \t\r\n]+<", "><", target).strip()
         stream = list(table.stream(input))
         output = tostring(StreamTree.tree(stream))
-        print "\n"
-        print repr(target)
-        print repr(output)
+        #print "\n"
+        #print repr(target)
+        #print repr(output)
         self.assertEqual(output, target)
         
         tables = Mixed(table, None)
                   126]
         self.assertEqual(stream, target)
 
+    def test_pycon(self):
+        m = Mixed((PyConsole(type="pycon"), BlockStart()))
+        input = """
+This is an example...
 
-
+  >>> a = 5
+  >>> b = 2
+  >>> print a + b
+  7
+  
+Nice, huh?
+"""
+        stream = list(m.stream(input))
+        self.assertEqual(stream, [(BREAK, (0, 0, {})), 'This is an example...\n',
+                                  (BREAK, (2, 2, {'type': 'pycon'})),
+                                  '>>> a = 5\n>>> b = 2\n>>> print a + b\n7\n',
+                                  (BREAK, (0, 0, {})), 'Nice, huh?\n', 85
+                                  ])
 
 
 if __name__ == '__main__':

File wikiparser/match/blocks.py

 #===============================================================================
 
 import re
+from textwrap import dedent
 
 from wikiparser.events import br
 from wikiparser.match.core import Matcher, Wrapper
     expression = re.compile(r"^(?P<nl> *\n)*(?P<indent> *)", re.MULTILINE)
     line_start = True
     
-    def __init__(self, emitbreak=True, allowindent=True, allowblank=True,
+    def __init__(self, expression=None, emitbreak=True,
+                 allowindent=True, allowblank=True,
                  attrs=None, **kwargs):
+        if isinstance(expression, basestring):
+            self.expression = re.compile(expression)
+        elif expression:
+            self.expression = expression
+        
         self.emitbreak = emitbreak
         self.allowindent = allowindent
         self.allowblank = allowblank
     
     first_chars = frozenset(" ")
     
-    def __init__(self, emitbreak=True, minindent=1, maxindent=999999, allowblank=True,
+    def __init__(self, expression=None, emitbreak=True, minindent=1, maxindent=999999, allowblank=True,
                  attrs=None, **kwargs):
-        super(IndentedLineStart, self).__init__(emitbreak=emitbreak, allowblank=allowblank,
+        super(IndentedLineStart, self).__init__(expression=expression,
+                                                emitbreak=emitbreak, allowblank=allowblank,
                                                 attrs=attrs, **kwargs)
         self.minindent = minindent
         self.maxindent = maxindent
     
     emitmodes = ["never", "always", "either", "blank", "indent"]
     
-    def __init__(self, emitbreak="either", allowindent=True, attrs=None, **kwargs):
+    def __init__(self, expression=None, emitbreak="either", allowindent=True,
+                 attrs=None, **kwargs):
+        if isinstance(expression, basestring):
+            self.expression = re.compile(expression)
+        elif expression:
+            self.expression = expression
+        
+        self.allowindent = allowindent
         self.emitbreak = -1
-        self.allowindent = allowindent
         try:
             self.emitbreak = self.emitmodes.index(emitbreak)
         except ValueError:
             yield match.end()
 
 
+class PyConsole(LineStart):
+    """Matches a Sphinx-style bare block of Python console interaction example,
+    begun with ">>>" and ending with the first blank line.
+    """
+    
+    expression = re.compile(r"^(?P<nl> *\n)*(?P<content>(?P<indent> *)>>>[^\n]*\n((?P=indent) *\S[^\n]*\n)*)", re.MULTILINE)
+    line_start = True
+    
+    def __init__(self, needblank=True, allowindent=True, dedent=True, attrs=None,
+                 **kwargs):
+        """
+        :param needblank: if True, the block must be preceeded by at least one
+            blank line to match.
+        :param allowindent: if False, will not match if the block is indented.
+        :param dedent: if True, the block is automatically dedented (using
+            textwrap.dedent) before it is emitted.
+        :param attrs: a dictionary of attributes to put on the BREAK event.
+            Any additional keyword arguments are also used as attributes.
+        """
+        
+        self.needblank = needblank
+        self.allowindent = allowindent
+        self.dedent = dedent
+        
+        self.attrs = (attrs or {}).copy()
+        self.attrs.update(kwargs)
+        
+    def parse(self, input, position, ctx):
+        match = self.expression.match(input, position)
+        if match:
+            nl = match.group("nl")
+            lines = 0
+            if position == 0:
+                # The pattern matched because we're at the start of the
+                # input string. Act like we saw a blank line.
+                lines = 2
+            elif nl:
+                # The pattern matched because of one or more newlines.
+                lines = nl.count("\n")
+            
+            if self.needblank and lines == 0:
+                return
+            
+            indent = len(match.group("indent"))
+            if indent and not self.allowindent:
+                return
+            
+            content = match.group("content")
+            if self.dedent:
+                content = dedent(content)
+            
+            yield br(ctx, indent, self.attrs.copy())
+            yield content
+            yield match.end()
+
+
 class StartMarker(Matcher):
     """Represents a "marker" at the start of a line that is added to the
     initial indent for subsequent lines.
             yield br(nctx, 0, **self.attrs)
             
 
+            
+            
 
 
 
 
 
 
-
-