@@ -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
@@ -184,20 +185,9 @@ def _getowndoc(obj):
184185 return None
185186
186187def _getdoc (object ):
187- """Get the documentation string for an object.
188-
189- All tabs are expanded to spaces. To clean up docstrings that are
190- indented to line up with blocks of code, any whitespace than can be
191- uniformly removed from the second line onwards is removed."""
192- doc = _getowndoc (object )
193- if doc is None :
194- try :
195- doc = _finddoc (object )
196- except (AttributeError , TypeError ):
197- return None
198- if not isinstance (doc , str ):
199- return None
200- return inspect .cleandoc (doc )
188+ return inspect .getdoc (object ,
189+ fallback_to_class_doc = False ,
190+ inherit_class_doc = False )
201191
202192def getdoc (object ):
203193 """Get the doc string or comments for an object."""
@@ -608,26 +598,26 @@ def repr1(self, x, level):
608598 methodname = 'repr_' + '_' .join (type (x ).__name__ .split ())
609599 if hasattr (self , methodname ):
610600 return getattr (self , methodname )(x , level )
611- return self . escape (cram (stripid (repr (x )), self .maxother ))
601+ return html_escape (cram (stripid (repr (x )), self .maxother ))
612602
613603 def repr_string (self , x , level ):
614604 test = cram (x , self .maxstring )
615605 testrepr = repr (test )
616606 if '\\ ' in test and '\\ ' not in replace (testrepr , r'\\' , '' ):
617607 # Backslashes are only literal in the string and are never
618608 # needed to make any special characters, so show a raw string.
619- return 'r' + testrepr [0 ] + self . escape (test ) + testrepr [0 ]
609+ return 'r' + testrepr [0 ] + html_escape (test ) + testrepr [0 ]
620610 return re .sub (r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)' ,
621611 r'<span class="repr">\1</span>' ,
622- self . escape (testrepr ))
612+ html_escape (testrepr , quote = False ))
623613
624614 repr_str = repr_string
625615
626616 def repr_instance (self , x , level ):
627617 try :
628- return self . escape (cram (stripid (repr (x )), self .maxstring ))
618+ return html_escape (cram (stripid (repr (x )), self .maxstring ))
629619 except :
630- return self . escape ('<%s instance>' % x .__class__ .__name__ )
620+ return html_escape ('<%s instance>' % x .__class__ .__name__ )
631621
632622 repr_unicode = repr_string
633623
@@ -689,7 +679,7 @@ def bigsection(self, title, *args):
689679
690680 def preformat (self , text ):
691681 """Format literal preformatted text."""
692- text = self . escape (text .expandtabs ())
682+ text = html_escape (text .expandtabs (), quote = False )
693683 return replace (text , '\n \n ' , '\n \n ' , '\n \n ' , '\n \n ' ,
694684 ' ' , ' ' , '\n ' , '<br>\n ' )
695685
@@ -767,7 +757,7 @@ def filelink(self, url, path):
767757 def markup (self , text , escape = None , funcs = {}, classes = {}, methods = {}):
768758 """Mark up some plain text, given a context of symbols to look for.
769759 Each context dictionary maps object names to anchor names."""
770- escape = escape or self . escape
760+ escape = escape or html_escape
771761 results = []
772762 here = 0
773763 pattern = re .compile (r'\b((http|https|ftp)://\S+[\w/]|'
@@ -850,9 +840,9 @@ def docmodule(self, object, name=None, mod=None, *ignored):
850840 version = str (object .__version__ )
851841 if version [:11 ] == '$' + 'Revision: ' and version [- 1 :] == '$' :
852842 version = version [11 :- 1 ].strip ()
853- info .append ('version %s' % self . escape (version ))
843+ info .append ('version %s' % html_escape (version ))
854844 if hasattr (object , '__date__' ):
855- info .append (self . escape (str (object .__date__ )))
845+ info .append (html_escape (str (object .__date__ )))
856846 if info :
857847 head = head + ' (%s)' % ', ' .join (info )
858848 docloc = self .getdocloc (object )
@@ -2212,6 +2202,11 @@ def showsymbol(self, symbol):
22122202 topic , _ , xrefs = target .partition (' ' )
22132203 self .showtopic (topic , xrefs )
22142204
2205+ def _getsymbol (self , symbol ):
2206+ target = self .symbols [symbol ]
2207+ topic , _ , xrefs = target .partition (' ' )
2208+ return self ._gettopic (topic , xrefs )
2209+
22152210 def listmodules (self , key = '' ):
22162211 if key :
22172212 self .output .write ('''
@@ -2377,6 +2372,7 @@ def _start_server(urlhandler, hostname, port):
23772372 import email .message
23782373 import select
23792374 import threading
2375+ from urllib .parse import unquote
23802376
23812377 class DocHandler (http .server .BaseHTTPRequestHandler ):
23822378
@@ -2496,11 +2492,14 @@ def page(self, title, contents):
24962492%s</head><body>%s<div style="clear:both;padding-top:.5em;">%s</div>
24972493</body></html>''' % (title , css_link , html_navbar (), contents )
24982494
2495+ def filelink (self , url , path ):
2496+ return ('<a href="getfile?key=%s">%s</a>' %
2497+ (html_escape (url ), html_escape (path )))
24992498
25002499 html = _HTMLDoc ()
25012500
25022501 def html_navbar ():
2503- version = html . escape ("%s [%s, %s]" % (platform .python_version (),
2502+ version = html_escape ("%s [%s, %s]" % (platform .python_version (),
25042503 platform .python_build ()[0 ],
25052504 platform .python_compiler ()))
25062505 return """
@@ -2512,6 +2511,7 @@ def html_navbar():
25122511 <a href="index.html">Module Index</a>
25132512 : <a href="topics.html">Topics</a>
25142513 : <a href="keywords.html">Keywords</a>
2514+ : <a href="symbols.html">Symbols</a>
25152515 </div>
25162516 <div>
25172517 <form action="get" style='display:inline;'>
@@ -2524,7 +2524,7 @@ def html_navbar():
25242524 </form>
25252525 </div>
25262526 </div>
2527- """ % (version , html . escape (platform .platform (terse = True )))
2527+ """ % (version , html_escape (platform .platform (terse = True )))
25282528
25292529 def html_index ():
25302530 """Module Index page."""
@@ -2580,7 +2580,20 @@ def bltinlink(name):
25802580 'key = %s' % key , 'index' , '<br>' .join (results ))
25812581 return 'Search Results' , contents
25822582
2583- def html_topics ():
2583+ def html_getfile (path ):
2584+ """Get and display a source file listing safely."""
2585+ path = urllib .parse .unquote (path )
2586+ with tokenize .open (path ) as fp :
2587+ lines = html_escape (fp .read ())
2588+ body = '<pre>%s</pre>' % lines
2589+ heading = html .heading (
2590+ '<strong class="title">File Listing</strong>' ,
2591+ )
2592+ contents = heading + html .bigsection (
2593+ 'File: %s' % path , 'index' , body )
2594+ return 'getfile %s' % path , contents
2595+
2596+ def html_topicindex (title ):
25842597 """Index of topic texts available."""
25852598
25862599 def bltinlink (name ):
@@ -2589,51 +2602,48 @@ def bltinlink(name):
25892602 heading = html .heading (
25902603 '<strong class="title">INDEX</strong>' ,
25912604 )
2592- names = sorted (Helper .topics .keys ())
25932605
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 )
2606+ keys = {
2607+ 'topics' : Helper .topics .keys ,
2608+ 'keywords' : Helper .keywords .keys ,
2609+ 'symbols' : Helper .symbols .keys ,
2610+ }
2611+ names = sorted (keys [title ]())
26082612
26092613 contents = html .multicolumn (names , bltinlink )
26102614 contents = heading + html .bigsection (
2611- 'Keywords' , 'index' , contents )
2612- return 'Keywords' , contents
2615+ title . capitalize () , 'index' , contents )
2616+ return title . capitalize () , contents
26132617
26142618 def html_topicpage (topic ):
26152619 """Topic or keyword help page."""
26162620 buf = io .StringIO ()
26172621 htmlhelp = Helper (buf , buf )
2618- contents , xrefs = htmlhelp ._gettopic (topic )
26192622 if topic in htmlhelp .keywords :
26202623 title = 'KEYWORD'
2621- else :
2624+ contents , xrefs = htmlhelp ._gettopic (topic )
2625+ elif topic in htmlhelp .topics :
26222626 title = 'TOPIC'
2627+ contents , xrefs = htmlhelp ._gettopic (topic )
2628+ elif topic in htmlhelp .symbols :
2629+ title = 'SYMBOL'
2630+ contents , xrefs = htmlhelp ._getsymbol (topic )
2631+ else :
2632+ raise ValueError (f'could not find topic { topic !r} ' )
26232633 heading = html .heading (
2624- '<strong class="title">%s </strong>' % title ,
2634+ f '<strong class="title">{ title } </strong>' ,
26252635 )
2626- contents = '<pre>%s</pre>' % html .markup (contents )
2636+ contents = f '<pre>{ html .markup (contents )} </pre>'
26272637 contents = html .bigsection (topic , 'index' , contents )
26282638 if xrefs :
26292639 xrefs = sorted (xrefs .split ())
26302640
26312641 def bltinlink (name ):
2632- return '<a href="topic?key=%s">%s </a>' % ( name , name )
2642+ return f '<a href="topic?key={ html_escape ( name ) } > { html_escape ( name ) } </a>'
26332643
26342644 xrefs = html .multicolumn (xrefs , bltinlink )
26352645 xrefs = html .section ('Related help topics: ' , 'index' , xrefs )
2636- return ('%s %s' % ( title , topic ) ,
2646+ return (f' { title } { topic } ' ,
26372647 '' .join ((heading , contents , xrefs )))
26382648
26392649 def html_getobj (url ):
@@ -2648,7 +2658,7 @@ def html_error(url, exc):
26482658 heading = html .heading (
26492659 '<strong class="title">Error</strong>' ,
26502660 )
2651- contents = '<br>' .join (html . escape (line ) for line in
2661+ contents = '<br>' .join (html_escape (line ) for line in
26522662 format_exception_only (type (exc ), exc ))
26532663 contents = heading + html .bigsection (url , 'error' , contents )
26542664 return "Error - %s" % url , contents
@@ -2661,21 +2671,21 @@ def get_html_page(url):
26612671 try :
26622672 if url in ("" , "index" ):
26632673 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" :
2674+ elif url in ("topics" , "keywords" , "symbols" ):
2675+ title , content = html_topicindex (url )
2676+ elif '?key=' in url :
2677+ op , _ , url = url .partition ('?key=' )
2678+ if op == "search" :
26712679 title , content = html_search (url )
2672- elif op == "topic?key" :
2680+ elif op == "getfile" :
2681+ title , content = html_getfile (url )
2682+ elif op == "topic" :
26732683 # try topics first, then objects.
26742684 try :
26752685 title , content = html_topicpage (url )
26762686 except ValueError :
26772687 title , content = html_getobj (url )
2678- elif op == "get?key " :
2688+ elif op == "get" :
26792689 # try objects first, then topics.
26802690 if url in ("" , "index" ):
26812691 title , content = html_index ()
@@ -2870,5 +2880,6 @@ class BadUsage(Exception): pass
28702880 it names a directory, documentation is written for all the contents.
28712881""" .format (cmd = cmd , sep = os .sep ))
28722882
2883+
28732884if __name__ == '__main__' :
28742885 cli ()
0 commit comments