diff -r ab6f520f3637 Lib/pydoc.py --- a/Lib/pydoc.py Mon Sep 21 01:11:26 2015 -0400 +++ b/Lib/pydoc.py Mon Sep 21 16:21:35 2015 +0300 @@ -660,7 +660,11 @@ class HTMLDoc(Doc): head = '%s' % linkedname try: path = inspect.getabsfile(object) - url = urllib.parse.quote(path) + encodedpath = os.fsencode(path) + if os.fsdecode(encodedpath) == path: + url = urllib.parse.quote_from_bytes(encodedpath) + else: + url = urllib.parse.quote(path, errors='surrogatepass') filelink = self.filelink(url, path) except TypeError: filelink = '(built-in)' diff -r ab6f520f3637 Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py Mon Sep 21 01:11:26 2015 -0400 +++ b/Lib/test/test_pydoc.py Mon Sep 21 16:21:35 2015 +0300 @@ -10,6 +10,7 @@ import keyword import _pickle import pkgutil import re +import shutil import stat import string import test.support @@ -23,9 +24,9 @@ from io import StringIO from collections import namedtuple from test.support.script_helper import assert_python_ok from test.support import ( - TESTFN, rmtree, + TESTFN, TESTFN_UNDECODABLE, TESTFN_UNENCODABLE, rmtree, reap_children, reap_threads, captured_output, captured_stdout, - captured_stderr, unlink, requires_docstrings + captured_stderr, unlink, requires_docstrings, temp_dir, swap_item ) from test import pydoc_mod @@ -347,7 +348,7 @@ def get_pydoc_html(module): "Returns pydoc generated output as html" doc = pydoc.HTMLDoc() output = doc.docmodule(module) - loc = doc.getdocloc(pydoc_mod) or "" + loc = doc.getdocloc(module) or "" if loc: loc = "
Module Docs" return output.strip(), loc @@ -355,7 +356,7 @@ def get_pydoc_html(module): def get_pydoc_text(module): "Returns pydoc generated output as text" doc = pydoc.TextDoc() - loc = doc.getdocloc(pydoc_mod) or "" + loc = doc.getdocloc(module) or "" if loc: loc = "\nMODULE DOCS\n " + loc + "\n" @@ -413,6 +414,59 @@ class PydocDocTest(unittest.TestCase): expected_html_data_docstrings) self.assertEqual(result, expected_html) + def load_mod(self, name, path): + loader = importlib._bootstrap_external.SourceFileLoader(name, path) + spec = importlib.util.spec_from_file_location(name, path, loader=loader) + return importlib._bootstrap._load(spec) + + @unittest.skipUnless(TESTFN_UNDECODABLE, + "needed undecodable filename") + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), + 'trace function introduces __locals__ unexpectedly') + @requires_docstrings + def test_html_doc_undecodable_path(self): + with swap_item(sys.modules, pydoc_mod.__name__, None), \ + temp_dir(TESTFN_UNDECODABLE): + path = os.path.join(TESTFN_UNDECODABLE, b'pydoc_mod.py') + shutil.copyfile(pydoc_mod.__file__, path) + path = os.path.abspath(os.fsdecode(path)) + mod = self.load_mod(pydoc_mod.__name__, path) + result, doc_loc = get_pydoc_html(mod) + mod_file = inspect.getabsfile(mod) + mod_url = urllib.parse.quote_from_bytes(os.fsencode(mod_file)) + expected_html = expected_html_pattern % ( + (mod_url, mod_file, doc_loc) + + expected_html_data_docstrings) + self.assertEqual(result, expected_html) + + @unittest.skipUnless(TESTFN_UNENCODABLE, + "needed unencodable filename") + @unittest.skipIf(sys.flags.optimize >= 2, + "Docstrings are omitted with -O2 and above") + @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), + 'trace function introduces __locals__ unexpectedly') + @requires_docstrings + def test_html_doc_unencodable_path(self): + with swap_item(sys.modules, pydoc_mod.__name__, None), \ + temp_dir(TESTFN_UNENCODABLE): + path = os.path.join(TESTFN_UNENCODABLE, 'pydoc_mod.py') + shutil.copyfile(pydoc_mod.__file__, path) + path = os.path.abspath(path) + mod = self.load_mod(pydoc_mod.__name__, path) + result, doc_loc = get_pydoc_html(mod) + mod_file = inspect.getabsfile(mod) + encodedpath = os.fsencode(mod_file) + if os.fsdecode(encodedpath) == mod_file: + mod_url = urllib.parse.quote_from_bytes(encodedpath) + else: + mod_url = urllib.parse.quote(mod_file, errors='surrogatepass') + expected_html = expected_html_pattern % ( + (mod_url, mod_file, doc_loc) + + expected_html_data_docstrings) + self.assertEqual(result, expected_html) + @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), @@ -425,6 +479,7 @@ class PydocDocTest(unittest.TestCase): expected_text_data_docstrings + (inspect.getabsfile(pydoc_mod),)) self.assertEqual(expected_text, result) + maxDiff=99990 def test_text_enum_member_with_value_zero(self): # Test issue #20654 to ensure enum member with value 0 can be