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 @@ backslash on Windows) it is treated as the path to a Python source file.
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 apropos(key):
# --------------------------------------- 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 _start_server(urlhandler, port):
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 _start_server(urlhandler, port):
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 _start_server(urlhandler, port):
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 _start_server(urlhandler, port):
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 _url_handler(url, content_type="text/html"):
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 @@ def cli():
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 @@ def cli():
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 @@ def cli():
{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 @@ class PydocServerTest(unittest.TestCase):
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.