Create a server factory that performs port scanning.

  This function returns a callable whose signature matches the
  specification of `TensorBoardServer.__init__`, using `cls` as an
  underlying implementation. It passes through `flags` unchanged except
  in the case that `flags.port is None`, in which case it repeatedly
  instantiates the underlying server with new port suggestions.

  Args:
    cls: A valid implementation of `TensorBoardServer`. This class's
      initializer should raise a `TensorBoardPortInUseError` upon
      failing to bind to a port when it is expected that binding to
      another nearby port might succeed.

      The initializer for `cls` will only ever be invoked with `flags`
      such that `flags.port is not None`.

  Returns:
    A function that implements the `__init__` contract of
    `TensorBoardServer`.

def with_port_scanning(cls):
  """Create a server factory that performs port scanning.

  This function returns a callable whose signature matches the
  specification of `TensorBoardServer.__init__`, using `cls` as an
  underlying implementation. It passes through `flags` unchanged except
  in the case that `flags.port is None`, in which case it repeatedly
  instantiates the underlying server with new port suggestions.

  Args:
    cls: A valid implementation of `TensorBoardServer`. This class's
      initializer should raise a `TensorBoardPortInUseError` upon
      failing to bind to a port when it is expected that binding to
      another nearby port might succeed.

      The initializer for `cls` will only ever be invoked with `flags`
      such that `flags.port is not None`.

  Returns:
    A function that implements the `__init__` contract of
    `TensorBoardServer`.
  """

  def init(wsgi_app, flags):
    # base_port: what's the first port to which we should try to bind?
    # should_scan: if that fails, shall we try additional ports?
    # max_attempts: how many ports shall we try?
    should_scan = flags.port is None
    base_port = core_plugin.DEFAULT_PORT if flags.port is None else flags.port
    max_attempts = 10 if should_scan else 1

    if base_port > 0xFFFF:
      raise TensorBoardServerException(
          'TensorBoard cannot bind to port %d > %d' % (base_port, 0xFFFF)
      )
    max_attempts = 10 if should_scan else 1
    base_port = min(base_port + max_attempts, 0x10000) - max_attempts

    for port in xrange(base_port, base_port + max_attempts):
      subflags = argparse.Namespace(**vars(flags))
      subflags.port = port
      try:
        return cls(wsgi_app=wsgi_app, flags=subflags)
      except TensorBoardPortInUseError:
        if not should_scan:
          raise
    # All attempts failed to bind.
    raise TensorBoardServerException(
        'TensorBoard could not bind to any port around %s '
        '(tried %d times)'
        % (base_port, max_attempts))

  return init