rename private fields with leading underscore in App (#4642)

* rename private fields with leading underscore in App

* fix constants API

* fix public API for some attributes of App()

* fix conflicts properly 🙈

* remove extra private

---------

Co-authored-by: Masen Furer <m_github@0x26.net>
Co-authored-by: Khaleel Al-Adhami <khaleel.aladhami@gmail.com>
This commit is contained in:
Thomas Brandého 2025-01-23 06:28:56 -08:00 committed by GitHub
parent c58db4d005
commit 858a85a4dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 191 additions and 156 deletions

View File

@ -122,7 +122,7 @@ def AppWithTenComponentsOnePage():
def index() -> rx.Component:
return rx.center(rx.vstack(*render_component(1)))
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)
@ -133,7 +133,7 @@ def AppWithHundredComponentOnePage():
def index() -> rx.Component:
return rx.center(rx.vstack(*render_component(100)))
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)
@ -144,7 +144,7 @@ def AppWithThousandComponentsOnePage():
def index() -> rx.Component:
return rx.center(rx.vstack(*render_component(1000)))
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)

View File

@ -162,7 +162,7 @@ def AppWithOnePage():
height="100vh",
)
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)
@ -170,7 +170,7 @@ def AppWithTenPages():
"""A reflex app with 10 pages."""
import reflex as rx
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
render_multiple_pages(app, 10)
@ -178,7 +178,7 @@ def AppWithHundredPages():
"""A reflex app with 100 pages."""
import reflex as rx
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
render_multiple_pages(app, 100)
@ -186,7 +186,7 @@ def AppWithThousandPages():
"""A reflex app with Thousand pages."""
import reflex as rx
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
render_multiple_pages(app, 1000)
@ -194,7 +194,7 @@ def AppWithTenThousandPages():
"""A reflex app with ten thousand pages."""
import reflex as rx
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
render_multiple_pages(app, 10000)

View File

