fix port handling (#4773)

* handle port better

* setting port via envvar is possible again

* change default deploy_url and api_url

* fix for review

* update docstring

* type new envvar as optional
This commit is contained in:
Thomas Brandého 2025-02-12 19:06:01 +01:00 committed by GitHub
parent 977e1dcb67
commit dd5b817f0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 48 additions and 26 deletions

View File

@ -562,6 +562,12 @@ class EnvironmentVariables:
# Whether to run the frontend only. Exclusive with REFLEX_BACKEND_ONLY.
REFLEX_FRONTEND_ONLY: EnvVar[bool] = env_var(False)
# The port to run the frontend on.
REFLEX_FRONTEND_PORT: EnvVar[int | None] = env_var(None)
# The port to run the backend on.
REFLEX_BACKEND_PORT: EnvVar[int | None] = env_var(None)
# Reflex internal env to reload the config.
RELOAD_CONFIG: EnvVar[bool] = env_var(False, internal=True)
@ -640,19 +646,21 @@ class Config(Base):
loglevel: constants.LogLevel = constants.LogLevel.DEFAULT
# The port to run the frontend on. NOTE: When running in dev mode, the next available port will be used if this is taken.
frontend_port: int = constants.DefaultPorts.FRONTEND_PORT
frontend_port: int | None = None
# The path to run the frontend on. For example, "/app" will run the frontend on http://localhost:3000/app
frontend_path: str = ""
# The port to run the backend on. NOTE: When running in dev mode, the next available port will be used if this is taken.
backend_port: int = constants.DefaultPorts.BACKEND_PORT
backend_port: int | None = None
# The backend url the frontend will connect to. This must be updated if the backend is hosted elsewhere, or in production.
api_url: str = f"http://localhost:{backend_port}"
api_url: str = f"http://localhost:{constants.DefaultPorts.BACKEND_PORT}"
# The url the frontend will be hosted on.
deploy_url: Optional[str] = f"http://localhost:{frontend_port}"
deploy_url: Optional[str] = (
f"http://localhost:{constants.DefaultPorts.FRONTEND_PORT}"
)
# The url the backend will be hosted on.
backend_host: str = "0.0.0.0"

View File

@ -127,8 +127,8 @@ def _run(
env: constants.Env = constants.Env.DEV,
frontend: bool = True,
backend: bool = True,
frontend_port: int = config.frontend_port,
backend_port: int = config.backend_port,
frontend_port: int | None = None,
backend_port: int | None = None,
backend_host: str = config.backend_host,
loglevel: constants.LogLevel = config.loglevel,
):
@ -158,17 +158,28 @@ def _run(
# Find the next available open port if applicable.
if frontend:
auto_increment_frontend = not bool(frontend_port or config.frontend_port)
frontend_port = processes.handle_port(
"frontend",
frontend_port,
constants.DefaultPorts.FRONTEND_PORT,
(
frontend_port
or config.frontend_port
or constants.DefaultPorts.FRONTEND_PORT
),
auto_increment=auto_increment_frontend,
)
if backend:
auto_increment_backend = not bool(backend_port or config.backend_port)
backend_port = processes.handle_port(
"backend",
backend_port,
constants.DefaultPorts.BACKEND_PORT,
(
backend_port
or config.backend_port
or constants.DefaultPorts.BACKEND_PORT
),
auto_increment=auto_increment_backend,
)
# Apply the new ports to the config.
@ -246,7 +257,7 @@ def _run(
# Start the frontend and backend.
with processes.run_concurrently_context(*commands):
# In dev mode, run the backend on the main thread.
if backend and env == constants.Env.DEV:
if backend and backend_port and env == constants.Env.DEV:
backend_cmd(
backend_host, int(backend_port), loglevel.subprocess_level(), frontend
)
@ -275,10 +286,14 @@ def run(
envvar=environment.REFLEX_BACKEND_ONLY.name,
),
frontend_port: int = typer.Option(
config.frontend_port, help="Specify a different frontend port."
config.frontend_port,
help="Specify a different frontend port.",
envvar=environment.REFLEX_FRONTEND_PORT.name,
),
backend_port: int = typer.Option(
config.backend_port, help="Specify a different backend port."
config.backend_port,
help="Specify a different backend port.",
envvar=environment.REFLEX_BACKEND_PORT.name,
),
backend_host: str = typer.Option(
config.backend_host, help="Specify the backend host."

View File

@ -116,17 +116,14 @@ def change_port(port: int, _type: str) -> int:
return new_port
def handle_port(service_name: str, port: int, default_port: int) -> int:
def handle_port(service_name: str, port: int, auto_increment: bool) -> int:
"""Change port if the specified port is in use and is not explicitly specified as a CLI arg or config arg.
otherwise tell the user the port is in use and exit the app.
We make an assumption that when port is the default port,then it hasn't been explicitly set since its not straightforward
to know whether a port was explicitly provided by the user unless its any other than the default.
Otherwise tell the user the port is in use and exit the app.
Args:
service_name: The frontend or backend.
port: The provided port.
default_port: The default port number associated with the specified service.
auto_increment: Whether to automatically increment the port.
Returns:
The port to run the service on.
@ -134,13 +131,15 @@ def handle_port(service_name: str, port: int, default_port: int) -> int:
Raises:
Exit:when the port is in use.
"""
if is_process_on_port(port):
if port == int(default_port):
return change_port(port, service_name)
else:
console.error(f"{service_name.capitalize()} port: {port} is already in use")
raise typer.Exit()
return port
if (process := get_process_on_port(port)) is None:
return port
if auto_increment:
return change_port(port, service_name)
else:
console.error(
f"{service_name.capitalize()} port: {port} is already in use by PID: {process.pid}."
)
raise typer.Exit()
def new_process(