[Epydoc-commits] SF.net SVN: epydoc: [1231] trunk/epydoc/src/epydoc/markup
Brought to you by:
edloper
From: <ed...@us...> - 2006-04-19 00:30:46
|
Revision: 1231 Author: edloper Date: 2006-04-18 17:30:37 -0700 (Tue, 18 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1231&view=rev Log Message: ----------- - Doctest colorization moved to epydoc.markup.doctest - Do doctest colorization for rst - Fixed the overridden starttag method for rendering rst as html Modified Paths: -------------- trunk/epydoc/src/epydoc/markup/epytext.py trunk/epydoc/src/epydoc/markup/restructuredtext.py Added Paths: ----------- trunk/epydoc/src/epydoc/markup/doctest.py Added: trunk/epydoc/src/epydoc/markup/doctest.py =================================================================== --- trunk/epydoc/src/epydoc/markup/doctest.py (rev 0) +++ trunk/epydoc/src/epydoc/markup/doctest.py 2006-04-19 00:30:37 UTC (rev 1231) @@ -0,0 +1,151 @@ +# +# doctest.py: Syntax Highlighting for doctest blocks +# Edward Loper +# +# Created [06/28/03 02:52 AM] +# $Id: restructuredtext.py 1210 2006-04-10 13:25:50Z edloper $ +# + +""" +Syntax highlighting for doctest blocks. This module defines two +functions, L{doctest_to_html()} and L{doctest_to_latex()}, which can +be used to perform syntax highlighting on doctest blocks. It also +defines the more general L{colorize_doctest()}, which could be used to +do syntac highlighting on doctest blocks with other output formats. +(Both C{doctest_to_html()} and C{doctest_to_latex()} are defined using +C{colorize_doctest()}.) +""" + +import re +from epydoc.util import plaintext_to_html, plaintext_to_latex + +def doctest_to_html(s): + """ + Perform syntax highlighting on the given doctest string, and + return the resulting HTML code. This code consists of a C{<pre>} + block with class=py-doctest. Syntax highlighting is performed + using the following css classes: 'py-prompt', 'py-keyword', + 'py-string', 'py-comment', and 'py-output'. + """ + return ('<pre class="py-doctest">\n%s\n</pre>\n' % + colorize_doctest(s, _tag_span_html).strip()) + +def doctest_to_latex(s): + """ + Perform syntax highlighting on the given doctest string, and + return the resulting LaTeX code. This code consists of an + C{alltt} environment. Syntax highlighting is performed using five + new latex commands, which must be defined externally: + '\pysrcprompt', '\pysrckeyword', '\pysrcstring', '\pysrccomment', + and '\pysrcoutput'. + """ + return ('\\begin{alltt}\n%s\n\\end{alltt}\n' % + colorize_doctest(s, _tag_span_latex).strip()) + +def _tag_span_html(s, tag): + return '<span class="py-%s">%s</span>' % (tag, plaintext_to_html(s)) + +def _tag_span_latex(s, tag): + return '\\pysrc%s{%s}' % (tag, plaintext_to_latex(s)) + +# Regular expressions for colorize_doctestblock +_KEYWORDS = ["del", "from", "lambda", "return", "and", "or", "is", + "global", "not", "try", "break", "else", "if", "elif", + "while", "class", "except", "import", "pass", "raise", + "continue", "finally", "in", "print", "def", "for"] +_KEYWORD = '|'.join([r'(\b%s\b)' % _KW for _KW in _KEYWORDS]) +_STRING = (r""" +[uU]?[rR]? + (?: # Single-quote (') strings + '''(?: # Tripple-quoted can contain... + [^'] | # a non-quote + \\' | # a backslashed quote + '{1,2}(?!') # one or two quotes + )*''' | + '(?: # Non-tripple quoted can contain... + [^'] | # a non-quote + \\' # a backslashded quote + )*'(?!') | """+ +r''' # Double-quote (") strings + """(?: # Tripple-quoted can contain... + [^"] | # a non-quote + \\" | # a backslashed single + "{1,2}(?!") # one or two quotes + )*""" | + "(?: # Non-tripple quoted can contain... + [^"] | # a non-quote + \\" # a backslashded quote + )*"(?!") +)''') +_COMMENT = '(#.*?$)' +_PROMPT = r'^\s*(?:>>>|\.\.\.)(?:\s|$)' + +PROMPT_RE = re.compile('(%s)' % _PROMPT, re.MULTILINE | re.DOTALL) +'''The regular expression used to find Python prompts (">>>" and +"...") in doctest blocks.''' + +DOCTEST_RE = re.compile( + '(?P<STRING>%s)|(?P<COMMENT>%s)|(?P<KEYWORD>%s)|(?P<PROMPT>%s)|.+?' % + (_STRING, _COMMENT, _KEYWORD, _PROMPT), re.MULTILINE | re.DOTALL) +'''The regular expression used by L{_doctest_sub} to colorize doctest +blocks.''' + +def colorize_doctest(s, markup_func): + """ + Colorize the given doctest string C{s} using C{markup_func()}. + C{markup_func()} should be a function that takes a substring and a + tag, and returns a colorized version of the substring. E.g.: + + >>> def html_markup_func(s, tag): + ... return '<span class="%s">%s</span>' % (tag, s) + + The tags that will be passed to the markup function are: + - C{prompt} -- a Python prompt (>>> or ...) + - C{keyword} -- a Python keyword (for, if, etc.) + - C{string} -- a string literal + - C{comment} -- a comment + - C{output} -- the output from a doctest block. + - C{other} -- anything else (does *not* include output.) + """ + pysrc = [] # the source code part of a docstest block (lines) + pyout = [] # the output part of a doctest block (lines) + result = [] + out = result.append + + def subfunc(match): + if match.group('PROMPT'): + return markup_func(match.group(), 'prompt') + if match.group('KEYWORD'): + return markup_func(match.group(), 'keyword') + if match.group('COMMENT'): + return markup_func(match.group(), 'comment') + if match.group('STRING') and '\n' not in match.group(): + return markup_func(match.group(), 'string') + elif match.group('STRING'): + # It's a multiline string; colorize the string & prompt + # portion of each line. + pieces = [markup_func(s, ['string','prompt'][i%2]) + for i, s in enumerate(PROMPT_RE.split(match.group()))] + return ''.join(pieces) + else: + return markup_func(match.group(), 'other') + + for line in s.split('\n')+['\n']: + if PROMPT_RE.match(line): + pysrc.append(line) + if pyout: + result.append(markup_func('\n'.join(pyout).strip(), 'output')) + pyout = [] + else: + pyout.append(line) + if pysrc: + pysrc = DOCTEST_RE.sub(subfunc, '\n'.join(pysrc)) + result.append(pysrc.strip()) + #result.append(markup_func(pysrc.strip(), 'python')) + pysrc = [] + + remainder = '\n'.join(pyout).strip() + if remainder: + result.append(markup_func(remainder, 'output')) + + return '\n'.join(result) Modified: trunk/epydoc/src/epydoc/markup/epytext.py =================================================================== --- trunk/epydoc/src/epydoc/markup/epytext.py 2006-04-13 07:24:22 UTC (rev 1230) +++ trunk/epydoc/src/epydoc/markup/epytext.py 2006-04-19 00:30:37 UTC (rev 1231) @@ -109,7 +109,7 @@ import xml.dom.minidom from epydoc.markup import * from epydoc.util import wordwrap, plaintext_to_html, plaintext_to_latex -from epydoc.docwriter.html_colorize import colorize_doctestblock +from epydoc.markup.doctest import doctest_to_html, doctest_to_latex ################################################## ## Constants @@ -1817,8 +1817,7 @@ elif tree.tagName == 'literalblock': return '<pre class="literalblock">\n%s\n</pre>\n' % childstr elif tree.tagName == 'doctestblock': - dtb = colorize_doctestblock(childstr.strip()) - return '<pre class="doctestblock">\n%s</pre>\n' % dtb + return doctest_to_html(tree.childNodes[0].data.strip()) elif tree.tagName == 'fieldlist': raise AssertionError("There should not be any field lists left") elif tree.tagName in ('epytext', 'section', 'tag', 'arg', @@ -1935,7 +1934,7 @@ elif tree.tagName == 'heading': return ' '*(indent-2) + '(section) %s\n\n' % childstr elif tree.tagName == 'doctestblock': - return '\\begin{alltt}\n%s\\end{alltt}\n\n' % childstr + return doctest_to_latex(tree.childNodes[0].data.strip()) elif tree.tagName == 'literalblock': return '\\begin{alltt}\n%s\\end{alltt}\n\n' % childstr elif tree.tagName == 'fieldlist': Modified: trunk/epydoc/src/epydoc/markup/restructuredtext.py =================================================================== --- trunk/epydoc/src/epydoc/markup/restructuredtext.py 2006-04-13 07:24:22 UTC (rev 1230) +++ trunk/epydoc/src/epydoc/markup/restructuredtext.py 2006-04-19 00:30:37 UTC (rev 1231) @@ -84,6 +84,7 @@ from epydoc.markup import * from epydoc.apidoc import ModuleDoc, ClassDoc from epydoc.docwriter.dotgraph import * +from epydoc.markup.doctest import doctest_to_html, doctest_to_latex #: A dictionary whose keys are the "consolidated fields" that are #: recognized by epydoc; and whose values are the corresponding epydoc @@ -455,15 +456,21 @@ target = self.encode(node.astext()) xref = self._linker.translate_identifier_xref(target, target) self.body.append(xref) - raise SkipNode + raise SkipNode() def visit_document(self, node): pass def depart_document(self, node): pass # For now, just ignore dotgraphs. [XXX] - def visit_dotgraph(self, node): pass - def depart_dotgraph(self, node): pass + def visit_dotgraph(self, node): + log.warning("Ignoring dotgraph in latex output (dotgraph " + "rendering for latex not implemented yet).") + raise SkipNode() + def visit_doctest_block(self, node): + self.body.append(doctest_to_latex(str(node[0]))) + raise SkipNode() + class _EpydocHTMLTranslator(HTMLTranslator): def __init__(self, document, docstring_linker, directory, docindex, context): @@ -484,7 +491,7 @@ target = self.encode(node.astext()) xref = self._linker.translate_identifier_xref(target, target) self.body.append(xref) - raise SkipNode + raise SkipNode() def should_be_compact_paragraph(self, node): if self.document.children == [node]: @@ -505,24 +512,35 @@ - hrefs not starting with C{'#'} are given target='_top' - all headings (C{<hM{n}>}) are given the css class C{'heading'} """ - # Prefix all CSS classes with "rst-" - if attributes.has_key('class'): - attributes['class'] = 'rst-%s' % attributes['class'] + # Get the list of all attribute dictionaries we need to munge. + attr_dicts = [attributes] + if isinstance(node, docutils.nodes.Node): + attr_dicts.append(node.attributes) + if isinstance(node, dict): + attr_dicts.append(node) + # Munge each attribute dictionary. Unfortunately, we need to + # iterate through attributes one at a time because some + # versions of docutils don't case-normalize attributes. + for attr_dict in attr_dicts: + for (key, val) in attr_dict.items(): + # Prefix all CSS classes with "rst-"; and prefix all + # names with "rst-" to avoid conflicts. + if key.lower() in ('class', 'id', 'name'): + attr_dict[key] = 'rst-%s' % val + elif key.lower() in ('classes', 'ids', 'names'): + attr_dict[key] = ['rst-%s' % cls for cls in val] + elif key.lower() == 'href': + if attr_dict[key][:1]=='#': + attr_dict[key] = '#rst-%s' % attr_dict[key][1:] + else: + # If it's an external link, open it in a new + # page. + attr_dict['target'] = '_top' - # Prefix all names with "rst-", to avoid conflicts - if attributes.has_key('id'): - attributes['id'] = 'rst-%s' % attributes['id'] - if attributes.has_key('name'): - attributes['name'] = 'rst-%s' % attributes['name'] - if attributes.has_key('href'): - if attributes['href'][:1]=='#': - attributes['href'] = '#rst-%s' % attributes['href'][1:] - else: - attributes['target'] = '_top' - # For headings, use class="heading" if re.match(r'^h\d+$', tagname): - attributes['class'] = 'heading' + attributes['class'] = ' '.join([attributes.get('class',''), + 'heading']).strip() return HTMLTranslator.starttag(self, node, tagname, suffix, **attributes) @@ -538,9 +556,11 @@ image_url = '%s.gif' % graph.uid image_file = os.path.join(self._directory, image_url) self.body.append(graph.to_html(image_file, image_url)) + raise SkipNode() - def depart_dotgraph(self, node): - pass # Nothing to do. + def visit_doctest_block(self, node): + self.body.append(doctest_to_html(str(node[0]))) + raise SkipNode() ###################################################################### #{ Graph Generation Directives This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |