changeset 2779:c9dd12befda7

wiki parser: match everything looking like a macro and either execute it (macro imports ok) or render the macro markup as text (macro import fails, error msg is in title). This makes scan_rules independent of wiki, so we can move all re.compile time to Parser class (doing it only once on module import time).
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Tue, 28 Aug 2007 13:43:11 +0200
parents c8dd346f146e
children 28b851be0844
files MoinMoin/formatter/__init__.py MoinMoin/formatter/dom_xml.py MoinMoin/formatter/text_docbook.py MoinMoin/formatter/text_gedit.py MoinMoin/formatter/text_python.py MoinMoin/parser/text_moin_wiki.py
diffstat 6 files changed, 33 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/formatter/__init__.py	Tue Aug 28 03:12:46 2007 +0200
+++ b/MoinMoin/formatter/__init__.py	Tue Aug 28 13:43:11 2007 +0200
@@ -304,10 +304,19 @@
 
     # Dynamic stuff / Plugins ############################################
 
-    def macro(self, macro_obj, name, args):
+    def macro(self, macro_obj, name, args, markup=None):
         # call the macro
-        return macro_obj.execute(name, args)
-
+        try:
+            return macro_obj.execute(name, args)
+        except ImportError, err:
+            errmsg = unicode(err)
+            if markup:
+                errmsg = wikiutil.escape(errmsg)
+                return (self.span(1, title=errmsg) +
+                        self.text(markup) +
+                        self.span(0))
+            else:
+                return self.text(errmsg)
     def _get_bang_args(self, line):
         if line.startswith('#!'):
             try:
--- a/MoinMoin/formatter/dom_xml.py	Tue Aug 28 03:12:46 2007 +0200
+++ b/MoinMoin/formatter/dom_xml.py	Tue Aug 28 13:43:11 2007 +0200
@@ -203,7 +203,7 @@
         kw['pagename'] = pagename
         return self._set_tag('interwiki', on, **kw)
 
-    def macro(self, macro_obj, name, args):
+    def macro(self, macro_obj, name, args, markup=None):
         # call the macro
         return self._add_tag('macro', name=name, args=(args or ''))
 
--- a/MoinMoin/formatter/text_docbook.py	Tue Aug 28 03:12:46 2007 +0200
+++ b/MoinMoin/formatter/text_docbook.py	Tue Aug 28 13:43:11 2007 +0200
@@ -565,7 +565,7 @@
         else:
             return ""
 
-    def macro(self, macro_obj, name, args):
+    def macro(self, macro_obj, name, args, markup=None):
         if name == "TableOfContents":
             # Table of content can be inserted in docbook transformation
             return u""
@@ -573,7 +573,7 @@
         # At the begining text mode contain some string which is later
         # exchange for real value. There is problem that data inserted
         # as text mode are encoded to xml, e.g. < is encoded in the output as &lt;
-        text = FormatterBase.macro(self, macro_obj, name, args)
+        text = FormatterBase.macro(self, macro_obj, name, args, markup)
         if len(text) > 0:
             # prepare identificator
             sKey = "EXCHANGESTRINGMACRO-" + str(len(self.exchangeKeys)) + "-EXCHANGESTRINGMACRO"
--- a/MoinMoin/formatter/text_gedit.py	Tue Aug 28 03:12:46 2007 +0200
+++ b/MoinMoin/formatter/text_gedit.py	Tue Aug 28 13:43:11 2007 +0200
@@ -95,7 +95,7 @@
 
     # Dynamic stuff / Plugins ############################################
 
-    def macro(self, macro_obj, name, args):
+    def macro(self, macro_obj, name, args, markup=None):
         #use ImageLink for resized images
         if name == "ImageLink" and args is not None:
 
@@ -127,11 +127,13 @@
                 kw['src'] = AttachFile.getAttachUrl(pagename, url, self.request, addts=1)
             return self.image(**kw)
 
+        elif markup is not None:
+            result = markup
         elif args is not None:
             result = "<<%s(%s)>>" % (name, args)
         else:
             result = "<<%s>>" % name
-        return '<span style="background-color:#ffff11">%s</span>' % result
+        return '<span style="background-color:#ffff11">%s</span>' % result # XXX XSS needs escaping!
 
     def parser(self, parser_name, lines):
         """ parser_name MUST be valid!
--- a/MoinMoin/formatter/text_python.py	Tue Aug 28 03:12:46 2007 +0200
+++ b/MoinMoin/formatter/text_python.py	Tue Aug 28 13:43:11 2007 +0200
@@ -185,16 +185,16 @@
         else:
             return self.formatter.div(on, **kw)
 
-    def macro(self, macro_obj, name, args):
+    def macro(self, macro_obj, name, args, markup=None):
         if self.__is_static(macro_obj.get_dependencies(name)):
             # XXX: why is this necessary??
             macro_obj.formatter = self
             return macro_obj.execute(name, args)
         else:
             return self.__insert_code(
-                '%srequest.write(%s.macro(macro_obj, %r, %r))' %
+                '%srequest.write(%s.macro(macro_obj, %r, %r, %r))' %
                 (self.__adjust_formatter_state(),
-                 self.__formatter, name, args))
+                 self.__formatter, name, args, markup))
 
     def parser(self, parser_name, lines):
         """ parser_name MUST be valid!
--- a/MoinMoin/parser/text_moin_wiki.py	Tue Aug 28 03:12:46 2007 +0200
+++ b/MoinMoin/parser/text_moin_wiki.py	Tue Aug 28 13:43:11 2007 +0200
@@ -143,6 +143,10 @@
     """
     dl_re = re.compile(dl_rule, re.VERBOSE|re.UNICODE)
 
+    # others
+    indent_re = re.compile(ur"^\s*", re.UNICODE)
+    eol_re = re.compile(r'\r?\n', re.UNICODE)
+
     # this is used inside <pre> / parser sections (we just want to know when it's over):
     pre_scan_rule = ur"""
 (?P<pre>
@@ -240,7 +244,7 @@
     (?=\s)  # we require some space after the smiley
 )|(?P<macro>
     <<
-    (?P<macro_name>(%%(macronames)s))  # name of the macro (only existing ones will match)
+    (?P<macro_name>\w+)  # name of the macro
     (?:\((?P<macro_args>.*?)\))?  # optionally macro arguments
     >>
 )|(?P<heading>
@@ -297,6 +301,7 @@
         'u': config.chars_upper,
         'l': config.chars_lower,
         'smiley': u'|'.join([re.escape(s) for s in config.smileys])}
+    scan_re = re.compile(scan_rules, re.UNICODE|re.VERBOSE)
 
     # Don't start p before these
     no_new_p_before = ("heading rule table tableZ tr td "
@@ -350,8 +355,6 @@
         self.list_indents = []
         self.list_types = []
 
-        # XXX TODO if we remove the runtime dependency, we can compile the scan_rules at module load time:
-        self.scan_rules = self.scan_rules % {'macronames': u'|'.join(macro.getNames(self.cfg))}
 
     def _close_item(self, result):
         #result.append("<!-- close item begin -->\n")
@@ -1113,7 +1116,7 @@
         # create macro instance
         if self.macro is None:
             self.macro = macro.Macro(self)
-        return self.formatter.macro(self.macro, macro_name, macro_args)
+        return self.formatter.macro(self.macro, macro_name, macro_args, markup=groups.get('macro'))
     _macro_name_repl = _macro_repl
     _macro_args_repl = _macro_repl
 
@@ -1149,8 +1152,7 @@
                 self.formatter.in_p) and lastpos < len(line):
             result.append(self.formatter.paragraph(1, css_class="line874"))
         if '}}}' in line and len(line[lastpos:].strip()) > 0:
-            scan_re = re.compile(self.scan_rules, re.UNICODE|re.VERBOSE)
-            result.append(self.scan(scan_re, line[lastpos:].strip(), inhibit_p=inhibit_p))
+            result.append(self.scan(self.scan_re, line[lastpos:].strip(), inhibit_p=inhibit_p))
         else:
             result.append(self.formatter.text(line[lastpos:]))
         return u''.join(result)
@@ -1205,19 +1207,12 @@
         self.formatter = formatter
         self.hilite_re = self.formatter.page.hilite_re
 
-        # prepare regex patterns
-        self.request.clock.start('compile_huge_and_pretty')
-        scan_re = re.compile(self.scan_rules, re.UNICODE|re.VERBOSE)
-        indent_re = re.compile(ur"^\s*", re.UNICODE)
-        eol_re = re.compile(r'\r?\n', re.UNICODE)
-        self.request.clock.stop('compile_huge_and_pretty')
-
         # get text and replace TABs
         rawtext = self.raw.expandtabs()
 
         # go through the lines
         self.lineno = self.start_line
-        self.lines = eol_re.split(rawtext)
+        self.lines = self.eol_re.split(rawtext)
         self.line_is_empty = 0
 
         self.in_processing_instructions = 1
@@ -1333,7 +1328,7 @@
                     continue
 
                 # Check indent level
-                indent = indent_re.match(line)
+                indent = self.indent_re.match(line)
                 indlen = len(indent.group(0))
                 indtype = "ul"
                 numtype = None
@@ -1389,7 +1384,7 @@
                     self.in_table = 0
 
             # Scan line, format and write
-            scanning_re = self.in_pre and self.pre_scan_re or scan_re
+            scanning_re = self.in_pre and self.pre_scan_re or self.scan_re
             if '{{{' in line:
                 self.in_nested_pre += 1
             formatted_line = self.scan(scanning_re, line, inhibit_p=inhibit_p)