@ -251,36 +251,36 @@ class App(MiddlewareMixin, LifespanMixin):
# Attributes to add to the html root tag of every page.
html_custom_attrs: Optional[Dict[str, str]] = None
# A map from a route to an unevaluated page. PRIVATE.
unevaluated_pages: Dict[str, UnevaluatedPage] = dataclasses.field(
# A map from a route to an unevaluated page.
_unevaluated_pages: Dict[str, UnevaluatedPage] = dataclasses.field(
default_factory=dict
)
# A map from a page route to the component to render. Users should use `add_page`. PRIVATE.
pages: Dict[str, Component] = dataclasses.field(default_factory=dict)
# A map from a page route to the component to render. Users should use `add_page`.
_pages: Dict[str, Component] = dataclasses.field(default_factory=dict)
# The backend API object. PRIVATE.
api: FastAPI = None # type: ignore
# The backend API object.
_api: FastAPI | None = None
# The state class to use for the app. PRIVATE.
state: Optional[Type[BaseState]] = None
# The state class to use for the app.
_state: Optional[Type[BaseState]] = None
# Class to manage many client states.
_state_manager: Optional[StateManager] = None
# Mapping from a route to event handlers to trigger when the page loads. PRIVATE.
load_events: Dict[str, List[IndividualEventType[[], Any]]] = dataclasses.field(
# Mapping from a route to event handlers to trigger when the page loads.
_load_events: Dict[str, List[IndividualEventType[[], Any]]] = dataclasses.field(
default_factory=dict
)
# Admin dashboard to view and manage the database. PRIVATE.
# Admin dashboard to view and manage the database.
admin_dash: Optional[AdminDash] = None
# The async server name space. PRIVATE.
event_namespace: Optional[EventNamespace] = None
# The async server name space.
_event_namespace: Optional[EventNamespace] = None
# Background tasks that are currently running. PRIVATE.
background_tasks: Set[asyncio.Task] = dataclasses.field(default_factory=set)
# Background tasks that are currently running.
_background_tasks: Set[asyncio.Task] = dataclasses.field(default_factory=set)
# Frontend Error Handler Function
frontend_exception_handler: Callable[[Exception], None] = (
@ -292,6 +292,24 @@ class App(MiddlewareMixin, LifespanMixin):
[Exception], Union[EventSpec, List[EventSpec], None]
] = default_backend_exception_handler
@property
def api(self) -> FastAPI | None:
"""Get the backend api.
Returns:
The backend api.
"""
return self._api
@property
def event_namespace(self) -> EventNamespace | None:
"""Get the event namespace.
Returns:
The event namespace.
"""
return self._event_namespace
def __post_init__(self):
"""Initialize the app.
@ -311,7 +329,7 @@ class App(MiddlewareMixin, LifespanMixin):
set_breakpoints(self.style.pop("breakpoints"))
# Set up the API.
self.api = FastAPI(lifespan=self._run_lifespan_tasks)
self._api = FastAPI(lifespan=self._run_lifespan_tasks)
self._add_cors()
self._add_default_endpoints()
@ -334,8 +352,8 @@ class App(MiddlewareMixin, LifespanMixin):
def _enable_state(self) -> None:
"""Enable state for the app."""
if not self.state:
self.state = State
if not self._state:
self._state = State
self._setup_state()
def _setup_state(self) -> None:
@ -344,13 +362,13 @@ class App(MiddlewareMixin, LifespanMixin):
Raises:
RuntimeError: If the socket server is invalid.
"""
if not self.state:
if not self._state:
return
config = get_config()
# Set up the state manager.
self._state_manager = StateManager.create(state=self.state)
self._state_manager = StateManager.create(state=self._state)
# Set up the Socket.IO AsyncServer.
if not self.sio:
@ -381,12 +399,13 @@ class App(MiddlewareMixin, LifespanMixin):
namespace = config.get_event_namespace()
# Create the event namespace and attach the main app. Not related to any paths.
self.event_namespace = EventNamespace(namespace, self)
self._event_namespace = EventNamespace(namespace, self)
# Register the event namespace with the socket.
self.sio.register_namespace(self.event_namespace)
# Mount the socket app with the API.
self.api.mount(str(constants.Endpoint.EVENT), socket_app)
if self.api:
self.api.mount(str(constants.Endpoint.EVENT), socket_app)
# Check the exception handlers
self._validate_exception_handlers()
@ -397,24 +416,35 @@ class App(MiddlewareMixin, LifespanMixin):
Returns:
The string representation of the app.
"""
return f"<App state={self.state.__name__ if self.state else None}>"
return f"<App state={self._state.__name__ if self._state else None}>"
def __call__(self) -> FastAPI:
"""Run the backend api instance.
Raises:
ValueError: If the app has not been initialized.
Returns:
The backend api.
"""
if not self.api:
raise ValueError("The app has not been initialized.")
return self.api
def _add_default_endpoints(self):
"""Add default api endpoints (ping)."""
# To test the server.
if not self.api:
return
self.api.get(str(constants.Endpoint.PING))(ping)
self.api.get(str(constants.Endpoint.HEALTH))(health)
def _add_optional_endpoints(self):
"""Add optional api endpoints (_upload)."""
if not self.api:
return
if Upload.is_used:
# To upload files.
self.api.post(str(constants.Endpoint.UPLOAD))(upload(self))
@ -432,6 +462,8 @@ class App(MiddlewareMixin, LifespanMixin):
def _add_cors(self):
"""Add CORS middleware to the app."""
if not self.api:
return
self.api.add_middleware(
cors.CORSMiddleware,
allow_credentials=True,
@ -521,13 +553,13 @@ class App(MiddlewareMixin, LifespanMixin):
# Check if the route given is valid
verify_route_validity(route)
if route in self.unevaluated_pages and environment.RELOAD_CONFIG.is_set():
if route in self._unevaluated_pages and environment.RELOAD_CONFIG.is_set():
# when the app is reloaded(typically for app harness tests), we should maintain
# the latest render function of a route.This applies typically to decorated pages
# since they are only added when app._compile is called.
self.unevaluated_pages.pop(route)
self._unevaluated_pages.pop(route)
if route in self.unevaluated_pages:
if route in self._unevaluated_pages:
route_name = (
f"`{route}` or `/`"
if route == constants.PageNames.INDEX_ROUTE
@ -540,15 +572,15 @@ class App(MiddlewareMixin, LifespanMixin):
# Setup dynamic args for the route.
# this state assignment is only required for tests using the deprecated state kwarg for App
state = self.state if self.state else State
state = self._state if self._state else State
state.setup_dynamic_args(get_route_args(route))
if on_load:
self.load_events[route] = (
self._load_events[route] = (
on_load if isinstance(on_load, list) else [on_load]
)
self.unevaluated_pages[route] = UnevaluatedPage(
self._unevaluated_pages[route] = UnevaluatedPage(
component=component,
route=route,
title=title,
@ -563,10 +595,10 @@ class App(MiddlewareMixin, LifespanMixin):
Args:
route: The route of the page to compile.
save_page: If True, the compiled page is saved to self.pages.
save_page: If True, the compiled page is saved to self._pages.
"""
component, enable_state = compiler.compile_unevaluated_page(
route, self.unevaluated_pages[route], self.state, self.style, self.theme
route, self._unevaluated_pages[route], self._state, self.style, self.theme
)
if enable_state:
@ -575,7 +607,7 @@ class App(MiddlewareMixin, LifespanMixin):
# Add the page.
self._check_routes_conflict(route)
if save_page:
self.pages[route] = component
self._pages[route] = component
def get_load_events(self, route: str) -> list[IndividualEventType[[], Any]]:
"""Get the load events for a route.
@ -589,7 +621,7 @@ class App(MiddlewareMixin, LifespanMixin):
route = route.lstrip("/")
if route == "":
route = constants.PageNames.INDEX_ROUTE
return self.load_events.get(route, [])
return self._load_events.get(route, [])
def _check_routes_conflict(self, new_route: str):
"""Verify if there is any conflict between the new route and any existing route.
@ -613,7 +645,7 @@ class App(MiddlewareMixin, LifespanMixin):
constants.RouteRegex.SINGLE_CATCHALL_SEGMENT,
constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT,
)
for route in self.pages:
for route in self._pages:
replaced_route = replace_brackets_with_keywords(route)
for rw, r, nr in zip(
replaced_route.split("/"), route.split("/"), new_route.split("/")
@ -671,6 +703,9 @@ class App(MiddlewareMixin, LifespanMixin):
def _setup_admin_dash(self):
"""Setup the admin dash."""
# Get the admin dash.
if not self.api:
return
admin_dash = self.admin_dash
if admin_dash and admin_dash.models:
@ -775,10 +810,10 @@ class App(MiddlewareMixin, LifespanMixin):
def _setup_overlay_component(self):
"""If a State is not used and no overlay_component is specified, do not render the connection modal."""
if self.state is None and self.overlay_component is default_overlay_component:
if self._state is None and self.overlay_component is default_overlay_component:
self.overlay_component = None
for k, component in self.pages.items():
self.pages[k] = self._add_overlay_to_component(component)
for k, component in self._pages.items():
self._pages[k] = self._add_overlay_to_component(component)
def _add_error_boundary_to_component(self, component: Component) -> Component:
if self.error_boundary is None:
@ -790,14 +825,14 @@ class App(MiddlewareMixin, LifespanMixin):
def _setup_error_boundary(self):
"""If a State is not used and no error_boundary is specified, do not render the error boundary."""
if self.state is None and self.error_boundary is default_error_boundary:
if self._state is None and self.error_boundary is default_error_boundary:
self.error_boundary = None
for k, component in self.pages.items():
for k, component in self._pages.items():
# Skip the 404 page
if k == constants.Page404.SLUG:
continue
self.pages[k] = self._add_error_boundary_to_component(component)
self._pages[k] = self._add_error_boundary_to_component(component)
def _apply_decorated_pages(self):
"""Add @rx.page decorated pages to the app.
@ -823,11 +858,11 @@ class App(MiddlewareMixin, LifespanMixin):
Raises:
VarDependencyError: When a computed var has an invalid dependency.
"""
if not self.state:
if not self._state:
return
if not state:
state = self.state
state = self._state
for var in state.computed_vars.values():
if not var._cache:
@ -853,13 +888,13 @@ class App(MiddlewareMixin, LifespanMixin):
"""
from reflex.utils.exceptions import ReflexRuntimeError
self.pages = {}
self._pages = {}
def get_compilation_time() -> str:
return str(datetime.now().time()).split(".")[0]
# Render a default 404 page if the user didn't supply one
if constants.Page404.SLUG not in self.unevaluated_pages:
if constants.Page404.SLUG not in self._unevaluated_pages:
self.add_page(route=constants.Page404.SLUG)
# Fix up the style.
@ -877,7 +912,7 @@ class App(MiddlewareMixin, LifespanMixin):
should_compile = self._should_compile()
for route in self.unevaluated_pages:
for route in self._unevaluated_pages:
console.debug(f"Evaluating page: {route}")
self._compile_page(route, save_page=should_compile)
@ -904,7 +939,7 @@ class App(MiddlewareMixin, LifespanMixin):
progress.start()
task = progress.add_task(
f"[{get_compilation_time()}] Compiling:",
total=len(self.pages)
total=len(self._pages)
+ fixed_pages_within_executor
+ adhoc_steps_without_executor,
)
@ -923,7 +958,7 @@ class App(MiddlewareMixin, LifespanMixin):
# This has to happen before compiling stateful components as that
# prevents recursive functions from reaching all components.
for component in self.pages.values():
for component in self._pages.values():
# Add component._get_all_imports() to all_imports.
all_imports.update(component._get_all_imports())
@ -938,12 +973,12 @@ class App(MiddlewareMixin, LifespanMixin):
stateful_components_path,
stateful_components_code,
page_components,
) = compiler.compile_stateful_components(self.pages.values())
) = compiler.compile_stateful_components(self._pages.values())
progress.advance(task)
# Catch "static" apps (that do not define a rx.State subclass) which are trying to access rx.State.
if code_uses_state_contexts(stateful_components_code) and self.state is None:
if code_uses_state_contexts(stateful_components_code) and self._state is None:
raise ReflexRuntimeError(
"To access rx.State in frontend components, at least one "
"subclass of rx.State must be defined in the app."
@ -980,10 +1015,10 @@ class App(MiddlewareMixin, LifespanMixin):
max_workers=environment.REFLEX_COMPILE_THREADS.get() or None
)
for route, component in zip(self.pages, page_components):
for route, component in zip(self._pages, page_components):
ExecutorSafeFunctions.COMPONENTS[route] = component
ExecutorSafeFunctions.STATE = self.state
ExecutorSafeFunctions.STATE = self._state
with executor:
result_futures = []
@ -993,7 +1028,7 @@ class App(MiddlewareMixin, LifespanMixin):
result_futures.append(f)
# Compile the pre-compiled pages.
for route in self.pages:
for route in self._pages:
_submit_work(
ExecutorSafeFunctions.compile_page,
route,
@ -1028,7 +1063,7 @@ class App(MiddlewareMixin, LifespanMixin):
# Compile the contexts.
compile_results.append(
compiler.compile_contexts(self.state, self.theme),
compiler.compile_contexts(self._state, self.theme),
)
if self.theme is not None:
# Fix #2992 by removing the top-level appearance prop
@ -1150,9 +1185,9 @@ class App(MiddlewareMixin, LifespanMixin):
)
task = asyncio.create_task(_coro())
self.background_tasks.add(task)
self._background_tasks.add(task)
# Clean up task from background_tasks set when complete.
task.add_done_callback(self.background_tasks.discard)
task.add_done_callback(self._background_tasks.discard)
return task
def _validate_exception_handlers(self):

View File

@ -296,7 +296,7 @@ class AppHarness:
self.app_instance = self.app_module.app
if isinstance(self.app_instance._state_manager, StateManagerRedis):
# Create our own redis connection for testing.
self.state_manager = StateManagerRedis.create(self.app_instance.state)
self.state_manager = StateManagerRedis.create(self.app_instance._state)
else:
self.state_manager = self.app_instance._state_manager
@ -324,7 +324,7 @@ class AppHarness:
return _shutdown_redis
def _start_backend(self, port=0):
if self.app_instance is None:
if self.app_instance is None or self.app_instance.api is None:
raise RuntimeError("App was not initialized.")
self.backend = uvicorn.Server(
uvicorn.Config(
@ -353,12 +353,12 @@ class AppHarness:
self.app_instance.state_manager,
StateManagerRedis,
)
and self.app_instance.state is not None
and self.app_instance._state is not None
):
with contextlib.suppress(RuntimeError):
await self.app_instance.state_manager.close()
self.app_instance._state_manager = StateManagerRedis.create(
state=self.app_instance.state,
state=self.app_instance._state,
)
if not isinstance(self.app_instance.state_manager, StateManagerRedis):
raise RuntimeError("Failed to reset state manager.")

View File

@ -172,7 +172,7 @@ def BackgroundTask():
rx.button("Reset", on_click=State.reset_counter, id="reset"),
)
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)
@ -288,7 +288,7 @@ def test_background_task(
assert background_task._poll_for(lambda: counter.text == "620", timeout=40)
# all tasks should have exited and cleaned up
assert background_task._poll_for(
lambda: not background_task.app_instance.background_tasks # type: ignore
lambda: not background_task.app_instance._background_tasks # type: ignore
)

View File

@ -188,7 +188,7 @@ def CallScript():
yield rx.call_script("inline_counter = 0; external_counter = 0")
self.reset()
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
Path("assets/external.js").write_text(external_scripts)
@app.add_page

View File

@ -127,7 +127,7 @@ def ClientSide():
rx.box(ClientSideSubSubState.s1s, id="s1s"),
)
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)
app.add_page(index, route="/foo")

View File

@ -72,7 +72,7 @@ def ComponentStateApp():
State=_Counter,
)
app = rx.App(state=rx.State) # noqa
app = rx.App(_state=rx.State) # noqa
@rx.page()
def index():

View File

@ -36,7 +36,7 @@ def ConnectionBanner():
rx.button("Delay", id="delay", on_click=State.delay),
)
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)

View File

@ -26,7 +26,7 @@ def DeployUrlSample() -> None:
rx.button("GOTO SELF", on_click=State.goto_self, id="goto_self")
)
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)

View File

@ -138,7 +138,7 @@ def DynamicRoute():
def redirect_page():
return rx.fragment(rx.text("redirecting..."))
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index, route="/page/[page_id]", on_load=DynamicState.on_load) # type: ignore
app.add_page(index, route="/static/x", on_load=DynamicState.on_load) # type: ignore
app.add_page(index)

View File

@ -156,7 +156,7 @@ def TestEventAction():
on_click=EventActionState.on_click("outer"), # type: ignore
)
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)

View File

@ -144,7 +144,7 @@ def EventChain():
time.sleep(0.5)
self.interim_value = "final"
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
token_input = rx.input(
value=State.router.session.client_token, is_read_only=True, id="token"

View File

@ -39,7 +39,7 @@ def TestApp():
"""
print(1 / number)
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
@app.add_page
def index():

View File

@ -30,7 +30,7 @@ def FormSubmit(form_component):
def form_submit(self, form_data: Dict):
self.form_data = form_data
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
@app.add_page
def index():
@ -90,7 +90,7 @@ def FormSubmitName(form_component):
def form_submit(self, form_data: Dict):
self.form_data = form_data
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
@app.add_page
def index():

View File

@ -16,7 +16,7 @@ def FullyControlledInput():
class State(rx.State):
text: str = "initial"
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
@app.add_page
def index():

View File

@ -45,7 +45,7 @@ def LoginSample():
rx.button("Do it", on_click=State.login, id="doit"),
)
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)
app.add_page(login)

