Skip to content

Commit 6a396c9

Browse files
feanilmerwok
authored andcommitted
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.
1 parent ccb3c76 commit 6a396c9

File tree

5 files changed

+39
-19
lines changed

5 files changed

+39
-19
lines changed

Doc/library/pydoc.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ will start a HTTP server on port 1234, allowing you to browse the
7070
documentation at ``http://localhost:1234/`` in your preferred Web browser.
7171
Specifying ``0`` as the port number will select an arbitrary unused port.
7272

73+
:program:`pydoc -n <hostname>` will start the server listening at the given
74+
hostname. By default the hostname is 'localhost' but if you want the server to
75+
be reached from other machines, you may want to change the host name that the
76+
server responds to. During development this is especially useful if you want
77+
to run pydoc from within a container.
78+
7379
:program:`pydoc -b` will start the server and additionally open a web
7480
browser to a module index page. Each served page has a navigation bar at the
7581
top where you can *Get* help on an individual item, *Search* all modules with a
@@ -98,3 +104,6 @@ Reference Manual pages.
98104
:mod:`pydoc` now uses :func:`inspect.signature` rather than
99105
:func:`inspect.getfullargspec` to extract signature information from
100106
callables.
107+
108+
.. versionchanged:: 3.7
109+
Added the ``-n`` option.

Lib/pydoc.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ class or function within a module or module in a package. If the
1616
Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
1717
of all available modules.
1818
19+
Run "pydoc -n <hostname>" to start an HTTP server with the given
20+
hostname (default: localhost) on the local machine.
21+
1922
Run "pydoc -p <port>" to start an HTTP server on the given port on the
2023
local machine. Port number 0 can be used to get an arbitrary unused port.
2124
2225
Run "pydoc -b" to start an HTTP server on an arbitrary unused port and
23-
open a Web browser to interactively browse documentation. The -p option
24-
can be used with the -b option to explicitly specify the server port.
26+
open a Web browser to interactively browse documentation. Combine with
27+
the -n and -p options to control the hostname and port used.
2528
2629
Run "pydoc -w <name>" to write out the HTML documentation for a module
2730
to a file named "<name>.html".
@@ -2162,7 +2165,7 @@ def onerror(modname):
21622165

21632166
# --------------------------------------- enhanced Web browser interface
21642167

