blob: e3575f8d147ffb7e8b8ffd5e6aa00cb7151fcb47 [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001import sys
2from docutils.core import publish_string
3from docutils import nodes
4from docutils.nodes import Text
5from docutils.writers.html4css1 import HTMLTranslator
6from epydoc.markup import DocstringLinker
7from epydoc.markup.restructuredtext import ParsedRstDocstring, _EpydocHTMLTranslator, _DocumentPseudoWriter, _EpydocReader
8
9
10class 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('&nbsp;' * (len(token) - 1) + ' ')
117 self.body.append('</tt>')
118 raise nodes.SkipNode
119
120
121class 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
133def 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
143try:
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()
166except:
167 exc_type, exc_value, exc_traceback = sys.exc_info()
168 sys.stderr.write("Error calculating docstring: " + str(exc_value))
169 sys.exit(1)