@@ -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
@@ -597,9 +598,6 @@ def __init__(self):
597598 self .maxdict = 10
598599 self .maxstring = self .maxother = 100
599600
600- def escape (self , text ):
601- return replace (text , '&' , '&' , '<' , '<' , '>' , '>' )
602-
603601 def repr (self , object ):
604602 return Repr .repr (self , object )
605603
@@ -608,26 +606,26 @@ def repr1(self, x, level):
608606 methodname = 'repr_' + '_' .join (type (x ).__name__ .split ())
609607 if hasattr (self , methodname ):
610608 return getattr (self , methodname )(x , level )
611- return self . escape (cram (stripid (repr (x )), self .maxother ))
609+ return html_escape (cram (stripid (repr (x )), self .maxother ))
612610
613611 def repr_string (self , x , level ):
614612 test = cram (x , self .maxstring )
615613 testrepr = repr (test )
616614 if '\\ ' in test and '\\ ' not in replace (testrepr , r'\\' , '' ):
617615 # Backslashes are only literal in the string and are never
618616 # needed to make any special characters, so show a raw string.
619- return 'r' + testrepr [0 ] + self . escape (test ) + testrepr [0 ]
617+ return 'r' + testrepr [0 ] + html_escape (test ) + testrepr [0 ]
620618 return re .sub (r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)' ,
621619 r'<span class="repr">\1</span>' ,
622- self . escape (testrepr ))
620+ html_escape (testrepr , quote = False ))
623621
624622 repr_str = repr_string
625623
626624 def repr_instance (self , x , level ):
627625 try :
628- return self . escape (cram (stripid (repr (x )), self .maxstring ))
626+ return html_escape (cram (stripid (repr (x )), self .maxstring ))
629627 except :
630- return self . escape ('<%s instance>' % x .__class__ .__name__ )
628+ return html_escape ('<%s instance>' % x .__class__ .__name__ )
631629
632630 repr_unicode = repr_string
633631
@@ -638,7 +636,6 @@ class HTMLDoc(Doc):
638636
639637 _repr_instance = HTMLRepr ()
640638 repr = _repr_instance .repr
641- escape = _repr_instance .escape
642639
643640 def page (self , title , contents ):
644641 """Format an HTML page."""
@@ -689,7 +686,7 @@ def bigsection(self, title, *args):
689686
690687 def preformat (self , text ):
691688 """Format literal preformatted text."""
692- text = self . escape (text .expandtabs ())
689+ text = html_escape (text .expandtabs (), quote = False )
693690 return replace (text , '\n \n ' , '\n \n ' , '\n \n ' , '\n \n ' ,
694691 ' ' , ' ' , '\n ' , '<br>\n ' )
695692
@@ -767,7 +764,7 @@ def filelink(self, url, path):
767764 def markup (self , text , escape = None , funcs = {}, classes = {}, methods = {}):
768765 """Mark up some plain text, given a context of symbols to look for.
769766 Each context dictionary maps object names to anchor names."""
770- escape = escape or self . escape
767+ escape = escape or html_escape
771768 results = []
772769 here = 0
773770 pattern = re .compile (r'\b((http|https|ftp)://\S+[\w/]|'
@@ -850,9 +847,9 @@ def docmodule(self, object, name=None, mod=None, *ignored):
850847 version = str (object .__version__ )
851848 if version [:11 ] == '$' + 'Revision: ' and version [- 1 :] == '$' :
852849 version = version [11 :- 1 ].strip ()
853- info .append ('version %s' % self . escape (version ))
850+ info .append ('version %s' % html_escape (version ))
854851 if hasattr (object , '__date__' ):
855- info .append (self . escape (str (object .__date__ )))
852+ info .append (html_escape (str (object .__date__ )))
856853 if info :
857854 head = head + ' (%s)' % ', ' .join (info )
858855 docloc = self .getdocloc (object )
@@ -2212,6 +2209,11 @@ def showsymbol(self, symbol):
22122209 topic , _ , xrefs = target .partition (' ' )
22132210 self .showtopic (topic , xrefs )
22142211
2212+ def _getsymbol (self , symbol ):
2213+ target = self .symbols [symbol ]
2214+ topic , _ , xrefs = target .partition (' ' )
2215+ return self ._gettopic (topic , xrefs )
2216+
22152217 def listmodules (self , key = '' ):
22162218 if key :
22172219 self .output .write ('''
@@ -2377,6 +2379,7 @@ def _start_server(urlhandler, hostname, port):
23772379 import email .message
23782380 import select
23792381 import threading
2382+ from urllib .parse import unquote
23802383
23812384 class DocHandler (http .server .BaseHTTPRequestHandler ):
23822385
@@ -2496,11 +2499,14 @@ def page(self, title, contents):
24962499%s</head><body>%s<div style="clear:both;padding-top:.5em;">%s</div>
24972500</body></html>''' % (title , css_link , html_navbar (), contents )
24982501
2502+ def filelink (self , url , path ):
2503+ return ('<a href="getfile?key=%s">%s</a>' %
2504+ (html_escape (url ), html_escape (path )))
24992505
25002506 html = _HTMLDoc ()
25012507
25022508 def html_navbar ():
2503- version = html . escape ("%s [%s, %s]" % (platform .python_version (),
2509+ version = html_escape ("%s [%s, %s]" % (platform .python_version (),
25042510 platform .python_build ()[0 ],
25052511 platform .python_compiler ()))
25062512 return """
@@ -2512,6 +2518,7 @@ def html_navbar():
25122518 <a href="index.html">Module Index</a>
25132519 : <a href="topics.html">Topics</a>
25142520 : <a href="keywords.html">Keywords</a>
2521+ : <a href="symbols.html">Symbols</a>
25152522 </div>
25162523 <div>
25172524 <form action="get" style='display:inline;'>
@@ -2524,7 +2531,7 @@ def html_navbar():
25242531 </form>
25252532 </div>
25262533 </div>
2527- """ % (version , html . escape (platform .platform (terse = True )))
2534+ """ % (version , html_escape (platform .platform (terse = True )))
25282535
25292536 def html_index ():
25302537 """Module Index page."""
@@ -2580,7 +2587,20 @@ def bltinlink(name):
25802587 'key = %s' % key , 'index' , '<br>' .join (results ))
25812588 return 'Search Results' , contents
25822589
2583- def html_topics ():
2590+ def html_getfile (path ):
2591+ """Get and display a source file listing safely."""
2592+ path = urllib .parse .unquote (path )
2593+ with tokenize .open (path ) as fp :
2594+ lines = html_escape (fp .read ())
2595+ body = '<pre>%s</pre>' % lines
2596+ heading = html .heading (
2597+ '<strong class="title">File Listing</strong>' ,
2598+ )
2599+ contents = heading + html .bigsection (
2600+ 'File: %s' % path , 'index' , body )
2601+ return 'getfile %s' % path , contents
2602+
2603+ def html_topicindex (title ):
25842604 """Index of topic texts available."""
25852605
25862606 def bltinlink (name ):
@@ -2589,37 +2609,34 @@ def bltinlink(name):
25892609 heading = html .heading (
25902610 '<strong class="title">INDEX</strong>' ,
25912611 )
2592- names = sorted (Helper .topics .keys ())
2593-
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 ())
26052612
2606- def bltinlink (name ):
2607- return '<a href="topic?key=%s">%s</a>' % (name , name )
2613+ keys = {
2614+ 'topics' : Helper .topics .keys ,
2615+ 'keywords' : Helper .keywords .keys ,
2616+ 'symbols' : Helper .symbols .keys ,
2617+ }
2618+ names = sorted (keys [title ]())
26082619
26092620 contents = html .multicolumn (names , bltinlink )
26102621 contents = heading + html .bigsection (
2611- 'Keywords' , 'index' , contents )
2612- return 'Keywords' , contents
2622+ title . capitalize () , 'index' , contents )
2623+ return title . capitalize () , contents
26132624
26142625 def html_topicpage (topic ):
26152626 """Topic or keyword help page."""
26162627 buf = io .StringIO ()
26172628 htmlhelp = Helper (buf , buf )
2618- contents , xrefs = htmlhelp ._gettopic (topic )
26192629 if topic in htmlhelp .keywords :
26202630 title = 'KEYWORD'
2621- else :
2631+ contents , xrefs = htmlhelp ._gettopic (topic )
2632+ elif topic in htmlhelp .topics :
26222633 title = 'TOPIC'
2634+ contents , xrefs = htmlhelp ._gettopic (topic )
2635+ elif topic in htmlhelp .symbols :
2636+ title = 'SYMBOL'
2637+ contents , xrefs = htmlhelp ._getsymbol (topic )
2638+ else :
2639+ raise ValueError ('could not find topic %s' % repr (topic ))
26232640 heading = html .heading (
26242641 '<strong class="title">%s</strong>' % title ,
26252642 )
@@ -2629,7 +2646,7 @@ def html_topicpage(topic):
26292646 xrefs = sorted (xrefs .split ())
26302647
26312648 def bltinlink (name ):
2632- return '<a href="topic?key=%s">%s</a>' % (name , name )
2649+ return '<a href="topic?key=%s">%s</a>' % (html_escape ( name ), html_escape ( name ) )
26332650
26342651 xrefs = html .multicolumn (xrefs , bltinlink )
26352652 xrefs = html .section ('Related help topics: ' , 'index' , xrefs )
@@ -2648,7 +2665,7 @@ def html_error(url, exc):
26482665 heading = html .heading (
26492666 '<strong class="title">Error</strong>' ,
26502667 )
2651- contents = '<br>' .join (html . escape (line ) for line in
2668+ contents = '<br>' .join (html_escape (line ) for line in
26522669 format_exception_only (type (exc ), exc ))
26532670 contents = heading + html .bigsection (url , 'error' , contents )
26542671 return "Error - %s" % url , contents
@@ -2661,21 +2678,21 @@ def get_html_page(url):
26612678 try :
26622679 if url in ("" , "index" ):
26632680 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" :
2681+ elif url in ("topics" , "keywords" , "symbols" ):
2682+ title , content = html_topicindex (url )
2683+ elif '?key=' in url :
2684+ op , _ , url = url .partition ('?key=' )
2685+ if op == "search" :
26712686 title , content = html_search (url )
2672- elif op == "topic?key" :
2687+ elif op == "getfile" :
2688+ title , content = html_getfile (url )
2689+ elif op == "topic" :
26732690 # try topics first, then objects.
26742691 try :
26752692 title , content = html_topicpage (url )
26762693 except ValueError :
26772694 title , content = html_getobj (url )
2678- elif op == "get?key " :
2695+ elif op == "get" :
26792696 # try objects first, then topics.
26802697 if url in ("" , "index" ):
26812698 title , content = html_index ()
@@ -2870,5 +2887,6 @@ class BadUsage(Exception): pass
28702887 it names a directory, documentation is written for all the contents.
28712888""" .format (cmd = cmd , sep = os .sep ))
28722889
2890+
28732891if __name__ == '__main__' :
28742892 cli ()
0 commit comments