diff options
author | Julien Palard <julien@palard.fr> | 2021-10-09 07:36:50 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-09 07:36:50 (GMT) |
commit | c91b6f57f3f75b482e4a9d30ad2afe37892a8ceb (patch) | |
tree | 765504d9d7408ce8359e0d1ce1557ad54d266926 /Lib | |
parent | a98b273ce42f33d04c8b85b8d574c47adf11dd2a (diff) | |
download | cpython-c91b6f57f3f75b482e4a9d30ad2afe37892a8ceb.zip cpython-c91b6f57f3f75b482e4a9d30ad2afe37892a8ceb.tar.gz cpython-c91b6f57f3f75b482e4a9d30ad2afe37892a8ceb.tar.bz2 |
bpo-10716: Migrating pydoc to html5. (GH-28651)
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/cgitb.py | 15 | ||||
-rwxr-xr-x | Lib/pydoc.py | 164 | ||||
-rw-r--r-- | Lib/pydoc_data/_pydoc.css | 106 | ||||
-rw-r--r-- | Lib/test/test_docxmlrpc.py | 12 | ||||
-rw-r--r-- | Lib/test/test_pydoc.py | 280 | ||||
-rw-r--r-- | Lib/xmlrpc/server.py | 37 |
6 files changed, 353 insertions, 261 deletions
diff --git a/Lib/cgitb.py b/Lib/cgitb.py index 17ddda3..ec15684 100644 --- a/Lib/cgitb.py +++ b/Lib/cgitb.py @@ -31,6 +31,7 @@ import tempfile import time import tokenize import traceback +from html import escape as html_escape def reset(): """Return a string that resets the CGI and browser to a known state.""" @@ -105,10 +106,16 @@ def html(einfo, context=5): etype = etype.__name__ pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable date = time.ctime(time.time()) - head = '<body bgcolor="#f0f0f8">' + pydoc.html.heading( - '<big><big>%s</big></big>' % - strong(pydoc.html.escape(str(etype))), - '#ffffff', '#6622aa', pyver + '<br>' + date) + ''' + head = f''' +<body bgcolor="#f0f0f8"> +<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading"> +<tr bgcolor="#6622aa"> +<td valign=bottom> <br> +<font color="#ffffff" face="helvetica, arial"> <br> +<big><big><strong>{html_escape(str(etype))}</strong></big></big></font></td> +<td align=right valign=bottom> +<font color="#ffffff" face="helvetica, arial">{pyver}<br>{date}</font></td> +</tr></table> <p>A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.</p>''' diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 34a6087..3a2ff21 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -542,7 +542,7 @@ class HTMLRepr(Repr): # needed to make any special characters, so show a raw string. return 'r' + testrepr[0] + self.escape(test) + testrepr[0] return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)', - r'<font color="#c040c0">\1</font>', + r'<span class="repr">\1</span>', self.escape(testrepr)) repr_str = repr_string @@ -567,49 +567,48 @@ class HTMLDoc(Doc): def page(self, title, contents): """Format an HTML page.""" return '''\ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> -<html><head><title>Python: %s</title> -<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> -</head><body bgcolor="#f0f0f8"> +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<title>Python: %s</title> +</head><body> %s </body></html>''' % (title, contents) - def heading(self, title, fgcol, bgcol, extras=''): + def heading(self, title, extras=''): """Format a page heading.""" return ''' -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading"> -<tr bgcolor="%s"> -<td valign=bottom> <br> -<font color="%s" face="helvetica, arial"> <br>%s</font></td -><td align=right valign=bottom -><font color="%s" face="helvetica, arial">%s</font></td></tr></table> - ''' % (bgcol, fgcol, title, fgcol, extras or ' ') - - def section(self, title, fgcol, bgcol, contents, width=6, +<table class="heading"> +<tr class="heading-text decor"> +<td class="title"> <br>%s</td> +<td class="extra">%s</td></tr></table> + ''' % (title, extras or ' ') + + def section(self, title, cls, contents, width=6, prelude='', marginalia=None, gap=' '): """Format a section with a heading.""" if marginalia is None: - marginalia = '<tt>' + ' ' * width + '</tt>' + marginalia = '<span class="code">' + ' ' * width + '</span>' result = '''<p> -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> -<tr bgcolor="%s"> -<td colspan=3 valign=bottom> <br> -<font color="%s" face="helvetica, arial">%s</font></td></tr> - ''' % (bgcol, fgcol, title) +<table class="section"> +<tr class="decor %s-decor heading-text"> +<td class="section-title" colspan=3> <br>%s</td></tr> + ''' % (cls, title) if prelude: result = result + ''' -<tr bgcolor="%s"><td rowspan=2>%s</td> -<td colspan=2>%s</td></tr> -<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap) +<tr><td class="decor %s-decor" rowspan=2>%s</td> +<td class="decor %s-decor" colspan=2>%s</td></tr> +<tr><td>%s</td>''' % (cls, marginalia, cls, prelude, gap) else: result = result + ''' -<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap) +<tr><td class="decor %s-decor">%s</td><td>%s</td>''' % (cls, marginalia, gap) - return result + '\n<td width="100%%">%s</td></tr></table>' % contents + return result + '\n<td class="singlecolumn">%s</td></tr></table>' % contents def bigsection(self, title, *args): """Format a section with a big heading.""" - title = '<big><strong>%s</strong></big>' % title + title = '<strong class="bigsection">%s</strong>' % title return self.section(title, *args) def preformat(self, text): @@ -618,19 +617,19 @@ class HTMLDoc(Doc): return replace(text, '\n\n', '\n \n', '\n\n', '\n \n', ' ', ' ', '\n', '<br>\n') - def multicolumn(self, list, format, cols=4): + def multicolumn(self, list, format): """Format a list of items into a multi-column list.""" result = '' - rows = (len(list)+cols-1)//cols - for col in range(cols): - result = result + '<td width="%d%%" valign=top>' % (100//cols) + rows = (len(list) + 3) // 4 + for col in range(4): + result = result + '<td class="multicolumn">' for i in range(rows*col, rows*col+rows): if i < len(list): result = result + format(list[i]) + '<br>\n' result = result + '</td>' - return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result + return '<table><tr>%s</tr></table>' % result - def grey(self, text): return '<font color="#909090">%s</font>' % text + def grey(self, text): return '<span class="grey">%s</span>' % text def namelink(self, name, *dicts): """Make a link for an identifier, given name-to-URL mappings.""" @@ -719,14 +718,14 @@ class HTMLDoc(Doc): for entry in tree: if type(entry) is type(()): c, bases = entry - result = result + '<dt><font face="helvetica, arial">' + result = result + '<dt class="heading-text">' result = result + self.classlink(c, modname) if bases and bases != (parent,): parents = [] for base in bases: parents.append(self.classlink(base, modname)) result = result + '(' + ', '.join(parents) + ')' - result = result + '\n</font></dt>' + result = result + '\n</dt>' elif type(entry) is type([]): result = result + '<dd>\n%s</dd>\n' % self.formattree( entry, modname, c) @@ -743,10 +742,10 @@ class HTMLDoc(Doc): links = [] for i in range(len(parts)-1): links.append( - '<a href="%s.html"><font color="#ffffff">%s</font></a>' % + '<a href="%s.html" class="white">%s</a>' % ('.'.join(parts[:i+1]), parts[i])) linkedname = '.'.join(links + parts[-1:]) - head = '<big><big><strong>%s</strong></big></big>' % linkedname + head = '<strong class="title">%s</strong>' % linkedname try: path = inspect.getabsfile(object) url = urllib.parse.quote(path) @@ -768,9 +767,7 @@ class HTMLDoc(Doc): docloc = '<br><a href="%(docloc)s">Module Reference</a>' % locals() else: docloc = '' - result = self.heading( - head, '#ffffff', '#7799ee', - '<a href=".">index</a><br>' + filelink + docloc) + result = self.heading(head, '<a href=".">index</a><br>' + filelink + docloc) modules = inspect.getmembers(object, inspect.ismodule) @@ -805,7 +802,7 @@ class HTMLDoc(Doc): data.append((key, value)) doc = self.markup(getdoc(object), self.preformat, fdict, cdict) - doc = doc and '<tt>%s</tt>' % doc + doc = doc and '<span class="code">%s</span>' % doc result = result + '<p>%s</p>\n' % doc if hasattr(object, '__path__'): @@ -815,12 +812,12 @@ class HTMLDoc(Doc): modpkgs.sort() contents = self.multicolumn(modpkgs, self.modpkglink) result = result + self.bigsection( - 'Package Contents', '#ffffff', '#aa55cc', contents) + 'Package Contents', 'pkg-content', contents) elif modules: contents = self.multicolumn( modules, lambda t: self.modulelink(t[1])) result = result + self.bigsection( - 'Modules', '#ffffff', '#aa55cc', contents) + 'Modules', 'pkg-content', contents) if classes: classlist = [value for (key, value) in classes] @@ -829,27 +826,25 @@ class HTMLDoc(Doc): for key, value in classes: contents.append(self.document(value, key, name, fdict, cdict)) result = result + self.bigsection( - 'Classes', '#ffffff', '#ee77aa', ' '.join(contents)) + 'Classes', 'index', ' '.join(contents)) if funcs: contents = [] for key, value in funcs: contents.append(self.document(value, key, name, fdict, cdict)) result = result + self.bigsection( - 'Functions', '#ffffff', '#eeaa77', ' '.join(contents)) + 'Functions', 'functions', ' '.join(contents)) if data: contents = [] for key, value in data: contents.append(self.document(value, key)) result = result + self.bigsection( - 'Data', '#ffffff', '#55aa55', '<br>\n'.join(contents)) + 'Data', 'data', '<br>\n'.join(contents)) if hasattr(object, '__author__'): contents = self.markup(str(object.__author__), self.preformat) - result = result + self.bigsection( - 'Author', '#ffffff', '#7799ee', contents) + result = result + self.bigsection('Author', 'author', contents) if hasattr(object, '__credits__'): contents = self.markup(str(object.__credits__), self.preformat) - result = result + self.bigsection( - 'Credits', '#ffffff', '#7799ee', contents) + result = result + self.bigsection('Credits', 'credits', contents) return result @@ -923,7 +918,7 @@ class HTMLDoc(Doc): else: doc = self.markup(getdoc(value), self.preformat, funcs, classes, mdict) - doc = '<dd><tt>%s</tt>' % doc + doc = '<dd><span class="code">%s</span>' % doc push('<dl><dt>%s%s</dl>\n' % (base, doc)) push('\n') return attrs @@ -1011,9 +1006,9 @@ class HTMLDoc(Doc): if decl: doc = decl + (doc or '') doc = self.markup(doc, self.preformat, funcs, classes, mdict) - doc = doc and '<tt>%s<br> </tt>' % doc + doc = doc and '<span class="code">%s<br> </span>' % doc - return self.section(title, '#000000', '#ffc8d8', contents, 3, doc) + return self.section(title, 'title', contents, 3, doc) def formatvalue(self, object): """Format an argument default value as text.""" @@ -1074,14 +1069,14 @@ class HTMLDoc(Doc): argspec = '(...)' decl = asyncqualifier + title + self.escape(argspec) + (note and - self.grey('<font face="helvetica, arial">%s</font>' % note)) + self.grey('<span class="heading-text">%s</span>' % note)) if skipdocs: return '<dl><dt>%s</dt></dl>\n' % decl else: doc = self.markup( getdoc(object), self.preformat, funcs, classes, methods) - doc = doc and '<dd><tt>%s</tt></dd>' % doc + doc = doc and '<dd><span class="code">%s</span></dd>' % doc return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc) def docdata(self, object, name=None, mod=None, cl=None): @@ -1093,7 +1088,7 @@ class HTMLDoc(Doc): push('<dl><dt><strong>%s</strong></dt>\n' % name) doc = self.markup(getdoc(object), self.preformat) if doc: - push('<dd><tt>%s</tt></dd>\n' % doc) + push('<dd><span class="code">%s</span></dd>\n' % doc) push('</dl>\n') return ''.join(results) @@ -1118,7 +1113,7 @@ class HTMLDoc(Doc): modpkgs.sort() contents = self.multicolumn(modpkgs, self.modpkglink) - return self.bigsection(dir, '#ffffff', '#ee77aa', contents) + return self.bigsection(dir, 'index', contents) # -------------------------------------------- text documentation generator @@ -2446,10 +2441,12 @@ def _url_handler(url, content_type="text/html"): '<link rel="stylesheet" type="text/css" href="%s">' % css_path) return '''\ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> -<html><head><title>Pydoc: %s</title> -<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> -%s</head><body bgcolor="#f0f0f8">%s<div style="clear:both;padding-top:.5em;">%s</div> +<!DOCTYPE> +<html lang="en"> +<head> +<meta charset="utf-8"> +<title>Pydoc: %s</title> +%s</head><body>%s<div style="clear:both;padding-top:.5em;">%s</div> </body></html>''' % (title, css_link, html_navbar(), contents) @@ -2489,22 +2486,21 @@ def _url_handler(url, content_type="text/html"): return '<a href="%s.html">%s</a>' % (name, name) heading = html.heading( - '<big><big><strong>Index of Modules</strong></big></big>', - '#ffffff', '#7799ee') + '<strong class="title">Index of Modules</strong>' + ) names = [name for name in sys.builtin_module_names if name != '__main__'] contents = html.multicolumn(names, bltinlink) contents = [heading, '<p>' + html.bigsection( - 'Built-in Modules', '#ffffff', '#ee77aa', contents)] + 'Built-in Modules', 'index', contents)] seen = {} for dir in sys.path: contents.append(html.index(dir, seen)) contents.append( - '<p align=right><font color="#909090" face="helvetica,' - 'arial"><strong>pydoc</strong> by Ka-Ping Yee' - '<ping@lfw.org></font>') + '<p align=right class="heading-text grey"><strong>pydoc</strong> by Ka-Ping Yee' + '<ping@lfw.org></p>') return 'Index of Modules', ''.join(contents) def html_search(key): @@ -2529,12 +2525,12 @@ def _url_handler(url, content_type="text/html"): results = [] heading = html.heading( - '<big><big><strong>Search Results</strong></big></big>', - '#ffffff', '#7799ee') + '<strong class="title">Search Results</strong>', + ) for name, desc in search_result: results.append(bltinlink(name) + desc) contents = heading + html.bigsection( - 'key = %s' % key, '#ffffff', '#ee77aa', '<br>'.join(results)) + 'key = %s' % key, 'index', '<br>'.join(results)) return 'Search Results', contents def html_topics(): @@ -2544,20 +2540,20 @@ def _url_handler(url, content_type="text/html"): return '<a href="topic?key=%s">%s</a>' % (name, name) heading = html.heading( - '<big><big><strong>INDEX</strong></big></big>', - '#ffffff', '#7799ee') + '<strong class="title">INDEX</strong>', + ) names = sorted(Helper.topics.keys()) contents = html.multicolumn(names, bltinlink) contents = heading + html.bigsection( - 'Topics', '#ffffff', '#ee77aa', contents) + 'Topics', 'index', contents) return 'Topics', contents def html_keywords(): """Index of keywords.""" heading = html.heading( - '<big><big><strong>INDEX</strong></big></big>', - '#ffffff', '#7799ee') + '<strong class="title">INDEX</strong>', + ) names = sorted(Helper.keywords.keys()) def bltinlink(name): @@ -2565,7 +2561,7 @@ def _url_handler(url, content_type="text/html"): contents = html.multicolumn(names, bltinlink) contents = heading + html.bigsection( - 'Keywords', '#ffffff', '#ee77aa', contents) + 'Keywords', 'index', contents) return 'Keywords', contents def html_topicpage(topic): @@ -2578,10 +2574,10 @@ def _url_handler(url, content_type="text/html"): else: title = 'TOPIC' heading = html.heading( - '<big><big><strong>%s</strong></big></big>' % title, - '#ffffff', '#7799ee') + '<strong class="title">%s</strong>' % title, + ) contents = '<pre>%s</pre>' % html.markup(contents) - contents = html.bigsection(topic , '#ffffff','#ee77aa', contents) + contents = html.bigsection(topic , 'index', contents) if xrefs: xrefs = sorted(xrefs.split()) @@ -2589,8 +2585,7 @@ def _url_handler(url, content_type="text/html"): return '<a href="topic?key=%s">%s</a>' % (name, name) xrefs = html.multicolumn(xrefs, bltinlink) - xrefs = html.section('Related help topics: ', - '#ffffff', '#ee77aa', xrefs) + xrefs = html.section('Related help topics: ', 'index', xrefs) return ('%s %s' % (title, topic), ''.join((heading, contents, xrefs))) @@ -2604,12 +2599,11 @@ def _url_handler(url, content_type="text/html"): def html_error(url, exc): heading = html.heading( - '<big><big><strong>Error</strong></big></big>', - '#ffffff', '#7799ee') + '<strong class="title">Error</strong>', + ) contents = '<br>'.join(html.escape(line) for line in format_exception_only(type(exc), exc)) - contents = heading + html.bigsection(url, '#ffffff', '#bb0000', - contents) + contents = heading + html.bigsection(url, 'error', contents) return "Error - %s" % url, contents def get_html_page(url): diff --git a/Lib/pydoc_data/_pydoc.css b/Lib/pydoc_data/_pydoc.css index f036ef3..a6aa2e4 100644 --- a/Lib/pydoc_data/_pydoc.css +++ b/Lib/pydoc_data/_pydoc.css @@ -4,3 +4,109 @@ Contents of this file are subject to change without notice. */ + +body { + background-color: #f0f0f8; +} + +table.heading tr { + background-color: #7799ee; +} + +.decor { + color: #ffffff; +} + +.title-decor { + background-color: #ffc8d8; + color: #000000; +} + +.pkg-content-decor { + background-color: #aa55cc; +} + +.index-decor { + background-color: #ee77aa; +} + +.functions-decor { + background-color: #eeaa77; +} + +.data-decor { + background-color: #55aa55; +} + +.author-decor { + background-color: #7799ee; +} + +.credits-decor { + background-color: #7799ee; +} + +.error-decor { + background-color: #bb0000; +} + +.grey { + color: #909090; +} + +.white { + color: #ffffff; +} + +.repr { + color: #c040c0; +} + +table.heading tr td.title { + vertical-align: bottom; +} + +table.heading tr td.extra { + vertical-align: bottom; + text-align: right; +} + +.heading-text { + font-family: helvetica, arial; +} + +.bigsection { + font-size: larger; +} + +.title { + font-size: x-large; +} + +.code { + font-family: monospace; +} + +table { + width: 100%; + border-spacing : 0; + border-collapse : collapse; + border: 0; +} + +td { + padding: 2; +} + +td.section-title { + vertical-align: bottom; +} + +td.multicolumn { + width: 25%; + vertical-align: bottom; +} + +td.singlecolumn { + width: 100%; +} diff --git a/Lib/test/test_docxmlrpc.py b/Lib/test/test_docxmlrpc.py index 7725250..9a06be4 100644 --- a/Lib/test/test_docxmlrpc.py +++ b/Lib/test/test_docxmlrpc.py @@ -90,7 +90,17 @@ class DocXMLRPCHTTPGETServer(unittest.TestCase): response = self.client.getresponse() self.assertEqual(response.status, 200) - self.assertEqual(response.getheader("Content-type"), "text/html") + self.assertEqual(response.getheader("Content-type"), "text/html; charset=UTF-8") + + # Server raises an exception if we don't start to read the data + response.read() + + def test_get_css(self): + self.client.request("GET", "/pydoc.css") + response = self.client.getresponse() + + self.assertEqual(response.status, 200) + self.assertEqual(response.getheader("Content-type"), "text/css; charset=UTF-8") # Server raises an exception if we don't start to read the data response.read() diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 25ac1fb..0a7d72c 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -132,128 +132,70 @@ FILE expected_text_data_docstrings = tuple('\n | ' + s if s else '' for s in expected_data_docstrings) -expected_html_pattern = """ -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading"> -<tr bgcolor="#7799ee"> -<td valign=bottom> <br> -<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong><a href="test.html"><font color="#ffffff">test</font></a>.pydoc_mod</strong></big></big> (version 1.2.3.4)</font></td -><td align=right valign=bottom -><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:%s">%s</a>%s</font></td></tr></table> - <p><tt>This is a test module for test_pydoc</tt></p> -<p> -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> -<tr bgcolor="#ee77aa"> -<td colspan=3 valign=bottom> <br> -<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr> -\x20\x20\x20\x20 -<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td> -<td width="100%%"><dl> -<dt><font face="helvetica, arial"><a href="builtins.html#object">builtins.object</a> -</font></dt><dd> -<dl> -<dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#A">A</a> -</font></dt><dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#B">B</a> -</font></dt><dt><font face="helvetica, arial"><a href="test.pydoc_mod.html#C">C</a> -</font></dt></dl> -</dd> -</dl> - <p> -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> -<tr bgcolor="#ffc8d8"> -<td colspan=3 valign=bottom> <br> -<font color="#000000" face="helvetica, arial"><a name="A">class <strong>A</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr> -\x20\x20\x20\x20 -<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td> -<td colspan=2><tt>Hello and goodbye<br> </tt></td></tr> -<tr><td> </td> -<td width="100%%">Methods defined here:<br> -<dl><dt><a name="A-__init__"><strong>__init__</strong></a>()</dt><dd><tt>Wow, I have no function!</tt></dd></dl> - -<hr> -Data descriptors defined here:<br> -<dl><dt><strong>__dict__</strong></dt> -<dd><tt>%s</tt></dd> -</dl> -<dl><dt><strong>__weakref__</strong></dt> -<dd><tt>%s</tt></dd> -</dl> -</td></tr></table> <p> -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> -<tr bgcolor="#ffc8d8"> -<td colspan=3 valign=bottom> <br> -<font color="#000000" face="helvetica, arial"><a name="B">class <strong>B</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr> -\x20\x20\x20\x20 -<tr><td bgcolor="#ffc8d8"><tt> </tt></td><td> </td> -<td width="100%%">Data descriptors defined here:<br> -<dl><dt><strong>__dict__</strong></dt> -<dd><tt>%s</tt></dd> -</dl> -<dl><dt><strong>__weakref__</strong></dt> -<dd><tt>%s</tt></dd> -</dl> -<hr> -Data and other attributes defined here:<br> -<dl><dt><strong>NO_MEANING</strong> = 'eggs'</dl> - -<dl><dt><strong>__annotations__</strong> = {'NO_MEANING': <class 'str'>}</dl> - -</td></tr></table> <p> -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> -<tr bgcolor="#ffc8d8"> -<td colspan=3 valign=bottom> <br> -<font color="#000000" face="helvetica, arial"><a name="C">class <strong>C</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr> -\x20\x20\x20\x20 -<tr><td bgcolor="#ffc8d8"><tt> </tt></td><td> </td> -<td width="100%%">Methods defined here:<br> -<dl><dt><a name="C-get_answer"><strong>get_answer</strong></a>(self)</dt><dd><tt>Return <a href="#C-say_no">say_no</a>()</tt></dd></dl> - -<dl><dt><a name="C-is_it_true"><strong>is_it_true</strong></a>(self)</dt><dd><tt>Return self.<a href="#C-get_answer">get_answer</a>()</tt></dd></dl> - -<dl><dt><a name="C-say_no"><strong>say_no</strong></a>(self)</dt></dl> - -<hr> -Data descriptors defined here:<br> -<dl><dt><strong>__dict__</strong></dt> -<dd><tt>dictionary for instance variables (if defined)</tt></dd> -</dl> -<dl><dt><strong>__weakref__</strong></dt> -<dd><tt>list of weak references to the object (if defined)</tt></dd> -</dl> -</td></tr></table></td></tr></table><p> -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> -<tr bgcolor="#eeaa77"> -<td colspan=3 valign=bottom> <br> -<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr> -\x20\x20\x20\x20 -<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td> -<td width="100%%"><dl><dt><a name="-doc_func"><strong>doc_func</strong></a>()</dt><dd><tt>This function solves all of the world's problems:<br> -hunger<br> -lack of Python<br> -war</tt></dd></dl> - <dl><dt><a name="-nodoc_func"><strong>nodoc_func</strong></a>()</dt></dl> -</td></tr></table><p> -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> -<tr bgcolor="#55aa55"> -<td colspan=3 valign=bottom> <br> -<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr> -\x20\x20\x20\x20 -<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td> -<td width="100%%"><strong>__xyz__</strong> = 'X, Y and Z'</td></tr></table><p> -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> -<tr bgcolor="#7799ee"> -<td colspan=3 valign=bottom> <br> -<font color="#ffffff" face="helvetica, arial"><big><strong>Author</strong></big></font></td></tr> -\x20\x20\x20\x20 -<tr><td bgcolor="#7799ee"><tt> </tt></td><td> </td> -<td width="100%%">Benjamin Peterson</td></tr></table><p> -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> -<tr bgcolor="#7799ee"> -<td colspan=3 valign=bottom> <br> -<font color="#ffffff" face="helvetica, arial"><big><strong>Credits</strong></big></font></td></tr> -\x20\x20\x20\x20 -<tr><td bgcolor="#7799ee"><tt> </tt></td><td> </td> -<td width="100%%">Nobody</td></tr></table> -""".strip() # ' <- emacs turd +html2text_of_expected = """ +test.pydoc_mod (version 1.2.3.4) +This is a test module for test_pydoc + +Classes + builtins.object + A + B + C + +class A(builtins.object) + Hello and goodbye + + Methods defined here: + __init__() + Wow, I have no function! + + Data descriptors defined here: + __dict__ + dictionary for instance variables (if defined) + __weakref__ + list of weak references to the object (if defined) + +class B(builtins.object) + Data descriptors defined here: + __dict__ + dictionary for instance variables (if defined) + __weakref__ + list of weak references to the object (if defined) + Data and other attributes defined here: + NO_MEANING = 'eggs' + __annotations__ = {'NO_MEANING': <class 'str'>} + + +class C(builtins.object) + Methods defined here: + get_answer(self) + Return say_no() + is_it_true(self) + Return self.get_answer() + say_no(self) + Data descriptors defined here: + __dict__ + dictionary for instance variables (if defined) + __weakref__ + list of weak references to the object (if defined) + +Functions + doc_func() + This function solves all of the world's problems: + hunger + lack of Python + war + nodoc_func() + +Data + __xyz__ = 'X, Y and Z' + +Author + Benjamin Peterson + +Credits + Nobody +""" expected_html_data_docstrings = tuple(s.replace(' ', ' ') for s in expected_data_docstrings) @@ -394,6 +336,16 @@ def get_html_title(text): return title +def html2text(html): + """A quick and dirty implementation of html2text. + + Tailored for pydoc tests only. + """ + return pydoc.replace( + re.sub("<.*?>", "", html), + " ", " ", ">", ">", "<", "<") + + class PydocBaseTest(unittest.TestCase): def _restricted_walk_packages(self, walk_packages, path=None): @@ -434,12 +386,16 @@ class PydocDocTest(unittest.TestCase): @requires_docstrings def test_html_doc(self): result, doc_loc = get_pydoc_html(pydoc_mod) + text_result = html2text(result) + expected_lines = [line.strip() for line in html2text_of_expected if line] + for line in expected_lines: + self.assertIn(line, text_result) mod_file = inspect.getabsfile(pydoc_mod) mod_url = urllib.parse.quote(mod_file) - expected_html = expected_html_pattern % ( - (mod_url, mod_file, doc_loc) + - expected_html_data_docstrings) - self.assertEqual(result, expected_html) + self.assertIn(mod_url, result) + self.assertIn(mod_file, result) + self.assertIn(doc_loc, result) + @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -845,47 +801,39 @@ class B(A) ''' % __name__) doc = pydoc.render_doc(B, renderer=pydoc.HTMLDoc()) - self.assertEqual(doc, '''\ -Python Library Documentation: class B in module %s + expected_text = """ +Python Library Documentation -<p> -<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> -<tr bgcolor="#ffc8d8"> -<td colspan=3 valign=bottom> <br> -<font color="#000000" face="helvetica, arial"><a name="B">class <strong>B</strong></a>(A)</font></td></tr> -\x20\x20\x20\x20 -<tr><td bgcolor="#ffc8d8"><tt> </tt></td><td> </td> -<td width="100%%"><dl><dt>Method resolution order:</dt> -<dd>B</dd> -<dd>A</dd> -<dd><a href="builtins.html#object">builtins.object</a></dd> -</dl> -<hr> -Methods defined here:<br> -<dl><dt><a name="B-b_size"><strong>b_size</strong></a> = <a href="#B-a_size">a_size</a>(self)</dt></dl> - -<dl><dt><a name="B-itemconfig"><strong>itemconfig</strong></a> = <a href="#B-itemconfigure">itemconfigure</a>(self, tagOrId, cnf=None, **kw)</dt></dl> - -<dl><dt><a name="B-itemconfigure"><strong>itemconfigure</strong></a>(self, tagOrId, cnf=None, **kw)</dt><dd><tt>Configure resources of an item TAGORID.</tt></dd></dl> - -<hr> -Methods inherited from A:<br> -<dl><dt><a name="B-a_size"><strong>a_size</strong></a>(self)</dt><dd><tt>Return size</tt></dd></dl> - -<dl><dt><a name="B-lift"><strong>lift</strong></a> = <a href="#B-tkraise">tkraise</a>(self, aboveThis=None)</dt></dl> - -<dl><dt><a name="B-tkraise"><strong>tkraise</strong></a>(self, aboveThis=None)</dt><dd><tt>Raise this widget in the stacking order.</tt></dd></dl> - -<hr> -Data descriptors inherited from A:<br> -<dl><dt><strong>__dict__</strong></dt> -<dd><tt>dictionary for instance variables (if defined)</tt></dd> -</dl> -<dl><dt><strong>__weakref__</strong></dt> -<dd><tt>list of weak references to the object (if defined)</tt></dd> -</dl> -</td></tr></table>\ -''' % __name__) +class B in module test.test_pydoc +class B(A) + Method resolution order: + B + A + builtins.object + + Methods defined here: + b_size = a_size(self) + itemconfig = itemconfigure(self, tagOrId, cnf=None, **kw) + itemconfigure(self, tagOrId, cnf=None, **kw) + Configure resources of an item TAGORID. + + Methods inherited from A: + a_size(self) + Return size + lift = tkraise(self, aboveThis=None) + tkraise(self, aboveThis=None) + Raise this widget in the stacking order. + + Data descriptors inherited from A: + __dict__ + dictionary for instance variables (if defined) + __weakref__ + list of weak references to the object (if defined) +""" + as_text = html2text(doc) + expected_lines = [line.strip() for line in expected_text.split("\n") if line] + for expected_line in expected_lines: + self.assertIn(expected_line, as_text) class PydocImportTest(PydocBaseTest): diff --git a/Lib/xmlrpc/server.py b/Lib/xmlrpc/server.py index 69a260f..e22e480 100644 --- a/Lib/xmlrpc/server.py +++ b/Lib/xmlrpc/server.py @@ -440,7 +440,7 @@ class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler): # Class attribute listing the accessible path components; # paths not on this list will result in a 404 error. - rpc_paths = ('/', '/RPC2') + rpc_paths = ('/', '/RPC2', '/pydoc.css') #if not None, encode responses larger than this, if possible encode_threshold = 1400 #a common MTU @@ -801,7 +801,7 @@ class ServerHTMLDoc(pydoc.HTMLDoc): server_name = self.escape(server_name) head = '<big><big><strong>%s</strong></big></big>' % server_name - result = self.heading(head, '#ffffff', '#7799ee') + result = self.heading(head) doc = self.markup(package_documentation, self.preformat, fdict) doc = doc and '<tt>%s</tt>' % doc @@ -812,10 +812,25 @@ class ServerHTMLDoc(pydoc.HTMLDoc): for key, value in method_items: contents.append(self.docroutine(value, key, funcs=fdict)) result = result + self.bigsection( - 'Methods', '#ffffff', '#eeaa77', ''.join(contents)) + 'Methods', 'functions', ''.join(contents)) return result + + def page(self, title, contents): + """Format an HTML page.""" + css_path = "/pydoc.css" + css_link = ( + '<link rel="stylesheet" type="text/css" href="%s">' % + css_path) + return '''\ +<!DOCTYPE> +<html lang="en"> +<head> +<meta charset="utf-8"> +<title>Python: %s</title> +%s</head><body>%s</body></html>''' % (title, css_link, contents) + class XMLRPCDocGenerator: """Generates documentation for an XML-RPC server. @@ -907,6 +922,12 @@ class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): for documentation. """ + def _get_css(self, url): + path_here = os.path.dirname(os.path.realpath(__file__)) + css_path = os.path.join(path_here, "..", "pydoc_data", "_pydoc.css") + with open(css_path, mode="rb") as fp: + return fp.read() + def do_GET(self): """Handles the HTTP GET request. @@ -918,9 +939,15 @@ class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): self.report_404() return - response = self.server.generate_html_documentation().encode('utf-8') + if self.path.endswith('.css'): + content_type = 'text/css' + response = self._get_css(self.path) + else: + content_type = 'text/html' + response = self.server.generate_html_documentation().encode('utf-8') + self.send_response(200) - self.send_header("Content-type", "text/html") + self.send_header('Content-Type', '%s; charset=UTF-8' % content_type) self.send_header("Content-length", str(len(response))) self.end_headers() self.wfile.write(response) |