aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/test/test_pydoc.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_pydoc.py')
-rw-r--r--Lib/test/test_pydoc.py290
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>&nbsp;<br>
@@ -92,19 +111,19 @@ expected_html_pattern = \
\x20\x20\x20\x20
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</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>&nbsp;<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>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Hello&nbsp;and&nbsp;goodbye<br>&nbsp;</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,&nbsp;I&nbsp;have&nbsp;no&nbsp;function!</tt></dd></dl>
+<hr>
+Data descriptors defined here:<br>
+<dl><dt><strong>__dict__</strong></dt>
+<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
+</dl>
+<dl><dt><strong>__weakref__</strong></dt>
+<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;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>&nbsp;<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>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</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>&nbsp;<br>
@@ -166,14 +191,14 @@ war</tt></dd></dl>
\x20\x20\x20\x20
<tr><td bgcolor="#7799ee"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</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()