New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Getting the CI to pass consistently on Windows (hopefully) #4624
Changes from 15 commits
3114e72
c625ca0
0053247
3b2eb79
8ae5985
0eb8538
4dbb07b
a2161fd
68074b3
4c620ed
26943d8
3f937f3
82b4e3a
4a22499
c39506d
9f2126f
5e5b83d
79a7e66
fa6da6c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
import requests | ||
import uvicorn | ||
|
||
from gradio.exceptions import ServerFailedToStartError | ||
from gradio.routes import App | ||
from gradio.tunneling import Tunnel | ||
|
||
|
@@ -35,8 +36,13 @@ def install_signal_handlers(self): | |
def run_in_thread(self): | ||
self.thread = threading.Thread(target=self.run, daemon=True) | ||
self.thread.start() | ||
start = time.time() | ||
while not self.started: | ||
time.sleep(1e-3) | ||
if time.time() - start > 5: | ||
raise ServerFailedToStartError( | ||
"Server failed to start. Please check that the port is available." | ||
) | ||
|
||
def close(self): | ||
self.should_exit = True | ||
|
@@ -108,36 +114,12 @@ def start_server( | |
app: the FastAPI app object | ||
server: the server object that is a subclass of uvicorn.Server (used to close the server) | ||
""" | ||
server_name = server_name or LOCALHOST_NAME | ||
# if port is not specified, search for first available port | ||
if server_port is None: | ||
port = get_first_available_port( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't removed this method |
||
INITIAL_PORT_VALUE, INITIAL_PORT_VALUE + TRY_NUM_PORTS | ||
) | ||
else: | ||
try: | ||
s = socket.socket() | ||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||
s.bind((LOCALHOST_NAME, server_port)) | ||
s.close() | ||
except OSError as err: | ||
raise OSError( | ||
f"Port {server_port} is in use. If a gradio.Blocks is running on the port, " | ||
f"you can close() it or gradio.close_all()." | ||
) from err | ||
port = server_port | ||
if ssl_keyfile is not None and ssl_certfile is None: | ||
raise ValueError("ssl_certfile must be provided if ssl_keyfile is provided.") | ||
|
||
server_name = server_name or LOCALHOST_NAME | ||
url_host_name = "localhost" if server_name == "0.0.0.0" else server_name | ||
|
||
if ssl_keyfile is not None: | ||
if ssl_certfile is None: | ||
raise ValueError( | ||
"ssl_certfile must be provided if ssl_keyfile is provided." | ||
) | ||
path_to_local_server = f"https://{url_host_name}:{port}/" | ||
else: | ||
path_to_local_server = f"http://{url_host_name}:{port}/" | ||
|
||
# Strip IPv6 brackets from the address if they exist. | ||
# This is needed as http://[::1]:port/ is a valid browser address, | ||
# but not a valid IPv6 address, so asyncio will throw an exception. | ||
|
@@ -148,20 +130,52 @@ def start_server( | |
|
||
app = App.create_app(blocks, app_kwargs=app_kwargs) | ||
|
||
if blocks.save_to is not None: # Used for selenium tests | ||
blocks.save_to["port"] = port | ||
config = uvicorn.Config( | ||
app=app, | ||
port=port, | ||
host=host, | ||
log_level="warning", | ||
ssl_keyfile=ssl_keyfile, | ||
ssl_certfile=ssl_certfile, | ||
ssl_keyfile_password=ssl_keyfile_password, | ||
ws_max_size=1024 * 1024 * 1024, # Setting max websocket size to be 1 GB | ||
# if port is not specified, search for an open port in the range of 7860 to 7959 | ||
server_ports = ( | ||
[server_port] | ||
if server_port is not None | ||
else range(INITIAL_PORT_VALUE, INITIAL_PORT_VALUE + TRY_NUM_PORTS) | ||
) | ||
server = Server(config=config) | ||
server.run_in_thread() | ||
|
||
for port in server_ports: | ||
try: | ||
# The fastest way to check if a port is available is to try to bind to it with socket. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for the copious comments, but I wanted to make sure the new logic is clear to someone who is reading the code for the first time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I appreciate it! |
||
# If the port is not available, socket will throw an OSError. | ||
s = socket.socket() | ||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||
# Really, we should be checking if (server_name, server_port) is available, but | ||
# socket.bind() doesn't seem to throw an OSError with ipv6 addresses, based on my testing. | ||
# Instead, we just check if the port is available on localhost. | ||
s.bind((LOCALHOST_NAME, port)) | ||
s.close() | ||
|
||
# To avoid race conditions, so we also check if the port by trying to start the uvicorn server. | ||
# If the port is not available, this will throw a ServerFailedToStartError. | ||
config = uvicorn.Config( | ||
app=app, | ||
port=port, | ||
host=host, | ||
log_level="warning", | ||
ssl_keyfile=ssl_keyfile, | ||
ssl_certfile=ssl_certfile, | ||
ssl_keyfile_password=ssl_keyfile_password, | ||
ws_max_size=1024 * 1024 * 1024, # Setting max websocket size to be 1 GB | ||
) | ||
server = Server(config=config) | ||
server.run_in_thread() | ||
break | ||
except (OSError, ServerFailedToStartError): | ||
pass | ||
else: | ||
raise OSError( | ||
f"Cannot find empty port in range: {min(server_ports)}-{max(server_ports)}. You can specify a different port by setting the GRADIO_SERVER_PORT environment variable or passing the `server_port` parameter to `launch()`." | ||
) | ||
|
||
if ssl_keyfile is not None: | ||
path_to_local_server = f"https://{url_host_name}:{port}/" | ||
else: | ||
path_to_local_server = f"http://{url_host_name}:{port}/" | ||
|
||
return server_name, port, path_to_local_server, app, server | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It takes <10 ms on my computer to start the server. So 5 seconds is a very generous allotment for slow computers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Might be good to add "to start within 5 seconds." for more context. Will push this up since you're away
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this message is not displayed to the user so won't make the change