blob: f7aa487580cc52437d734ab29536fe8c1c0ec3de [file] [log] [blame]
Georg Brandlb533e262008-05-25 18:19:30 +00001import sys
2import os
Benjamin Petersond76c8da2009-06-28 17:35:48 +00003import os.path
Georg Brandlb533e262008-05-25 18:19:30 +00004import difflib
5import subprocess
6import re
7import pydoc
8import inspect
9import unittest
10import test.support
Brian Curtin0d8a1dd2010-03-31 03:22:46 +000011import xml.etree
Benjamin Petersond76c8da2009-06-28 17:35:48 +000012from contextlib import contextmanager
13from test.support import TESTFN, forget, rmtree, EnvironmentVarGuard
Georg Brandlb533e262008-05-25 18:19:30 +000014
15from test import pydoc_mod
16
17expected_text_pattern = \
18"""
19NAME
20 test.pydoc_mod - This is a test module for test_pydoc
Georg Brandlb533e262008-05-25 18:19:30 +000021%s
22CLASSES
23 builtins.object
24 A
25 B
26\x20\x20\x20\x20
27 class A(builtins.object)
28 | Hello and goodbye
29 |\x20\x20
30 | Methods defined here:
31 |\x20\x20
32 | __init__()
33 | Wow, I have no function!
34 |\x20\x20
35 | ----------------------------------------------------------------------
36 | Data descriptors defined here:
37 |\x20\x20
38 | __dict__
39 | dictionary for instance variables (if defined)
40 |\x20\x20
41 | __weakref__
42 | list of weak references to the object (if defined)
43\x20\x20\x20\x20
44 class B(builtins.object)
45 | Data descriptors defined here:
46 |\x20\x20
47 | __dict__
48 | dictionary for instance variables (if defined)
49 |\x20\x20
50 | __weakref__
51 | list of weak references to the object (if defined)
52 |\x20\x20
53 | ----------------------------------------------------------------------
54 | Data and other attributes defined here:
55 |\x20\x20
56 | NO_MEANING = 'eggs'
57
58FUNCTIONS
59 doc_func()
60 This function solves all of the world's problems:
61 hunger
62 lack of Python
63 war
64\x20\x20\x20\x20
65 nodoc_func()
66
67DATA
Alexander Belopolsky4979b9b2010-11-18 01:58:16 +000068 __xyz__ = 'X, Y and Z'
Georg Brandlb533e262008-05-25 18:19:30 +000069
70VERSION
71 1.2.3.4
72
73AUTHOR
74 Benjamin Peterson
75
76CREDITS
77 Nobody
Alexander Belopolsky4979b9b2010-11-18 01:58:16 +000078
79FILE
80 %s
Georg Brandlb533e262008-05-25 18:19:30 +000081""".strip()
82
83expected_html_pattern = \
84"""
85<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
86<tr bgcolor="#7799ee">
87<td valign=bottom>&nbsp;<br>
88<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong><a href="test.html"><font color="#ffffff">test</font></a>.pydoc_mod</strong></big></big> (version 1.2.3.4)</font></td
89><td align=right valign=bottom
90><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:%s">%s</a>%s</font></td></tr></table>
91 <p><tt>This&nbsp;is&nbsp;a&nbsp;test&nbsp;module&nbsp;for&nbsp;test_pydoc</tt></p>
92<p>
93<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
94<tr bgcolor="#ee77aa">
95<td colspan=3 valign=bottom>&nbsp;<br>
96<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
97\x20\x20\x20\x20
98<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
99<td width="100%%"><dl>
100<dt><font face="helvetica, arial"><a href="builtins.html#object">builtins.object</a>
101</font></dt><dd>
102<dl>
103<dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#A">A</a>
104</font></dt><dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#B">B</a>
105</font></dt></dl>
106</dd>
107</dl>
108 <p>
109<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
110<tr bgcolor="#ffc8d8">
111<td colspan=3 valign=bottom>&nbsp;<br>
112<font color="#000000" face="helvetica, arial"><a name="A">class <strong>A</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
113\x20\x20\x20\x20
114<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
115<td colspan=2><tt>Hello&nbsp;and&nbsp;goodbye<br>&nbsp;</tt></td></tr>
116<tr><td>&nbsp;</td>
117<td width="100%%">Methods defined here:<br>
118<dl><dt><a name="A-__init__"><strong>__init__</strong></a>()</dt><dd><tt>Wow,&nbsp;I&nbsp;have&nbsp;no&nbsp;function!</tt></dd></dl>
119
120<hr>
121Data descriptors defined here:<br>
122<dl><dt><strong>__dict__</strong></dt>
123<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
124</dl>
125<dl><dt><strong>__weakref__</strong></dt>
126<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
127</dl>
128</td></tr></table> <p>
129<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
130<tr bgcolor="#ffc8d8">
131<td colspan=3 valign=bottom>&nbsp;<br>
132<font color="#000000" face="helvetica, arial"><a name="B">class <strong>B</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
133\x20\x20\x20\x20
134<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
135<td width="100%%">Data descriptors defined here:<br>
136<dl><dt><strong>__dict__</strong></dt>
137<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
138</dl>
139<dl><dt><strong>__weakref__</strong></dt>
140<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
141</dl>
142<hr>
143Data and other attributes defined here:<br>
144<dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl>
145
146</td></tr></table></td></tr></table><p>
147<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
148<tr bgcolor="#eeaa77">
149<td colspan=3 valign=bottom>&nbsp;<br>
150<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
151\x20\x20\x20\x20
152<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
153<td width="100%%"><dl><dt><a name="-doc_func"><strong>doc_func</strong></a>()</dt><dd><tt>This&nbsp;function&nbsp;solves&nbsp;all&nbsp;of&nbsp;the&nbsp;world's&nbsp;problems:<br>
154hunger<br>
155lack&nbsp;of&nbsp;Python<br>
156war</tt></dd></dl>
157 <dl><dt><a name="-nodoc_func"><strong>nodoc_func</strong></a>()</dt></dl>
158</td></tr></table><p>
159<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
160<tr bgcolor="#55aa55">
161<td colspan=3 valign=bottom>&nbsp;<br>
162<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
163\x20\x20\x20\x20
164<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
Alexander Belopolsky4979b9b2010-11-18 01:58:16 +0000165<td width="100%%"><strong>__xyz__</strong> = 'X, Y and Z'</td></tr></table><p>
Georg Brandlb533e262008-05-25 18:19:30 +0000166<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
167<tr bgcolor="#7799ee">
168<td colspan=3 valign=bottom>&nbsp;<br>
169<font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr>
170\x20\x20\x20\x20
171<tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
172<td width="100%%">Benjamin&nbsp;Peterson</td></tr></table><p>
173<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
174<tr bgcolor="#7799ee">
175<td colspan=3 valign=bottom>&nbsp;<br>
176<font color="#ffffff" face="helvetica, arial"><big><strong>Credits</strong></big></font></td></tr>
177\x20\x20\x20\x20
178<tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
179<td width="100%%">Nobody</td></tr></table>
180""".strip()
181
182
183# output pattern for missing module
184missing_pattern = "no Python documentation found for '%s'"
185
Benjamin Petersond76c8da2009-06-28 17:35:48 +0000186# output pattern for module with bad imports
187badimport_pattern = "problem in %s - ImportError: No module named %s"
188
Georg Brandlb533e262008-05-25 18:19:30 +0000189def run_pydoc(module_name, *args):
190 """
191 Runs pydoc on the specified module. Returns the stripped
192 output of pydoc.
193 """
194 cmd = [sys.executable, pydoc.__file__, " ".join(args), module_name]
195 output = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout.read()
196 return output.strip()
197
198def get_pydoc_html(module):
199 "Returns pydoc generated output as html"
200 doc = pydoc.HTMLDoc()
201 output = doc.docmodule(module)
202 loc = doc.getdocloc(pydoc_mod) or ""
203 if loc:
204 loc = "<br><a href=\"" + loc + "\">Module Docs</a>"
205 return output.strip(), loc
206
207def get_pydoc_text(module):
208 "Returns pydoc generated output as text"
209 doc = pydoc.TextDoc()
210 loc = doc.getdocloc(pydoc_mod) or ""
211 if loc:
212 loc = "\nMODULE DOCS\n " + loc + "\n"
213
214 output = doc.docmodule(module)
215
216 # cleanup the extra text formatting that pydoc preforms
217 patt = re.compile('\b.')
218 output = patt.sub('', output)
219 return output.strip(), loc
220
221def print_diffs(text1, text2):
222 "Prints unified diffs for two texts"
223 lines1 = text1.splitlines(True)
224 lines2 = text2.splitlines(True)
225 diffs = difflib.unified_diff(lines1, lines2, n=0, fromfile='expected',
226 tofile='got')
227 print('\n' + ''.join(diffs))
228
229
230class PyDocDocTest(unittest.TestCase):
231
232 def test_html_doc(self):
233 result, doc_loc = get_pydoc_html(pydoc_mod)
234 mod_file = inspect.getabsfile(pydoc_mod)
Benjamin Petersonc5e94642008-06-14 23:04:46 +0000235 if sys.platform == 'win32':
236 import nturl2path
237 mod_url = nturl2path.pathname2url(mod_file)
238 else:
239 mod_url = mod_file
240 expected_html = expected_html_pattern % (mod_url, mod_file, doc_loc)
Georg Brandlb533e262008-05-25 18:19:30 +0000241 if result != expected_html:
242 print_diffs(expected_html, result)
243 self.fail("outputs are not equal, see diff above")
244
245 def test_text_doc(self):
246 result, doc_loc = get_pydoc_text(pydoc_mod)
247 expected_text = expected_text_pattern % \
Alexander Belopolsky4979b9b2010-11-18 01:58:16 +0000248 (doc_loc, inspect.getabsfile(pydoc_mod))
Georg Brandlb533e262008-05-25 18:19:30 +0000249 if result != expected_text:
250 print_diffs(expected_text, result)
251 self.fail("outputs are not equal, see diff above")
252
Brian Curtin0d8a1dd2010-03-31 03:22:46 +0000253 def test_issue8225(self):
254 # Test issue8225 to ensure no doc link appears for xml.etree
255 result, doc_loc = get_pydoc_text(xml.etree)
256 self.assertEqual(doc_loc, "", "MODULE DOCS incorrectly includes a link")
257
Georg Brandlb533e262008-05-25 18:19:30 +0000258 def test_not_here(self):
259 missing_module = "test.i_am_not_here"
260 result = str(run_pydoc(missing_module), 'ascii')
261 expected = missing_pattern % missing_module
262 self.assertEqual(expected, result,
263 "documentation for missing module found")
264
Benjamin Petersond76c8da2009-06-28 17:35:48 +0000265 def test_badimport(self):
266 # This tests the fix for issue 5230, where if pydoc found the module
267 # but the module had an internal import error pydoc would report no doc
268 # found.
269 modname = 'testmod_xyzzy'
270 testpairs = (
271 ('i_am_not_here', 'i_am_not_here'),
272 ('test.i_am_not_here_either', 'i_am_not_here_either'),
273 ('test.i_am_not_here.neither_am_i', 'i_am_not_here.neither_am_i'),
274 ('i_am_not_here.{}'.format(modname), 'i_am_not_here.{}'.format(modname)),
275 ('test.{}'.format(modname), modname),
276 )
277
278 @contextmanager
279 def newdirinpath(dir):
280 os.mkdir(dir)
281 sys.path.insert(0, dir)
282 yield
283 sys.path.pop(0)
284 rmtree(dir)
285
286 with newdirinpath(TESTFN), EnvironmentVarGuard() as env:
287 env['PYTHONPATH'] = TESTFN
288 fullmodname = os.path.join(TESTFN, modname)
289 sourcefn = fullmodname + os.extsep + "py"
290 for importstring, expectedinmsg in testpairs:
291 f = open(sourcefn, 'w')
292 f.write("import {}\n".format(importstring))
293 f.close()
294 try:
295 result = run_pydoc(modname).decode("ascii")
296 finally:
297 forget(modname)
298 expected = badimport_pattern % (modname, expectedinmsg)
299 self.assertEqual(expected, result)
300
R. David Murray1f1b9d32009-05-27 20:56:59 +0000301 def test_input_strip(self):
302 missing_module = " test.i_am_not_here "
303 result = str(run_pydoc(missing_module), 'ascii')
304 expected = missing_pattern % missing_module.strip()
305 self.assertEqual(expected, result)
306
Ezio Melottibf839b92010-02-16 23:32:24 +0000307 def test_stripid(self):
308 # test with strings, other implementations might have different repr()
309 stripid = pydoc.stripid
310 # strip the id
311 self.assertEqual(stripid('<function stripid at 0x88dcee4>'),
312 '<function stripid>')
313 self.assertEqual(stripid('<function stripid at 0x01F65390>'),
314 '<function stripid>')
315 # nothing to strip, return the same text
316 self.assertEqual(stripid('42'), '42')
317 self.assertEqual(stripid("<type 'exceptions.Exception'>"),
318 "<type 'exceptions.Exception'>")
319
Georg Brandlb533e262008-05-25 18:19:30 +0000320
321class TestDescriptions(unittest.TestCase):
322
323 def test_module(self):
324 # Check that pydocfodder module can be described
325 from test import pydocfodder
326 doc = pydoc.render_doc(pydocfodder)
Georg Brandlab91fde2009-08-13 08:51:18 +0000327 self.assertTrue("pydocfodder" in doc)
Georg Brandlb533e262008-05-25 18:19:30 +0000328
329 def test_classic_class(self):
330 class C: "Classic class"
331 c = C()
332 self.assertEqual(pydoc.describe(C), 'class C')
333 self.assertEqual(pydoc.describe(c), 'C')
334 expected = 'C in module %s' % __name__
Georg Brandlab91fde2009-08-13 08:51:18 +0000335 self.assertTrue(expected in pydoc.render_doc(c))
Georg Brandlb533e262008-05-25 18:19:30 +0000336
337 def test_class(self):
338 class C(object): "New-style class"
339 c = C()
340
341 self.assertEqual(pydoc.describe(C), 'class C')
342 self.assertEqual(pydoc.describe(c), 'C')
343 expected = 'C in module %s object' % __name__
Georg Brandlab91fde2009-08-13 08:51:18 +0000344 self.assertTrue(expected in pydoc.render_doc(c))
Georg Brandlb533e262008-05-25 18:19:30 +0000345
346
347def test_main():
348 test.support.run_unittest(PyDocDocTest, TestDescriptions)
349
350if __name__ == "__main__":
351 test_main()