瀏覽代碼

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
Thomas Brandého 3 月之前
父節點
當前提交
dd5b817f0f
共有 3 個文件被更改,包括 48 次插入26 次删除
  1. 12 4
      reflex/config.py
  2. 24 9
      reflex/reflex.py
  3. 12 13
      reflex/utils/processes.py

+ 12 - 4
reflex/config.py

@@ -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"

+ 24 - 9
reflex/reflex.py

@@ -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,
-    backend_port: int = config.backend_port,
+    frontend_port: int | None = None,
+    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."

+ 12 - 13
reflex/utils/processes.py

@@ -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.
-
-    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:
     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 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(
 def new_process(