diff options
author | Georg Brandl <georg@python.org> | 2011-01-30 08:37:19 (GMT) |
---|---|---|
committer | Georg Brandl <georg@python.org> | 2011-01-30 08:37:19 (GMT) |
commit | d2f3857c40a340f252397eb16b4289403af8bf76 (patch) | |
tree | e9a6396db72685fb5972a6863dbd63fdc8ff4c01 /Lib | |
parent | ce227e3518ccc8ed26e4befdbb883c24517ba858 (diff) | |
download | cpython-d2f3857c40a340f252397eb16b4289403af8bf76.zip cpython-d2f3857c40a340f252397eb16b4289403af8bf76.tar.gz cpython-d2f3857c40a340f252397eb16b4289403af8bf76.tar.bz2 |
#10961: fix exception handling in new pydoc server code.
Patch by Ron Adam, reviewed by Eric Araujo.
Diffstat (limited to 'Lib')
-rwxr-xr-x | Lib/pydoc.py | 159 | ||||
-rw-r--r-- | Lib/test/test_pydoc.py | 45 |
2 files changed, 105 insertions, 99 deletions
diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 1855cd1..9d3cdd5 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -70,7 +70,7 @@ import time import warnings from collections import deque from reprlib import Repr -from traceback import extract_tb +from traceback import extract_tb, format_exception_only # --------------------------------------------------------- common routines @@ -1882,7 +1882,9 @@ module "pydoc_data.topics" could not be found. def _gettopic(self, topic, more_xrefs=''): """Return unbuffered tuple of (topic, xrefs). - If an error occurs, topic is the error message, and xrefs is ''. + If an error occurs here, the exception is caught and displayed by + the url handler. + This function duplicates the showtopic method but returns its result directly so it can be formatted for display in an html page. """ @@ -1895,14 +1897,11 @@ module "pydoc_data.topics" could not be found. ''' , '') target = self.topics.get(topic, self.keywords.get(topic)) if not target: - return 'no documentation found for %r' % topic, '' + raise ValueError('could not find topic') if isinstance(target, str): return self._gettopic(target, more_xrefs) label, xrefs = target - try: - doc = pydoc_data.topics.topics[label] - except KeyError: - return 'no documentation found for %r' % topic, '' + doc = pydoc_data.topics.topics[label] if more_xrefs: xrefs = (xrefs or '') + ' ' + more_xrefs return doc, xrefs @@ -2387,7 +2386,7 @@ def _start_server(urlhandler, port): else: content_type = 'text/html' self.send_response(200) - self.send_header('Content-Type', '%s;charset=UTF-8' % content_type) + self.send_header('Content-Type', '%s; charset=UTF-8' % content_type) self.end_headers() self.wfile.write(self.urlhandler( self.path, content_type).encode('utf-8')) @@ -2479,10 +2478,10 @@ def _url_handler(url, content_type="text/html"): css_path) return '''\ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> -<html><head><title>Python: %s</title> +<html><head><title>Pydoc: %s</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> -%s</head><body bgcolor="#f0f0f8">%s -</body></html>''' % (title, css_link, contents) +%s</head><body bgcolor="#f0f0f8">%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>' % (url, path) @@ -2491,12 +2490,12 @@ def _url_handler(url, content_type="text/html"): html = _HTMLDoc() def html_navbar(): - version = "%s [%s, %s]" % (platform.python_version(), - platform.python_build()[0], - platform.python_compiler()) + version = html.escape("%s [%s, %s]" % (platform.python_version(), + platform.python_build()[0], + platform.python_compiler())) return """ <div style='float:left'> - Python %s<br>%s<br><br> + Python %s<br>%s </div> <div style='float:right'> <div style='text-align:center'> @@ -2505,19 +2504,17 @@ def _url_handler(url, content_type="text/html"): : <a href="keywords.html">Keywords</a> </div> <div> - <form action="get" style='float:left'> + <form action="get" style='display:inline;'> <input type=text name=key size=15> <input type=submit value="Get"> - - </form> - <form action="search" style='float:right'> + </form> + <form action="search" style='display:inline;'> <input type=text name=key size=15> <input type=submit value="Search"> </form> </div> </div> - <div clear='all'> </div> - """ % (version, platform.platform(terse=True)) + """ % (version, html.escape(platform.platform(terse=True))) def html_index(): """Module Index page.""" @@ -2589,7 +2586,7 @@ def _url_handler(url, content_type="text/html"): """Index of topic texts available.""" def bltinlink(name): - return '<a href="%s.html">%s</a>' % (name, name) + return '<a href="topic?key=%s">%s</a>' % (name, name) heading = html.heading( '<big><big><strong>INDEX</strong></big></big>', @@ -2609,7 +2606,7 @@ def _url_handler(url, content_type="text/html"): names = sorted(Helper.keywords.keys()) def bltinlink(name): - return '<a href="%s.html">%s</a>' % (name, name) + return '<a href="topic?key=%s">%s</a>' % (name, name) contents = html.multicolumn(names, bltinlink) contents = heading + html.bigsection( @@ -2628,79 +2625,91 @@ def _url_handler(url, content_type="text/html"): heading = html.heading( '<big><big><strong>%s</strong></big></big>' % title, '#ffffff', '#7799ee') - contents = '<pre>%s</pre>' % contents + contents = '<pre>%s</pre>' % html.markup(contents) contents = html.bigsection(topic , '#ffffff','#ee77aa', contents) - xrefs = sorted(xrefs.split()) + if xrefs: + xrefs = sorted(xrefs.split()) - def bltinlink(name): - return '<a href="%s.html">%s</a>' % (name, name) + def bltinlink(name): + 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.multicolumn(xrefs, bltinlink) + xrefs = html.section('Related help topics: ', + '#ffffff', '#ee77aa', xrefs) return ('%s %s' % (title, topic), ''.join((heading, contents, xrefs))) - def html_error(url): + def html_getobj(url): + obj = locate(url, forceload=1) + if obj is None and url != 'None': + raise ValueError('could not find object') + title = describe(obj) + content = html.document(obj, url) + return title, content + + def html_error(url, exc): heading = html.heading( '<big><big><strong>Error</strong></big></big>', - '#ffffff', '#ee0000') - return heading + url + '#ffffff', '#7799ee') + contents = '<br>'.join(html.escape(line) for line in + format_exception_only(type(exc), exc)) + contents = heading + html.bigsection(url, '#ffffff', '#bb0000', + contents) + return "Error - %s" % url, contents def get_html_page(url): """Generate an HTML page for url.""" + complete_url = url if url.endswith('.html'): url = url[:-5] - if url.startswith('/'): - url = url[1:] - if url.startswith("get?key="): - url = url[8:] - title = url - contents = '' - if url in ("", ".", "index"): - title, contents = html_index() - elif url == "topics": - title, contents = html_topics() - elif url == "keywords": - title, contents = html_keywords() - elif url.startswith("search?key="): - title, contents = html_search(url[11:]) - elif url.startswith("getfile?key="): - url = url[12:] - try: - title, contents = html_getfile(url) - except IOError: - contents = html_error('could not read file %r' % url) - title = 'Read Error' - else: - obj = None - try: - obj = locate(url, forceload=1) - except ErrorDuringImport as value: - contents = html.escape(str(value)) - if obj: - title = describe(obj) - contents = html.document(obj, url) - elif url in Helper.keywords or url in Helper.topics: - title, contents = html_topicpage(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": + title, content = html_search(url) + elif op == "getfile?key": + title, content = html_getfile(url) + elif op == "topic?key": + # try topics first, then objects. + try: + title, content = html_topicpage(url) + except ValueError: + title, content = html_getobj(url) + elif op == "get?key": + # try objects first, then topics. + if url in ("", "index"): + title, content = html_index() + else: + try: + title, content = html_getobj(url) + except ValueError: + title, content = html_topicpage(url) + else: + raise ValueError('bad pydoc url') else: - contents = html_error( - 'no Python documentation found for %r' % url) - title = 'Error' - return html.page(title, html_navbar() + contents) + title, content = html_getobj(url) + except Exception as exc: + # Catch any errors and display them in an error page. + title, content = html_error(complete_url, exc) + return html.page(title, content) if url.startswith('/'): url = url[1:] if content_type == 'text/css': path_here = os.path.dirname(os.path.realpath(__file__)) - try: - with open(os.path.join(path_here, url)) as fp: - return ''.join(fp.readlines()) - except IOError: - return 'Error: can not open css file %r' % url + css_path = os.path.join(path_here, url) + with open(css_path) as fp: + return ''.join(fp.readlines()) elif content_type == 'text/html': return get_html_page(url) - return 'Error: unknown content type %r' % content_type + # Errors outside the url handler are caught by the server. + raise TypeError('unknown content type %r for url %s' % (content_type, url)) def browse(port=0, *, open_browser=True): diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 7f927f6..1d575cd 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -244,7 +244,7 @@ def get_html_title(text): return title -class PyDocDocTest(unittest.TestCase): +class PydocDocTest(unittest.TestCase): @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -392,7 +392,7 @@ class TestDescriptions(unittest.TestCase): self.assertIn(expected, pydoc.render_doc(c)) -class PyDocServerTest(unittest.TestCase): +class PydocServerTest(unittest.TestCase): """Tests for pydoc._start_server""" def test_server(self): @@ -415,34 +415,31 @@ class PyDocServerTest(unittest.TestCase): self.assertEqual(serverthread.error, None) -class PyDocUrlHandlerTest(unittest.TestCase): +class PydocUrlHandlerTest(unittest.TestCase): """Tests for pydoc._url_handler""" def test_content_type_err(self): - err = 'Error: unknown content type ' f = pydoc._url_handler - result = f("", "") - self.assertEqual(result, err + "''") - result = f("", "foobar") - self.assertEqual(result, err + "'foobar'") + self.assertRaises(TypeError, f, 'A', '') + self.assertRaises(TypeError, f, 'B', 'foobar') def test_url_requests(self): # Test for the correct title in the html pages returned. # This tests the different parts of the URL handler without # getting too picky about the exact html. requests = [ - ("", "Python: Index of Modules"), - ("get?key=", "Python: Index of Modules"), - ("index", "Python: Index of Modules"), - ("topics", "Python: Topics"), - ("keywords", "Python: Keywords"), - ("pydoc", "Python: module pydoc"), - ("get?key=pydoc", "Python: module pydoc"), - ("search?key=pydoc", "Python: Search Results"), - ("def", "Python: KEYWORD def"), - ("STRINGS", "Python: TOPIC STRINGS"), - ("foobar", "Python: Error"), - ("getfile?key=foobar", "Python: Read Error"), + ("", "Pydoc: Index of Modules"), + ("get?key=", "Pydoc: Index of Modules"), + ("index", "Pydoc: Index of Modules"), + ("topics", "Pydoc: Topics"), + ("keywords", "Pydoc: Keywords"), + ("pydoc", "Pydoc: module pydoc"), + ("get?key=pydoc", "Pydoc: module pydoc"), + ("search?key=pydoc", "Pydoc: Search Results"), + ("topic?key=def", "Pydoc: KEYWORD def"), + ("topic?key=STRINGS", "Pydoc: TOPIC STRINGS"), + ("foobar", "Pydoc: Error - foobar"), + ("getfile?key=foobar", "Pydoc: Error - getfile?key=foobar"), ] for url, title in requests: @@ -451,7 +448,7 @@ class PyDocUrlHandlerTest(unittest.TestCase): self.assertEqual(result, title) path = string.__file__ - title = "Python: getfile " + path + title = "Pydoc: getfile " + path url = "getfile?key=" + path text = pydoc._url_handler(url, "text/html") result = get_html_title(text) @@ -459,10 +456,10 @@ class PyDocUrlHandlerTest(unittest.TestCase): def test_main(): - test.support.run_unittest(PyDocDocTest, + test.support.run_unittest(PydocDocTest, TestDescriptions, - PyDocServerTest, - PyDocUrlHandlerTest, + PydocServerTest, + PydocUrlHandlerTest, ) if __name__ == "__main__": |