bpo-31128: Allow pydoc to bind to arbitrary hostnames (#3011)

New -n flag allow overriding localhost with custom value,
for example to run from containers.
This commit is contained in:
Feanil Patel 2017-09-14 17:54:09 -04:00 committed by Éric Araujo
parent ccb3c7654c
commit 6a396c9807
5 changed files with 39 additions and 19 deletions

View File

@ -70,6 +70,12 @@ 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 -n <hostname>` will start the server listening at the given
hostname. By default the hostname is 'localhost' but if you want the server to
be reached from other machines, you may want to change the host name that the
server responds to. During development this is especially useful if you want
to run pydoc from within a container.
: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
@ -98,3 +104,6 @@ Reference Manual pages.
:mod:`pydoc` now uses :func:`inspect.signature` rather than
:func:`inspect.getfullargspec` to extract signature information from
callables.
.. versionchanged:: 3.7
Added the ``-n`` option.

View File

@ -16,12 +16,15 @@ class or function within a module or module in a package. If the
Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
of all available modules.
Run "pydoc -n <hostname>" to start an HTTP server with the given
hostname (default: localhost) on the local machine.
Run "pydoc -p <port>" to start an HTTP server on the given port on the
local machine. Port number 0 can be used to get an arbitrary unused port.
Run "pydoc -b" to start an HTTP server on an arbitrary unused port and
open a Web browser to interactively browse documentation. The -p option
can be used with the -b option to explicitly specify the server port.
open a Web browser to interactively browse documentation. Combine with
the -n and -p options to control the hostname and port used.
Run "pydoc -w <name>" to write out the HTML documentation for a module
to a file named "<name>.html".
@ -2162,7 +2165,7 @@ def onerror(modname):
# --------------------------------------- enhanced Web browser interface
def _start_server(urlhandler, port):
def _start_server(urlhandler, hostname, port):
"""Start an HTTP server thread on a specific port.
Start an HTML/text server thread, so HTML or text documents can be
@ -2247,8 +2250,8 @@ def log_message(self, *args):
class DocServer(http.server.HTTPServer):
def __init__(self, port, callback):
self.host = 'localhost'
def __init__(self, host, port, callback):
self.host = host
self.address = (self.host, port)
self.callback = callback
self.base.__init__(self, self.address, self.handler)
@ -2268,8 +2271,9 @@ def server_activate(self):
class ServerThread(threading.Thread):
def __init__(self, urlhandler, port):
def __init__(self, urlhandler, host, port):
self.urlhandler = urlhandler
self.host = host
self.port = int(port)
threading.Thread.__init__(self)
self.serving = False
@ -2282,7 +2286,7 @@ def run(self):
DocServer.handler = DocHandler
DocHandler.MessageClass = email.message.Message
DocHandler.urlhandler = staticmethod(self.urlhandler)
docsvr = DocServer(self.port, self.ready)
docsvr = DocServer(self.host, self.port, self.ready)
self.docserver = docsvr
docsvr.serve_until_quit()
except Exception as e:
@ -2304,7 +2308,7 @@ def stop(self):
self.serving = False
self.url = None
thread = ServerThread(urlhandler, port)
thread = ServerThread(urlhandler, hostname, port)
thread.start()
# Wait until thread.serving is True to make sure we are
# really up before returning.
@ -2568,14 +2572,14 @@ def get_html_page(url):
raise TypeError('unknown content type %r for url %s' % (content_type, url))
def browse(port=0, *, open_browser=True):
def browse(port=0, *, open_browser=True, hostname='localhost'):
"""Start the enhanced pydoc Web server and open a Web browser.
Use port '0' to start the server on an arbitrary port.
Set open_browser to False to suppress opening a browser.
"""
import webbrowser
serverthread = _start_server(_url_handler, port)
serverthread = _start_server(_url_handler, hostname, port)
if serverthread.error:
print(serverthread.error)
return
@ -2622,11 +2626,12 @@ class BadUsage(Exception): pass
sys.path.insert(0, '.')
try:
opts, args = getopt.getopt(sys.argv[1:], 'bk:p:w')
opts, args = getopt.getopt(sys.argv[1:], 'bk:n:p:w')
writing = False
start_server = False
open_browser = False
port = None
port = 0
hostname = 'localhost'
for opt, val in opts:
if opt == '-b':
start_server = True
@ -2639,11 +2644,12 @@ class BadUsage(Exception): pass
port = val
if opt == '-w':
writing = True
if opt == '-n':
start_server = True
hostname = val
if start_server:
if port is None:
port = 0
browse(port, open_browser=open_browser)
browse(port, hostname=hostname, open_browser=open_browser)
return
if not args: raise BadUsage
@ -2679,14 +2685,17 @@ class BadUsage(Exception): pass
{cmd} -k <keyword>
Search for a keyword in the synopsis lines of all available modules.
{cmd} -n <hostname>
Start an HTTP server with the given hostname (default: localhost).
{cmd} -p <port>
Start an HTTP server on the given port on the local machine. Port
number 0 can be used to get an arbitrary unused port.
{cmd} -b
Start an HTTP server on an arbitrary unused port and open a Web browser
to interactively browse documentation. The -p option can be used with
the -b option to explicitly specify the server port.
to interactively browse documentation. This option can be used in
combination with -n and/or -p.
{cmd} -w <name> ...
Write out the HTML documentation for a module to a file in the current

View File

@ -909,8 +909,8 @@ 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)
self.assertIn('localhost', serverthread.docserver.address)
serverthread = pydoc._start_server(my_url_handler, hostname='0.0.0.0', port=0)
self.assertIn('0.0.0.0', serverthread.docserver.address)
starttime = time.time()
timeout = 1 #seconds

View File

@ -1177,6 +1177,7 @@ Claude Paroz
Heikki Partanen
Harri Pasanen
Gaël Pasgrimaud
Feanil Patel
Ashish Nitin Patil
Alecsandru Patrascu
Randy Pausch

View File

@ -0,0 +1 @@
Allow the pydoc server to bind to arbitrary hostnames.