From 6a396c9807b1674a24e240731f18e20de97117a5 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Thu, 14 Sep 2017 17:54:09 -0400 Subject: [PATCH] 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. --- Doc/library/pydoc.rst | 9 ++++ Lib/pydoc.py | 43 +++++++++++-------- Lib/test/test_pydoc.py | 4 +- Misc/ACKS | 1 + .../2017-08-31.bpo-31128.uoa3cr.rst | 1 + 5 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 Misc/NEWS.d/next/Documentation/2017-08-31.bpo-31128.uoa3cr.rst diff --git a/Doc/library/pydoc.rst b/Doc/library/pydoc.rst index f1bfab9a3b1..f956b9d2176 100644 --- a/Doc/library/pydoc.rst +++ b/Doc/library/pydoc.rst @@ -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 ` 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. diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 5edc77005a8..8dc3c0ace3c 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -16,12 +16,15 @@ backslash on Windows) it is treated as the path to a Python source file. Run "pydoc -k " to search for a keyword in the synopsis lines of all available modules. +Run "pydoc -n " to start an HTTP server with the given +hostname (default: localhost) on the local machine. + Run "pydoc -p " 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 " to write out the HTML documentation for a module to a file named ".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 Search for a keyword in the synopsis lines of all available modules. +{cmd} -n + Start an HTTP server with the given hostname (default: localhost). + {cmd} -p 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 ... Write out the HTML documentation for a module to a file in the current diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 1ac08edb48e..52830b49aea 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -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 diff --git a/Misc/ACKS b/Misc/ACKS index 462a74eb056..0f6ac7d898a 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1177,6 +1177,7 @@ Claude Paroz Heikki Partanen Harri Pasanen Gaƫl Pasgrimaud +Feanil Patel Ashish Nitin Patil Alecsandru Patrascu Randy Pausch diff --git a/Misc/NEWS.d/next/Documentation/2017-08-31.bpo-31128.uoa3cr.rst b/Misc/NEWS.d/next/Documentation/2017-08-31.bpo-31128.uoa3cr.rst new file mode 100644 index 00000000000..480ec6b2869 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2017-08-31.bpo-31128.uoa3cr.rst @@ -0,0 +1 @@ +Allow the pydoc server to bind to arbitrary hostnames.