Commit 6a396c98 authored by Feanil Patel's avatar Feanil Patel Committed by Éric Araujo

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.
parent ccb3c765
...@@ -70,6 +70,12 @@ will start a HTTP server on port 1234, allowing you to browse the ...@@ -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. documentation at ``http://localhost:1234/`` in your preferred Web browser.
Specifying ``0`` as the port number will select an arbitrary unused port. 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 :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 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 top where you can *Get* help on an individual item, *Search* all modules with a
...@@ -98,3 +104,6 @@ Reference Manual pages. ...@@ -98,3 +104,6 @@ Reference Manual pages.
:mod:`pydoc` now uses :func:`inspect.signature` rather than :mod:`pydoc` now uses :func:`inspect.signature` rather than
:func:`inspect.getfullargspec` to extract signature information from :func:`inspect.getfullargspec` to extract signature information from
callables. callables.
.. versionchanged:: 3.7
Added the ``-n`` option.
...@@ -16,12 +16,15 @@ backslash on Windows) it is treated as the path to a Python source 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 Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
of all available modules. 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 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. 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 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 open a Web browser to interactively browse documentation. Combine with
can be used with the -b option to explicitly specify the server port. 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 Run "pydoc -w <name>" to write out the HTML documentation for a module
to a file named "<name>.html". to a file named "<name>.html".
...@@ -2162,7 +2165,7 @@ def apropos(key): ...@@ -2162,7 +2165,7 @@ def apropos(key):
# --------------------------------------- enhanced Web browser interface # --------------------------------------- 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 HTTP server thread on a specific port.
Start an HTML/text server thread, so HTML or text documents can be Start an HTML/text server thread, so HTML or text documents can be
...@@ -2247,8 +2250,8 @@ def _start_server(urlhandler, port): ...@@ -2247,8 +2250,8 @@ def _start_server(urlhandler, port):
class DocServer(http.server.HTTPServer): class DocServer(http.server.HTTPServer):
def __init__(self, port, callback): def __init__(self, host, port, callback):
self.host = 'localhost' self.host = host
self.address = (self.host, port) self.address = (self.host, port)
self.callback = callback self.callback = callback
self.base.__init__(self, self.address, self.handler) self.base.__init__(self, self.address, self.handler)
...@@ -2268,8 +2271,9 @@ def _start_server(urlhandler, port): ...@@ -2268,8 +2271,9 @@ def _start_server(urlhandler, port):
class ServerThread(threading.Thread): class ServerThread(threading.Thread):
def __init__(self, urlhandler, port): def __init__(self, urlhandler, host, port):
self.urlhandler = urlhandler self.urlhandler = urlhandler
self.host = host
self.port = int(port) self.port = int(port)
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.serving = False self.serving = False
...@@ -2282,7 +2286,7 @@ def _start_server(urlhandler, port): ...@@ -2282,7 +2286,7 @@ def _start_server(urlhandler, port):
DocServer.handler = DocHandler DocServer.handler = DocHandler
DocHandler.MessageClass = email.message.Message DocHandler.MessageClass = email.message.Message
DocHandler.urlhandler = staticmethod(self.urlhandler) DocHandler.urlhandler = staticmethod(self.urlhandler)
docsvr = DocServer(self.port, self.ready) docsvr = DocServer(self.host, self.port, self.ready)
self.docserver = docsvr self.docserver = docsvr
docsvr.serve_until_quit() docsvr.serve_until_quit()
except Exception as e: except Exception as e:
...@@ -2304,7 +2308,7 @@ def _start_server(urlhandler, port): ...@@ -2304,7 +2308,7 @@ def _start_server(urlhandler, port):
self.serving = False self.serving = False
self.url = None self.url = None
thread = ServerThread(urlhandler, port) thread = ServerThread(urlhandler, hostname, port)
thread.start() thread.start()
# Wait until thread.serving is True to make sure we are # Wait until thread.serving is True to make sure we are
# really up before returning. # really up before returning.
...@@ -2568,14 +2572,14 @@ def _url_handler(url, content_type="text/html"): ...@@ -2568,14 +2572,14 @@ def _url_handler(url, content_type="text/html"):
raise TypeError('unknown content type %r for url %s' % (content_type, 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. """Start the enhanced pydoc Web server and open a Web browser.
Use port '0' to start the server on an arbitrary port. Use port '0' to start the server on an arbitrary port.
Set open_browser to False to suppress opening a browser. Set open_browser to False to suppress opening a browser.
""" """
import webbrowser import webbrowser
serverthread = _start_server(_url_handler, port) serverthread = _start_server(_url_handler, hostname, port)
if serverthread.error: if serverthread.error:
print(serverthread.error) print(serverthread.error)
return return
...@@ -2622,11 +2626,12 @@ def cli(): ...@@ -2622,11 +2626,12 @@ def cli():
sys.path.insert(0, '.') sys.path.insert(0, '.')
try: try:
opts, args = getopt.getopt(sys.argv[1:], 'bk:p:w') opts, args = getopt.getopt(sys.argv[1:], 'bk:n:p:w')
writing = False writing = False
start_server = False start_server = False
open_browser = False open_browser = False
port = None port = 0
hostname = 'localhost'
for opt, val in opts: for opt, val in opts:
if opt == '-b': if opt == '-b':
start_server = True start_server = True
...@@ -2639,11 +2644,12 @@ def cli(): ...@@ -2639,11 +2644,12 @@ def cli():
port = val port = val
if opt == '-w': if opt == '-w':
writing = True writing = True
if opt == '-n':
start_server = True
hostname = val
if start_server: if start_server:
if port is None: browse(port, hostname=hostname, open_browser=open_browser)
port = 0
browse(port, open_browser=open_browser)
return return
if not args: raise BadUsage if not args: raise BadUsage
...@@ -2679,14 +2685,17 @@ def cli(): ...@@ -2679,14 +2685,17 @@ def cli():
{cmd} -k <keyword> {cmd} -k <keyword>
Search for a keyword in the synopsis lines of all available modules. 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> {cmd} -p <port>
Start an HTTP server on the given port on the local machine. 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. number 0 can be used to get an arbitrary unused port.
{cmd} -b {cmd} -b
Start an HTTP server on an arbitrary unused port and open a Web browser 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 to interactively browse documentation. This option can be used in
the -b option to explicitly specify the server port. combination with -n and/or -p.
{cmd} -w <name> ... {cmd} -w <name> ...
Write out the HTML documentation for a module to a file in the current Write out the HTML documentation for a module to a file in the current
......
...@@ -909,8 +909,8 @@ class PydocServerTest(unittest.TestCase): ...@@ -909,8 +909,8 @@ class PydocServerTest(unittest.TestCase):
text = 'the URL sent was: (%s, %s)' % (url, content_type) text = 'the URL sent was: (%s, %s)' % (url, content_type)
return text return text
serverthread = pydoc._start_server(my_url_handler, port=0) serverthread = pydoc._start_server(my_url_handler, hostname='0.0.0.0', port=0)
self.assertIn('localhost', serverthread.docserver.address) self.assertIn('0.0.0.0', serverthread.docserver.address)
starttime = time.time() starttime = time.time()
timeout = 1 #seconds timeout = 1 #seconds
......
...@@ -1177,6 +1177,7 @@ Claude Paroz ...@@ -1177,6 +1177,7 @@ Claude Paroz
Heikki Partanen Heikki Partanen
Harri Pasanen Harri Pasanen
Gaël Pasgrimaud Gaël Pasgrimaud
Feanil Patel
Ashish Nitin Patil Ashish Nitin Patil
Alecsandru Patrascu Alecsandru Patrascu
Randy Pausch Randy Pausch
......
Allow the pydoc server to bind to arbitrary hostnames.
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