@@ -75,6 +75,7 @@ class or function within a module or module in a package. If the
7575import warnings
7676from annotationlib import Format
7777from collections import deque
78+ from html import escape as html_escape
7879from reprlib import Repr
7980from traceback import format_exception_only
8081
@@ -608,26 +609,26 @@ def repr1(self, x, level):
608609 methodname = 'repr_' + '_' .join (type (x ).__name__ .split ())
609610 if hasattr (self , methodname ):
610611 return getattr (self , methodname )(x , level )
611- return self . escape (cram (stripid (repr (x )), self .maxother ))
612+ return html_escape (cram (stripid (repr (x )), self .maxother ))
612613
613614 def repr_string (self , x , level ):
614615 test = cram (x , self .maxstring )
615616 testrepr = repr (test )
616617 if '\\ ' in test and '\\ ' not in replace (testrepr , r'\\' , '' ):
617618 # Backslashes are only literal in the string and are never
618619 # needed to make any special characters, so show a raw string.
619- return 'r' + testrepr [0 ] + self . escape (test ) + testrepr [0 ]
620+ return 'r' + testrepr [0 ] + html_escape (test ) + testrepr [0 ]
620621 return re .sub (r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)' ,
621622 r'<span class="repr">\1</span>' ,
622- self . escape (testrepr ))
623+ html_escape (testrepr , quote = False ))
623624
624625 repr_str = repr_string
625626
626627 def repr_instance (self , x , level ):
627628 try :
628- return self . escape (cram (stripid (repr (x )), self .maxstring ))
629+ return html_escape (cram (stripid (repr (x )), self .maxstring ))
629630 except :
630- return self . escape ('<%s instance>' % x .__class__ .__name__ )
631+ return html_escape ('<%s instance>' % x .__class__ .__name__ )
631632
632633 repr_unicode = repr_string
633634
@@ -689,7 +690,7 @@ def bigsection(self, title, *args):
689690
690691 def preformat (self , text ):
691692 """Format literal preformatted text."""
692- text = self . escape (text .expandtabs ())
693+ text = html_escape (text .expandtabs (), quote = False )
693694 return replace (text , '\n \n ' , '\n \n ' , '\n \n ' , '\n \n ' ,
694695 ' ' , ' ' , '\n ' , '<br>\n ' )
695696
@@ -767,7 +768,7 @@ def filelink(self, url, path):
767768 def markup (self , text , escape = None , funcs = {}, classes = {}, methods = {}):
768769 """Mark up some plain text, given a context of symbols to look for.
769770 Each context dictionary maps object names to anchor names."""
770- escape = escape or self . escape
771+ escape = escape or html_escape
771772 results = []
772773 here = 0
773774 pattern = re .compile (r'\b((http|https|ftp)://\S+[\w/]|'
@@ -850,9 +851,9 @@ def docmodule(self, object, name=None, mod=None, *ignored):
850851 version = str (object .__version__ )
851852 if version [:11 ] == '$' + 'Revision: ' and version [- 1 :] == '$' :
852853 version = version [11 :- 1 ].strip ()
853- info .append ('version %s' % self . escape (version ))
854+ info .append ('version %s' % html_escape (version ))
854855 if hasattr (object , '__date__' ):
855- info .append (self . escape (str (object .__date__ )))
856+ info .append (html_escape (str (object .__date__ )))
856857 if info :
857858 head = head + ' (%s)' % ', ' .join (info )
858859 docloc = self .getdocloc (object )
@@ -2212,6 +2213,11 @@ def showsymbol(self, symbol):
22122213 topic , _ , xrefs = target .partition (' ' )
22132214 self .showtopic (topic , xrefs )
22142215
2216+ def _getsymbol (self , symbol ):
2217+ target = self .symbols [symbol ]
2218+ topic , _ , xrefs = target .partition (' ' )
2219+ return self ._gettopic (topic , xrefs )
2220+
22152221 def listmodules (self , key = '' ):
22162222 if key :
22172223 self .output .write ('''
@@ -2377,6 +2383,7 @@ def _start_server(urlhandler, hostname, port):
23772383 import email .message
23782384 import select
23792385 import threading
2386+ from urllib .parse import unquote
23802387
23812388 class DocHandler (http .server .BaseHTTPRequestHandler ):
23822389
@@ -2496,11 +2503,14 @@ def page(self, title, contents):
24962503%s</head><body>%s<div style="clear:both;padding-top:.5em;">%s</div>
24972504</body></html>''' % (title , css_link , html_navbar (), contents )
24982505
2506+ def filelink (self , url , path ):
2507+ return ('<a href="getfile?key=%s">%s</a>' %
2508+ (html_escape (url ), html_escape (path )))
24992509
25002510 html = _HTMLDoc ()
25012511
25022512 def html_navbar ():
2503- version = html . escape ("%s [%s, %s]" % (platform .python_version (),
2513+ version = html_escape ("%s [%s, %s]" % (platform .python_version (),
25042514 platform .python_build ()[0 ],
25052515 platform .python_compiler ()))
25062516 return """
@@ -2512,6 +2522,7 @@ def html_navbar():
25122522 <a href="index.html">Module Index</a>
25132523 : <a href="topics.html">Topics</a>
25142524 : <a href="keywords.html">Keywords</a>
2525+ : <a href="symbols.html">Symbols</a>
25152526 </div>
25162527 <div>
25172528 <form action="get" style='display:inline;'>
@@ -2524,7 +2535,7 @@ def html_navbar():
25242535 </form>
25252536 </div>
25262537 </div>
2527- """ % (version , html . escape (platform .platform (terse = True )))
2538+ """ % (version , html_escape (platform .platform (terse = True )))
25282539
25292540 def html_index ():
25302541 """Module Index page."""
@@ -2580,7 +2591,20 @@ def bltinlink(name):
25802591 'key = %s' % key , 'index' , '<br>' .join (results ))
25812592 return 'Search Results' , contents
25822593
2583- def html_topics ():
2594+ def html_getfile (path ):
2595+ """Get and display a source file listing safely."""
2596+ path = urllib .parse .unquote (path )
2597+ with tokenize .open (path ) as fp :
2598+ lines = html_escape (fp .read ())
2599+ body = '<pre>%s</pre>' % lines
2600+ heading = html .heading (
2601+ '<strong class="title">File Listing</strong>' ,
2602+ )
2603+ contents = heading + html .bigsection (
2604+ 'File: %s' % path , 'index' , body )
2605+ return 'getfile %s' % path , contents
2606+
2607+ def html_topicindex (title ):
25842608 """Index of topic texts available."""
25852609
25862610 def bltinlink (name ):
@@ -2589,51 +2613,48 @@ def bltinlink(name):
25892613 heading = html .heading (
25902614 '<strong class="title">INDEX</strong>' ,
25912615 )
2592- names = sorted (Helper .topics .keys ())
25932616
2594- contents = html .multicolumn (names , bltinlink )
2595- contents = heading + html .bigsection (
2596- 'Topics' , 'index' , contents )
2597- return 'Topics' , contents
2598-
2599- def html_keywords ():
2600- """Index of keywords."""
2601- heading = html .heading (
2602- '<strong class="title">INDEX</strong>' ,
2603- )
2604- names = sorted (Helper .keywords .keys ())
2605-
2606- def bltinlink (name ):
2607- return '<a href="topic?key=%s">%s</a>' % (name , name )
2617+ keys = {
2618+ 'topics' : Helper .topics .keys ,
2619+ 'keywords' : Helper .keywords .keys ,
2620+ 'symbols' : Helper .symbols .keys ,
2621+ }
2622+ names = sorted (keys [title ]())
26082623
26092624 contents = html .multicolumn (names , bltinlink )
26102625 contents = heading + html .bigsection (
2611- 'Keywords' , 'index' , contents )
2612- return 'Keywords' , contents
2626+ title . capitalize () , 'index' , contents )
2627+ return title . capitalize () , contents
26132628
26142629 def html_topicpage (topic ):
26152630 """Topic or keyword help page."""
26162631 buf = io .StringIO ()
26172632 htmlhelp = Helper (buf , buf )
2618- contents , xrefs = htmlhelp ._gettopic (topic )
26192633 if topic in htmlhelp .keywords :
26202634 title = 'KEYWORD'
2621- else :
2635+ contents , xrefs = htmlhelp ._gettopic (topic )
2636+ elif topic in htmlhelp .topics :
26222637 title = 'TOPIC'
2638+ contents , xrefs = htmlhelp ._gettopic (topic )
2639+ elif topic in htmlhelp .symbols :
2640+ title = 'SYMBOL'
2641+ contents , xrefs = htmlhelp ._getsymbol (topic )
2642+ else :
2643+ raise ValueError (f'could not find topic { topic !r} ' )
26232644 heading = html .heading (
2624- '<strong class="title">%s </strong>' % title ,
2645+ f '<strong class="title">{ title } </strong>' ,
26252646 )
2626- contents = '<pre>%s</pre>' % html .markup (contents )
2647+ contents = f '<pre>{ html .markup (contents )} </pre>'
26272648 contents = html .bigsection (topic , 'index' , contents )
26282649 if xrefs :
26292650 xrefs = sorted (xrefs .split ())
26302651
26312652 def bltinlink (name ):
2632- return '<a href="topic?key=%s">%s </a>' % ( name , name )
2653+ return f '<a href="topic?key={ html_escape ( name ) } > { html_escape ( name ) } </a>'
26332654
26342655 xrefs = html .multicolumn (xrefs , bltinlink )
26352656 xrefs = html .section ('Related help topics: ' , 'index' , xrefs )
2636- return ('%s %s' % ( title , topic ) ,
2657+ return (f' { title } { topic } ' ,
26372658 '' .join ((heading , contents , xrefs )))
26382659
26392660 def html_getobj (url ):
@@ -2648,7 +2669,7 @@ def html_error(url, exc):
26482669 heading = html .heading (
26492670 '<strong class="title">Error</strong>' ,
26502671 )
2651- contents = '<br>' .join (html . escape (line ) for line in
2672+ contents = '<br>' .join (html_escape (line ) for line in
26522673 format_exception_only (type (exc ), exc ))
26532674 contents = heading + html .bigsection (url , 'error' , contents )
26542675 return "Error - %s" % url , contents
@@ -2661,21 +2682,21 @@ def get_html_page(url):
26612682 try :
26622683 if url in ("" , "index" ):
26632684 title , content = html_index ()
2664- elif url == "topics" :
2665- title , content = html_topics ()
2666- elif url == "keywords" :
2667- title , content = html_keywords ()
2668- elif '=' in url :
2669- op , _ , url = url .partition ('=' )
2670- if op == "search?key" :
2685+ elif url in ("topics" , "keywords" , "symbols" ):
2686+ title , content = html_topicindex (url )
2687+ elif '?key=' in url :
2688+ op , _ , url = url .partition ('?key=' )
2689+ if op == "search" :
26712690 title , content = html_search (url )
2672- elif op == "topic?key" :
2691+ elif op == "getfile" :
2692+ title , content = html_getfile (url )
2693+ elif op == "topic" :
26732694 # try topics first, then objects.
26742695 try :
26752696 title , content = html_topicpage (url )
26762697 except ValueError :
26772698 title , content = html_getobj (url )
2678- elif op == "get?key " :
2699+ elif op == "get" :
26792700 # try objects first, then topics.
26802701 if url in ("" , "index" ):
26812702 title , content = html_index ()
@@ -2870,5 +2891,6 @@ class BadUsage(Exception): pass
28702891 it names a directory, documentation is written for all the contents.
28712892""" .format (cmd = cmd , sep = os .sep ))
28722893
2894+
28732895if __name__ == '__main__' :
28742896 cli ()
0 commit comments