util.py
8.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#
# epydoc -- Utility functions used by regression tests (*.doctest)
# Edward Loper
#
# Created [01/30/01 05:18 PM]
# $Id: html.py 1420 2007-01-28 14:19:30Z dvarrazzo $
#
"""
Utility functions used by the regression tests (C{*.doctest}).
"""
__docformat__ = 'epytext en'
import tempfile, re, os, os.path, textwrap, sys
from epydoc.docbuilder import build_doc, build_doc_index
from epydoc.docparser import parse_docs
from epydoc.docintrospecter import introspect_docs
from epydoc.apidoc import ClassDoc, RoutineDoc
from epydoc.markup import ParsedDocstring
from epydoc.docwriter.html import HTMLWriter
######################################################################
#{ Test Functions
######################################################################
def buildvaluedoc(s):
"""
This test function takes a string containing the contents of a
module. It writes the string contents to a file, imports the file
as a module, and uses build_doc to build documentation, and
returns it as a C{ValueDoc} object.
"""
tmp_dir = write_pystring_to_tmp_dir(s)
val_doc = build_doc(os.path.join(tmp_dir, 'epydoc_test.py'))
cleanup_tmp_dir(tmp_dir)
return val_doc
def runbuilder(s, attribs='', build=None, exclude=''):
"""
This test function takes a string containing the contents of a
module. It writes the string contents to a file, imports the file
as a module, and uses build_doc to build documentation, and pretty
prints the resulting ModuleDoc object. The C{attribs} argument
specifies which attributes of the C{APIDoc}s should be displayed.
The C{build} argument gives the name of a variable in the module
whose documentation should be built, instead of bilding docs for
the whole module.
"""
# Write it to a temp file.
tmp_dir = write_pystring_to_tmp_dir(s)
# Build it.
val_doc = build_doc(os.path.join(tmp_dir, 'epydoc_test.py'))
if build: val_doc = val_doc.variables[build].value
# Display it.
if isinstance(val_doc, ClassDoc):
for val in val_doc.variables.values():
if isinstance(val.value, RoutineDoc):
fun_to_plain(val.value)
s = val_doc.pp(include=attribs.split(),exclude=exclude.split())
s = re.sub(r"(filename = ).*", r"\1...", s)
s = re.sub(r"(<module 'epydoc_test' from ).*", r'\1...', s)
s = re.sub(r"(<function \w+ at )0x\w+>", r"\1...>", s)
s = re.sub(r"(<\w+ object at )0x\w+>", r"\1...>", s)
print s
# Clean up.
cleanup_tmp_dir(tmp_dir)
def runparser(s, attribs='', show=None, exclude=''):
"""
This test function takes a string containing the contents of a
module, and writes it to a file, uses `parse_docs` to parse it,
and pretty prints the resulting ModuleDoc object. The `attribs`
argument specifies which attributes of the `APIDoc`s should be
displayed. The `show` argument, if specifies, gives the name of
the object in the module that should be displayed (but the whole
module will always be inspected; this just selects what to
display).
"""
# Write it to a temp file.
tmp_dir = write_pystring_to_tmp_dir(s)
# Parse it.
val_doc = parse_docs(os.path.join(tmp_dir, 'epydoc_test.py'))
if show is not None:
for name in show.split('.'):
if isinstance(val_doc, ClassDoc):
val_doc = val_doc.local_variables[name].value
else:
val_doc = val_doc.variables[name].value
# Display it.
s = val_doc.pp(include=attribs.split(), exclude=exclude.split())
s = re.sub(r"filename = .*", "filename = ...", s)
print s
# Clean up.
cleanup_tmp_dir(tmp_dir)
def runintrospecter(s, attribs='', introspect=None, exclude=''):
"""
This test function takes a string containing the contents of a
module. It writes the string contents to a file, imports the file
as a module, and uses C{introspect_docs} to introspect it, and
pretty prints the resulting ModuleDoc object. The C{attribs}
argument specifies which attributes of the C{APIDoc}s should be
displayed. The C{introspect} argument gives the name of a variable
in the module whose value should be introspected, instead of
introspecting the whole module.
"""
# Write it to a temp file.
tmp_dir = write_pystring_to_tmp_dir(s)
# Import it.
sys.path.insert(0, tmp_dir)
if introspect is None:
import epydoc_test as val
else:
exec("from epydoc_test import %s as val" % introspect)
del sys.path[0]
# Introspect it.
val_doc = introspect_docs(val)
# Display it.
s = val_doc.pp(include=attribs.split(),exclude=exclude.split())
s = re.sub(r"(filename = ).*", r"\1...", s)
s = re.sub(r"(<module 'epydoc_test' from ).*", r'\1...', s)
s = re.sub(r"(<function \w+ at )0x\w+>", r"\1...>", s)
s = re.sub(r"(<\w+ object at )0x\w+>", r"\1...>", s)
print s
# Clean up.
cleanup_tmp_dir(tmp_dir)
def print_warnings():
"""
Register a logger that will print warnings & errors.
"""
from epydoc import log
del log._loggers[:]
log.register_logger(log.SimpleLogger(log.DOCSTRING_WARNING))
def testencoding(s, introspect=True, parse=True, debug=False):
"""
An end-to-end test for unicode encodings. This function takes a
given string, writes it to a python file, and processes that
file's documentation. It then generates HTML output from the
documentation, extracts all docstrings from the generated HTML
output, and displays them. (In order to extract & display all
docstrings, it monkey-patches the HMTLwriter.docstring_to_html()
method.)"""
# Monkey-patch docstring_to_html
original_docstring_to_html = HTMLWriter.docstring_to_html
HTMLWriter.docstring_to_html = print_docstring_as_html
# Write s to a temporary file.
tmp_dir = tempfile.mkdtemp()
path = os.path.join(tmp_dir, 'enc_test.py')
out = open(path, 'w')
out.write(textwrap.dedent(s))
out.close()
# Build docs for it
docindex = build_doc_index([path], introspect, parse)
if docindex is None: return
sys.modules.pop('enc_test', None)
# Write html output.
writer = HTMLWriter(docindex, mark_docstrings=True)
writer.write(tmp_dir)
for file in os.listdir(tmp_dir):
os.unlink(os.path.join(tmp_dir,file))
os.rmdir(tmp_dir)
# Restore the HTMLWriter class to its original state.
HTMLWriter.docstring_to_html = original_docstring_to_html
######################################################################
#{ Helper Functions
######################################################################
def write_pystring_to_tmp_dir(s):
tmp_dir = tempfile.mkdtemp()
out = open(os.path.join(tmp_dir, 'epydoc_test.py'), 'w')
out.write(textwrap.dedent(s))
out.close()
return tmp_dir
def cleanup_tmp_dir(tmp_dir):
os.unlink(os.path.join(tmp_dir, 'epydoc_test.py'))
try: os.unlink(os.path.join(tmp_dir, 'epydoc_test.pyc'))
except OSError: pass
os.rmdir(tmp_dir)
sys.modules.pop('epydoc_test', None)
def to_plain(docstring):
"""Conver a parsed docstring into plain text"""
if isinstance(docstring, ParsedDocstring):
docstring = docstring.to_plaintext(None)
return docstring.rstrip()
def fun_to_plain(val_doc):
"""Convert parsed docstrings in text from a RoutineDoc"""
for k, v in val_doc.arg_types.items():
val_doc.arg_types[k] = to_plain(v)
for i, (k, v) in enumerate(val_doc.arg_descrs):
val_doc.arg_descrs[i] = (k, to_plain(v))
def print_docstring_as_html(self, parsed_docstring, *varargs, **kwargs):
"""
Convert the given parsed_docstring to HTML and print it. Ignore
any other arguments. This function is used by L{testencoding} to
monkey-patch the HTMLWriter class's docstring_to_html() method.
"""
s = parsed_docstring.to_html(None).strip()
s = s.encode('ascii', 'xmlcharrefreplace')
s = remove_surrogates(s)
print s
return ''
def remove_surrogates(s):
"""
The following is a helper function, used to convert two-character
surrogate sequences into single characters. This is needed
because some systems create surrogates but others don't.
"""
pieces = re.split('(&#\d+;)', s)
for i in range(3, len(pieces)-1, 2):
if pieces[i-1] != '': continue
high,low = int(pieces[i-2][2:-1]), int(pieces[i][2:-1])
if 0xd800 <= high <= 0xdbff and 0xdc00 <= low <= 0xdfff:
pieces[i-2] = '&#%d;' % (((high&0x3ff)<<10) +
(low&0x3ff) + 0x10000)
pieces[i] = ''
return ''.join(pieces)