blob: c1c19f608309d0c4d886dcd509a64363352eed0f [file] [log] [blame]
Georg Brandl8632cc22008-05-18 16:32:48 +00001import sys
Benjamin Peterson3a7305e2008-05-22 23:09:26 +00002import os
R. David Murrayef087da2009-06-23 18:02:46 +00003import os.path
Georg Brandl8632cc22008-05-18 16:32:48 +00004import difflib
5import subprocess
6import re
7import pydoc
Georg Brandlb7e419e2008-05-20 08:10:03 +00008import inspect
Georg Brandl8632cc22008-05-18 16:32:48 +00009import unittest
10import test.test_support
R. David Murrayef087da2009-06-23 18:02:46 +000011from contextlib import contextmanager
12from test.test_support import TESTFN, forget, rmtree, EnvironmentVarGuard
Benjamin Petersonf5c38da2008-05-18 20:48:07 +000013
Georg Brandl8632cc22008-05-18 16:32:48 +000014from test import pydoc_mod
15
16expected_text_pattern = \
17"""
18NAME
19 test.pydoc_mod - This is a test module for test_pydoc
20
21FILE
22 %s
Benjamin Peterson3a7305e2008-05-22 23:09:26 +000023%s
Georg Brandl8632cc22008-05-18 16:32:48 +000024CLASSES
25 __builtin__.object
26 B
27 A
28\x20\x20\x20\x20
29 class A
30 | Hello and goodbye
31 |\x20\x20
32 | Methods defined here:
33 |\x20\x20
34 | __init__()
35 | Wow, I have no function!
36\x20\x20\x20\x20
37 class B(__builtin__.object)
38 | Data descriptors defined here:
39 |\x20\x20
40 | __dict__
41 | dictionary for instance variables (if defined)
42 |\x20\x20
43 | __weakref__
44 | list of weak references to the object (if defined)
45 |\x20\x20
46 | ----------------------------------------------------------------------
47 | Data and other attributes defined here:
48 |\x20\x20
49 | NO_MEANING = 'eggs'
50
51FUNCTIONS
52 doc_func()
53 This function solves all of the world's problems:
54 hunger
55 lack of Python
56 war
57\x20\x20\x20\x20
58 nodoc_func()
59
60DATA
61 __author__ = 'Benjamin Peterson'
62 __credits__ = 'Nobody'
Georg Brandl8632cc22008-05-18 16:32:48 +000063 __version__ = '1.2.3.4'
64
65VERSION
66 1.2.3.4
67
68AUTHOR
69 Benjamin Peterson
70
71CREDITS
72 Nobody
73""".strip()
74
75expected_html_pattern = \
76"""
77<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
78<tr bgcolor="#7799ee">
79<td valign=bottom>&nbsp;<br>
80<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
81><td align=right valign=bottom
Benjamin Peterson3a7305e2008-05-22 23:09:26 +000082><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:%s">%s</a>%s</font></td></tr></table>
Georg Brandl8632cc22008-05-18 16:32:48 +000083 <p><tt>This&nbsp;is&nbsp;a&nbsp;test&nbsp;module&nbsp;for&nbsp;test_pydoc</tt></p>
84<p>
85<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
86<tr bgcolor="#ee77aa">
87<td colspan=3 valign=bottom>&nbsp;<br>
88<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
89\x20\x20\x20\x20
90<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
91<td width="100%%"><dl>
92<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
93</font></dt><dd>
94<dl>
95<dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#B">B</a>
96</font></dt></dl>
97</dd>
98<dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#A">A</a>
99</font></dt></dl>
100 <p>
101<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
102<tr bgcolor="#ffc8d8">
103<td colspan=3 valign=bottom>&nbsp;<br>
104<font color="#000000" face="helvetica, arial"><a name="A">class <strong>A</strong></a></font></td></tr>
105\x20\x20\x20\x20
106<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
107<td colspan=2><tt>Hello&nbsp;and&nbsp;goodbye<br>&nbsp;</tt></td></tr>
108<tr><td>&nbsp;</td>
109<td width="100%%">Methods defined here:<br>
110<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>
111
112</td></tr></table> <p>
113<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
114<tr bgcolor="#ffc8d8">
115<td colspan=3 valign=bottom>&nbsp;<br>
116<font color="#000000" face="helvetica, arial"><a name="B">class <strong>B</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
117\x20\x20\x20\x20
118<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
119<td width="100%%">Data descriptors defined here:<br>
120<dl><dt><strong>__dict__</strong></dt>
121<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
122</dl>
123<dl><dt><strong>__weakref__</strong></dt>
124<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
125</dl>
126<hr>
127Data and other attributes defined here:<br>
128<dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl>
129
130</td></tr></table></td></tr></table><p>
131<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
132<tr bgcolor="#eeaa77">
133<td colspan=3 valign=bottom>&nbsp;<br>
134<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
135\x20\x20\x20\x20
136<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
137<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>
138hunger<br>
139lack&nbsp;of&nbsp;Python<br>
140war</tt></dd></dl>
141 <dl><dt><a name="-nodoc_func"><strong>nodoc_func</strong></a>()</dt></dl>
142</td></tr></table><p>
143<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
144<tr bgcolor="#55aa55">
145<td colspan=3 valign=bottom>&nbsp;<br>
146<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
147\x20\x20\x20\x20
148<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
149<td width="100%%"><strong>__author__</strong> = 'Benjamin Peterson'<br>
150<strong>__credits__</strong> = 'Nobody'<br>
Georg Brandl8632cc22008-05-18 16:32:48 +0000151<strong>__version__</strong> = '1.2.3.4'</td></tr></table><p>
152<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
153<tr bgcolor="#7799ee">
154<td colspan=3 valign=bottom>&nbsp;<br>
155<font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr>
156\x20\x20\x20\x20
157<tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
158<td width="100%%">Benjamin&nbsp;Peterson</td></tr></table><p>
159<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
160<tr bgcolor="#7799ee">
161<td colspan=3 valign=bottom>&nbsp;<br>
162<font color="#ffffff" face="helvetica, arial"><big><strong>Credits</strong></big></font></td></tr>
163\x20\x20\x20\x20
164<tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
165<td width="100%%">Nobody</td></tr></table>
166""".strip()
167
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000168
Georg Brandl8632cc22008-05-18 16:32:48 +0000169# output pattern for missing module
170missing_pattern = "no Python documentation found for '%s'"
171
R. David Murrayef087da2009-06-23 18:02:46 +0000172# output pattern for module with bad imports
173badimport_pattern = "problem in %s - <type 'exceptions.ImportError'>: No module named %s"
174
Georg Brandl8632cc22008-05-18 16:32:48 +0000175def run_pydoc(module_name, *args):
176 """
177 Runs pydoc on the specified module. Returns the stripped
178 output of pydoc.
179 """
180 cmd = [sys.executable, pydoc.__file__, " ".join(args), module_name]
181 output = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout.read()
182 return output.strip()
183
184def get_pydoc_html(module):
185 "Returns pydoc generated output as html"
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000186 doc = pydoc.HTMLDoc()
187 output = doc.docmodule(module)
188 loc = doc.getdocloc(pydoc_mod) or ""
189 if loc:
190 loc = "<br><a href=\"" + loc + "\">Module Docs</a>"
191 return output.strip(), loc
Georg Brandl8632cc22008-05-18 16:32:48 +0000192
193def get_pydoc_text(module):
194 "Returns pydoc generated output as text"
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000195 doc = pydoc.TextDoc()
196 loc = doc.getdocloc(pydoc_mod) or ""
197 if loc:
198 loc = "\nMODULE DOCS\n " + loc + "\n"
199
200 output = doc.docmodule(module)
Georg Brandl8632cc22008-05-18 16:32:48 +0000201
202 # cleanup the extra text formatting that pydoc preforms
203 patt = re.compile('\b.')
204 output = patt.sub('', output)
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000205 return output.strip(), loc
Georg Brandl8632cc22008-05-18 16:32:48 +0000206
207def print_diffs(text1, text2):
208 "Prints unified diffs for two texts"
Georg Brandlfb3de1f2008-05-20 08:07:36 +0000209 lines1 = text1.splitlines(True)
210 lines2 = text2.splitlines(True)
211 diffs = difflib.unified_diff(lines1, lines2, n=0, fromfile='expected',
212 tofile='got')
213 print '\n' + ''.join(diffs)
Georg Brandl8632cc22008-05-18 16:32:48 +0000214
Georg Brandl8632cc22008-05-18 16:32:48 +0000215
Georg Brandl8632cc22008-05-18 16:32:48 +0000216class PyDocDocTest(unittest.TestCase):
217
218 def test_html_doc(self):
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000219 result, doc_loc = get_pydoc_html(pydoc_mod)
Georg Brandlb7e419e2008-05-20 08:10:03 +0000220 mod_file = inspect.getabsfile(pydoc_mod)
Amaury Forgeot d'Arc8e8de4a2008-06-10 21:37:15 +0000221 if sys.platform == 'win32':
222 import nturl2path
223 mod_url = nturl2path.pathname2url(mod_file)
224 else:
225 mod_url = mod_file
226 expected_html = expected_html_pattern % (mod_url, mod_file, doc_loc)
Georg Brandl8632cc22008-05-18 16:32:48 +0000227 if result != expected_html:
Georg Brandlfb3de1f2008-05-20 08:07:36 +0000228 print_diffs(expected_html, result)
Georg Brandl8632cc22008-05-18 16:32:48 +0000229 self.fail("outputs are not equal, see diff above")
230
231 def test_text_doc(self):
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000232 result, doc_loc = get_pydoc_text(pydoc_mod)
233 expected_text = expected_text_pattern % \
234 (inspect.getabsfile(pydoc_mod), doc_loc)
Georg Brandl8632cc22008-05-18 16:32:48 +0000235 if result != expected_text:
Georg Brandlfb3de1f2008-05-20 08:07:36 +0000236 print_diffs(expected_text, result)
Georg Brandl8632cc22008-05-18 16:32:48 +0000237 self.fail("outputs are not equal, see diff above")
238
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000239 def test_not_here(self):
240 missing_module = "test.i_am_not_here"
241 result = run_pydoc(missing_module)
242 expected = missing_pattern % missing_module
243 self.assertEqual(expected, result,
244 "documentation for missing module found")
245
R. David Murrayef087da2009-06-23 18:02:46 +0000246 def test_badimport(self):
247 # This tests the fix for issue 5230, where if pydoc found the module
248 # but the module had an internal import error pydoc would report no doc
249 # found.
250 modname = 'testmod_xyzzy'
251 testpairs = (
252 ('i_am_not_here', 'i_am_not_here'),
253 ('test.i_am_not_here_either', 'i_am_not_here_either'),
254 ('test.i_am_not_here.neither_am_i', 'i_am_not_here.neither_am_i'),
255 ('i_am_not_here.{}'.format(modname), 'i_am_not_here.{}'.format(modname)),
256 ('test.{}'.format(modname), modname),
257 )
258
259 @contextmanager
260 def newdirinpath(dir):
261 os.mkdir(dir)
262 sys.path.insert(0, dir)
263 yield
264 sys.path.pop(0)
265 rmtree(dir)
266
267 with newdirinpath(TESTFN), EnvironmentVarGuard() as env:
268 env['PYTHONPATH'] = TESTFN
269 fullmodname = os.path.join(TESTFN, modname)
270 sourcefn = fullmodname + os.extsep + "py"
271 for importstring, expectedinmsg in testpairs:
272 f = open(sourcefn, 'w')
273 f.write("import {}\n".format(importstring))
274 f.close()
275 try:
276 result = run_pydoc(modname)
277 finally:
278 forget(modname)
279 expected = badimport_pattern % (modname, expectedinmsg)
280 self.assertEqual(expected, result)
281
R. David Murrayd67ea7d2009-05-27 20:07:21 +0000282 def test_input_strip(self):
283 missing_module = " test.i_am_not_here "
284 result = run_pydoc(missing_module)
285 expected = missing_pattern % missing_module.strip()
286 self.assertEqual(expected, result,
287 "white space was not stripped from module name "
288 "or other error output mismatch")
289
Georg Brandl8632cc22008-05-18 16:32:48 +0000290
291class TestDescriptions(unittest.TestCase):
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000292
Georg Brandl8632cc22008-05-18 16:32:48 +0000293 def test_module(self):
294 # Check that pydocfodder module can be described
295 from test import pydocfodder
296 doc = pydoc.render_doc(pydocfodder)
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000297 self.assert_("pydocfodder" in doc)
Georg Brandl8632cc22008-05-18 16:32:48 +0000298
299 def test_classic_class(self):
300 class C: "Classic class"
301 c = C()
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000302 self.assertEqual(pydoc.describe(C), 'class C')
303 self.assertEqual(pydoc.describe(c), 'instance of C')
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000304 expected = 'instance of C in module %s' % __name__
305 self.assert_(expected in pydoc.render_doc(c))
Georg Brandl8632cc22008-05-18 16:32:48 +0000306
307 def test_class(self):
308 class C(object): "New-style class"
309 c = C()
310
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000311 self.assertEqual(pydoc.describe(C), 'class C')
312 self.assertEqual(pydoc.describe(c), 'C')
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000313 expected = 'C in module %s object' % __name__
314 self.assert_(expected in pydoc.render_doc(c))
Georg Brandl8632cc22008-05-18 16:32:48 +0000315
316
317def test_main():
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000318 test.test_support.run_unittest(PyDocDocTest,
Georg Brandl8632cc22008-05-18 16:32:48 +0000319 TestDescriptions)
320
321if __name__ == "__main__":
322 test_main()