Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 67 additions & 45 deletions Lib/pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class or function within a module or module in a package. If the
import warnings
from annotationlib import Format
from collections import deque
from html import escape as html_escape
from reprlib import Repr
from traceback import format_exception_only

Expand Down Expand Up @@ -573,26 +574,26 @@ def repr1(self, x, level):
methodname = 'repr_' + '_'.join(type(x).__name__.split())
if hasattr(self, methodname):
return getattr(self, methodname)(x, level)
return self.escape(cram(stripid(repr(x)), self.maxother))
return html_escape(cram(stripid(repr(x)), self.maxother))

def repr_string(self, x, level):
test = cram(x, self.maxstring)
testrepr = repr(test)
if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
# Backslashes are only literal in the string and are never
# needed to make any special characters, so show a raw string.
return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
return 'r' + testrepr[0] + html_escape(test) + testrepr[0]
return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
r'<span class="repr">\1</span>',
self.escape(testrepr))
html_escape(testrepr, quote=False))

repr_str = repr_string

def repr_instance(self, x, level):
try:
return self.escape(cram(stripid(repr(x)), self.maxstring))
return html_escape(cram(stripid(repr(x)), self.maxstring))
except:
return self.escape('<%s instance>' % x.__class__.__name__)
return html_escape('<%s instance>' % x.__class__.__name__)

repr_unicode = repr_string

Expand Down Expand Up @@ -654,7 +655,7 @@ def bigsection(self, title, *args):

def preformat(self, text):
"""Format literal preformatted text."""
text = self.escape(text.expandtabs())
text = html_escape(text.expandtabs(), quote=False)
return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
' ', '&nbsp;', '\n', '<br>\n')