View File

@ -38,7 +38,7 @@ def ServerSideEvent():
def set_value_return_c(self):
return rx.set_value("c", "")
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
@app.add_page
def index():

View File

@ -166,7 +166,7 @@ def UploadFile():
rx.text(UploadState.event_order.to_string(), id="event-order"),
)
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
app.add_page(index)

View File

@ -39,7 +39,7 @@ def VarOperations():
dict2: Dict[int, int] = {3: 4}
html_str: str = "<div>hello</div>"
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
@rx.memo
def memo_comp(list1: List[int], int_var1: int, id: str):

View File

@ -16,7 +16,7 @@ def DatetimeOperationsApp():
date2: datetime = datetime(2031, 1, 1)
date3: datetime = datetime(2021, 1, 1)
app = rx.App(state=DtOperationsState)
app = rx.App(_state=DtOperationsState)
@app.add_page
def index():

View File

@ -20,7 +20,7 @@ def Table():
"""App using table component."""
import reflex as rx
app = rx.App(state=rx.State)
app = rx.App(_state=rx.State)
@app.add_page
def index():

View File

@ -41,7 +41,7 @@ async def test_preprocess_no_events(hydrate_middleware, event1, mocker):
mocker.patch("reflex.state.State.class_subclasses", {TestState})
state = State()
update = await hydrate_middleware.preprocess(
app=App(state=State),
app=App(_state=State),
event=event1,
state=state,
)

View File

@ -236,14 +236,14 @@ def test_add_page_default_route(app: App, index_page, about_page):
index_page: The index page.
about_page: The about page.
"""
assert app.pages == {}
assert app.unevaluated_pages == {}
assert app._pages == {}
assert app._unevaluated_pages == {}
app.add_page(index_page)
app._compile_page("index")
assert app.pages.keys() == {"index"}
assert app._pages.keys() == {"index"}
app.add_page(about_page)
app._compile_page("about")
assert app.pages.keys() == {"index", "about"}
assert app._pages.keys() == {"index", "about"}
def test_add_page_set_route(app: App, index_page, windows_platform: bool):
@ -255,10 +255,10 @@ def test_add_page_set_route(app: App, index_page, windows_platform: bool):
windows_platform: Whether the system is windows.
"""
route = "test" if windows_platform else "/test"
assert app.unevaluated_pages == {}
assert app._unevaluated_pages == {}
app.add_page(index_page, route=route)
app._compile_page("test")
assert app.pages.keys() == {"test"}
assert app._pages.keys() == {"test"}
def test_add_page_set_route_dynamic(index_page, windows_platform: bool):
@ -268,18 +268,18 @@ def test_add_page_set_route_dynamic(index_page, windows_platform: bool):
index_page: The index page.
windows_platform: Whether the system is windows.
"""
app = App(state=EmptyState)
assert app.state is not None
app = App(_state=EmptyState)
assert app._state is not None
route = "/test/[dynamic]"
assert app.unevaluated_pages == {}
assert app._unevaluated_pages == {}
app.add_page(index_page, route=route)
app._compile_page("test/[dynamic]")
assert app.pages.keys() == {"test/[dynamic]"}
assert "dynamic" in app.state.computed_vars
assert app.state.computed_vars["dynamic"]._deps(objclass=EmptyState) == {
assert app._pages.keys() == {"test/[dynamic]"}
assert "dynamic" in app._state.computed_vars
assert app._state.computed_vars["dynamic"]._deps(objclass=EmptyState) == {
constants.ROUTER
}
assert constants.ROUTER in app.state()._computed_var_dependencies
assert constants.ROUTER in app._state()._computed_var_dependencies
def test_add_page_set_route_nested(app: App, index_page, windows_platform: bool):
@ -291,9 +291,9 @@ def test_add_page_set_route_nested(app: App, index_page, windows_platform: bool)
windows_platform: Whether the system is windows.
"""
route = "test\\nested" if windows_platform else "/test/nested"
assert app.unevaluated_pages == {}
assert app._unevaluated_pages == {}
app.add_page(index_page, route=route)
assert app.unevaluated_pages.keys() == {route.strip(os.path.sep)}
assert app._unevaluated_pages.keys() == {route.strip(os.path.sep)}
def test_add_page_invalid_api_route(app: App, index_page):
@ -413,8 +413,8 @@ async def test_initialize_with_state(test_state: Type[ATestState], token: str):
test_state: The default state.
token: a Token.
"""
app = App(state=test_state)
assert app.state == test_state
app = App(_state=test_state)
assert app._state == test_state
# Get a state for a given token.
state = await app.state_manager.get_state(_substate_key(token, test_state))
@ -432,7 +432,7 @@ async def test_set_and_get_state(test_state):
Args:
test_state: The default state.
"""
app = App(state=test_state)
app = App(_state=test_state)
# Create two tokens.
token1 = str(uuid.uuid4()) + f"_{test_state.get_full_name()}"
@ -827,7 +827,7 @@ async def test_upload_file_without_annotation(state, tmp_path, token):
token: a Token.
"""
state._tmp_path = tmp_path
app = App(state=State)
app = App(_state=State)
request_mock = unittest.mock.Mock()
request_mock.headers = {
@ -861,7 +861,7 @@ async def test_upload_file_background(state, tmp_path, token):
token: a Token.
"""
state._tmp_path = tmp_path
app = App(state=State)
app = App(_state=State)
request_mock = unittest.mock.Mock()
request_mock.headers = {
@ -938,8 +938,8 @@ def test_dynamic_arg_shadow(
"""
arg_name = "counter"
route = f"/test/[{arg_name}]"
app = app_module_mock.app = App(state=DynamicState)
assert app.state is not None
app = app_module_mock.app = App(_state=DynamicState)
assert app._state is not None
with pytest.raises(NameError):
app.add_page(index_page, route=route, on_load=DynamicState.on_load) # type: ignore
@ -963,7 +963,7 @@ def test_multiple_dynamic_args(
arg_name = "my_arg"
route = f"/test/[{arg_name}]"
route2 = f"/test2/[{arg_name}]"
app = app_module_mock.app = App(state=EmptyState)
app = app_module_mock.app = App(_state=EmptyState)
app.add_page(index_page, route=route)
app.add_page(index_page, route=route2)
@ -990,16 +990,16 @@ async def test_dynamic_route_var_route_change_completed_on_load(
"""
arg_name = "dynamic"
route = f"/test/[{arg_name}]"
app = app_module_mock.app = App(state=DynamicState)
assert app.state is not None
assert arg_name not in app.state.vars
app = app_module_mock.app = App(_state=DynamicState)
assert app._state is not None
assert arg_name not in app._state.vars
app.add_page(index_page, route=route, on_load=DynamicState.on_load) # type: ignore
assert arg_name in app.state.vars
assert arg_name in app.state.computed_vars
assert app.state.computed_vars[arg_name]._deps(objclass=DynamicState) == {
assert arg_name in app._state.vars
assert arg_name in app._state.computed_vars
assert app._state.computed_vars[arg_name]._deps(objclass=DynamicState) == {
constants.ROUTER
}
assert constants.ROUTER in app.state()._computed_var_dependencies
assert constants.ROUTER in app._state()._computed_var_dependencies
substate_token = _substate_key(token, DynamicState)
sid = "mock_sid"
@ -1174,7 +1174,7 @@ async def test_process_events(mocker, token: str):
"headers": {},
"ip": "127.0.0.1",
}
app = App(state=GenState)
app = App(_state=GenState)
mocker.patch.object(app, "_postprocess", AsyncMock())
event = Event(
@ -1220,7 +1220,7 @@ def test_overlay_component(
overlay_component: The overlay_component to pass to App.
exp_page_child: The type of the expected child in the page fragment.
"""
app = App(state=state, overlay_component=overlay_component)
app = App(_state=state, overlay_component=overlay_component)
app._setup_overlay_component()
if exp_page_child is None:
assert app.overlay_component is None
@ -1243,7 +1243,7 @@ def test_overlay_component(
# overlay components are wrapped during compile only
app._compile_page("test")
app._setup_overlay_component()
page = app.pages["test"]
page = app._pages["test"]
if exp_page_child is not None:
assert len(page.children) == 3
@ -1361,52 +1361,52 @@ def test_app_wrap_priority(compilable_app: tuple[App, Path]):
def test_app_state_determination():
"""Test that the stateless status of an app is determined correctly."""
a1 = App()
assert a1.state is None
assert a1._state is None
# No state, no router, no event handlers.
a1.add_page(rx.box("Index"), route="/")
assert a1.state is None
assert a1._state is None
# Add a page with `on_load` enables state.
a1.add_page(rx.box("About"), route="/about", on_load=rx.console_log(""))
a1._compile_page("about")
assert a1.state is not None
assert a1._state is not None
a2 = App()
assert a2.state is None
assert a2._state is None
# Referencing a state Var enables state.
a2.add_page(rx.box(rx.text(GenState.value)), route="/")
a2._compile_page("index")
assert a2.state is not None
assert a2._state is not None
a3 = App()
assert a3.state is None
assert a3._state is None
# Referencing router enables state.
a3.add_page(rx.box(rx.text(State.router.page.full_path)), route="/")
a3._compile_page("index")
assert a3.state is not None
assert a3._state is not None
a4 = App()
assert a4.state is None
assert a4._state is None
a4.add_page(rx.box(rx.button("Click", on_click=rx.console_log(""))), route="/")
assert a4.state is None
assert a4._state is None
a4.add_page(
rx.box(rx.button("Click", on_click=DynamicState.on_counter)), route="/page2"
)
a4._compile_page("page2")
assert a4.state is not None
assert a4._state is not None
def test_raise_on_state():
"""Test that the state is set."""
# state kwargs is deprecated, we just make sure the app is created anyway.
_app = App(state=State)
assert _app.state is not None
assert issubclass(_app.state, State)
_app = App(_state=State)
assert _app._state is not None
assert issubclass(_app._state, State)
def test_call_app():
@ -1473,7 +1473,7 @@ def test_add_page_component_returning_tuple():
app._compile_page("index")
app._compile_page("page2")
fragment_wrapper = app.pages["index"].children[0]
fragment_wrapper = app._pages["index"].children[0]
assert isinstance(fragment_wrapper, Fragment)
first_text = fragment_wrapper.children[0]
assert isinstance(first_text, Text)
@ -1483,7 +1483,7 @@ def test_add_page_component_returning_tuple():
assert str(second_text.children[0].contents) == '"second"' # type: ignore
# Test page with trailing comma.
page2_fragment_wrapper = app.pages["page2"].children[0]
page2_fragment_wrapper = app._pages["page2"].children[0]
assert isinstance(page2_fragment_wrapper, Fragment)
third_text = page2_fragment_wrapper.children[0]
assert isinstance(third_text, Text)
@ -1557,7 +1557,7 @@ def test_app_with_valid_var_dependencies(compilable_app: tuple[App, Path]):
def bar(self) -> str:
return "bar"
app.state = ValidDepState
app._state = ValidDepState
app._compile()
@ -1569,7 +1569,7 @@ def test_app_with_invalid_var_dependencies(compilable_app: tuple[App, Path]):
def bar(self) -> str:
return "bar"
app.state = InvalidDepState
app._state = InvalidDepState
with pytest.raises(exceptions.VarDependencyError):
app._compile()

View File

@ -89,7 +89,7 @@ def app():
],
)
def test_check_routes_conflict_invalid(mocker, app, route1, route2):
mocker.patch.object(app, "pages", {route1: []})
mocker.patch.object(app, "_pages", {route1: []})
with pytest.raises(ValueError):
app._check_routes_conflict(route2)
@ -117,6 +117,6 @@ def test_check_routes_conflict_invalid(mocker, app, route1, route2):
],
)
def test_check_routes_conflict_valid(mocker, app, route1, route2):
mocker.patch.object(app, "pages", {route1: []})
mocker.patch.object(app, "_pages", {route1: []})
# test that running this does not throw an error.
app._check_routes_conflict(route2)

View File

@ -1907,12 +1907,12 @@ def mock_app_simple(monkeypatch) -> rx.App:
Returns:
The app, after mocking out prerequisites.get_app()
"""
app = App(state=TestState)
app = App(_state=TestState)
app_module = Mock()
setattr(app_module, CompileVars.APP, app)
app.state = TestState
app._state = TestState
app.event_namespace.emit = CopyingAsyncMock() # type: ignore
def _mock_get_app(*args, **kwargs):
@ -2153,7 +2153,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
token: A token.
"""
router_data = {"query": {}}
mock_app.state_manager.state = mock_app.state = BackgroundTaskState
mock_app.state_manager.state = mock_app._state = BackgroundTaskState
async for update in rx.app.process( # type: ignore
mock_app,
Event(
@ -2171,7 +2171,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
# wait for the coroutine to start
await asyncio.sleep(0.5 if CI else 0.1)
assert len(mock_app.background_tasks) == 1
assert len(mock_app._background_tasks) == 1
# Process another normal event
async for update in rx.app.process( # type: ignore
@ -2203,9 +2203,9 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
)
# Explicit wait for background tasks
for task in tuple(mock_app.background_tasks):
for task in tuple(mock_app._background_tasks):
await task
assert not mock_app.background_tasks
assert not mock_app._background_tasks
exp_order = [
"background_task:start",
@ -2280,7 +2280,7 @@ async def test_background_task_reset(mock_app: rx.App, token: str):
token: A token.
"""
router_data = {"query": {}}
mock_app.state_manager.state = mock_app.state = BackgroundTaskState
mock_app.state_manager.state = mock_app._state = BackgroundTaskState
async for update in rx.app.process( # type: ignore
mock_app,
Event(
@ -2297,9 +2297,9 @@ async def test_background_task_reset(mock_app: rx.App, token: str):
assert update == StateUpdate()
# Explicit wait for background tasks
for task in tuple(mock_app.background_tasks):
for task in tuple(mock_app._background_tasks):
await task
assert not mock_app.background_tasks
assert not mock_app._background_tasks
assert (
await mock_app.state_manager.get_state(
@ -2884,7 +2884,7 @@ async def test_preprocess(app_module_mock, token, test_state, expected, mocker):
"reflex.state.State.class_subclasses", {test_state, OnLoadInternalState}
)
app = app_module_mock.app = App(
state=State, load_events={"index": [test_state.test_handler]}
_state=State, _load_events={"index": [test_state.test_handler]}
)
async with app.state_manager.modify_state(_substate_key(token, State)) as state:
state.router_data = {"simulate": "hydrate"}
@ -2931,8 +2931,8 @@ async def test_preprocess_multiple_load_events(app_module_mock, token, mocker):
"reflex.state.State.class_subclasses", {OnLoadState, OnLoadInternalState}
)
app = app_module_mock.app = App(
state=State,
load_events={"index": [OnLoadState.test_handler, OnLoadState.test_handler]},
_state=State,
_load_events={"index": [OnLoadState.test_handler, OnLoadState.test_handler]},
)
async with app.state_manager.modify_state(_substate_key(token, State)) as state:
state.router_data = {"simulate": "hydrate"}
@ -2977,7 +2977,7 @@ async def test_get_state(mock_app: rx.App, token: str):
mock_app: An app that will be returned by `get_app()`
token: A token.
"""
mock_app.state_manager.state = mock_app.state = TestState
mock_app.state_manager.state = mock_app._state = TestState
# Get instance of ChildState2.
test_state = await mock_app.state_manager.get_state(
@ -3147,7 +3147,7 @@ async def test_get_state_from_sibling_not_cached(mock_app: rx.App, token: str):
pass
mock_app.state_manager.state = mock_app.state = Parent
mock_app.state_manager.state = mock_app._state = Parent
# Get the top level state via unconnected sibling.
root = await mock_app.state_manager.get_state(_substate_key(token, Child))

View File

@ -222,7 +222,7 @@ async def state_manager_redis(
Yields:
A state manager instance
"""
app_module_mock.app = rx.App(state=Root)
app_module_mock.app = rx.App(_state=Root)
state_manager = app_module_mock.app.state_manager
if not isinstance(state_manager, StateManagerRedis):

View File

@ -23,7 +23,7 @@ def test_app_harness(tmp_path):
class State(rx.State):
pass
app = rx.App(state=State)
app = rx.App(_state=State)
app.add_page(lambda: rx.text("Basic App"), route="/", title="index")
app._compile()