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. # Whether to run the frontend only. Exclusive with REFLEX_BACKEND_ONLY.
REFLEX_FRONTEND_ONLY: EnvVar[bool] = env_var(False) 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. # Reflex internal env to reload the config.
RELOAD_CONFIG: EnvVar[bool] = env_var(False, internal=True) RELOAD_CONFIG: EnvVar[bool] = env_var(False, internal=True)
@ -640,19 +646,21 @@ class Config(Base):
loglevel: constants.LogLevel = constants.LogLevel.DEFAULT 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. # 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 # The path to run the frontend on. For example, "/app" will run the frontend on http://localhost:3000/app
frontend_path: str = "" 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. # 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. # 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. # 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. # The url the backend will be hosted on.
backend_host: str = "0.0.0.0" backend_host: str = "0.0.0.0"

View File

@ -127,8 +127,8 @@ def _run(
env: constants.Env = constants.Env.DEV, env: constants.Env = constants.Env.DEV,
frontend: bool = True, frontend: bool = True,
backend: bool = True, backend: bool = True,
frontend_port: int = config.frontend_port, frontend_port: int | None = None,
backend_port: int = config.backend_port, backend_port: int | None = None,
backend_host: str = config.backend_host, backend_host: str = config.backend_host,
loglevel: constants.LogLevel = config.loglevel, loglevel: constants.LogLevel = config.loglevel,
): ):
@ -158,17 +158,28 @@ def _run(
# Find the next available open port if applicable. # Find the next available open port if applicable.
if frontend: if frontend:
auto_increment_frontend = not bool(frontend_port or config.frontend_port)
frontend_port = processes.handle_port( frontend_port = processes.handle_port(
"frontend", "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: if backend:
auto_increment_backend = not bool(backend_port or config.backend_port)
backend_port = processes.handle_port( backend_port = processes.handle_port(
"backend", "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. # Apply the new ports to the config.
@ -246,7 +257,7 @@ def _run(
# Start the frontend and backend. # Start the frontend and backend.
with processes.run_concurrently_context(*commands): with processes.run_concurrently_context(*commands):
# In dev mode, run the backend on the main thread. # 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_cmd(
backend_host, int(backend_port), loglevel.subprocess_level(), frontend backend_host, int(backend_port), loglevel.subprocess_level(), frontend
) )
@ -275,10 +286,14 @@ def run(
envvar=environment.REFLEX_BACKEND_ONLY.name, envvar=environment.REFLEX_BACKEND_ONLY.name,
), ),
frontend_port: int = typer.Option( 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( 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( backend_host: str = typer.Option(
config.backend_host, help="Specify the backend host." 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 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. """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. 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.
Args: Args:
service_name: The frontend or backend. service_name: The frontend or backend.
port: The provided port. port: The provided port.
default_port: The default port number associated with the specified service. auto_increment: Whether to automatically increment the port.
Returns: Returns:
The port to run the service on. The port to run the service on.
@ -134,13 +131,15 @@ def handle_port(service_name: str, port: int, default_port: int) -> int:
Raises: Raises:
Exit:when the port is in use. Exit:when the port is in use.
""" """
if is_process_on_port(port): if (process := get_process_on_port(port)) is None:
if port == int(default_port): return port
return change_port(port, service_name) if auto_increment:
else: return change_port(port, service_name)
console.error(f"{service_name.capitalize()} port: {port} is already in use") else:
raise typer.Exit() console.error(
return port f"{service_name.capitalize()} port: {port} is already in use by PID: {process.pid}."
)
raise typer.Exit()
def new_process( def new_process(