|
24 | 24 | from pathlib import Path |
25 | 25 | from typing import TYPE_CHECKING, Any, Literal, TypeVar |
26 | 26 |
|
27 | | -import psutil |
28 | | - |
29 | 27 | import reflex |
30 | 28 | import reflex.reflex |
31 | 29 | import reflex.utils.exec |
@@ -105,6 +103,24 @@ class ReflexProcessExitNonZeroError(RuntimeError): |
105 | 103 | """Exception raised when the reflex process exits with a non-zero status.""" |
106 | 104 |
|
107 | 105 |
|
| 106 | +def _is_port_responsive(port: int) -> bool: |
| 107 | + """Check if a port is responsive. |
| 108 | +
|
| 109 | + Args: |
| 110 | + port: the port to check |
| 111 | +
|
| 112 | + Returns: |
| 113 | + True if the port is responsive, False otherwise |
| 114 | + """ |
| 115 | + try: |
| 116 | + with contextlib.closing( |
| 117 | + socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 118 | + ) as sock: |
| 119 | + return sock.connect_ex(("127.0.0.1", port)) == 0 |
| 120 | + except (OverflowError, PermissionError, OSError): |
| 121 | + return False |
| 122 | + |
| 123 | + |
108 | 124 | @dataclasses.dataclass |
109 | 125 | class AppHarness: |
110 | 126 | """AppHarness executes a reflex app in-process for testing.""" |
@@ -366,29 +382,28 @@ def _wait_for_servers(self, backend: bool, frontend: bool): |
366 | 382 | msg = "Reflex process has no pid." |
367 | 383 | raise RuntimeError(msg) |
368 | 384 |
|
| 385 | + if backend and self.backend_port is None: |
| 386 | + msg = "Backend port is not set." |
| 387 | + raise RuntimeError(msg) |
| 388 | + |
| 389 | + if frontend and self.frontend_port is None: |
| 390 | + msg = "Frontend port is not set." |
| 391 | + raise RuntimeError(msg) |
| 392 | + |
369 | 393 | frontend_ready = False |
370 | 394 | backend_ready = False |
371 | | - timeout = 30 |
372 | | - start_time = time.time() |
373 | 395 |
|
374 | | - process = psutil.Process(self.reflex_process.pid) |
375 | 396 | while not ((not frontend or frontend_ready) and (not backend or backend_ready)): |
376 | | - if time.time() - start_time > timeout: |
377 | | - msg = f"Timeout waiting for servers. Frontend ready: {frontend_ready}, Backend ready: {backend_ready}" |
378 | | - raise RuntimeError(msg) |
379 | | - |
380 | | - for proc in process.children(recursive=True): |
381 | | - with contextlib.suppress(psutil.NoSuchProcess, psutil.AccessDenied): |
382 | | - if ncs := proc.net_connections(): |
383 | | - for net_conn in ncs: |
384 | | - if net_conn.status == psutil.CONN_LISTEN: |
385 | | - if net_conn.laddr.port == self.frontend_port: |
386 | | - frontend_ready = True |
387 | | - self.frontend_url = ( |
388 | | - f"http://localhost:{self.frontend_port}/" |
389 | | - ) |
390 | | - elif net_conn.laddr.port == self.backend_port: |
391 | | - backend_ready = True |
| 397 | + if backend and self.backend_port and _is_port_responsive(self.backend_port): |
| 398 | + backend_ready = True |
| 399 | + if ( |
| 400 | + frontend |
| 401 | + and self.frontend_port |
| 402 | + and _is_port_responsive(self.frontend_port) |
| 403 | + ): |
| 404 | + frontend_ready = True |
| 405 | + self.frontend_url = f"http://localhost:{self.frontend_port}/" |
| 406 | + time.sleep(POLL_INTERVAL) |
392 | 407 |
|
393 | 408 | async def _reset_backend_state_manager(self): |
394 | 409 | """Reset the StateManagerRedis event loop affinity. |
|
0 commit comments