diff options
Diffstat (limited to 'Lib/test/test_pydoc.py')
-rw-r--r-- | Lib/test/test_pydoc.py | 290 |
1 files changed, 213 insertions, 77 deletions
diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index d95e7069ce1..c5a8e983568 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -1,43 +1,62 @@ import os import sys +import builtins import difflib -import __builtin__ -import re -import pydoc import inspect +import pydoc import keyword +import re +import string +import test.support +import time import unittest import xml.etree -import test.test_support +import textwrap +from io import StringIO from collections import namedtuple from test.script_helper import assert_python_ok -from test.test_support import ( - TESTFN, rmtree, reap_children, captured_stdout) - +from test.support import ( + TESTFN, rmtree, + reap_children, reap_threads, captured_output, captured_stdout, unlink +) from test import pydoc_mod -expected_text_pattern = \ -""" +try: + import threading +except ImportError: + threading = None + +# Just in case sys.modules["test"] has the optional attribute __loader__. +if hasattr(pydoc_mod, "__loader__"): + del pydoc_mod.__loader__ + +expected_text_pattern = """ NAME test.pydoc_mod - This is a test module for test_pydoc - -FILE - %s %s CLASSES - __builtin__.object + builtins.object + A B - A \x20\x20\x20\x20 - class A + class A(builtins.object) | Hello and goodbye |\x20\x20 | Methods defined here: |\x20\x20 | __init__() | Wow, I have no function! + |\x20\x20 + | ---------------------------------------------------------------------- + | Data descriptors defined here: + |\x20\x20 + | __dict__ + | dictionary for instance variables (if defined) + |\x20\x20 + | __weakref__ + | list of weak references to the object (if defined) \x20\x20\x20\x20 - class B(__builtin__.object) + class B(builtins.object) | Data descriptors defined here: |\x20\x20 | __dict__ @@ -61,9 +80,7 @@ FUNCTIONS nodoc_func() DATA - __author__ = 'Benjamin Peterson' - __credits__ = 'Nobody' - __version__ = '1.2.3.4' + __xyz__ = 'X, Y and Z' VERSION 1.2.3.4 @@ -73,10 +90,12 @@ AUTHOR CREDITS Nobody + +FILE + %s """.strip() -expected_html_pattern = \ -""" +expected_html_pattern = """ <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading"> <tr bgcolor="#7799ee"> <td valign=bottom> <br> @@ -92,19 +111,19 @@ expected_html_pattern = \ \x20\x20\x20\x20 <tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td> <td width="100%%"><dl> -<dt><font face="helvetica, arial"><a href="__builtin__.html#object">__builtin__.object</a> +<dt><font face="helvetica, arial"><a href="builtins.html#object">builtins.object</a> </font></dt><dd> <dl> -<dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#B">B</a> -</font></dt></dl> -</dd> <dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#A">A</a> +</font></dt><dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#B">B</a> </font></dt></dl> +</dd> +</dl> <p> <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> <tr bgcolor="#ffc8d8"> <td colspan=3 valign=bottom> <br> -<font color="#000000" face="helvetica, arial"><a name="A">class <strong>A</strong></a></font></td></tr> +<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> \x20\x20\x20\x20 <tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> <td colspan=2><tt>Hello and goodbye<br> </tt></td></tr> @@ -112,11 +131,19 @@ expected_html_pattern = \ <td width="100%%">Methods defined here:<br> <dl><dt><a name="A-__init__"><strong>__init__</strong></a>()</dt><dd><tt>Wow, I have no function!</tt></dd></dl> +<hr> +Data descriptors defined here:<br> +<dl><dt><strong>__dict__</strong></dt> +<dd><tt>dictionary for instance variables (if defined)</tt></dd> +</dl> +<dl><dt><strong>__weakref__</strong></dt> +<dd><tt>list of weak references to the object (if defined)</tt></dd> +</dl> </td></tr></table> <p> <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> <tr bgcolor="#ffc8d8"> <td colspan=3 valign=bottom> <br> -<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> +<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> \x20\x20\x20\x20 <tr><td bgcolor="#ffc8d8"><tt> </tt></td><td> </td> <td width="100%%">Data descriptors defined here:<br> @@ -149,9 +176,7 @@ war</tt></dd></dl> <font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr> \x20\x20\x20\x20 <tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td> -<td width="100%%"><strong>__author__</strong> = 'Benjamin Peterson'<br> -<strong>__credits__</strong> = 'Nobody'<br> -<strong>__version__</strong> = '1.2.3.4'</td></tr></table><p> +<td width="100%%"><strong>__xyz__</strong> = 'X, Y and Z'</td></tr></table><p> <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> <tr bgcolor="#7799ee"> <td colspan=3 valign=bottom> <br> @@ -166,14 +191,14 @@ war</tt></dd></dl> \x20\x20\x20\x20 <tr><td bgcolor="#7799ee"><tt> </tt></td><td> </td> <td width="100%%">Nobody</td></tr></table> -""".strip() +""".strip() # ' <- emacs turd # output pattern for missing module missing_pattern = "no Python documentation found for '%s'" # output pattern for module with bad imports -badimport_pattern = "problem in %s - <type 'exceptions.ImportError'>: No module named %s" +badimport_pattern = "problem in %s - ImportError: No module named %s" def run_pydoc(module_name, *args, **env): """ @@ -203,21 +228,29 @@ def get_pydoc_text(module): output = doc.docmodule(module) - # cleanup the extra text formatting that pydoc preforms + # clean up the extra text formatting that pydoc performs patt = re.compile('\b.') output = patt.sub('', output) return output.strip(), loc def print_diffs(text1, text2): "Prints unified diffs for two texts" + # XXX now obsolete, use unittest built-in support lines1 = text1.splitlines(True) lines2 = text2.splitlines(True) diffs = difflib.unified_diff(lines1, lines2, n=0, fromfile='expected', tofile='got') - print '\n' + ''.join(diffs) + print('\n' + ''.join(diffs)) + +def get_html_title(text): + # Bit of hack, but good enough for test purposes + header, _, _ = text.partition("</head>") + _, _, title = header.partition("<title>") + title, _, _ = title.partition("</title>") + return title -class PyDocDocTest(unittest.TestCase): +class PydocDocTest(unittest.TestCase): @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -239,7 +272,7 @@ class PyDocDocTest(unittest.TestCase): def test_text_doc(self): result, doc_loc = get_pydoc_text(pydoc_mod) expected_text = expected_text_pattern % \ - (inspect.getabsfile(pydoc_mod), doc_loc) + (doc_loc, inspect.getabsfile(pydoc_mod)) if result != expected_text: print_diffs(expected_text, result) self.fail("outputs are not equal, see diff above") @@ -262,18 +295,16 @@ class PyDocDocTest(unittest.TestCase): def test_not_here(self): missing_module = "test.i_am_not_here" - result = run_pydoc(missing_module) + result = str(run_pydoc(missing_module), 'ascii') expected = missing_pattern % missing_module self.assertEqual(expected, result, "documentation for missing module found") def test_input_strip(self): missing_module = " test.i_am_not_here " - result = run_pydoc(missing_module) + result = str(run_pydoc(missing_module), 'ascii') expected = missing_pattern % missing_module.strip() - self.assertEqual(expected, result, - "white space was not stripped from module name " - "or other error output mismatch") + self.assertEqual(expected, result) def test_stripid(self): # test with strings, other implementations might have different repr() @@ -288,6 +319,61 @@ class PyDocDocTest(unittest.TestCase): self.assertEqual(stripid("<type 'exceptions.Exception'>"), "<type 'exceptions.Exception'>") + @unittest.skipIf(sys.flags.optimize >= 2, + 'Docstrings are omitted with -O2 and above') + def test_help_output_redirect(self): + # issue 940286, if output is set in Helper, then all output from + # Helper.help should be redirected + old_pattern = expected_text_pattern + getpager_old = pydoc.getpager + getpager_new = lambda: (lambda x: x) + self.maxDiff = None + + buf = StringIO() + helper = pydoc.Helper(output=buf) + unused, doc_loc = get_pydoc_text(pydoc_mod) + module = "test.pydoc_mod" + help_header = """ + Help on module test.pydoc_mod in test: + + """.lstrip() + help_header = textwrap.dedent(help_header) + expected_help_pattern = help_header + expected_text_pattern + + pydoc.getpager = getpager_new + try: + with captured_output('stdout') as output, \ + captured_output('stderr') as err: + helper.help(module) + result = buf.getvalue().strip() + expected_text = expected_help_pattern % \ + (doc_loc, inspect.getabsfile(pydoc_mod)) + self.assertEqual('', output.getvalue()) + self.assertEqual('', err.getvalue()) + self.assertEqual(expected_text, result) + finally: + pydoc.getpager = getpager_old + + def test_namedtuple_public_underscore(self): + NT = namedtuple('NT', ['abc', 'def'], rename=True) + with captured_stdout() as help_io: + help(NT) + helptext = help_io.getvalue() + self.assertIn('_1', helptext) + self.assertIn('_replace', helptext) + self.assertIn('_asdict', helptext) + + def test_synopsis(self): + self.addCleanup(unlink, TESTFN) + for encoding in ('ISO-8859-1', 'UTF-8'): + with open(TESTFN, 'w', encoding=encoding) as script: + if encoding != 'UTF-8': + print('#coding: {}'.format(encoding), file=script) + print('"""line 1: h\xe9', file=script) + print('line 2: hi"""', file=script) + synopsis = pydoc.synopsis(TESTFN, {}) + self.assertEqual(synopsis, 'line 1: h\xe9') + class PydocImportTest(unittest.TestCase): @@ -313,7 +399,7 @@ class PydocImportTest(unittest.TestCase): for importstring, expectedinmsg in testpairs: with open(sourcefn, 'w') as f: f.write("import {}\n".format(importstring)) - result = run_pydoc(modname, PYTHONPATH=TESTFN) + result = run_pydoc(modname, PYTHONPATH=TESTFN).decode("ascii") expected = badimport_pattern % (modname, expectedinmsg) self.assertEqual(expected, result) @@ -325,7 +411,7 @@ class PydocImportTest(unittest.TestCase): with open(badsyntax, 'w') as f: f.write("invalid python syntax = $1\n") result = run_pydoc('zqwykjv', '-k', PYTHONPATH=TESTFN) - self.assertEqual('', result) + self.assertEqual(b'', result) def test_apropos_with_unreadable_dir(self): # Issue 7367 - pydoc -k failed when unreadable dir on path @@ -335,7 +421,7 @@ class PydocImportTest(unittest.TestCase): # Note, on Windows the directory appears to be still # readable so this is not really testing the issue there result = run_pydoc('zqwykjv', '-k', PYTHONPATH=TESTFN) - self.assertEqual('', result) + self.assertEqual(b'', result) class TestDescriptions(unittest.TestCase): @@ -346,16 +432,8 @@ class TestDescriptions(unittest.TestCase): doc = pydoc.render_doc(pydocfodder) self.assertIn("pydocfodder", doc) - def test_classic_class(self): - class C: "Classic class" - c = C() - self.assertEqual(pydoc.describe(C), 'class C') - self.assertEqual(pydoc.describe(c), 'instance of C') - expected = 'instance of C in module %s' % __name__ - self.assertIn(expected, pydoc.render_doc(c)) - def test_class(self): - class C(object): "New-style class" + class C: "New-style class" c = C() self.assertEqual(pydoc.describe(C), 'class C') @@ -363,24 +441,9 @@ class TestDescriptions(unittest.TestCase): expected = 'C in module %s object' % __name__ self.assertIn(expected, pydoc.render_doc(c)) - def test_namedtuple_public_underscore(self): - NT = namedtuple('NT', ['abc', 'def'], rename=True) - with captured_stdout() as help_io: - help(NT) - helptext = help_io.getvalue() - self.assertIn('_1', helptext) - self.assertIn('_replace', helptext) - self.assertIn('_asdict', helptext) - - -class TestHelper(unittest.TestCase): - def test_keywords(self): - self.assertEqual(sorted(pydoc.Helper.keywords), - sorted(keyword.kwlist)) - def test_builtin(self): - for name in ('str', 'str.translate', '__builtin__.str', - '__builtin__.str.translate'): + for name in ('str', 'str.translate', 'builtins.str', + 'builtins.str.translate'): # test low-level function self.assertIsNotNone(pydoc.locate(name)) # test high-level function @@ -389,19 +452,92 @@ class TestHelper(unittest.TestCase): except ImportError: self.fail('finding the doc of {!r} failed'.format(o)) - for name in ('not__builtin__', 'strrr', 'strr.translate', - 'str.trrrranslate', '__builtin__.strrr', - '__builtin__.str.trrranslate'): + for name in ('notbuiltins', 'strrr', 'strr.translate', + 'str.trrrranslate', 'builtins.strrr', + 'builtins.str.trrranslate'): self.assertIsNone(pydoc.locate(name)) self.assertRaises(ImportError, pydoc.render_doc, name) +@unittest.skipUnless(threading, 'Threading required for this test.') +class PydocServerTest(unittest.TestCase): + """Tests for pydoc._start_server""" + + def test_server(self): + + # Minimal test that starts the server, then stops it. + def my_url_handler(url, content_type): + text = 'the URL sent was: (%s, %s)' % (url, content_type) + return text + + serverthread = pydoc._start_server(my_url_handler, port=0) + starttime = time.time() + timeout = 1 #seconds + + while serverthread.serving: + time.sleep(.01) + if serverthread.serving and time.time() - starttime > timeout: + serverthread.stop() + break + + self.assertEqual(serverthread.error, None) + + +class PydocUrlHandlerTest(unittest.TestCase): + """Tests for pydoc._url_handler""" + + def test_content_type_err(self): + f = pydoc._url_handler + self.assertRaises(TypeError, f, 'A', '') + self.assertRaises(TypeError, f, 'B', 'foobar') + + def test_url_requests(self): + # Test for the correct title in the html pages returned. + # This tests the different parts of the URL handler without + # getting too picky about the exact html. + requests = [ + ("", "Pydoc: Index of Modules"), + ("get?key=", "Pydoc: Index of Modules"), + ("index", "Pydoc: Index of Modules"), + ("topics", "Pydoc: Topics"), + ("keywords", "Pydoc: Keywords"), + ("pydoc", "Pydoc: module pydoc"), + ("get?key=pydoc", "Pydoc: module pydoc"), + ("search?key=pydoc", "Pydoc: Search Results"), + ("topic?key=def", "Pydoc: KEYWORD def"), + ("topic?key=STRINGS", "Pydoc: TOPIC STRINGS"), + ("foobar", "Pydoc: Error - foobar"), + ("getfile?key=foobar", "Pydoc: Error - getfile?key=foobar"), + ] + + for url, title in requests: + text = pydoc._url_handler(url, "text/html") + result = get_html_title(text) + self.assertEqual(result, title) + + path = string.__file__ + title = "Pydoc: getfile " + path + url = "getfile?key=" + path + text = pydoc._url_handler(url, "text/html") + result = get_html_title(text) + self.assertEqual(result, title) + + +class TestHelper(unittest.TestCase): + def test_keywords(self): + self.assertEqual(sorted(pydoc.Helper.keywords), + sorted(keyword.kwlist)) + +@reap_threads def test_main(): try: - test.test_support.run_unittest(PyDocDocTest, - PydocImportTest, - TestDescriptions, - TestHelper) + test.support.run_unittest(PydocDocTest, + PydocImportTest, + TestDescriptions, + PydocServerTest, + PydocUrlHandlerTest, + TestHelper, + ) finally: reap_children() |