2165-
def _start_server(urlhandler, port):
2168+
def _start_server(urlhandler, hostname, port):
21662169
"""Start an HTTP server thread on a specific port.
21672170
21682171
Start an HTML/text server thread, so HTML or text documents can be
@@ -2247,8 +2250,8 @@ def log_message(self, *args):
22472250

22482251
class DocServer(http.server.HTTPServer):
22492252

2250-
def __init__(self, port, callback):
2251-
self.host = 'localhost'
2253+
def __init__(self, host, port, callback):
2254+
self.host = host
22522255
self.address = (self.host, port)
22532256
self.callback = callback
22542257
self.base.__init__(self, self.address, self.handler)
@@ -2268,8 +2271,9 @@ def server_activate(self):
22682271

22692272
class ServerThread(threading.Thread):
22702273

2271-
def __init__(self, urlhandler, port):
2274+
def __init__(self, urlhandler, host, port):
22722275
self.urlhandler = urlhandler
2276+
self.host = host
22732277
self.port = int(port)
22742278
threading.Thread.__init__(self)
22752279
self.serving = False
@@ -2282,7 +2286,7 @@ def run(self):
22822286
DocServer.handler = DocHandler
22832287
DocHandler.MessageClass = email.message.Message
22842288
DocHandler.urlhandler = staticmethod(self.urlhandler)
2285-
docsvr = DocServer(self.port, self.ready)
2289+
docsvr = DocServer(self.host, self.port, self.ready)
22862290
self.docserver = docsvr
22872291
docsvr.serve_until_quit()
22882292
except Exception as e:
@@ -2304,7 +2308,7 @@ def stop(self):
23042308
self.serving = False
23052309
self.url = None
23062310

2307-
thread = ServerThread(urlhandler, port)
2311+
thread = ServerThread(urlhandler, hostname, port)
23082312
thread.start()
23092313
# Wait until thread.serving is True to make sure we are
23102314
# really up before returning.
@@ -2568,14 +2572,14 @@ def get_html_page(url):
25682572
raise TypeError('unknown content type %r for url %s' % (content_type, url))
25692573

25702574

2571-
def browse(port=0, *, open_browser=True):
2575+
def browse(port=0, *, open_browser=True, hostname='localhost'):
25722576
"""Start the enhanced pydoc Web server and open a Web browser.
25732577
25742578
Use port '0' to start the server on an arbitrary port.
25752579
Set open_browser to False to suppress opening a browser.
25762580
"""
25772581
import webbrowser
2578-
serverthread = _start_server(_url_handler, port)
2582+
serverthread = _start_server(_url_handler, hostname, port)
25792583
if serverthread.error:
25802584
print(serverthread.error)
25812585
return
@@ -2622,11 +2626,12 @@ class BadUsage(Exception): pass
26222626
sys.path.insert(0, '.')
26232627

26242628
try:
2625-
opts, args = getopt.getopt(sys.argv[1:], 'bk:p:w')
2629+
opts, args = getopt.getopt(sys.argv[1:], 'bk:n:p:w')
26262630
writing = False
26272631
start_server = False
26282632
open_browser = False
2629-
port = None
2633+
port = 0
2634+
hostname = 'localhost'
26302635
for opt, val in opts:
26312636
if opt == '-b':
26322637
start_server = True
@@ -2639,11 +2644,12 @@ class BadUsage(Exception): pass
26392644
port = val
26402645
if opt == '-w':
26412646
writing = True
2647+
if opt == '-n':
2648+
start_server = True
2649+
hostname = val
26422650

26432651
if start_server:
2644-
if port is None:
2645-
port = 0
2646-
browse(port, open_browser=open_browser)
2652+
browse(port, hostname=hostname, open_browser=open_browser)
26472653
return
26482654

26492655
if not args: raise BadUsage
@@ -2679,14 +2685,17 @@ class BadUsage(Exception): pass
26792685
{cmd} -k <keyword>
26802686
Search for a keyword in the synopsis lines of all available modules.
26812687
2688+
{cmd} -n <hostname>
2689+
Start an HTTP server with the given hostname (default: localhost).
2690+
26822691
{cmd} -p <port>
26832692
Start an HTTP server on the given port on the local machine. Port
26842693
number 0 can be used to get an arbitrary unused port.
26852694
26862695
{cmd} -b
26872696
Start an HTTP server on an arbitrary unused port and open a Web browser
2688-
to interactively browse documentation. The -p option can be used with
2689-
the -b option to explicitly specify the server port.
2697+
to interactively browse documentation. This option can be used in
2698+
combination with -n and/or -p.
26902699
26912700
{cmd} -w <name> ...
26922701
Write out the HTML documentation for a module to a file in the current

Lib/test/test_pydoc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -909,8 +909,8 @@ def my_url_handler(url, content_type):
909909
text = 'the URL sent was: (%s, %s)' % (url, content_type)
910910
return text
911911

912-
serverthread = pydoc._start_server(my_url_handler, port=0)
913-
self.assertIn('localhost', serverthread.docserver.address)
912+
serverthread = pydoc._start_server(my_url_handler, hostname='0.0.0.0', port=0)
913+
self.assertIn('0.0.0.0', serverthread.docserver.address)
914914

915915
starttime = time.time()
916916
timeout = 1 #seconds

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,7 @@ Claude Paroz
11771177
Heikki Partanen
11781178
Harri Pasanen
11791179
Gaël Pasgrimaud
1180+
Feanil Patel
11801181
Ashish Nitin Patil
11811182
Alecsandru Patrascu
11821183
Randy Pausch
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow the pydoc server to bind to arbitrary hostnames.

0 commit comments

Comments
 (0)