Repository: xorbitsai/inference

Issue:
ENH: auto find available port for API
### Describe the bug
```
~ ❯ xinference                                                                                       6s  base 18:24:18
Traceback (most recent call last):
  File "/Users/hekaisheng/miniconda3/bin/xinference", line 33, in <module>
    sys.exit(load_entry_point('xinference', 'console_scripts', 'xinference')())
  File "/Users/hekaisheng/miniconda3/lib/python3.9/site-packages/click/core.py", line 1128, in __call__
    return self.main(*args, **kwargs)
  File "/Users/hekaisheng/miniconda3/lib/python3.9/site-packages/click/core.py", line 1053, in main
    rv = self.invoke(ctx)
  File "/Users/hekaisheng/miniconda3/lib/python3.9/site-packages/click/core.py", line 1637, in invoke
    super().invoke(ctx)
  File "/Users/hekaisheng/miniconda3/lib/python3.9/site-packages/click/core.py", line 1395, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/hekaisheng/miniconda3/lib/python3.9/site-packages/click/core.py", line 754, in invoke
    return __callback(*args, **kwargs)
  File "/Users/hekaisheng/miniconda3/lib/python3.9/site-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/Users/hekaisheng/Documents/projects/inference/xinference/deploy/cmdline.py", line 51, in cli
    main(
  File "/Users/hekaisheng/Documents/projects/inference/xinference/deploy/local.py", line 50, in main
    loop.run_until_complete(task)
  File "/Users/hekaisheng/miniconda3/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
    return future.result()
  File "/Users/hekaisheng/Documents/projects/inference/xinference/deploy/local.py", line 36, in _start_local_cluster
    url = await start_supervisor_components(address=address, host=host, port=port)
  File "/Users/hekaisheng/Documents/projects/inference/xinference/deploy/supervisor.py", line 35, in start_supervisor_components
    sock.bind((host, port))
OSError: [Errno 48] Address already in use
```

Use available port if users not specify.


Discussion:

Resolving patch:
diff --git a/xinference/deploy/supervisor.py b/xinference/deploy/supervisor.py
--- a/xinference/deploy/supervisor.py
+++ b/xinference/deploy/supervisor.py
@@ -18,7 +18,9 @@
 from typing import Dict, Optional
 
 import xoscar as xo
+from xoscar.utils import get_next_port
 
+from ..constants import XINFERENCE_DEFAULT_ENDPOINT_PORT
 from ..core.gradio import GradioApp
 from ..core.restful_api import RESTfulAPIActor
 from ..core.service import SupervisorActor
@@ -30,10 +32,26 @@ async def start_supervisor_components(address: str, host: str, port: int):
     await xo.create_actor(SupervisorActor, address=address, uid=SupervisorActor.uid())
     gradio_block = GradioApp(address).build()
     # create a socket for RESTful API
-    sockets = []
-    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    sock.bind((host, port))
-    sockets.append(sock)
+    try:
+        sockets = []
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        sock.bind((host, port))
+        sockets.append(sock)
+    except OSError:
+        if port is XINFERENCE_DEFAULT_ENDPOINT_PORT:
+            while True:
+                try:
+                    sockets = []
+                    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                    port = get_next_port()
+                    sock.bind((host, port))
+                    sockets.append(sock)
+                    break
+                except OSError:
+                    pass
+        else:
+            raise OSError
+
     restful_actor = await xo.create_actor(
         RESTfulAPIActor,
         address=address,


Test patch: