| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame^] | 1 | import sys |
| 2 | from docutils.core import publish_string |
| 3 | from docutils import nodes |
| 4 | from docutils.nodes import Text |
| 5 | from docutils.writers.html4css1 import HTMLTranslator |
| 6 | from epydoc.markup import DocstringLinker |
| 7 | from epydoc.markup.restructuredtext import ParsedRstDocstring, _EpydocHTMLTranslator, _DocumentPseudoWriter, _EpydocReader |
| 8 | |
| 9 | |
| 10 | class RestHTMLTranslator(_EpydocHTMLTranslator): |
| 11 | def visit_field_name(self, node): |
| 12 | atts = {} |
| 13 | if self.in_docinfo: |
| 14 | atts['class'] = 'docinfo-name' |
| 15 | else: |
| 16 | atts['class'] = 'field-name' |
| 17 | |
| 18 | self.context.append('') |
| 19 | atts['align'] = "right" |
| 20 | self.body.append(self.starttag(node, 'th', '', **atts)) |
| 21 | |
| 22 | def visit_field_body(self, node): |
| 23 | self.body.append(self.starttag(node, 'td', '', CLASS='field-body')) |
| 24 | parent_text = node.parent[0][0].astext() |
| 25 | if hasattr(node.parent, "type"): |
| 26 | self.body.append("(") |
| 27 | self.body.append(self.starttag(node, 'a', '', |
| 28 | **{"href": 'psi_element://#typename#' + node.parent.type})) |
| 29 | self.body.append(node.parent.type) |
| 30 | self.body.append("</a>") |
| 31 | self.body.append(") ") |
| 32 | elif parent_text.startswith("type "): |
| 33 | index = parent_text.index("type ") |
| 34 | type_string = parent_text[index + 5] |
| 35 | self.body.append(self.starttag(node, 'a', '', |
| 36 | **{"href": 'psi_element://#typename#' + type_string})) |
| 37 | elif parent_text.startswith("rtype"): |
| 38 | type_string = node.children[0][0].astext() |
| 39 | self.body.append(self.starttag(node, 'a', '', |
| 40 | **{"href": 'psi_element://#typename#' + type_string})) |
| 41 | |
| 42 | self.set_class_on_child(node, 'first', 0) |
| 43 | field = node.parent |
| 44 | if (self.compact_field_list or |
| 45 | isinstance(field.parent, nodes.docinfo) or |
| 46 | field.parent.index(field) == len(field.parent) - 1): |
| 47 | # If we are in a compact list, the docinfo, or if this is |
| 48 | # the last field of the field list, do not add vertical |
| 49 | # space after last element. |
| 50 | self.set_class_on_child(node, 'last', -1) |
| 51 | |
| 52 | def depart_field_body(self, node): |
| 53 | if node.parent[0][0].astext().startswith("type "): |
| 54 | self.body.append("</a>") |
| 55 | HTMLTranslator.depart_field_body(self, node) |
| 56 | |
| 57 | |
| 58 | def visit_field_list(self, node): |
| 59 | fields = {} |
| 60 | for n in node.children: |
| 61 | if len(n.children) == 0: continue |
| 62 | child = n.children[0] |
| 63 | rawsource = child.rawsource |
| 64 | if rawsource.startswith("param "): |
| 65 | index = rawsource.index("param ") |
| 66 | if len(child.children) == 0: continue |
| 67 | child.children[0] = Text(rawsource[index + 6:]) |
| 68 | fields[rawsource[index + 6:]] = n |
| 69 | if rawsource == "return": |
| 70 | fields["return"] = n |
| 71 | |
| 72 | for n in node.children: |
| 73 | if len(n.children) == 0: continue |
| 74 | child = n.children[0] |
| 75 | rawsource = child.rawsource |
| 76 | if rawsource.startswith("type "): |
| 77 | index = rawsource.index("type ") |
| 78 | name = rawsource[index + 5:] |
| 79 | if fields.has_key(name): |
| 80 | fields[name].type = n.children[1][0][0] |
| 81 | node.children.remove(n) |
| 82 | if rawsource == "rtype": |
| 83 | if fields.has_key("return"): |
| 84 | fields["return"].type = n.children[1][0][0] |
| 85 | node.children.remove(n) |
| 86 | |
| 87 | HTMLTranslator.visit_field_list(self, node) |
| 88 | |
| 89 | |
| 90 | def unknown_visit(self, node): |
| 91 | """ Ignore unknown nodes """ |
| 92 | |
| 93 | def unknown_departure(self, node): |
| 94 | """ Ignore unknown nodes """ |
| 95 | |
| 96 | def visit_block_quote(self, node): |
| 97 | self.body.append(self.emptytag(node, "br")) |
| 98 | |
| 99 | def depart_block_quote(self, node): |
| 100 | pass |
| 101 | |
| 102 | def visit_literal(self, node): |
| 103 | """Process text to prevent tokens from wrapping.""" |
| 104 | self.body.append( |
| 105 | self.starttag(node, 'tt', '', CLASS='docutils literal')) |
| 106 | text = node.astext() |
| 107 | for token in self.words_and_spaces.findall(text): |
| 108 | if token.strip(): |
| 109 | self.body.append('<code>%s</code>' |
| 110 | % self.encode(token)) |
| 111 | elif token in ('\n', ' '): |
| 112 | # Allow breaks at whitespace: |
| 113 | self.body.append(token) |
| 114 | else: |
| 115 | # Protect runs of multiple spaces; the last space can wrap: |
| 116 | self.body.append(' ' * (len(token) - 1) + ' ') |
| 117 | self.body.append('</tt>') |
| 118 | raise nodes.SkipNode |
| 119 | |
| 120 | |
| 121 | class MyParsedRstDocstring(ParsedRstDocstring): |
| 122 | def __init__(self, document): |
| 123 | ParsedRstDocstring.__init__(self, document) |
| 124 | |
| 125 | def to_html(self, docstring_linker, directory=None, |
| 126 | docindex=None, context=None, **options): |
| 127 | visitor = RestHTMLTranslator(self._document, docstring_linker, |
| 128 | directory, docindex, context) |
| 129 | self._document.walkabout(visitor) |
| 130 | return ''.join(visitor.body) |
| 131 | |
| 132 | |
| 133 | def parse_docstring(docstring, errors, **options): |
| 134 | writer = _DocumentPseudoWriter() |
| 135 | reader = _EpydocReader(errors) # Outputs errors to the list. |
| 136 | publish_string(docstring, writer=writer, reader=reader, |
| 137 | settings_overrides={'report_level': 10000, |
| 138 | 'halt_level': 10000, |
| 139 | 'warning_stream': None}) |
| 140 | return MyParsedRstDocstring(writer.document) |
| 141 | |
| 142 | |
| 143 | try: |
| 144 | src = sys.stdin.read() |
| 145 | |
| 146 | errors = [] |
| 147 | |
| 148 | class EmptyLinker(DocstringLinker): |
| 149 | def translate_indexterm(self, indexterm): |
| 150 | return "" |
| 151 | |
| 152 | def translate_identifier_xref(self, identifier, label=None): |
| 153 | return identifier |
| 154 | |
| 155 | docstring = parse_docstring(src, errors) |
| 156 | html = docstring.to_html(EmptyLinker()) |
| 157 | |
| 158 | if errors and not html: |
| 159 | sys.stderr.write("Error parsing docstring:\n") |
| 160 | for error in errors: |
| 161 | sys.stderr.write(str(error) + "\n") |
| 162 | sys.exit(1) |
| 163 | |
| 164 | sys.stdout.write(html) |
| 165 | sys.stdout.flush() |
| 166 | except: |
| 167 | exc_type, exc_value, exc_traceback = sys.exc_info() |
| 168 | sys.stderr.write("Error calculating docstring: " + str(exc_value)) |
| 169 | sys.exit(1) |