Back to index

moin  1.9.0~rc2
text_rst.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - ReStructured Text Parser
00004 
00005     @copyright: 2004 Matthew Gilbert <gilbert AT voxmea DOT net>,
00006                 2004 Alexander Schremmer <alex AT alexanderweb DOT de>
00007     @license: GNU GPL, see COPYING for details.
00008 
00009     REQUIRES docutils 0.3.10 or later (must be later than December 30th, 2005)
00010 """
00011 
00012 import re
00013 import new
00014 import StringIO
00015 import __builtin__
00016 import sys
00017 
00018 # docutils imports are below
00019 from MoinMoin.parser.text_moin_wiki import Parser as WikiParser
00020 from MoinMoin.Page import Page
00021 from MoinMoin.action import AttachFile
00022 from MoinMoin import wikiutil
00023 
00024 Dependencies = [] # this parser just depends on the raw text
00025 
00026 # --- make docutils safe by overriding all module-scoped names related to IO ---
00027 
00028 # TODO: Add an error message to dummyOpen so that the user knows that
00029 # they requested an unsupported feature of Docutils in MoinMoin.
00030 def dummyOpen(x, y=None, z=None): return
00031 
00032 class dummyIO(StringIO.StringIO):
00033     def __init__(self, destination=None, destination_path=None,
00034                  encoding=None, error_handler='', autoclose=1,
00035                  handle_io_errors=1, source_path=None):
00036         StringIO.StringIO.__init__(self)
00037 
00038 class dummyUrllib2:
00039     def urlopen(a):
00040         return StringIO.StringIO()
00041     urlopen = staticmethod(urlopen)
00042 
00043 # # # All docutils imports must be contained below here
00044 try:
00045     import docutils
00046     from docutils.core import publish_parts
00047     from docutils.writers import html4css1
00048     from docutils.nodes import reference
00049     from docutils.parsers import rst
00050     from docutils.parsers.rst import directives, roles
00051 # # # All docutils imports must be contained above here
00052 
00053     ErrorParser = None # used in the case of missing docutils
00054     docutils.io.FileOutput = docutils.io.FileInput = dummyIO
00055 except ImportError:
00056     # we need to workaround this totally broken plugin interface that does
00057     # not allow us to raise exceptions
00058     class ErrorParser:
00059         caching = 0
00060         Dependencies = Dependencies # copy dependencies from module-scope
00061 
00062         def __init__(self, raw, request, **kw):
00063             self.raw = raw
00064             self.request = request
00065 
00066         def format(self, formatter):
00067             _ = self.request.getText
00068             from MoinMoin.parser.text import Parser as TextParser
00069             self.request.write(formatter.sysmsg(1) +
00070                                formatter.rawHTML(_('Rendering of reStructured text is not possible, please install Docutils.')) +
00071                                formatter.sysmsg(0))
00072             TextParser(self.raw, self.request).format(formatter)
00073 
00074     # Create a pseudo docutils environment
00075     docutils = html4css1 = dummyUrllib2()
00076     html4css1.HTMLTranslator = html4css1.Writer = object
00077 
00078 def safe_import(name, globals = None, locals = None, fromlist = None):
00079     mod = __builtin__.__import__(name, globals, locals, fromlist)
00080     if mod:
00081         mod.open = dummyOpen
00082         mod.urllib2 = dummyUrllib2
00083     return mod
00084 
00085 # Go through and change all docutils modules to use a dummyOpen and dummyUrllib2
00086 # module. Also make sure that any docutils imported modules also get the dummy
00087 # implementations.
00088 for i in sys.modules.keys():
00089     if i.startswith('docutils') and sys.modules[i]:
00090         sys.modules[i].open = dummyOpen
00091         sys.modules[i].urllib2 = dummyUrllib2
00092         sys.modules[i].__import__ = safe_import
00093 
00094 # --- End of dummy-code --------------------------------------------------------
00095 
00096 def html_escape_unicode(node):
00097     # Find Python function that does this for me. string.encode('ascii',
00098     # 'xmlcharrefreplace') only 2.3 and above.
00099     for i in node:
00100         if ord(i) > 127:
00101             node = node.replace(i, '&#%d;' % (ord(i)))
00102     return node
00103 
00104 class MoinWriter(html4css1.Writer):
00105 
00106     config_section = 'MoinMoin writer'
00107     config_section_dependencies = ('writers', )
00108 
00109     #"""Final translated form of `document`."""
00110     output = None
00111 
00112     def wiki_resolver(self, node):
00113         """
00114             Normally an unknown reference would be an error in an reST document.
00115             However, this is how new documents are created in the wiki. This
00116             passes on unknown references to eventually be handled by
00117             MoinMoin.
00118         """
00119         if hasattr(node, 'indirect_reference_name'):
00120             node['refuri'] = node.indirect_reference_name
00121         elif (len(node['ids']) != 0):
00122             # If the node has an id then it's probably an internal link. Let
00123             # docutils generate an error.
00124             return False
00125         elif node.hasattr('name'):
00126             node['refuri'] = node['name']
00127         else:
00128             node['refuri'] = node['refname']
00129         del node['refname']
00130         node.resolved = 1
00131         self.nodes.append(node)
00132         return True
00133 
00134     wiki_resolver.priority = 1
00135 
00136     def __init__(self, formatter, request):
00137         html4css1.Writer.__init__(self)
00138         self.formatter = formatter
00139         self.request = request
00140         # Add our wiki unknown_reference_resolver to our list of functions to
00141         # run when a target isn't found
00142         self.unknown_reference_resolvers = [self.wiki_resolver]
00143         # We create a new parser to process MoinMoin wiki style links in the
00144         # reST.
00145         self.wikiparser = WikiParser('', self.request)
00146         self.wikiparser.formatter = self.formatter
00147         self.wikiparser.hilite_re = None
00148         self.nodes = []
00149         # Make sure it's a supported docutils version.
00150         required_version = (0, 3, 10)
00151         current_version = tuple([int(i) for i in (docutils.__version__.split('.') + ['0', '0'])[:3]])
00152         if current_version < required_version:
00153             err = 'ERROR: The installed docutils version is %s;' % ('.'.join([str(i) for i in current_version]))
00154             err += ' version %s or later is required.' % ('.'.join([str(i) for i in required_version]))
00155             raise RuntimeError, err
00156 
00157     def translate(self):
00158         visitor = MoinTranslator(self.document,
00159                                  self.formatter,
00160                                  self.request,
00161                                  self.wikiparser,
00162                                  self)
00163         self.document.walkabout(visitor)
00164         self.visitor = visitor
00165         # Docutils 0.5 and later require the writer to have the visitor
00166         # attributes.
00167         if (hasattr(html4css1.Writer, 'visitor_attributes')):
00168             for attr in html4css1.Writer.visitor_attributes:
00169                 setattr(self, attr, getattr(visitor, attr))
00170         self.output = html_escape_unicode(visitor.astext())
00171 
00172 # mark quickhelp as translatable
00173 _ = lambda x: x
00174 
00175 class Parser:
00176     caching = 1
00177     Dependencies = Dependencies # copy dependencies from module-scope
00178     quickhelp = _("""\
00179 {{{
00180 Emphasis: *italic* **bold** ``monospace``
00181 
00182 Headings: Heading 1  Heading 2  Heading 3
00183           =========  ---------  ~~~~~~~~~
00184 
00185 Horizontal rule: ----
00186 
00187 Links: TrailingUnderscore_ `multi word with backticks`_ external_
00188 
00189 .. _external: http://external-site.example.org/foo/
00190 
00191 Lists: * bullets; 1., a. numbered items.
00192 }}}
00193 (!) For more help, see the
00194 [[http://docutils.sourceforge.net/docs/user/rst/quickref.html|reStructuredText Quick Reference]].
00195 """)
00196 
00197     def __init__(self, raw, request, **kw):
00198         self.raw = raw
00199         self.request = request
00200         self.form = request.form
00201 
00202     def format(self, formatter):
00203         # Create our simple parser
00204         parser = MoinDirectives(self.request)
00205 
00206         parts = publish_parts(
00207             source=self.raw,
00208             writer=MoinWriter(formatter, self.request),
00209             settings_overrides={
00210                 'halt_level': 5,
00211                 'traceback': True,
00212                 'file_insertion_enabled': 0,
00213                 'raw_enabled': 0,
00214                 'stylesheet_path': '',
00215                 'template': '',
00216             }
00217         )
00218 
00219         html = []
00220         if parts['title']:
00221             # Document title.
00222             html.append(formatter.rawHTML('<h1>%s</h1>' % parts['title']))
00223         # If there is only one subtitle it is propagated by Docutils
00224         # to a document subtitle and is held in parts['subtitle'].
00225         # However, if there is more than one subtitle then this is
00226         # empty and fragment contains all of the subtitles.
00227         if parts['subtitle']:
00228             html.append(formatter.rawHTML('<h2>%s</h2>' % parts['subtitle']))
00229         if parts['docinfo']:
00230             html.append(parts['docinfo'])
00231         html.append(parts['fragment'])
00232         self.request.write(html_escape_unicode('\n'.join(html)))
00233 
00234 class RawHTMLList(list):
00235     """
00236         RawHTMLList catches all html appended to internal HTMLTranslator lists.
00237         It passes the HTML through the MoinMoin rawHTML formatter to strip
00238         markup when necessary. This is to support other formatting outputs
00239         (such as ?action=show&mimetype=text/plain).
00240     """
00241 
00242     def __init__(self, formatter):
00243         self.formatter = formatter
00244 
00245     def append(self, text):
00246         f = sys._getframe()
00247         if f.f_back.f_code.co_filename.endswith('html4css1.py'):
00248             if isinstance(text, (str, unicode)):
00249                 text = self.formatter.rawHTML(text)
00250         list.append(self, text)
00251 
00252 class MoinTranslator(html4css1.HTMLTranslator):
00253 
00254     def __init__(self, document, formatter, request, parser, writer):
00255         html4css1.HTMLTranslator.__init__(self, document)
00256         self.formatter = formatter
00257         self.request = request
00258         # Using our own writer when needed. Save the old one to restore
00259         # after the page has been processed by the html4css1 parser.
00260         self.original_write, self.request.write = self.request.write, self.capture_wiki_formatting
00261         self.wikiparser = parser
00262         self.wikiparser.request = request
00263         # MoinMoin likes to start the initial headers at level 3 and the title
00264         # gets level 2, so to comply with their styles, we do here also.
00265         # TODO: Could this be fixed by passing this value in settings_overrides?
00266         self.initial_header_level = 3
00267         # Temporary place for wiki returned markup. This will be filled when
00268         # replacing the default writer with the capture_wiki_formatting
00269         # function (see visit_image for an example).
00270         self.wiki_text = ''
00271         self.setup_wiki_handlers()
00272         self.setup_admonitions_handlers()
00273 
00274         # Make all internal lists RawHTMLLists, see RawHTMLList class
00275         # comment for more information.
00276         for i in self.__dict__:
00277             if isinstance(getattr(self, i), list):
00278                 setattr(self, i, RawHTMLList(formatter))
00279 
00280     def depart_docinfo(self, node):
00281         """
00282             depart_docinfo assigns a new list to self.body, we need to re-make that
00283             into a RawHTMLList.
00284         """
00285         html4css1.HTMLTranslator.depart_docinfo(self, node)
00286         self.body = RawHTMLList(self.formatter)
00287 
00288     def capture_wiki_formatting(self, text):
00289         """
00290             Captures MoinMoin generated markup to the instance variable
00291             wiki_text.
00292         """
00293         # For some reason getting empty strings here which of course overwrites
00294         # what we really want (this is called multiple times per MoinMoin
00295         # format call, which I don't understand).
00296         self.wiki_text += text
00297 
00298     def process_wiki_text(self, text):
00299         """
00300             This sequence is repeated numerous times, so its captured as a
00301             single call here. Its important that wiki_text is blanked before we
00302             make the format call. format will call request.write which we've
00303             hooked to capture_wiki_formatting. If wiki_text is not blanked
00304             before a call to request.write we will get the old markup as well as
00305             the newly generated markup.
00306 
00307             TODO: Could implement this as a list so that it acts as a stack. I
00308             don't like having to remember to blank wiki_text.
00309         """
00310         self.wiki_text = ''
00311         self.wikiparser.raw = text
00312         self.wikiparser.format(self.formatter)
00313 
00314     def add_wiki_markup(self):
00315         """
00316             Place holder in case this becomes more elaborate someday. For now it
00317             only appends the MoinMoin generated markup to the html body and
00318             raises SkipNode.
00319         """
00320         self.body.append(self.wiki_text)
00321         self.wiki_text = ''
00322         raise docutils.nodes.SkipNode
00323 
00324     def astext(self):
00325         self.request.write = self.original_write
00326         return html4css1.HTMLTranslator.astext(self)
00327 
00328     def fixup_wiki_formatting(self, text):
00329         replacement = {'\n': '', '> ': '>'}
00330         for src, dst in replacement.items():
00331             text = text.replace(src, dst)
00332         # Fixup extraneous markup
00333         # Removes any empty span tags
00334         text = re.sub(r'\s*<\s*span.*?>\s*<\s*/\s*span\s*>', '', text)
00335         # Removes the first paragraph tag
00336         text = re.sub(r'^\s*<\s*p[^>]*?>', '', text)
00337         # Removes the ending paragraph close tag and any remaining whitespace
00338         text = re.sub(r'<\s*/\s*p\s*>\s*$', '', text)
00339         return text
00340 
00341     def visit_reference(self, node):
00342         """
00343             Pass links to MoinMoin to get the correct wiki space url. Extract
00344             the url and pass it on to the html4css1 writer to handle. Inline
00345             images are also handled by visit_image. Not sure what the "drawing:"
00346             link scheme is used for, so for now it is handled here.
00347 
00348             Also included here is a hack to allow MoinMoin macros. This routine
00349             checks for a link which starts with "<<". This link is passed to the
00350             MoinMoin formatter and the resulting markup is inserted into the
00351             document in the place of the original link reference.
00352         """
00353         if 'refuri' in node.attributes:
00354             refuri = node['refuri']
00355             prefix = ''
00356             link = refuri
00357             if ':' in refuri:
00358                 prefix, link = refuri.lstrip().split(':', 1)
00359 
00360             # First see if MoinMoin should handle completely. Exits through add_wiki_markup.
00361             if refuri.startswith('<<') and refuri.endswith('>>'): # moin macro
00362                 self.process_wiki_text(refuri)
00363                 self.wiki_text = self.fixup_wiki_formatting(self.wiki_text)
00364                 self.add_wiki_markup()
00365 
00366             if prefix == 'drawing':
00367                 self.process_wiki_text("[[%s]]" % refuri)
00368                 self.wiki_text = self.fixup_wiki_formatting(self.wiki_text)
00369                 self.add_wiki_markup()
00370 
00371             # From here down, all links are handled by docutils (except
00372             # missing attachments), just fixup node['refuri'].
00373             if prefix == 'attachment':
00374                 if not AttachFile.exists(self.request, self.request.page.page_name, link):
00375                     # Attachment doesn't exist, give to MoinMoin to insert upload text.
00376                     self.process_wiki_text("[[%s]]" % refuri)
00377                     self.wiki_text = self.fixup_wiki_formatting(self.wiki_text)
00378                     self.add_wiki_markup()
00379                 # Attachment exists, just get a link to it.
00380                 node['refuri'] = AttachFile.getAttachUrl(self.request.page.page_name, link, self.request)
00381                 if not [i for i in node.children if i.__class__ == docutils.nodes.image]:
00382                     node['classes'].append(prefix)
00383             elif prefix == 'wiki':
00384                 wiki_name, page_name = wikiutil.split_interwiki(link)
00385                 wikitag, wikiurl, wikitail, err = wikiutil.resolve_interwiki(self.request, wiki_name, page_name)
00386                 wikiurl = wikiutil.mapURL(self.request, wikiurl)
00387                 node['refuri'] = wikiutil.join_wiki(wikiurl, wikitail)
00388                 # Only add additional class information if the reference does
00389                 # not have a child image (don't want to add additional markup
00390                 # for images with targets).
00391                 if not [i for i in node.children if i.__class__ == docutils.nodes.image]:
00392                     node['classes'].append('interwiki')
00393             elif prefix != '':
00394                 # Some link scheme (http, file, https, mailto, etc.), add class
00395                 # information if the reference doesn't have a child image (don't
00396                 # want additional markup for images with targets).
00397                 # Don't touch the refuri.
00398                 if not [i for i in node.children if i.__class__ == docutils.nodes.image]:
00399                     node['classes'].append(prefix)
00400             else:
00401                 # Default case - make a link to a wiki page.
00402                 pagename, anchor = wikiutil.split_anchor(refuri)
00403                 page = Page(self.request, wikiutil.AbsPageName(self.formatter.page.page_name, pagename))
00404                 node['refuri'] = page.url(self.request, anchor=anchor)
00405                 if not page.exists():
00406                     node['classes'].append('nonexistent')
00407         html4css1.HTMLTranslator.visit_reference(self, node)
00408 
00409     def visit_image(self, node):
00410         """
00411             Need to intervene in the case of inline images. We need MoinMoin to
00412             give us the actual src line to the image and then we can feed this
00413             to the default html4css1 writer. NOTE: Since the writer can't "open"
00414             this image the scale attribute doesn't work without directly
00415             specifying the height or width (or both).
00416 
00417             TODO: Need to handle figures similarly.
00418         """
00419         uri = node['uri'].lstrip()
00420         prefix = ''          # assume no prefix
00421         attach_name = uri
00422         if ':' in uri:
00423             prefix = uri.split(':', 1)[0]
00424             attach_name = uri.split(':', 1)[1]
00425         # if prefix isn't URL, try to display in page
00426         if not prefix.lower() in ('file', 'http', 'https', 'ftp'):
00427             if not AttachFile.exists(self.request, self.request.page.page_name, attach_name):
00428                 # Attachment doesn't exist, MoinMoin should process it
00429                 if prefix == '':
00430                     prefix = 'attachment:'
00431                 self.process_wiki_text("{{%s%s}}" % (prefix, attach_name))
00432                 self.wiki_text = self.fixup_wiki_formatting(self.wiki_text)
00433                 self.add_wiki_markup()
00434             # Attachment exists, get a link to it.
00435             # create the url
00436             node['uri'] = AttachFile.getAttachUrl(self.request.page.page_name, attach_name, self.request, addts=1)
00437             if not node.hasattr('alt'):
00438                 node['alt'] = node.get('name', uri)
00439         html4css1.HTMLTranslator.visit_image(self, node)
00440 
00441     def create_wiki_functor(self, moin_func):
00442         moin_callable = getattr(self.formatter, moin_func)
00443         def visit_func(self, node):
00444             self.wiki_text = ''
00445             self.request.write(moin_callable(1))
00446             self.body.append(self.wiki_text)
00447         def depart_func(self, node):
00448             self.wiki_text = ''
00449             self.request.write(moin_callable(0))
00450             self.body.append(self.wiki_text)
00451         return visit_func, depart_func
00452 
00453     def setup_wiki_handlers(self):
00454         """
00455             Have the MoinMoin formatter handle markup when it makes sense. These
00456             are portions of the document that do not contain reST specific
00457             markup. This allows these portions of the document to look
00458             consistent with other wiki pages.
00459 
00460             Setup dispatch routines to handle basic document markup. The
00461             hanlders dict is the html4css1 handler name followed by the wiki
00462             handler name.
00463         """
00464         handlers = {
00465             # Text Markup
00466             'emphasis': 'emphasis',
00467             'strong': 'strong',
00468             'literal': 'code',
00469             # Blocks
00470             'literal_block': 'preformatted',
00471             # Simple Lists
00472             # bullet-lists are handled completely by docutils because it uses
00473             # the node context to decide when to make a compact list
00474             # (no <p> tags).
00475             'list_item': 'listitem',
00476             # Definition List
00477             'definition_list': 'definition_list',
00478         }
00479         for rest_func, moin_func in handlers.items():
00480             visit_func, depart_func = self.create_wiki_functor(moin_func)
00481             visit_func = new.instancemethod(visit_func, self, MoinTranslator)
00482             depart_func = new.instancemethod(depart_func, self, MoinTranslator)
00483             setattr(self, 'visit_%s' % (rest_func), visit_func)
00484             setattr(self, 'depart_%s' % (rest_func), depart_func)
00485 
00486     # Enumerated list takes an extra paramter so we handle this differently
00487     def visit_enumerated_list(self, node):
00488         self.wiki_text = ''
00489         self.request.write(self.formatter.number_list(1, start=node.get('start', None)))
00490         self.body.append(self.wiki_text)
00491 
00492     def depart_enumerated_list(self, node):
00493         self.wiki_text = ''
00494         self.request.write(self.formatter.number_list(0))
00495         self.body.append(self.wiki_text)
00496 
00497     # Admonitions are handled here -=- tmacam
00498     def create_admonition_functor(self, admotion_class):
00499         def visit_func(self, node):
00500             self.wiki_text = ''
00501             self.request.write(self.formatter.div(1,
00502                                                   attr={'class': admotion_class},
00503                                                   allowed_attrs=[]))
00504             self.body.append(self.wiki_text)
00505         def depart_func(self, node):
00506             self.wiki_text = ''
00507             self.request.write(self.formatter.div(0))
00508             self.body.append(self.wiki_text)
00509 
00510         return visit_func, depart_func
00511 
00512     def setup_admonitions_handlers(self):
00513         """
00514             Admonitions are handled here... We basically surround admonitions
00515             in a div with class admonition_{name of the admonition}.
00516         """
00517         handled_admonitions = [
00518             'attention',
00519             'caution',
00520             'danger',
00521             'error',
00522             'hint',
00523             'important',
00524             'note',
00525             'tip',
00526             'warning',
00527         ]
00528         for adm in handled_admonitions:
00529             visit_func, depart_func = self.create_admonition_functor(adm)
00530             visit_func = new.instancemethod(visit_func, self, MoinTranslator)
00531             depart_func = new.instancemethod(depart_func, self, MoinTranslator)
00532             setattr(self, 'visit_%s' % (adm), visit_func)
00533             setattr(self, 'depart_%s' % (adm), depart_func)
00534 
00535 
00536 class MoinDirectives:
00537     """
00538         Class to handle all custom directive handling. This code is called as
00539         part of the parsing stage.
00540     """
00541 
00542     def __init__(self, request):
00543         self.request = request
00544 
00545         # include MoinMoin pages
00546         directives.register_directive('include', self.include)
00547 
00548         # used for MoinMoin macros
00549         directives.register_directive('macro', self.macro)
00550 
00551         # disallow a few directives in order to prevent XSS
00552         # for directive in ('meta', 'include', 'raw'):
00553         for directive in ('meta', 'raw'):
00554             directives.register_directive(directive, None)
00555 
00556         # disable the raw role
00557         roles._roles['raw'] = None
00558 
00559         # As a quick fix for infinite includes we only allow a fixed number of
00560         # includes per page
00561         self.num_includes = 0
00562         self.max_includes = 10
00563 
00564     # Handle the include directive rather than letting the default docutils
00565     # parser handle it. This allows the inclusion of MoinMoin pages instead of
00566     # something from the filesystem.
00567     def include(self, name, arguments, options, content, lineno,
00568                 content_offset, block_text, state, state_machine):
00569         # content contains the included file name
00570 
00571         _ = self.request.getText
00572 
00573         # Limit the number of documents that can be included
00574         if self.num_includes < self.max_includes:
00575             self.num_includes += 1
00576         else:
00577             lines = [_("**Maximum number of allowed includes exceeded**")]
00578             state_machine.insert_input(lines, 'MoinDirectives')
00579             return
00580 
00581         if len(content):
00582             pagename = content[0]
00583             page = Page(page_name=pagename, request=self.request)
00584             if not self.request.user.may.read(pagename):
00585                 lines = [_("**You are not allowed to read the page: %s**") % (pagename, )]
00586             else:
00587                 if page.exists():
00588                     text = page.get_raw_body()
00589                     lines = text.split('\n')
00590                     # Remove the "#format rst" line
00591                     if lines[0].startswith("#format"):
00592                         del lines[0]
00593                 else:
00594                     lines = [_("**Could not find the referenced page: %s**") % (pagename, )]
00595             # Insert the text from the included document and then continue parsing
00596             state_machine.insert_input(lines, 'MoinDirectives')
00597         return
00598 
00599     include.has_content = include.content = True
00600     include.option_spec = {}
00601     include.required_arguments = 1
00602     include.optional_arguments = 0
00603 
00604     # Add additional macro directive.
00605     # This allows MoinMoin macros to be used either by using the directive
00606     # directly or by using the substitution syntax. Much cleaner than using the
00607     # reference hack (`<<SomeMacro>>`_). This however simply adds a node to the
00608     # document tree which is a reference, but through a much better user
00609     # interface.
00610     def macro(self, name, arguments, options, content, lineno,
00611                 content_offset, block_text, state, state_machine):
00612         # content contains macro to be called
00613         if len(content):
00614             # Allow either with or without brackets
00615             if content[0].startswith('<<'):
00616                 macro = content[0]
00617             else:
00618                 macro = '<<%s>>' % content[0]
00619             ref = reference(macro, refuri=macro)
00620             ref['name'] = macro
00621             return [ref]
00622         return
00623 
00624     macro.has_content = macro.content = True
00625     macro.option_spec = {}
00626     macro.required_arguments = 1
00627     macro.optional_arguments = 0
00628 
00629 if ErrorParser: # fixup in case of missing docutils
00630     Parser = ErrorParser
00631 
00632 del _