blob: 6cfe7e785363aa1c3b0ae862bc9b009206ddc0e9 [file] [log] [blame]
Benjamin Peterson3a7305e2008-05-22 23:09:26 +00001import os
Éric Araujo9a528302011-07-29 17:34:35 +02002import sys
Georg Brandl8632cc22008-05-18 16:32:48 +00003import difflib
Éric Araujo9a528302011-07-29 17:34:35 +02004import __builtin__
Georg Brandl8632cc22008-05-18 16:32:48 +00005import re
Berker Peksagdc9d41d2015-02-20 12:10:33 +02006import py_compile
Georg Brandl8632cc22008-05-18 16:32:48 +00007import pydoc
Antoine Pitrouf41ffed2013-05-19 15:44:54 +02008import contextlib
Georg Brandlb7e419e2008-05-20 08:10:03 +00009import inspect
Ezio Melottibdfa2e62011-04-28 07:59:33 +030010import keyword
Antoine Pitrouf41ffed2013-05-19 15:44:54 +020011import pkgutil
Georg Brandl8632cc22008-05-18 16:32:48 +000012import unittest
Brian Curtinaeb2e822010-03-31 03:10:21 +000013import xml.etree
R David Murray984f6302014-01-05 12:35:59 -050014import types
Georg Brandl8632cc22008-05-18 16:32:48 +000015import test.test_support
Raymond Hettinger9aa5a342011-03-25 16:00:13 -070016from collections import namedtuple
Ned Deily1a96f8d2011-10-06 14:17:34 -070017from test.script_helper import assert_python_ok
Charles-François Natali94919a42014-06-20 22:57:19 +010018from test.test_support import (TESTFN, rmtree, reap_children, captured_stdout,
19 captured_stderr, requires_docstrings)
Benjamin Petersonf5c38da2008-05-18 20:48:07 +000020
Georg Brandl8632cc22008-05-18 16:32:48 +000021from test import pydoc_mod
22
Serhiy Storchaka72121c62013-01-27 19:45:49 +020023if test.test_support.HAVE_DOCSTRINGS:
24 expected_data_docstrings = (
25 'dictionary for instance variables (if defined)',
26 'list of weak references to the object (if defined)',
27 )
28else:
29 expected_data_docstrings = ('', '')
30
Georg Brandl8632cc22008-05-18 16:32:48 +000031expected_text_pattern = \
32"""
33NAME
34 test.pydoc_mod - This is a test module for test_pydoc
35
36FILE
37 %s
Benjamin Peterson3a7305e2008-05-22 23:09:26 +000038%s
Georg Brandl8632cc22008-05-18 16:32:48 +000039CLASSES
40 __builtin__.object
41 B
Benjamin Petersonc3e1e902014-06-07 16:44:00 -070042 C
Georg Brandl8632cc22008-05-18 16:32:48 +000043 A
44\x20\x20\x20\x20
45 class A
46 | Hello and goodbye
47 |\x20\x20
48 | Methods defined here:
49 |\x20\x20
50 | __init__()
51 | Wow, I have no function!
52\x20\x20\x20\x20
53 class B(__builtin__.object)
54 | Data descriptors defined here:
55 |\x20\x20
Serhiy Storchaka72121c62013-01-27 19:45:49 +020056 | __dict__%s
Georg Brandl8632cc22008-05-18 16:32:48 +000057 |\x20\x20
Serhiy Storchaka72121c62013-01-27 19:45:49 +020058 | __weakref__%s
Georg Brandl8632cc22008-05-18 16:32:48 +000059 |\x20\x20
60 | ----------------------------------------------------------------------
61 | Data and other attributes defined here:
62 |\x20\x20
63 | NO_MEANING = 'eggs'
Benjamin Petersonc3e1e902014-06-07 16:44:00 -070064\x20\x20\x20\x20
65 class C(__builtin__.object)
66 | Methods defined here:
67 |\x20\x20
68 | get_answer(self)
69 | Return say_no()
70 |\x20\x20
71 | is_it_true(self)
72 | Return self.get_answer()
73 |\x20\x20
74 | say_no(self)
75 |\x20\x20
76 | ----------------------------------------------------------------------
77 | Data descriptors defined here:
78 |\x20\x20
79 | __dict__
80 | dictionary for instance variables (if defined)
81 |\x20\x20
82 | __weakref__
83 | list of weak references to the object (if defined)
Georg Brandl8632cc22008-05-18 16:32:48 +000084
85FUNCTIONS
86 doc_func()
87 This function solves all of the world's problems:
88 hunger
89 lack of Python
90 war
91\x20\x20\x20\x20
92 nodoc_func()
93
94DATA
95 __author__ = 'Benjamin Peterson'
96 __credits__ = 'Nobody'
Georg Brandl8632cc22008-05-18 16:32:48 +000097 __version__ = '1.2.3.4'
98
99VERSION
100 1.2.3.4
101
102AUTHOR
103 Benjamin Peterson
104
105CREDITS
106 Nobody
107""".strip()
108
Serhiy Storchaka72121c62013-01-27 19:45:49 +0200109expected_text_data_docstrings = tuple('\n | ' + s if s else ''
110 for s in expected_data_docstrings)
111
Georg Brandl8632cc22008-05-18 16:32:48 +0000112expected_html_pattern = \
113"""
114<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
115<tr bgcolor="#7799ee">
116<td valign=bottom>&nbsp;<br>
117<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
118><td align=right valign=bottom
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000119><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 +0000120 <p><tt>This&nbsp;is&nbsp;a&nbsp;test&nbsp;module&nbsp;for&nbsp;test_pydoc</tt></p>
121<p>
122<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
123<tr bgcolor="#ee77aa">
124<td colspan=3 valign=bottom>&nbsp;<br>
125<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
126\x20\x20\x20\x20
127<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
128<td width="100%%"><dl>
129<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a>
130</font></dt><dd>
131<dl>
132<dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#B">B</a>
Benjamin Petersonc3e1e902014-06-07 16:44:00 -0700133</font></dt><dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#C">C</a>
Georg Brandl8632cc22008-05-18 16:32:48 +0000134</font></dt></dl>
135</dd>
136<dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#A">A</a>
137</font></dt></dl>
138 <p>
139<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
140<tr bgcolor="#ffc8d8">
141<td colspan=3 valign=bottom>&nbsp;<br>
142<font color="#000000" face="helvetica, arial"><a name="A">class <strong>A</strong></a></font></td></tr>
143\x20\x20\x20\x20
144<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
145<td colspan=2><tt>Hello&nbsp;and&nbsp;goodbye<br>&nbsp;</tt></td></tr>
146<tr><td>&nbsp;</td>
147<td width="100%%">Methods defined here:<br>
148<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>
149
150</td></tr></table> <p>
151<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
152<tr bgcolor="#ffc8d8">
153<td colspan=3 valign=bottom>&nbsp;<br>
154<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>
155\x20\x20\x20\x20
156<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
157<td width="100%%">Data descriptors defined here:<br>
158<dl><dt><strong>__dict__</strong></dt>
Serhiy Storchaka72121c62013-01-27 19:45:49 +0200159<dd><tt>%s</tt></dd>
Georg Brandl8632cc22008-05-18 16:32:48 +0000160</dl>
161<dl><dt><strong>__weakref__</strong></dt>
Serhiy Storchaka72121c62013-01-27 19:45:49 +0200162<dd><tt>%s</tt></dd>
Georg Brandl8632cc22008-05-18 16:32:48 +0000163</dl>
164<hr>
165Data and other attributes defined here:<br>
166<dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl>
167
Benjamin Petersonc3e1e902014-06-07 16:44:00 -0700168</td></tr></table> <p>
169<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
170<tr bgcolor="#ffc8d8">
171<td colspan=3 valign=bottom>&nbsp;<br>
172<font color="#000000" face="helvetica, arial"><a name="C">class <strong>C</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
173\x20\x20\x20\x20
174<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
175<td width="100%%">Methods defined here:<br>
176<dl><dt><a name="C-get_answer"><strong>get_answer</strong></a>(self)</dt><dd><tt>Return&nbsp;<a href="#C-say_no">say_no</a>()</tt></dd></dl>
177
178<dl><dt><a name="C-is_it_true"><strong>is_it_true</strong></a>(self)</dt><dd><tt>Return&nbsp;self.<a href="#C-get_answer">get_answer</a>()</tt></dd></dl>
179
180<dl><dt><a name="C-say_no"><strong>say_no</strong></a>(self)</dt></dl>
181
182<hr>
183Data descriptors defined here:<br>
184<dl><dt><strong>__dict__</strong></dt>
185<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
186</dl>
187<dl><dt><strong>__weakref__</strong></dt>
188<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
189</dl>
Georg Brandl8632cc22008-05-18 16:32:48 +0000190</td></tr></table></td></tr></table><p>
191<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
192<tr bgcolor="#eeaa77">
193<td colspan=3 valign=bottom>&nbsp;<br>
194<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
195\x20\x20\x20\x20
196<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
197<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>
198hunger<br>
199lack&nbsp;of&nbsp;Python<br>
200war</tt></dd></dl>
201 <dl><dt><a name="-nodoc_func"><strong>nodoc_func</strong></a>()</dt></dl>
202</td></tr></table><p>
203<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
204<tr bgcolor="#55aa55">
205<td colspan=3 valign=bottom>&nbsp;<br>
206<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
207\x20\x20\x20\x20
208<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
209<td width="100%%"><strong>__author__</strong> = 'Benjamin Peterson'<br>
210<strong>__credits__</strong> = 'Nobody'<br>
Georg Brandl8632cc22008-05-18 16:32:48 +0000211<strong>__version__</strong> = '1.2.3.4'</td></tr></table><p>
212<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
213<tr bgcolor="#7799ee">
214<td colspan=3 valign=bottom>&nbsp;<br>
215<font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr>
216\x20\x20\x20\x20
217<tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
218<td width="100%%">Benjamin&nbsp;Peterson</td></tr></table><p>
219<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
220<tr bgcolor="#7799ee">
221<td colspan=3 valign=bottom>&nbsp;<br>
222<font color="#ffffff" face="helvetica, arial"><big><strong>Credits</strong></big></font></td></tr>
223\x20\x20\x20\x20
224<tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
225<td width="100%%">Nobody</td></tr></table>
226""".strip()
227
Serhiy Storchaka72121c62013-01-27 19:45:49 +0200228expected_html_data_docstrings = tuple(s.replace(' ', '&nbsp;')
229 for s in expected_data_docstrings)
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000230
Georg Brandl8632cc22008-05-18 16:32:48 +0000231# output pattern for missing module
232missing_pattern = "no Python documentation found for '%s'"
233
R. David Murrayef087da2009-06-23 18:02:46 +0000234# output pattern for module with bad imports
235badimport_pattern = "problem in %s - <type 'exceptions.ImportError'>: No module named %s"
236
Ned Deily1a96f8d2011-10-06 14:17:34 -0700237def run_pydoc(module_name, *args, **env):
Georg Brandl8632cc22008-05-18 16:32:48 +0000238 """
239 Runs pydoc on the specified module. Returns the stripped
240 output of pydoc.
241 """
Ned Deily1a96f8d2011-10-06 14:17:34 -0700242 args = args + (module_name,)
243 # do not write bytecode files to avoid caching errors
244 rc, out, err = assert_python_ok('-B', pydoc.__file__, *args, **env)
245 return out.strip()
Georg Brandl8632cc22008-05-18 16:32:48 +0000246
247def get_pydoc_html(module):
248 "Returns pydoc generated output as html"
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000249 doc = pydoc.HTMLDoc()
250 output = doc.docmodule(module)
251 loc = doc.getdocloc(pydoc_mod) or ""
252 if loc:
253 loc = "<br><a href=\"" + loc + "\">Module Docs</a>"
254 return output.strip(), loc
Georg Brandl8632cc22008-05-18 16:32:48 +0000255
256def get_pydoc_text(module):
257 "Returns pydoc generated output as text"
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000258 doc = pydoc.TextDoc()
259 loc = doc.getdocloc(pydoc_mod) or ""
260 if loc:
261 loc = "\nMODULE DOCS\n " + loc + "\n"
262
263 output = doc.docmodule(module)
Georg Brandl8632cc22008-05-18 16:32:48 +0000264
265 # cleanup the extra text formatting that pydoc preforms
266 patt = re.compile('\b.')
267 output = patt.sub('', output)
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000268 return output.strip(), loc
Georg Brandl8632cc22008-05-18 16:32:48 +0000269
270def print_diffs(text1, text2):
271 "Prints unified diffs for two texts"
Georg Brandlfb3de1f2008-05-20 08:07:36 +0000272 lines1 = text1.splitlines(True)
273 lines2 = text2.splitlines(True)
274 diffs = difflib.unified_diff(lines1, lines2, n=0, fromfile='expected',
275 tofile='got')
276 print '\n' + ''.join(diffs)
Georg Brandl8632cc22008-05-18 16:32:48 +0000277
Georg Brandl8632cc22008-05-18 16:32:48 +0000278
Antoine Pitrouf41ffed2013-05-19 15:44:54 +0200279class PydocBaseTest(unittest.TestCase):
280
281 def _restricted_walk_packages(self, walk_packages, path=None):
282 """
283 A version of pkgutil.walk_packages() that will restrict itself to
284 a given path.
285 """
286 default_path = path or [os.path.dirname(__file__)]
287 def wrapper(path=None, prefix='', onerror=None):
288 return walk_packages(path or default_path, prefix, onerror)
289 return wrapper
290
291 @contextlib.contextmanager
292 def restrict_walk_packages(self, path=None):
293 walk_packages = pkgutil.walk_packages
294 pkgutil.walk_packages = self._restricted_walk_packages(walk_packages,
295 path)
296 try:
297 yield
298 finally:
299 pkgutil.walk_packages = walk_packages
300
301
302class PydocDocTest(unittest.TestCase):
Georg Brandl8632cc22008-05-18 16:32:48 +0000303
Charles-François Natali94919a42014-06-20 22:57:19 +0100304 @requires_docstrings
R. David Murrayf28fd242010-02-23 00:24:49 +0000305 @unittest.skipIf(sys.flags.optimize >= 2,
306 "Docstrings are omitted with -O2 and above")
Georg Brandl8632cc22008-05-18 16:32:48 +0000307 def test_html_doc(self):
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000308 result, doc_loc = get_pydoc_html(pydoc_mod)
Georg Brandlb7e419e2008-05-20 08:10:03 +0000309 mod_file = inspect.getabsfile(pydoc_mod)
Amaury Forgeot d'Arc8e8de4a2008-06-10 21:37:15 +0000310 if sys.platform == 'win32':
311 import nturl2path
312 mod_url = nturl2path.pathname2url(mod_file)
313 else:
314 mod_url = mod_file
Serhiy Storchaka72121c62013-01-27 19:45:49 +0200315 expected_html = expected_html_pattern % (
316 (mod_url, mod_file, doc_loc) +
317 expected_html_data_docstrings)
Georg Brandl8632cc22008-05-18 16:32:48 +0000318 if result != expected_html:
Georg Brandlfb3de1f2008-05-20 08:07:36 +0000319 print_diffs(expected_html, result)
Georg Brandl8632cc22008-05-18 16:32:48 +0000320 self.fail("outputs are not equal, see diff above")
321
Charles-François Natali94919a42014-06-20 22:57:19 +0100322 @requires_docstrings
R. David Murrayf28fd242010-02-23 00:24:49 +0000323 @unittest.skipIf(sys.flags.optimize >= 2,
324 "Docstrings are omitted with -O2 and above")
Georg Brandl8632cc22008-05-18 16:32:48 +0000325 def test_text_doc(self):
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000326 result, doc_loc = get_pydoc_text(pydoc_mod)
Serhiy Storchaka72121c62013-01-27 19:45:49 +0200327 expected_text = expected_text_pattern % (
328 (inspect.getabsfile(pydoc_mod), doc_loc) +
329 expected_text_data_docstrings)
Georg Brandl8632cc22008-05-18 16:32:48 +0000330 if result != expected_text:
Georg Brandlfb3de1f2008-05-20 08:07:36 +0000331 print_diffs(expected_text, result)
Georg Brandl8632cc22008-05-18 16:32:48 +0000332 self.fail("outputs are not equal, see diff above")
333
Brian Curtinaeb2e822010-03-31 03:10:21 +0000334 def test_issue8225(self):
335 # Test issue8225 to ensure no doc link appears for xml.etree
336 result, doc_loc = get_pydoc_text(xml.etree)
337 self.assertEqual(doc_loc, "", "MODULE DOCS incorrectly includes a link")
338
Benjamin Peterson75a55c32014-06-07 20:14:26 -0700339 def test_getpager_with_stdin_none(self):
340 previous_stdin = sys.stdin
341 try:
342 sys.stdin = None
343 pydoc.getpager() # Shouldn't fail.
344 finally:
345 sys.stdin = previous_stdin
346
R David Murrayc313b1d2012-04-23 13:27:11 -0400347 def test_non_str_name(self):
348 # issue14638
349 # Treat illegal (non-str) name like no name
350 class A:
351 __name__ = 42
352 class B:
353 pass
354 adoc = pydoc.render_doc(A())
355 bdoc = pydoc.render_doc(B())
356 self.assertEqual(adoc.replace("A", "B"), bdoc)
357
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000358 def test_not_here(self):
359 missing_module = "test.i_am_not_here"
360 result = run_pydoc(missing_module)
361 expected = missing_pattern % missing_module
362 self.assertEqual(expected, result,
363 "documentation for missing module found")
364
R. David Murrayd67ea7d2009-05-27 20:07:21 +0000365 def test_input_strip(self):
366 missing_module = " test.i_am_not_here "
367 result = run_pydoc(missing_module)
368 expected = missing_pattern % missing_module.strip()
369 self.assertEqual(expected, result,
370 "white space was not stripped from module name "
371 "or other error output mismatch")
372
Ezio Melottie511fc72010-02-16 23:26:09 +0000373 def test_stripid(self):
374 # test with strings, other implementations might have different repr()
375 stripid = pydoc.stripid
376 # strip the id
377 self.assertEqual(stripid('<function stripid at 0x88dcee4>'),
378 '<function stripid>')
379 self.assertEqual(stripid('<function stripid at 0x01F65390>'),
380 '<function stripid>')
381 # nothing to strip, return the same text
382 self.assertEqual(stripid('42'), '42')
383 self.assertEqual(stripid("<type 'exceptions.Exception'>"),
384 "<type 'exceptions.Exception'>")
385
Berker Peksagdc9d41d2015-02-20 12:10:33 +0200386 def test_synopsis(self):
387 with test.test_support.temp_cwd() as test_dir:
388 init_path = os.path.join(test_dir, 'dt.py')
389 with open(init_path, 'w') as fobj:
390 fobj.write('''\
391"""
392my doc
393
394second line
395"""
396foo = 1
397''')
398 py_compile.compile(init_path)
399 synopsis = pydoc.synopsis(init_path, {})
400 self.assertEqual(synopsis, 'my doc')
401
Serhiy Storchaka2b8c00d2015-03-01 15:31:21 +0200402 @unittest.skipIf(sys.flags.optimize >= 2,
403 'Docstrings are omitted with -OO and above')
Berker Peksagdc9d41d2015-02-20 12:10:33 +0200404 def test_synopsis_sourceless_empty_doc(self):
405 with test.test_support.temp_cwd() as test_dir:
406 init_path = os.path.join(test_dir, 'foomod42.py')
407 cached_path = os.path.join(test_dir, 'foomod42.pyc')
408 with open(init_path, 'w') as fobj:
409 fobj.write("foo = 1")
410 py_compile.compile(init_path)
411 synopsis = pydoc.synopsis(init_path, {})
412 self.assertIsNone(synopsis)
413 synopsis_cached = pydoc.synopsis(cached_path, {})
414 self.assertIsNone(synopsis_cached)
415
Georg Brandl8632cc22008-05-18 16:32:48 +0000416
Antoine Pitrouf41ffed2013-05-19 15:44:54 +0200417class PydocImportTest(PydocBaseTest):
Ned Deily1a96f8d2011-10-06 14:17:34 -0700418
419 def setUp(self):
420 self.test_dir = os.mkdir(TESTFN)
421 self.addCleanup(rmtree, TESTFN)
422
423 def test_badimport(self):
424 # This tests the fix for issue 5230, where if pydoc found the module
425 # but the module had an internal import error pydoc would report no doc
426 # found.
427 modname = 'testmod_xyzzy'
428 testpairs = (
429 ('i_am_not_here', 'i_am_not_here'),
430 ('test.i_am_not_here_either', 'i_am_not_here_either'),
431 ('test.i_am_not_here.neither_am_i', 'i_am_not_here.neither_am_i'),
432 ('i_am_not_here.{}'.format(modname),
433 'i_am_not_here.{}'.format(modname)),
434 ('test.{}'.format(modname), modname),
435 )
436
437 sourcefn = os.path.join(TESTFN, modname) + os.extsep + "py"
438 for importstring, expectedinmsg in testpairs:
439 with open(sourcefn, 'w') as f:
440 f.write("import {}\n".format(importstring))
441 result = run_pydoc(modname, PYTHONPATH=TESTFN)
442 expected = badimport_pattern % (modname, expectedinmsg)
443 self.assertEqual(expected, result)
444
445 def test_apropos_with_bad_package(self):
446 # Issue 7425 - pydoc -k failed when bad package on path
447 pkgdir = os.path.join(TESTFN, "syntaxerr")
448 os.mkdir(pkgdir)
449 badsyntax = os.path.join(pkgdir, "__init__") + os.extsep + "py"
450 with open(badsyntax, 'w') as f:
451 f.write("invalid python syntax = $1\n")
Antoine Pitrouf41ffed2013-05-19 15:44:54 +0200452 with self.restrict_walk_packages(path=[TESTFN]):
453 with captured_stdout() as out:
454 with captured_stderr() as err:
455 pydoc.apropos('xyzzy')
456 # No result, no error
457 self.assertEqual(out.getvalue(), '')
458 self.assertEqual(err.getvalue(), '')
459 # The package name is still matched
460 with captured_stdout() as out:
461 with captured_stderr() as err:
462 pydoc.apropos('syntaxerr')
463 self.assertEqual(out.getvalue().strip(), 'syntaxerr')
464 self.assertEqual(err.getvalue(), '')
Ned Deily1a96f8d2011-10-06 14:17:34 -0700465
466 def test_apropos_with_unreadable_dir(self):
467 # Issue 7367 - pydoc -k failed when unreadable dir on path
468 self.unreadable_dir = os.path.join(TESTFN, "unreadable")
469 os.mkdir(self.unreadable_dir, 0)
470 self.addCleanup(os.rmdir, self.unreadable_dir)
471 # Note, on Windows the directory appears to be still
472 # readable so this is not really testing the issue there
Antoine Pitrouf41ffed2013-05-19 15:44:54 +0200473 with self.restrict_walk_packages(path=[TESTFN]):
474 with captured_stdout() as out:
475 with captured_stderr() as err:
476 pydoc.apropos('SOMEKEY')
477 # No result, no error
478 self.assertEqual(out.getvalue(), '')
479 self.assertEqual(err.getvalue(), '')
Ned Deily1a96f8d2011-10-06 14:17:34 -0700480
481
Georg Brandl8632cc22008-05-18 16:32:48 +0000482class TestDescriptions(unittest.TestCase):
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000483
Georg Brandl8632cc22008-05-18 16:32:48 +0000484 def test_module(self):
485 # Check that pydocfodder module can be described
486 from test import pydocfodder
487 doc = pydoc.render_doc(pydocfodder)
Ezio Melottiaa980582010-01-23 23:04:36 +0000488 self.assertIn("pydocfodder", doc)
Georg Brandl8632cc22008-05-18 16:32:48 +0000489
490 def test_classic_class(self):
491 class C: "Classic class"
492 c = C()
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000493 self.assertEqual(pydoc.describe(C), 'class C')
494 self.assertEqual(pydoc.describe(c), 'instance of C')
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000495 expected = 'instance of C in module %s' % __name__
Ezio Melottiaa980582010-01-23 23:04:36 +0000496 self.assertIn(expected, pydoc.render_doc(c))
Georg Brandl8632cc22008-05-18 16:32:48 +0000497
498 def test_class(self):
499 class C(object): "New-style class"
500 c = C()
501
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000502 self.assertEqual(pydoc.describe(C), 'class C')
503 self.assertEqual(pydoc.describe(c), 'C')
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000504 expected = 'C in module %s object' % __name__
Ezio Melottiaa980582010-01-23 23:04:36 +0000505 self.assertIn(expected, pydoc.render_doc(c))
Georg Brandl8632cc22008-05-18 16:32:48 +0000506
Raymond Hettinger9aa5a342011-03-25 16:00:13 -0700507 def test_namedtuple_public_underscore(self):
508 NT = namedtuple('NT', ['abc', 'def'], rename=True)
509 with captured_stdout() as help_io:
Terry Jan Reedyc0e60472013-11-04 21:45:33 -0500510 pydoc.help(NT)
Raymond Hettinger9aa5a342011-03-25 16:00:13 -0700511 helptext = help_io.getvalue()
512 self.assertIn('_1', helptext)
513 self.assertIn('_replace', helptext)
514 self.assertIn('_asdict', helptext)
515
Georg Brandl8632cc22008-05-18 16:32:48 +0000516
R David Murray984f6302014-01-05 12:35:59 -0500517@unittest.skipUnless(test.test_support.have_unicode,
518 "test requires unicode support")
519class TestUnicode(unittest.TestCase):
520
521 def setUp(self):
522 # Better not to use unicode escapes in literals, lest the
523 # parser choke on it if Python has been built without
524 # unicode support.
525 self.Q = types.ModuleType(
526 'Q', 'Rational numbers: \xe2\x84\x9a'.decode('utf8'))
527 self.Q.__version__ = '\xe2\x84\x9a'.decode('utf8')
528 self.Q.__date__ = '\xe2\x84\x9a'.decode('utf8')
529 self.Q.__author__ = '\xe2\x84\x9a'.decode('utf8')
530 self.Q.__credits__ = '\xe2\x84\x9a'.decode('utf8')
531
532 self.assertIsInstance(self.Q.__doc__, unicode)
533
534 def test_render_doc(self):
535 # render_doc is robust against unicode in docstrings
536 doc = pydoc.render_doc(self.Q)
537 self.assertIsInstance(doc, str)
538
539 def test_encode(self):
540 # _encode is robust against characters out the specified encoding
541 self.assertEqual(pydoc._encode(self.Q.__doc__, 'ascii'), 'Rational numbers: &#8474;')
542
543 def test_pipepager(self):
544 # pipepager does not choke on unicode
545 doc = pydoc.render_doc(self.Q)
546
547 saved, os.popen = os.popen, open
548 try:
549 with test.test_support.temp_cwd():
550 pydoc.pipepager(doc, 'pipe')
551 self.assertEqual(open('pipe').read(), pydoc._encode(doc))
552 finally:
553 os.popen = saved
554
555 def test_tempfilepager(self):
556 # tempfilepager does not choke on unicode
557 doc = pydoc.render_doc(self.Q)
558
559 output = {}
560 def mock_system(cmd):
Serhiy Storchakaee105dc2014-01-10 22:43:03 +0200561 filename = cmd.strip()[1:-1]
562 self.assertEqual('"' + filename + '"', cmd.strip())
563 output['content'] = open(filename).read()
R David Murray984f6302014-01-05 12:35:59 -0500564 saved, os.system = os.system, mock_system
565 try:
566 pydoc.tempfilepager(doc, '')
567 self.assertEqual(output['content'], pydoc._encode(doc))
568 finally:
569 os.system = saved
570
571 def test_plainpager(self):
572 # plainpager does not choke on unicode
573 doc = pydoc.render_doc(self.Q)
574
575 # Note: captured_stdout is too permissive when it comes to
576 # unicode, and using it here would make the test always
577 # pass.
578 with test.test_support.temp_cwd():
579 with open('output', 'w') as f:
580 saved, sys.stdout = sys.stdout, f
581 try:
582 pydoc.plainpager(doc)
583 finally:
584 sys.stdout = saved
585 self.assertIn('Rational numbers:', open('output').read())
586
587 def test_ttypager(self):
588 # ttypager does not choke on unicode
589 doc = pydoc.render_doc(self.Q)
590 # Test ttypager
591 with test.test_support.temp_cwd(), test.test_support.captured_stdin():
592 with open('output', 'w') as f:
593 saved, sys.stdout = sys.stdout, f
594 try:
595 pydoc.ttypager(doc)
596 finally:
597 sys.stdout = saved
598 self.assertIn('Rational numbers:', open('output').read())
599
600 def test_htmlpage(self):
601 # html.page does not choke on unicode
602 with test.test_support.temp_cwd():
603 with captured_stdout() as output:
604 pydoc.writedoc(self.Q)
605 self.assertEqual(output.getvalue(), 'wrote Q.html\n')
606
Ezio Melottibdfa2e62011-04-28 07:59:33 +0300607class TestHelper(unittest.TestCase):
608 def test_keywords(self):
609 self.assertEqual(sorted(pydoc.Helper.keywords),
610 sorted(keyword.kwlist))
611
Éric Araujo9a528302011-07-29 17:34:35 +0200612 def test_builtin(self):
613 for name in ('str', 'str.translate', '__builtin__.str',
614 '__builtin__.str.translate'):
615 # test low-level function
616 self.assertIsNotNone(pydoc.locate(name))
617 # test high-level function
618 try:
619 pydoc.render_doc(name)
620 except ImportError:
Terry Jan Reedy72998182014-06-20 14:59:07 -0400621 self.fail('finding the doc of {!r} failed'.format(name))
Éric Araujo9a528302011-07-29 17:34:35 +0200622
623 for name in ('not__builtin__', 'strrr', 'strr.translate',
624 'str.trrrranslate', '__builtin__.strrr',
625 '__builtin__.str.trrranslate'):
626 self.assertIsNone(pydoc.locate(name))
627 self.assertRaises(ImportError, pydoc.render_doc, name)
628
Ezio Melottibdfa2e62011-04-28 07:59:33 +0300629
Georg Brandl8632cc22008-05-18 16:32:48 +0000630def test_main():
Ned Deily1a96f8d2011-10-06 14:17:34 -0700631 try:
Antoine Pitrouf41ffed2013-05-19 15:44:54 +0200632 test.test_support.run_unittest(PydocDocTest,
Ned Deily1a96f8d2011-10-06 14:17:34 -0700633 PydocImportTest,
634 TestDescriptions,
R David Murray984f6302014-01-05 12:35:59 -0500635 TestUnicode,
Ned Deily1a96f8d2011-10-06 14:17:34 -0700636 TestHelper)
637 finally:
638 reap_children()
Georg Brandl8632cc22008-05-18 16:32:48 +0000639
640if __name__ == "__main__":
641 test_main()