Expand Down Expand Up @@ -732,7 +733,7 @@ def filelink(self, url, path):
def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
"""Mark up some plain text, given a context of symbols to look for.
Each context dictionary maps object names to anchor names."""
escape = escape or self.escape
escape = escape or html_escape
results = []
here = 0
pattern = re.compile(r'\b((http|https|ftp)://\S+[\w/]|'
Expand Down Expand Up @@ -815,9 +816,9 @@ def docmodule(self, object, name=None, mod=None, *ignored):
if version := self._get_version(object):
if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
version = version[11:-1].strip()
info.append('version %s' % self.escape(version))
info.append('version %s' % html_escape(version))
if hasattr(object, '__date__'):
info.append(self.escape(str(object.__date__)))
info.append(html_escape(str(object.__date__)))
if info:
head = head + ' (%s)' % ', '.join(info)
docloc = self.getdocloc(object)
Expand Down Expand Up @@ -2177,6 +2178,11 @@ def showsymbol(self, symbol):
topic, _, xrefs = target.partition(' ')
self.showtopic(topic, xrefs)

def _getsymbol(self, symbol):
target = self.symbols[symbol]
topic, _, xrefs = target.partition(' ')
return self._gettopic(topic, xrefs)

def listmodules(self, key=''):
if key:
self.output.write('''
Expand Down Expand Up @@ -2342,6 +2348,7 @@ def _start_server(urlhandler, hostname, port):
import email.message
import select
import threading
from urllib.parse import unquote

class DocHandler(http.server.BaseHTTPRequestHandler):

Expand Down Expand Up @@ -2461,11 +2468,14 @@ def page(self, title, contents):
%s</head><body>%s<div style="clear:both;padding-top:.5em;">%s</div>
</body></html>''' % (title, css_link, html_navbar(), contents)

def filelink(self, url, path):
return ('<a href="getfile?key=%s">%s</a>' %
(html_escape(url), html_escape(path)))

html = _HTMLDoc()

def html_navbar():
version = html.escape("%s [%s, %s]" % (platform.python_version(),
version = html_escape("%s [%s, %s]" % (platform.python_version(),
platform.python_build()[0],
platform.python_compiler()))
return """
Expand All @@ -2477,6 +2487,7 @@ def html_navbar():
<a href="index.html">Module Index</a>
: <a href="topics.html">Topics</a>
: <a href="keywords.html">Keywords</a>
: <a href="symbols.html">Symbols</a>
</div>
<div>
<form action="get" style='display:inline;'>
Expand All @@ -2489,7 +2500,7 @@ def html_navbar():
</form>
</div>
</div>
""" % (version, html.escape(platform.platform(terse=True)))
""" % (version, html_escape(platform.platform(terse=True)))

def html_index():
"""Module Index page."""
Expand Down Expand Up @@ -2545,7 +2556,20 @@ def bltinlink(name):
'key = %s' % key, 'index', '<br>'.join(results))
return 'Search Results', contents

def html_topics():
def html_getfile(path):
"""Get and display a source file listing safely."""
path = urllib.parse.unquote(path)
with tokenize.open(path) as fp:
lines = html_escape(fp.read())
body = '<pre>%s</pre>' % lines
heading = html.heading(
'<strong class="title">File Listing</strong>',
)
contents = heading + html.bigsection(
'File: %s' % path, 'index', body)
return 'getfile %s' % path, contents

def html_topicindex(title):
"""Index of topic texts available."""

def bltinlink(name):
Expand All @@ -2554,51 +2578,48 @@ def bltinlink(name):
heading = html.heading(
'<strong class="title">INDEX</strong>',
)
names = sorted(Helper.topics.keys())

contents = html.multicolumn(names, bltinlink)
contents = heading + html.bigsection(
'Topics', 'index', contents)
return 'Topics', contents

def html_keywords():
"""Index of keywords."""
heading = html.heading(
'<strong class="title">INDEX</strong>',
)
names = sorted(Helper.keywords.keys())

def bltinlink(name):
return '<a href="topic?key=%s">%s</a>' % (name, name)
keys = {
'topics': Helper.topics.keys,
'keywords': Helper.keywords.keys,
'symbols': Helper.symbols.keys,
}
names = sorted(keys[title]())

contents = html.multicolumn(names, bltinlink)
contents = heading + html.bigsection(
'Keywords', 'index', contents)
return 'Keywords', contents
title.capitalize(), 'index', contents)
return title.capitalize(), contents

def html_topicpage(topic):
"""Topic or keyword help page."""
buf = io.StringIO()
htmlhelp = Helper(buf, buf)
contents, xrefs = htmlhelp._gettopic(topic)
if topic in htmlhelp.keywords:
title = 'KEYWORD'
else:
contents, xrefs = htmlhelp._gettopic(topic)
elif topic in htmlhelp.topics:
title = 'TOPIC'
contents, xrefs = htmlhelp._gettopic(topic)
elif topic in htmlhelp.symbols:
title = 'SYMBOL'
contents, xrefs = htmlhelp._getsymbol(topic)
else:
raise ValueError(f'could not find topic {topic!r}')
heading = html.heading(
'<strong class="title">%s</strong>' % title,
f'<strong class="title">{title}</strong>',
)
contents = '<pre>%s</pre>' % html.markup(contents)
contents = f'<pre>{html.markup(contents)}</pre>'
contents = html.bigsection(topic , 'index', contents)
if xrefs:
xrefs = sorted(xrefs.split())

def bltinlink(name):
return '<a href="topic?key=%s">%s</a>' % (name, name)
return f'<a href="topic?key={html_escape(name)}>{html_escape(name)}</a>'

xrefs = html.multicolumn(xrefs, bltinlink)
xrefs = html.section('Related help topics: ', 'index', xrefs)
return ('%s %s' % (title, topic),
return (f'{title} {topic}',
''.join((heading, contents, xrefs)))

def html_getobj(url):
Expand All @@ -2613,7 +2634,7 @@ def html_error(url, exc):
heading = html.heading(
'<strong class="title">Error</strong>',
)
contents = '<br>'.join(html.escape(line) for line in
contents = '<br>'.join(html_escape(line) for line in
format_exception_only(type(exc), exc))
contents = heading + html.bigsection(url, 'error', contents)
return "Error - %s" % url, contents
Expand All @@ -2626,21 +2647,21 @@ def get_html_page(url):
try:
if url in ("", "index"):
title, content = html_index()
elif url == "topics":
title, content = html_topics()
elif url == "keywords":
title, content = html_keywords()
elif '=' in url:
op, _, url = url.partition('=')
if op == "search?key":
elif url in ("topics", "keywords", "symbols"):
title, content = html_topicindex(url)
elif '?key=' in url:
op, _, url = url.partition('?key=')
if op == "search":
title, content = html_search(url)
elif op == "topic?key":
elif op == "getfile":
title, content = html_getfile(url)
elif op == "topic":
# try topics first, then objects.
try:
title, content = html_topicpage(url)
except ValueError:
title, content = html_getobj(url)
elif op == "get?key":
elif op == "get":
# try objects first, then topics.
if url in ("", "index"):
title, content = html_index()
Expand Down Expand Up @@ -2835,5 +2856,6 @@ class BadUsage(Exception): pass
it names a directory, documentation is written for all the contents.
""".format(cmd=cmd, sep=os.sep))


if __name__ == '__main__':
cli()
1 change: 1 addition & 0 deletions Lib/test/test_pydoc/test_pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2130,6 +2130,7 @@ def test_url_requests(self):
("index", "Pydoc: Index of Modules"),
("topics", "Pydoc: Topics"),
("keywords", "Pydoc: Keywords"),
("symbols", "Pydoc: Symbols"),
("pydoc", "Pydoc: module pydoc"),
("get?key=pydoc", "Pydoc: module pydoc"),
("search?key=pydoc", "Pydoc: Search Results"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add 'symbols' link to pydoc's html menu bar. Original patch by Ron Adam.
Enhanced by Sanyam Khurana.
Loading