From 18acd0a82919e7e98440dec0d531b88e47c79d00 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 25 Apr 2025 11:45:52 -0700 Subject: [PATCH 1/6] delay api mounting until app finishes compile --- reflex/app.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/reflex/app.py b/reflex/app.py index 34ee12689b2..727db8ae91e 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -613,6 +613,20 @@ def __call__(self) -> ASGIApp: Returns: The backend api. """ + # For py3.9 compatibility when redis is used, we MUST add any decorator pages + # before compiling the app in a thread to avoid event loop error (REF-2172). + self._apply_decorated_pages() + + compile_future = concurrent.futures.ThreadPoolExecutor(max_workers=1).submit( + self._compile + ) + compile_future.add_done_callback( + # Force background compile errors to print eagerly + lambda f: f.result() + ) + # Wait for the compile to finish to ensure all optional endpoints are mounted. + compile_future.result() + if self._cached_fastapi_app is not None: asgi_app = self._cached_fastapi_app @@ -626,21 +640,6 @@ def __call__(self) -> ASGIApp: if not asgi_app: raise ValueError("The app has not been initialized.") - # For py3.9 compatibility when redis is used, we MUST add any decorator pages - # before compiling the app in a thread to avoid event loop error (REF-2172). - self._apply_decorated_pages() - - compile_future = concurrent.futures.ThreadPoolExecutor(max_workers=1).submit( - self._compile - ) - compile_future.add_done_callback( - # Force background compile errors to print eagerly - lambda f: f.result() - ) - # Wait for the compile to finish in prod mode to ensure all optional endpoints are mounted. - if is_prod_mode(): - compile_future.result() - if self.api_transformer is not None: api_transformers: Sequence[Starlette | Callable[[ASGIApp], ASGIApp]] = ( [self.api_transformer] From 8a8c0405557f44894b73bb2bb87ce2ca35aacefb Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 25 Apr 2025 12:11:16 -0700 Subject: [PATCH 2/6] mock _compile --- tests/units/test_app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/units/test_app.py b/tests/units/test_app.py index 1e9fa33f1b3..7857e1606c8 100644 --- a/tests/units/test_app.py +++ b/tests/units/test_app.py @@ -1502,6 +1502,7 @@ def test_raise_on_state(): def test_call_app(): """Test that the app can be called.""" app = App() + app._compile = unittest.mock.Mock() api = app() assert isinstance(api, Starlette) From f683ea8db2b50fa22f726c483f7836bfba82e4fe Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 25 Apr 2025 16:56:12 -0700 Subject: [PATCH 3/6] add cors --- reflex/app.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/reflex/app.py b/reflex/app.py index 727db8ae91e..523a8c93978 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -489,7 +489,7 @@ def __post_init__(self): # Set up the API. self._api = Starlette(lifespan=self._run_lifespan_tasks) - self._add_cors() + App._add_cors(self._api) self._add_default_endpoints() for clz in App.__mro__: @@ -650,6 +650,7 @@ def __call__(self) -> ASGIApp: for api_transformer in api_transformers: if isinstance(api_transformer, Starlette): # Mount the api to the fastapi app. + App._add_cors(api_transformer) api_transformer.mount("", asgi_app) asgi_app = api_transformer else: @@ -708,11 +709,10 @@ def _add_optional_endpoints(self): if environment.REFLEX_ADD_ALL_ROUTES_ENDPOINT.get(): self.add_all_routes_endpoint() - def _add_cors(self): + @staticmethod + def _add_cors(api: Starlette): """Add CORS middleware to the app.""" - if not self._api: - return - self._api.add_middleware( + api.add_middleware( cors.CORSMiddleware, allow_credentials=True, allow_methods=["*"], From 817da2cb25421c66eccfca90bd6ccf7c5251b98a Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 25 Apr 2025 17:01:49 -0700 Subject: [PATCH 4/6] add cors to asgi app --- reflex/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reflex/app.py b/reflex/app.py index 523a8c93978..c4ae2f888f2 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -634,6 +634,8 @@ def __call__(self) -> ASGIApp: raise ValueError("The app has not been initialized.") asgi_app.mount("", self._api) + + App._add_cors(asgi_app) else: asgi_app = self._api From aa41d319141f66a9a9c966c208710673c9841ca3 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 25 Apr 2025 17:12:28 -0700 Subject: [PATCH 5/6] dang it darglint --- reflex/app.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/reflex/app.py b/reflex/app.py index c4ae2f888f2..777f78e7506 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -713,7 +713,11 @@ def _add_optional_endpoints(self): @staticmethod def _add_cors(api: Starlette): - """Add CORS middleware to the app.""" + """Add CORS middleware to the app. + + Args: + api: The Starlette app to add CORS middleware to. + """ api.add_middleware( cors.CORSMiddleware, allow_credentials=True, From e9a94ae23d60e96738371bda3cc479fba9f47fda Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 25 Apr 2025 17:43:36 -0700 Subject: [PATCH 6/6] refactor code to make it more readable thanks to @masenf --- reflex/app.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/reflex/app.py b/reflex/app.py index 777f78e7506..f8f1a63ef76 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -627,21 +627,15 @@ def __call__(self) -> ASGIApp: # Wait for the compile to finish to ensure all optional endpoints are mounted. compile_future.result() + if not self._api: + raise ValueError("The app has not been initialized.") if self._cached_fastapi_app is not None: asgi_app = self._cached_fastapi_app - - if not asgi_app or not self._api: - raise ValueError("The app has not been initialized.") - asgi_app.mount("", self._api) - App._add_cors(asgi_app) else: asgi_app = self._api - if not asgi_app: - raise ValueError("The app has not been initialized.") - if self.api_transformer is not None: api_transformers: Sequence[Starlette | Callable[[ASGIApp], ASGIApp]] = ( [self.api_transformer]