blob: 5bb97e245fb45249a6e2b80079a0bb689a0e51a2 [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
402 def test_synopsis_sourceless_empty_doc(self):
403 with test.test_support.temp_cwd() as test_dir:
404 init_path = os.path.join(test_dir, 'foomod42.py')
405 cached_path = os.path.join(test_dir, 'foomod42.pyc')
406 with open(init_path, 'w') as fobj:
407 fobj.write("foo = 1")
408 py_compile.compile(init_path)
409 synopsis = pydoc.synopsis(init_path, {})
410 self.assertIsNone(synopsis)
411 synopsis_cached = pydoc.synopsis(cached_path, {})
412 self.assertIsNone(synopsis_cached)
413
Georg Brandl8632cc22008-05-18 16:32:48 +0000414
Antoine Pitrouf41ffed2013-05-19 15:44:54 +0200415class PydocImportTest(PydocBaseTest):
Ned Deily1a96f8d2011-10-06 14:17:34 -0700416
417 def setUp(self):
418 self.test_dir = os.mkdir(TESTFN)
419 self.addCleanup(rmtree, TESTFN)
420
421 def test_badimport(self):
422 # This tests the fix for issue 5230, where if pydoc found the module
423 # but the module had an internal import error pydoc would report no doc
424 # found.
425 modname = 'testmod_xyzzy'
426 testpairs = (
427 ('i_am_not_here', 'i_am_not_here'),
428 ('test.i_am_not_here_either', 'i_am_not_here_either'),
429 ('test.i_am_not_here.neither_am_i', 'i_am_not_here.neither_am_i'),
430 ('i_am_not_here.{}'.format(modname),
431 'i_am_not_here.{}'.format(modname)),
432 ('test.{}'.format(modname), modname),
433 )
434
435 sourcefn = os.path.join(TESTFN, modname) + os.extsep + "py"
436 for importstring, expectedinmsg in testpairs:
437 with open(sourcefn, 'w') as f:
438 f.write("import {}\n".format(importstring))
439 result = run_pydoc(modname, PYTHONPATH=TESTFN)
440 expected = badimport_pattern % (modname, expectedinmsg)
441 self.assertEqual(expected, result)
442
443 def test_apropos_with_bad_package(self):
444 # Issue 7425 - pydoc -k failed when bad package on path
445 pkgdir = os.path.join(TESTFN, "syntaxerr")
446 os.mkdir(pkgdir)
447 badsyntax = os.path.join(pkgdir, "__init__") + os.extsep + "py"
448 with open(badsyntax, 'w') as f:
449 f.write("invalid python syntax = $1\n")
Antoine Pitrouf41ffed2013-05-19 15:44:54 +0200450 with self.restrict_walk_packages(path=[TESTFN]):
451 with captured_stdout() as out:
452 with captured_stderr() as err:
453 pydoc.apropos('xyzzy')
454 # No result, no error
455 self.assertEqual(out.getvalue(), '')
456 self.assertEqual(err.getvalue(), '')
457 # The package name is still matched
458 with captured_stdout() as out:
459 with captured_stderr() as err:
460 pydoc.apropos('syntaxerr')
461 self.assertEqual(out.getvalue().strip(), 'syntaxerr')
462 self.assertEqual(err.getvalue(), '')
Ned Deily1a96f8d2011-10-06 14:17:34 -0700463
464 def test_apropos_with_unreadable_dir(self):
465 # Issue 7367 - pydoc -k failed when unreadable dir on path
466 self.unreadable_dir = os.path.join(TESTFN, "unreadable")
467 os.mkdir(self.unreadable_dir, 0)
468 self.addCleanup(os.rmdir, self.unreadable_dir)
469 # Note, on Windows the directory appears to be still
470 # readable so this is not really testing the issue there
Antoine Pitrouf41ffed2013-05-19 15:44:54 +0200471 with self.restrict_walk_packages(path=[TESTFN]):
472 with captured_stdout() as out:
473 with captured_stderr() as err:
474 pydoc.apropos('SOMEKEY')
475 # No result, no error
476 self.assertEqual(out.getvalue(), '')
477 self.assertEqual(err.getvalue(), '')
Ned Deily1a96f8d2011-10-06 14:17:34 -0700478
479
Georg Brandl8632cc22008-05-18 16:32:48 +0000480class TestDescriptions(unittest.TestCase):
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000481
Georg Brandl8632cc22008-05-18 16:32:48 +0000482 def test_module(self):
483 # Check that pydocfodder module can be described
484 from test import pydocfodder
485 doc = pydoc.render_doc(pydocfodder)
Ezio Melottiaa980582010-01-23 23:04:36 +0000486 self.assertIn("pydocfodder", doc)
Georg Brandl8632cc22008-05-18 16:32:48 +0000487
488 def test_classic_class(self):
489 class C: "Classic class"
490 c = C()
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000491 self.assertEqual(pydoc.describe(C), 'class C')
492 self.assertEqual(pydoc.describe(c), 'instance of C')
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000493 expected = 'instance of C in module %s' % __name__
Ezio Melottiaa980582010-01-23 23:04:36 +0000494 self.assertIn(expected, pydoc.render_doc(c))
Georg Brandl8632cc22008-05-18 16:32:48 +0000495
496 def test_class(self):
497 class C(object): "New-style class"
498 c = C()
499
Benjamin Petersonf5c38da2008-05-18 20:48:07 +0000500 self.assertEqual(pydoc.describe(C), 'class C')
501 self.assertEqual(pydoc.describe(c), 'C')
Benjamin Peterson3a7305e2008-05-22 23:09:26 +0000502 expected = 'C in module %s object' % __name__
Ezio Melottiaa980582010-01-23 23:04:36 +0000503 self.assertIn(expected, pydoc.render_doc(c))
Georg Brandl8632cc22008-05-18 16:32:48 +0000504
Raymond Hettinger9aa5a342011-03-25 16:00:13 -0700505 def test_namedtuple_public_underscore(self):
506 NT = namedtuple('NT', ['abc', 'def'], rename=True)
507 with captured_stdout() as help_io:
Terry Jan Reedyc0e60472013-11-04 21:45:33 -0500508 pydoc.help(NT)
Raymond Hettinger9aa5a342011-03-25 16:00:13 -0700509 helptext = help_io.getvalue()
510 self.assertIn('_1', helptext)
511 self.assertIn('_replace', helptext)
512 self.assertIn('_asdict', helptext)
513
Georg Brandl8632cc22008-05-18 16:32:48 +0000514
R David Murray984f6302014-01-05 12:35:59 -0500515@unittest.skipUnless(test.test_support.have_unicode,
516 "test requires unicode support")
517class TestUnicode(unittest.TestCase):
518
519 def setUp(self):
520 # Better not to use unicode escapes in literals, lest the
521 # parser choke on it if Python has been built without
522 # unicode support.
523 self.Q = types.ModuleType(
524 'Q', 'Rational numbers: \xe2\x84\x9a'.decode('utf8'))
525 self.Q.__version__ = '\xe2\x84\x9a'.decode('utf8')
526 self.Q.__date__ = '\xe2\x84\x9a'.decode('utf8')
527 self.Q.__author__ = '\xe2\x84\x9a'.decode('utf8')
528 self.Q.__credits__ = '\xe2\x84\x9a'.decode('utf8')
529
530 self.assertIsInstance(self.Q.__doc__, unicode)
531
532 def test_render_doc(self):
533 # render_doc is robust against unicode in docstrings
534 doc = pydoc.render_doc(self.Q)
535 self.assertIsInstance(doc, str)
536
537 def test_encode(self):
538 # _encode is robust against characters out the specified encoding
539 self.assertEqual(pydoc._encode(self.Q.__doc__, 'ascii'), 'Rational numbers: &#8474;')
540
541 def test_pipepager(self):
542 # pipepager does not choke on unicode
543 doc = pydoc.render_doc(self.Q)
544
545 saved, os.popen = os.popen, open
546 try:
547 with test.test_support.temp_cwd():
548 pydoc.pipepager(doc, 'pipe')
549 self.assertEqual(open('pipe').read(), pydoc._encode(doc))
550 finally:
551 os.popen = saved
552
553 def test_tempfilepager(self):
554 # tempfilepager does not choke on unicode
555 doc = pydoc.render_doc(self.Q)
556
557 output = {}
558 def mock_system(cmd):
Serhiy Storchakaee105dc2014-01-10 22:43:03 +0200559 filename = cmd.strip()[1:-1]
560 self.assertEqual('"' + filename + '"', cmd.strip())
561 output['content'] = open(filename).read()
R David Murray984f6302014-01-05 12:35:59 -0500562 saved, os.system = os.system, mock_system
563 try:
564 pydoc.tempfilepager(doc, '')
565 self.assertEqual(output['content'], pydoc._encode(doc))
566 finally:
567 os.system = saved
568
569 def test_plainpager(self):
570 # plainpager does not choke on unicode
571 doc = pydoc.render_doc(self.Q)
572
573 # Note: captured_stdout is too permissive when it comes to
574 # unicode, and using it here would make the test always
575 # pass.
576 with test.test_support.temp_cwd():
577 with open('output', 'w') as f:
578 saved, sys.stdout = sys.stdout, f
579 try:
580 pydoc.plainpager(doc)
581 finally:
582 sys.stdout = saved
583 self.assertIn('Rational numbers:', open('output').read())
584
585 def test_ttypager(self):
586 # ttypager does not choke on unicode
587 doc = pydoc.render_doc(self.Q)
588 # Test ttypager
589 with test.test_support.temp_cwd(), test.test_support.captured_stdin():
590 with open('output', 'w') as f:
591 saved, sys.stdout = sys.stdout, f
592 try:
593 pydoc.ttypager(doc)
594 finally:
595 sys.stdout = saved
596 self.assertIn('Rational numbers:', open('output').read())
597
598 def test_htmlpage(self):
599 # html.page does not choke on unicode
600 with test.test_support.temp_cwd():
601 with captured_stdout() as output:
602 pydoc.writedoc(self.Q)
603 self.assertEqual(output.getvalue(), 'wrote Q.html\n')
604
Ezio Melottibdfa2e62011-04-28 07:59:33 +0300605class TestHelper(unittest.TestCase):
606 def test_keywords(self):
607 self.assertEqual(sorted(pydoc.Helper.keywords),
608 sorted(keyword.kwlist))
609
Éric Araujo9a528302011-07-29 17:34:35 +0200610 def test_builtin(self):
611 for name in ('str', 'str.translate', '__builtin__.str',
612 '__builtin__.str.translate'):
613 # test low-level function
614 self.assertIsNotNone(pydoc.locate(name))
615 # test high-level function
616 try:
617 pydoc.render_doc(name)
618 except ImportError:
Terry Jan Reedy72998182014-06-20 14:59:07 -0400619 self.fail('finding the doc of {!r} failed'.format(name))
Éric Araujo9a528302011-07-29 17:34:35 +0200620
621 for name in ('not__builtin__', 'strrr', 'strr.translate',
622 'str.trrrranslate', '__builtin__.strrr',
623 '__builtin__.str.trrranslate'):
624 self.assertIsNone(pydoc.locate(name))
625 self.assertRaises(ImportError, pydoc.render_doc, name)
626
Ezio Melottibdfa2e62011-04-28 07:59:33 +0300627
Georg Brandl8632cc22008-05-18 16:32:48 +0000628def test_main():
Ned Deily1a96f8d2011-10-06 14:17:34 -0700629 try:
Antoine Pitrouf41ffed2013-05-19 15:44:54 +0200630 test.test_support.run_unittest(PydocDocTest,
Ned Deily1a96f8d2011-10-06 14:17:34 -0700631 PydocImportTest,
632 TestDescriptions,
R David Murray984f6302014-01-05 12:35:59 -0500633 TestUnicode,
Ned Deily1a96f8d2011-10-06 14:17:34 -0700634 TestHelper)
635 finally:
636 reap_children()
Georg Brandl8632cc22008-05-18 16:32:48 +0000637
638if __name__ == "__main__":
639 test_main()