Commit 7bb30b72 authored by Nick Coghlan's avatar Nick Coghlan

Improve Pydoc interactive browsing (#2001). Patch by Ron Adam.

* A -b option to start an enhanced browsing session.
* Allow -b and -p options to be used together.
* Specifying port 0 will pick an arbitrary unused socket port.
* A new browse() function to start the new server and browser.
* Show Python version information in the header.
* A *Get* field which takes the same input as the help() function.
* A *Search* field which replaces the Tkinter search box.
* Links to *Module Index*, *Topics*, and *Keywords*.
* Improved source file viewing.
* An HTMLDoc.filelink() method.
* The -g option and the gui() and serve() functions are deprecated.
parent 9af2a6e5
......@@ -50,12 +50,21 @@ manner similar to the Unix :program:`man` command. The synopsis line of a
module is the first line of its documentation string.
You can also use :program:`pydoc` to start an HTTP server on the local machine
that will serve documentation to visiting Web browsers. :program:`pydoc -p 1234`
will start a HTTP server on port 1234, allowing you to browse
the documentation at ``http://localhost:1234/`` in your preferred Web browser.
that will serve documentation to visiting Web browsers. :program:`pydoc -p 1234`
will start a HTTP server on port 1234, allowing you to browse the
documentation at ``http://localhost:1234/`` in your preferred Web browser.
Specifying ``0`` as the port number will select an arbitrary unused port.
:program:`pydoc -g` will start the server and additionally bring up a
small :mod:`tkinter`\ -based graphical interface to help you search for
documentation pages.
documentation pages. The ``-g`` option is deprecated, since the server can
now be controlled directly from HTTP clients.
:program:`pydoc -b` will start the server and additionally open a web
browser to a module index page. Each served page has a navigation bar at the
top where you can *Get* help on an individual item, *Search* all modules with a
keyword in their synopsis line, and go to the *Module index*, *Topics* and
*Keywords* pages.
When :program:`pydoc` generates documentation, it uses the current environment
and path to locate modules. Thus, invoking :program:`pydoc spam`
......@@ -69,3 +78,4 @@ be overridden by setting the :envvar:`PYTHONDOCS` environment variable
to a different URL or to a local directory containing the Library
Reference Manual pages.
.. versionchanged:: 3.2
......@@ -566,7 +566,6 @@ New, Improved, and Deprecated Modules
(Contributed by R. David Murray, :issue:`10321`.)
* The :mod:`inspect` module has a new function :func:`getgenatorstate`
to easily identify the current state of a generator as one of
``GEN_CREATED``, ``GEN_RUNNING``, ``GEN_SUSPENDED`` or ``GEN_CLOSED``.
......@@ -583,6 +582,12 @@ New, Improved, and Deprecated Modules
- non-UTF8 percent encoding of non-ASCII characters
Issue 2987 for IPv6 (RFC2732) support in urlparse
* The :mod:`pydoc` module now provides a much improved Web server interface,
as well as a new command-line option to automatically open a browser
window to display that server.
(Contributed by Ron Adam; :issue:`2001`.)
Multi-threading
===============
......
This diff is collapsed.
import sys
import os
import os.path
import sys
import difflib
import subprocess
import re
import pydoc
import inspect
import unittest
import pydoc
import re
import string
import subprocess
import test.support
import time
import unittest
import xml.etree
import textwrap
from io import StringIO
......@@ -221,19 +222,25 @@ def get_pydoc_text(module):
output = doc.docmodule(module)
# cleanup the extra text formatting that pydoc preforms
# clean up the extra text formatting that pydoc performs
patt = re.compile('\b.')
output = patt.sub('', output)
return output.strip(), loc
def print_diffs(text1, text2):
"Prints unified diffs for two texts"
# XXX now obsolete, use unittest built-in support
lines1 = text1.splitlines(True)
lines2 = text2.splitlines(True)
diffs = difflib.unified_diff(lines1, lines2, n=0, fromfile='expected',
tofile='got')
print('\n' + ''.join(diffs))
def get_html_title(text):
_, _, text = text.rpartition("<title>")
title, _, _ = text.rpartition("</title>")
return title
class PyDocDocTest(unittest.TestCase):
......@@ -373,16 +380,8 @@ class TestDescriptions(unittest.TestCase):
doc = pydoc.render_doc(pydocfodder)
self.assertIn("pydocfodder", doc)
def test_classic_class(self):
class C: "Classic class"
c = C()
self.assertEqual(pydoc.describe(C), 'class C')
self.assertEqual(pydoc.describe(c), 'C')
expected = 'C in module %s' % __name__
self.assertIn(expected, pydoc.render_doc(c))
def test_class(self):
class C(object): "New-style class"
class C: "New-style class"
c = C()
self.assertEqual(pydoc.describe(C), 'class C')
......@@ -391,8 +390,78 @@ class TestDescriptions(unittest.TestCase):
self.assertIn(expected, pydoc.render_doc(c))
class PyDocServerTest(unittest.TestCase):
"""Tests for pydoc._start_server"""
def test_server(self):
# Minimal test that starts the server, then stops it.
def my_url_handler(url, content_type):
text = 'the URL sent was: (%s, %s)' % (url, content_type)
return text
serverthread = pydoc._start_server(my_url_handler, port=0)
starttime = time.time()
timeout = 1 #seconds
while serverthread.serving:
time.sleep(.01)
if serverthread.serving and time.time() - starttime > timeout:
serverthread.stop()
break
self.assertEqual(serverthread.error, None)
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'")
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"),
]
for url, title in requests:
text = pydoc._url_handler(url, "text/html")
result = get_html_title(text)
self.assertEqual(result, title)
path = string.__file__
title = "Python: getfile /" + path
url = "getfile?key=" + path
text = pydoc._url_handler(url, "text/html")
result = get_html_title(text)
self.assertEqual(result, title)
def test_main():
test.support.run_unittest(PyDocDocTest, TestDescriptions)
test.support.run_unittest(PyDocDocTest,
TestDescriptions,
PyDocServerTest,
PyDocUrlHandlerTest,
)
if __name__ == "__main__":
test_main()
......@@ -12,6 +12,7 @@ PS: In the standard Python distribution, this file is encoded in UTF-8
and the list is in rough alphabetical order by last names.
David Abrahams
Ron Adam
Jim Ahlstrom
Farhan Ahmad
Matthew Ahrens
......
......@@ -33,6 +33,9 @@ Core and Builtins
Library
-------
- Issue #2001: New HTML server with enhanced Web page features. Patch by Ron
Adam.
- Issue #10360: In WeakSet, do not raise TypeErrors when testing for membership
of non-weakrefable objects.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment