diff --git a/pyproject.toml b/pyproject.toml index 460e5130354..8adb2c651da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ keywords = ["web", "framework"] requires-python = ">=3.10,<4.0" dependencies = [ "alembic >=1.15.2,<2.0", - "fastapi >=0.115.0", + "click >=8.2", "granian[reload] >=2.4.0", "httpx >=0.28.0,<1.0", "jinja2 >=3.1.2,<4.0", @@ -29,13 +29,13 @@ dependencies = [ "platformdirs >=4.3.7,<5.0", "psutil >=7.0.0,<8.0; sys_platform == 'win32'", "pydantic >=1.10.21,<3.0", - "python-socketio >=5.12.0,<6.0", "python-multipart >=0.0.20,<1.0", + "python-socketio >=5.12.0,<6.0", "redis >=5.2.1,<7.0", "reflex-hosting-cli >=0.1.51", "rich >=13,<15", "sqlmodel >=0.0.24,<0.1", - "click >=8.2", + "starlette >=0.47.0", "typing_extensions >=4.13.0", "wrapt >=1.17.0,<2.0", ] @@ -58,18 +58,49 @@ documentation = "https://reflex.dev/docs/getting-started/introduction" [project.scripts] reflex = "reflex.reflex:cli" +[dependency-groups] +dev = [ + "asynctest", + "darglint", + "dill", + "fastapi", + "hatchling", + "libsass", + "numpy", + "pandas", + "pillow", + "playwright", + "plotly", + "pre-commit", + "psutil", + "psycopg[binary]", + "pyright", + "pytest-asyncio", + "pytest-benchmark", + "pytest-codspeed", + "pytest-cov", + "pytest-mock", + "pytest-playwright", + "pytest-retry", + "pytest-split", + "pytest", + "python-dotenv", + "ruff", + "selenium", + "starlette-admin", + "toml", + "uvicorn", +] + + [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build] include = ["reflex", "scripts/hatch_build.py"] - -[tool.hatch.build.targets.sdist] -artifacts = ["*.pyi"] - -[tool.hatch.build.targets.wheel] -artifacts = ["*.pyi"] +targets.sdist.artifacts = ["*.pyi"] +targets.wheel.artifacts = ["*.pyi"] [tool.hatch.build.hooks.custom] path = "scripts/hatch_build.py" @@ -152,39 +183,6 @@ asyncio_mode = "auto" skip = "docs/*,*.html,examples/*, *.pyi, poetry.lock, uv.lock" ignore-words-list = "te, TreeE" -[dependency-groups] -dev = [ - "asynctest >=0.13", - "darglint >=1.8", - "dill >=0.3", - "hatchling >=1.27", - "libsass >=0.23", - "numpy >=2.2", - "pandas >=2.2", - "pillow >=11", - "playwright >=1.51", - "plotly >=6.0", - "pre-commit ==4.2.0", - "psutil >=7.0.0", - "psycopg[binary] >=3.2", - "pyright >=1.1.400", - "pytest >=8.3", - "pytest-asyncio >=0.26", - "pytest-benchmark >=5.1", - "pytest-codspeed >=3.2", - "pytest-cov >=6.1", - "pytest-mock >=3.14", - "pytest-playwright >=0.7", - "pytest-retry >=1.7", - "pytest-split >=0.10", - "python-dotenv >=1", - "ruff >=0.11", - "selenium >=4.31", - "starlette-admin >=0.14", - "toml >=0.10.2", - "uvicorn >=0.34.0", -] - [tool.coverage.run] source = ["reflex"] diff --git a/reflex/app.py b/reflex/app.py index f6e1a621591..0927146061d 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -28,7 +28,6 @@ from types import SimpleNamespace from typing import TYPE_CHECKING, Any, BinaryIO, ParamSpec, get_args, get_type_hints -from fastapi import FastAPI from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn from socketio import ASGIApp as EngineIOApp from socketio import AsyncNamespace, AsyncServer @@ -441,9 +440,6 @@ class App(MiddlewareMixin, LifespanMixin): | None ) = None - # FastAPI app for compatibility with FastAPI. - _cached_fastapi_app: FastAPI | None = None - @property def event_namespace(self) -> EventNamespace | None: """Get the event namespace. @@ -598,32 +594,22 @@ def __call__(self) -> ASGIApp: Returns: The backend api. """ + from reflex.vars.base import GLOBAL_CACHE + # 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, prerender_routes=is_prod_mode() - ) + self._compile(prerender_routes=is_prod_mode()) - def callback(f: concurrent.futures.Future): - # Force background compile errors to print eagerly - return f.result() - - compile_future.add_done_callback(callback) - # Wait for the compile to finish to ensure all optional endpoints are mounted. - compile_future.result() + # We will not be making more vars, so we can clear the global cache to free up memory. + GLOBAL_CACHE.clear() if not self._api: msg = "The app has not been initialized." raise ValueError(msg) - if self._cached_fastapi_app is not None: - asgi_app = self._cached_fastapi_app - asgi_app.mount("", self._api) - App._add_cors(asgi_app) - else: - asgi_app = self._api + asgi_app = self._api if self.api_transformer is not None: api_transformers: Sequence[Starlette | Callable[[ASGIApp], ASGIApp]] = ( diff --git a/reflex/utils/exec.py b/reflex/utils/exec.py index c76541151d7..d140b57242d 100644 --- a/reflex/utils/exec.py +++ b/reflex/utils/exec.py @@ -405,7 +405,10 @@ def get_reload_paths() -> Sequence[Path]: module_path = module_path.parent while module_path.parent.name and _has_child_file(module_path, "__init__.py"): - if _has_child_file(module_path, "rxconfig.py"): + if ( + _has_child_file(module_path, "rxconfig.py") + and module_path == Path.cwd() + ): init_file = module_path / "__init__.py" init_file_content = init_file.read_text() if init_file_content.strip(): diff --git a/tests/units/test_app.py b/tests/units/test_app.py index 4ffb919b051..b013da0df41 100644 --- a/tests/units/test_app.py +++ b/tests/units/test_app.py @@ -11,10 +11,10 @@ import pytest import sqlmodel -from fastapi.responses import StreamingResponse from pytest_mock import MockerFixture from starlette.applications import Starlette from starlette.datastructures import UploadFile +from starlette.responses import StreamingResponse from starlette_admin.auth import AuthProvider from starlette_admin.contrib.sqla.admin import Admin from starlette_admin.contrib.sqla.view import ModelView diff --git a/uv.lock b/uv.lock index 651e7dc6485..ed10ff6ac4d 100644 --- a/uv.lock +++ b/uv.lock @@ -1636,7 +1636,6 @@ source = { editable = "." } dependencies = [ { name = "alembic" }, { name = "click" }, - { name = "fastapi" }, { name = "granian", extra = ["reload"] }, { name = "httpx" }, { name = "jinja2" }, @@ -1650,6 +1649,7 @@ dependencies = [ { name = "reflex-hosting-cli" }, { name = "rich" }, { name = "sqlmodel" }, + { name = "starlette" }, { name = "typing-extensions" }, { name = "wrapt" }, ] @@ -1659,6 +1659,7 @@ dev = [ { name = "asynctest" }, { name = "darglint" }, { name = "dill" }, + { name = "fastapi" }, { name = "hatchling" }, { name = "libsass" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -1692,7 +1693,6 @@ dev = [ requires-dist = [ { name = "alembic", specifier = ">=1.15.2,<2.0" }, { name = "click", specifier = ">=8.2" }, - { name = "fastapi", specifier = ">=0.115.0" }, { name = "granian", extras = ["reload"], specifier = ">=2.4.0" }, { name = "httpx", specifier = ">=0.28.0,<1.0" }, { name = "jinja2", specifier = ">=3.1.2,<4.0" }, @@ -1706,41 +1706,43 @@ requires-dist = [ { name = "reflex-hosting-cli", specifier = ">=0.1.51" }, { name = "rich", specifier = ">=13,<15" }, { name = "sqlmodel", specifier = ">=0.0.24,<0.1" }, + { name = "starlette", specifier = ">=0.47.0" }, { name = "typing-extensions", specifier = ">=4.13.0" }, { name = "wrapt", specifier = ">=1.17.0,<2.0" }, ] [package.metadata.requires-dev] dev = [ - { name = "asynctest", specifier = ">=0.13" }, - { name = "darglint", specifier = ">=1.8" }, - { name = "dill", specifier = ">=0.3" }, - { name = "hatchling", specifier = ">=1.27" }, - { name = "libsass", specifier = ">=0.23" }, - { name = "numpy", specifier = ">=2.2" }, - { name = "pandas", specifier = ">=2.2" }, - { name = "pillow", specifier = ">=11" }, - { name = "playwright", specifier = ">=1.51" }, - { name = "plotly", specifier = ">=6.0" }, - { name = "pre-commit", specifier = "==4.2.0" }, - { name = "psutil", specifier = ">=7.0.0" }, - { name = "psycopg", extras = ["binary"], specifier = ">=3.2" }, - { name = "pyright", specifier = ">=1.1.400" }, - { name = "pytest", specifier = ">=8.3" }, - { name = "pytest-asyncio", specifier = ">=0.26" }, - { name = "pytest-benchmark", specifier = ">=5.1" }, - { name = "pytest-codspeed", specifier = ">=3.2" }, - { name = "pytest-cov", specifier = ">=6.1" }, - { name = "pytest-mock", specifier = ">=3.14" }, - { name = "pytest-playwright", specifier = ">=0.7" }, - { name = "pytest-retry", specifier = ">=1.7" }, - { name = "pytest-split", specifier = ">=0.10" }, - { name = "python-dotenv", specifier = ">=1" }, - { name = "ruff", specifier = ">=0.11" }, - { name = "selenium", specifier = ">=4.31" }, - { name = "starlette-admin", specifier = ">=0.14" }, - { name = "toml", specifier = ">=0.10.2" }, - { name = "uvicorn", specifier = ">=0.34.0" }, + { name = "asynctest" }, + { name = "darglint" }, + { name = "dill" }, + { name = "fastapi" }, + { name = "hatchling" }, + { name = "libsass" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "playwright" }, + { name = "plotly" }, + { name = "pre-commit" }, + { name = "psutil" }, + { name = "psycopg", extras = ["binary"] }, + { name = "pyright" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-benchmark" }, + { name = "pytest-codspeed" }, + { name = "pytest-cov" }, + { name = "pytest-mock" }, + { name = "pytest-playwright" }, + { name = "pytest-retry" }, + { name = "pytest-split" }, + { name = "python-dotenv" }, + { name = "ruff" }, + { name = "selenium" }, + { name = "starlette-admin" }, + { name = "toml" }, + { name = "uvicorn" }, ] [[package]]