summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xLib/pydoc.py46
-rw-r--r--Lib/test/test_pydoc.py52
-rw-r--r--Misc/NEWS2
3 files changed, 77 insertions, 23 deletions
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index 2e63226..1369e8a 100755
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -225,34 +225,34 @@ def synopsis(filename, cache={}):
mtime = os.stat(filename).st_mtime
lastupdate, result = cache.get(filename, (None, None))
if lastupdate is None or lastupdate < mtime:
- try:
- file = tokenize.open(filename)
- except OSError:
- # module can't be opened, so skip it
- return None
- binary_suffixes = importlib.machinery.BYTECODE_SUFFIXES[:]
- binary_suffixes += importlib.machinery.EXTENSION_SUFFIXES[:]
- if any(filename.endswith(x) for x in binary_suffixes):
- # binary modules have to be imported
- file.close()
- if any(filename.endswith(x) for x in
- importlib.machinery.BYTECODE_SUFFIXES):
- loader = importlib.machinery.SourcelessFileLoader('__temp__',
- filename)
- else:
- loader = importlib.machinery.ExtensionFileLoader('__temp__',
- filename)
+ # Look for binary suffixes first, falling back to source.
+ if filename.endswith(tuple(importlib.machinery.BYTECODE_SUFFIXES)):
+ loader_cls = importlib.machinery.SourcelessFileLoader
+ elif filename.endswith(tuple(importlib.machinery.EXTENSION_SUFFIXES)):
+ loader_cls = importlib.machinery.ExtensionFileLoader
+ else:
+ loader_cls = None
+ # Now handle the choice.
+ if loader_cls is None:
+ # Must be a source file.
+ try:
+ file = tokenize.open(filename)
+ except OSError:
+ # module can't be opened, so skip it
+ return None
+ # text modules can be directly examined
+ with file:
+ result = source_synopsis(file)
+ else:
+ # Must be a binary module, which has to be imported.
+ loader = loader_cls('__temp__', filename)
try:
module = loader.load_module('__temp__')
except:
return None
- result = (module.__doc__ or '').splitlines()[0]
del sys.modules['__temp__']
- else:
- # text modules can be directly examined
- result = source_synopsis(file)
- file.close()
-
+ result = (module.__doc__ or '').splitlines()[0]
+ # Cache the result.
cache[filename] = (mtime, result)
return result
diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py
index cdf28a4..7fe0644 100644
--- a/Lib/test/test_pydoc.py
+++ b/Lib/test/test_pydoc.py
@@ -487,6 +487,13 @@ class PydocDocTest(unittest.TestCase):
synopsis = pydoc.synopsis(TESTFN, {})
self.assertEqual(synopsis, 'line 1: h\xe9')
+ def test_synopsis_sourceless(self):
+ expected = os.__doc__.splitlines()[0]
+ filename = os.__cached__
+ synopsis = pydoc.synopsis(filename)
+
+ self.assertEqual(synopsis, expected)
+
def test_splitdoc_with_description(self):
example_string = "I Am A Doc\n\n\nHere is my description"
self.assertEqual(pydoc.splitdoc(example_string),
@@ -600,6 +607,50 @@ class PydocImportTest(PydocBaseTest):
self.assertEqual(out.getvalue(), '')
self.assertEqual(err.getvalue(), '')
+ def test_modules(self):
+ # See Helper.listmodules().
+ num_header_lines = 2
+ num_module_lines_min = 5 # Playing it safe.
+ num_footer_lines = 3
+ expected = num_header_lines + num_module_lines_min + num_footer_lines
+
+ output = StringIO()
+ helper = pydoc.Helper(output=output)
+ helper('modules')
+ result = output.getvalue().strip()
+ num_lines = len(result.splitlines())
+
+ self.assertGreaterEqual(num_lines, expected)
+
+ def test_modules_search(self):
+ # See Helper.listmodules().
+ expected = 'pydoc - '
+
+ output = StringIO()
+ helper = pydoc.Helper(output=output)
+ with captured_stdout() as help_io:
+ helper('modules pydoc')
+ result = help_io.getvalue()
+
+ self.assertIn(expected, result)
+
+ def test_modules_search_builtin(self):
+ expected = 'gc - '
+
+ output = StringIO()
+ helper = pydoc.Helper(output=output)
+ with captured_stdout() as help_io:
+ helper('modules garbage')
+ result = help_io.getvalue()
+
+ self.assertTrue(result.startswith(expected))
+
+ def test_importfile(self):
+ loaded_pydoc = pydoc.importfile(pydoc.__file__)
+
+ self.assertEqual(loaded_pydoc.__name__, 'pydoc')
+ self.assertEqual(loaded_pydoc.__file__, pydoc.__file__)
+
class TestDescriptions(unittest.TestCase):
@@ -827,6 +878,7 @@ class PydocWithMetaClasses(unittest.TestCase):
print_diffs(expected_text, result)
self.fail("outputs are not equal, see diff above")
+
@reap_threads
def test_main():
try:
diff --git a/Misc/NEWS b/Misc/NEWS
index 3429655..a640295 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -221,6 +221,8 @@ Library
- Issue #19782: imaplib now supports SSLContext.check_hostname and server name
indication for TLS/SSL connections.
+- Issue 20123: Fix pydoc.synopsis() for "binary" modules.
+
- Issue #19834: Support unpickling of exceptions pickled by Python 2.
- Issue #19781: ftplib now supports SSLContext.check_hostname and server name