Skip to content

Commit

Permalink
Merge pull request #1820 from bfroehle/port_selection_2
Browse files Browse the repository at this point in the history
NotebookApp: Make the number of ports to retry user configurable.
  • Loading branch information
ellisonbg committed Jun 1, 2012
2 parents 2ec1c56 + 6bd4e43 commit d15448f
Showing 1 changed file with 24 additions and 5 deletions.
29 changes: 24 additions & 5 deletions IPython/frontend/html/notebook/notebookapp.py
Expand Up @@ -20,6 +20,7 @@
import errno
import logging
import os
import random
import re
import select
import signal
Expand Down Expand Up @@ -97,6 +98,17 @@ def url_path_join(a,b):
else:
return a+b

def random_ports(port, n):
"""Generate a list of n random ports near the given port.
The first 5 ports will be sequential, and the remaining n-5 will be
randomly selected in the range [port-2*n, port+2*n].
"""
for i in range(min(5, n)):
yield port + i
for i in range(n-5):
yield port + random.randint(-2*n, 2*n)

#-----------------------------------------------------------------------------
# The Tornado web application
#-----------------------------------------------------------------------------
Expand Down Expand Up @@ -212,6 +224,7 @@ def __init__(self, ipython_app, kernel_manager, notebook_manager,
aliases.update({
'ip': 'NotebookApp.ip',
'port': 'NotebookApp.port',
'port-retries': 'NotebookApp.port_retries',
'keyfile': 'NotebookApp.keyfile',
'certfile': 'NotebookApp.certfile',
'notebook-dir': 'NotebookManager.notebook_dir',
Expand All @@ -222,7 +235,7 @@ def __init__(self, ipython_app, kernel_manager, notebook_manager,
# multi-kernel evironment:
aliases.pop('f', None)

notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile',
notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
u'notebook-dir']

#-----------------------------------------------------------------------------
Expand Down Expand Up @@ -272,6 +285,9 @@ def _ip_changed(self, name, old, new):
port = Integer(8888, config=True,
help="The port the notebook server will listen on."
)
port_retries = Integer(50, config=True,
help="The number of additional ports to try if the specified port is not available."
)

certfile = Unicode(u'', config=True,
help="""The full path to an SSL/TLS certificate file."""
Expand Down Expand Up @@ -422,10 +438,8 @@ def init_webapp(self):
'but not using any encryption or authentication. This is highly '
'insecure and not recommended.')

# Try random ports centered around the default.
from random import randint
n = 50 # Max number of attempts, keep reasonably large.
for port in range(self.port, self.port+5) + [self.port + randint(-2*n, 2*n) for i in range(n-5)]:
success = None
for port in random_ports(self.port, self.port_retries+1):
try:
self.http_server.listen(port, self.ip)
except socket.error, e:
Expand All @@ -434,7 +448,12 @@ def init_webapp(self):
self.log.info('The port %i is already in use, trying another random port.' % port)
else:
self.port = port
success = True
break
if not success:
self.log.critical('ERROR: the notebook server could not be started because '
'no available port could be found.')
self.exit(1)

def init_signal(self):
# FIXME: remove this check when pyzmq dependency is >= 2.1.11
Expand Down

0 comments on commit d15448f

Please sign in to comment.