# comment out the line below to enable debug output
debug = lambda *args: None
+def freeport(portlist, expr):
+ Find a free server port to use. Specifically, evaluate 'expr' (a
+ callable(port)) until it stops raising EADDRINUSE exception.
+ portlist: an iterable (e.g. xrange()) of ports to try. If you exhaust the
+ range, freeport() lets the socket.error exception propagate. If you want
+ unbounded, you could pass itertools.count(baseport), though of course in
+ practice the ceiling is 2^16-1 anyway. But it seems prudent to constrain
+ the range much more sharply: if we're iterating an absurd number of times,
+ probably something else is wrong.
+ expr: a callable accepting a port number, specifically one of the items
+ from portlist. If calling that callable raises socket.error with
+ EADDRINUSE, freeport() retrieves the next item from portlist and retries.
+ Returns: (expr(port), port)
+ port: the value from portlist for which expr(port) succeeded
+ Any exception raised by expr(port) other than EADDRINUSE.
+ socket.error if, for every item from portlist, expr(port) raises
+ socket.error. The exception you see is the one from the last item in
+ StopIteration if portlist is completely empty.
+ server, port = freeport(xrange(8000, 8010),
+ lambda port: HTTPServer(("localhost", port),
+ # pass 'port' to client code
+ # call server.serve_forever()
+ # If portlist is completely empty, let StopIteration propagate: that's an
+ # error because we can't return meaningful values. We have no 'port',
+ # therefore no 'expr(port)'.
+ portiter = iter(portlist)
+ # If this value of port works, return as promised.
+ return expr(port), port
+ except socket.error, err:
+ # Anything other than 'Address already in use', propagate
+ if err.args != errno.EADDRINUSE:
+ # Here we want the next port from portiter. But on StopIteration,
+ # we want to raise the original exception rather than
+ # StopIteration. So save the original exc_info().
+ type, value, tb = sys.exc_info()
+ # Clean up local traceback, see docs for sys.exc_info()
+ # Recap of the control flow above:
+ # If expr(port) doesn't raise, return as promised.
+ # If expr(port) raises anything but EADDRINUSE, propagate that
+ # If portiter.next() raises StopIteration -- that is, if the port
+ # value we just passed to expr(port) was the last available -- reraise
+ # the EADDRINUSE exception.
+ # If we've actually arrived at this point, portiter.next() delivered a
+ # new port value. Loop back to pass that to expr(port).
"""All positional arguments collectively form a command line, executed as
a synchronous child process.