This commit is contained in:
Sanyam Khurana 2026-02-05 04:06:35 -04:00 committed by GitHub
commit 1c1ffbfebe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 71 additions and 46 deletions

View file

@ -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
@ -552,7 +553,7 @@ 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)
@ -560,18 +561,18 @@ def repr_string(self, x, level):
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
@ -633,7 +634,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')
@ -711,7 +712,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/]|'
@ -794,9 +795,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)
@ -2156,6 +2157,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('''
@ -2321,6 +2327,7 @@ def _start_server(urlhandler, hostname, port):
import email.message
import select
import threading
from urllib.parse import unquote
class DocHandler(http.server.BaseHTTPRequestHandler):
@ -2440,11 +2447,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 """
@ -2456,6 +2466,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;'>
@ -2468,7 +2479,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."""
@ -2524,7 +2535,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):
@ -2533,51 +2557,48 @@ def bltinlink(name):
heading = html.heading(
'<strong class="title">INDEX</strong>',
)
names = sorted(Helper.topics.keys())
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(
'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)
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):
@ -2592,7 +2613,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
@ -2605,21 +2626,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()
@ -2814,5 +2835,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()

View file

@ -2128,6 +2128,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"),

View file

@ -0,0 +1,2 @@
Add 'symbols' link to pydoc's html menu bar. Original patch by Ron Adam.
Enhanced by Sanyam Khurana.