Skip to content

Commit 46ef6e6

Browse files
committed
bpo-40051: Fix doc links in module help() output
An explicit mapping of module names to documentation names is added to pydoc_data.topics and is used by pydoc's `getdocloc` function to generate more-correct URLs in more cases without special-casing particular modules. In some cases (tkinter.simpledialog, lib2to3, etc.) this corrects the displayed link to give a real page and in other cases (private or undocumented modules) removes what was previously an incorrect link entirely.
1 parent fd41125 commit 46ef6e6

File tree

5 files changed

+415
-51
lines changed

5 files changed

+415
-51
lines changed

Doc/tools/extensions/pyspecific.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111

1212
import re
1313
import io
14+
from functools import partial
1415
from os import getenv, path
15-
from time import asctime
1616
from pprint import pformat
17+
from time import asctime
18+
19+
from docutils import nodes, utils
1720
from docutils.io import StringOutput
1821
from docutils.parsers.rst import Directive
1922
from docutils.utils import new_document
20-
21-
from docutils import nodes, utils
22-
2323
from sphinx import addnodes
2424
from sphinx.builders import Builder
2525
try:
@@ -30,7 +30,6 @@
3030
from sphinx.util import status_iterator, logging
3131
from sphinx.util.nodes import split_explicit_title
3232
from sphinx.writers.text import TextWriter, TextTranslator
33-
from sphinx.writers.latex import LaTeXTranslator
3433

3534
try:
3635
from sphinx.domains.python import PyFunction, PyMethod
@@ -470,6 +469,7 @@ class PydocTopicsBuilder(Builder):
470469

471470
def init(self):
472471
self.topics = {}
472+
self.modules = {}
473473
self.secnumbers = {}
474474

475475
def get_outdated_docs(self):
@@ -479,6 +479,11 @@ def get_target_uri(self, docname, typ=None):
479479
return '' # no URIs
480480

481481
def write(self, *ignored):
482+
modules = self.env.domaindata['py']['modules']
483+
for module in status_iterator(modules,
484+
'extracting documented modules... ',
485+
length=len(modules)):
486+
self.modules[module] = modules[module].docname
482487
writer = TextWriter(self)
483488
for label in status_iterator(pydoc_topic_labels,
484489
'building topics... ',
@@ -495,13 +500,13 @@ def write(self, *ignored):
495500
self.topics[label] = writer.output
496501

497502
def finish(self):
498-
f = open(path.join(self.outdir, 'topics.py'), 'wb')
499-
try:
500-
f.write('# -*- coding: utf-8 -*-\n'.encode('utf-8'))
501-
f.write(('# Autogenerated by Sphinx on %s\n' % asctime()).encode('utf-8'))
502-
f.write(('topics = ' + pformat(self.topics) + '\n').encode('utf-8'))
503-
finally:
504-
f.close()
503+
fn = path.join(self.outdir, 'topics.py')
504+
with open(fn, 'w', encoding='utf-8') as f:
505+
write = partial(print, file=f)
506+
write('# -*- coding: utf-8 -*-')
507+
write('# Autogenerated by Sphinx on', asctime())
508+
write('topics =', pformat(self.topics))
509+
write('modules =', pformat(self.modules))
505510

506511

507512
# Support for documenting Opcodes

Lib/pydoc.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ def safeimport(path, forceload=0, cache={}):
458458
class Doc:
459459

460460
PYTHONDOCS = os.environ.get("PYTHONDOCS",
461-
"https://docs.python.org/%d.%d/library"
461+
"https://docs.python.org/%d.%d"
462462
% sys.version_info[:2])
463463

464464
def document(self, object, name=None, *args):
@@ -488,28 +488,33 @@ def fail(self, object, name=None, *args):
488488
def getdocloc(self, object, basedir=sysconfig.get_path('stdlib')):
489489
"""Return the location of module docs or None"""
490490

491+
if not inspect.ismodule(object):
492+
return None
493+
494+
try:
495+
from pydoc_data.topics import modules
496+
except ImportError:
497+
return None
498+
499+
if object.__name__ not in modules:
500+
return None
501+
491502
try:
492503
file = inspect.getabsfile(object)
493504
except TypeError:
494505
file = '(built-in)'
495506

507+
if 'site-packages' in file:
508+
# XXX: Maybe we could add a hook for including a documentation URL?
509+
return None
510+
496511
docloc = os.environ.get("PYTHONDOCS", self.PYTHONDOCS)
512+
html_file = modules[object.__name__] + '.html'
513+
514+
if docloc.startswith('http'):
515+
return f'{docloc.rstrip("/")}/{html_file}'
516+
return os.path.join(docloc, html_file)
497517

498-
basedir = os.path.normcase(basedir)
499-
if (isinstance(object, type(os)) and
500-
(object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
501-
'marshal', 'posix', 'signal', 'sys',
502-
'_thread', 'zipimport') or
503-
(file.startswith(basedir) and
504-
not file.startswith(os.path.join(basedir, 'site-packages')))) and
505-
object.__name__ not in ('xml.etree', 'test.pydoc_mod')):
506-
if docloc.startswith(("http://", "https://")):
507-
docloc = "{}/{}.html".format(docloc.rstrip("/"), object.__name__.lower())
508-
else:
509-
docloc = os.path.join(docloc, object.__name__.lower() + ".html")
510-
else:
511-
docloc = None
512-
return docloc
513518

514519
# -------------------------------------------- HTML documentation generator
515520

0 commit comments

Comments
 (0)