Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/6974.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Make `reflex.testing` importable without test-only dependencies and provide a `testing` extra for `AppHarness`.
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ db = [
"sqlmodel >=0.0.24,<0.1",
]
pydantic = ["reflex-base[pydantic]"]
testing = [
"psutil >=7.0.0,<8.0",
"selenium >=4.0.0,<5.0",
"uvicorn >=0.34.0,<1.0",
]

[project.urls]
homepage = "https://reflex.dev"
Expand Down
35 changes: 33 additions & 2 deletions reflex/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
from pathlib import Path
from typing import TYPE_CHECKING, Any, ClassVar, Literal, TypeVar

import uvicorn
from reflex_base.components.memo import MEMOS
from reflex_base.config import get_config, reload_config
from reflex_base.environment import environment
Expand Down Expand Up @@ -61,12 +60,34 @@
except ImportError:
has_selenium = False

if TYPE_CHECKING:
import uvicorn
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.

# The timeout (minutes) to check for the port.
DEFAULT_TIMEOUT = 15
POLL_INTERVAL = 0.25
FRONTEND_POPEN_ARGS = {}
T = TypeVar("T")
TimeoutType = int | float | None


def _get_uvicorn():
"""Import uvicorn for an AppHarness server.

Returns:
The imported uvicorn module.
"""
try:
import uvicorn
except ImportError as exc:
msg = (
"AppHarness backend support requires `uvicorn`. Install it with "
"`pip install 'reflex[testing]'`."
)
raise ImportError(msg) from exc
return uvicorn


if platform.system() == "Windows":
FRONTEND_POPEN_ARGS["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP # pyright: ignore [reportAttributeAccessIssue]
FRONTEND_POPEN_ARGS["shell"] = True
Expand Down Expand Up @@ -341,6 +362,7 @@ def _start_backend(self, port: int = 0):
if self.app_asgi is None:
msg = "App was not initialized."
raise RuntimeError(msg)
uvicorn = _get_uvicorn()
self.backend = uvicorn.Server(
uvicorn.Config(
app=self.app_asgi,
Expand Down Expand Up @@ -470,7 +492,14 @@ def __enter__(self) -> Self:

def stop(self) -> None:
"""Stop the frontend and backend servers."""
import psutil
try:
import psutil
except ImportError as exc:
msg = (
"AppHarness cleanup requires `psutil`. Install it with "
"`pip install 'reflex[testing]'`."
)
raise ImportError(msg) from exc

# Quit browsers first to avoid any lingering events being sent during shutdown.
for driver in self._frontends:
Expand Down Expand Up @@ -825,6 +854,7 @@ class AppHarnessProd(AppHarness):
frontend_server: uvicorn.Server | None = None

def _run_frontend(self):
uvicorn = _get_uvicorn()
with chdir(self.app_path):
frontend_app = reflex.utils.exec._frontend_prod_app()
self.frontend_server = uvicorn.Server(
Expand Down Expand Up @@ -893,6 +923,7 @@ def _start_backend(self):
msg = "App was not initialized."
raise RuntimeError(msg)
environment.REFLEX_SKIP_COMPILE.set(True)
uvicorn = _get_uvicorn()
self.backend = uvicorn.Server(
uvicorn.Config(
app=self.app_asgi,
Expand Down
5 changes: 5 additions & 0 deletions tests/units/test_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
from reflex.utils.exec import should_prerender_routes


def test_testing_module_does_not_import_uvicorn_at_module_load():
"""Importing reflex.testing does not require the AppHarness backend runtime."""
assert "uvicorn" not in reflex_testing.__dict__


@pytest.mark.skip("Slow test that makes network requests.")
def test_app_harness(tmp_path):
"""Ensure that AppHarness can compile and start an app.
Expand Down
5 changes: 5 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading