|
@@ -102,60 +102,78 @@ class OverlayFragment(Fragment):
|
|
|
|
|
|
|
|
|
class App(Base):
|
|
|
- """A Reflex application."""
|
|
|
+ """The main Reflex app that encapsulates the backend and frontend.
|
|
|
|
|
|
- # A map from a page route to the component to render.
|
|
|
- pages: Dict[str, Component] = {}
|
|
|
-
|
|
|
- # A list of URLs to stylesheets to include in the app.
|
|
|
- stylesheets: List[str] = []
|
|
|
+ Every Reflex app needs an app defined in its main module.
|
|
|
|
|
|
- # The backend API object.
|
|
|
- api: FastAPI = None # type: ignore
|
|
|
+ ```python
|
|
|
+ # app.py
|
|
|
+ import reflex as rx
|
|
|
|
|
|
- # The Socket.IO AsyncServer.
|
|
|
- sio: Optional[AsyncServer] = None
|
|
|
+ # Define state and pages
|
|
|
+ ...
|
|
|
|
|
|
- # The state class to use for the app.
|
|
|
- state: Optional[Type[BaseState]] = None
|
|
|
+ app = rx.App(
|
|
|
+ # Set global level style.
|
|
|
+ style={...},
|
|
|
+ # Set the top level theme.
|
|
|
+ theme=rx.theme(accent_color="blue"),
|
|
|
+ )
|
|
|
+ ```
|
|
|
+ """
|
|
|
|
|
|
- # Class to manage many client states.
|
|
|
- _state_manager: Optional[StateManager] = None
|
|
|
+ # The global [theme](https://reflex.dev/docs/styling/theming/#theme) for the entire app.
|
|
|
+ theme: Optional[Component] = themes.theme(accent_color="blue")
|
|
|
|
|
|
- # The styling to apply to each component.
|
|
|
+ # The [global style](https://reflex.dev/docs/styling/overview/#global-styles}) for the app.
|
|
|
style: ComponentStyle = {}
|
|
|
|
|
|
- # Middleware to add to the app.
|
|
|
- middleware: List[Middleware] = []
|
|
|
-
|
|
|
- # List of event handlers to trigger when a page loads.
|
|
|
- load_events: Dict[str, List[Union[EventHandler, EventSpec]]] = {}
|
|
|
-
|
|
|
- # Admin dashboard
|
|
|
- admin_dash: Optional[AdminDash] = None
|
|
|
+ # A list of URLs to [stylesheets](https://reflex.dev/docs/styling/custom-stylesheets/) to include in the app.
|
|
|
+ stylesheets: List[str] = []
|
|
|
|
|
|
- # The async server name space
|
|
|
- event_namespace: Optional[EventNamespace] = None
|
|
|
+ # A component that is present on every page (defaults to the Connection Error banner).
|
|
|
+ overlay_component: Optional[
|
|
|
+ Union[Component, ComponentCallable]
|
|
|
+ ] = default_overlay_component
|
|
|
|
|
|
# Components to add to the head of every page.
|
|
|
head_components: List[Component] = []
|
|
|
|
|
|
+ # The Socket.IO AsyncServer instance.
|
|
|
+ sio: Optional[AsyncServer] = None
|
|
|
+
|
|
|
# The language to add to the html root tag of every page.
|
|
|
html_lang: Optional[str] = None
|
|
|
|
|
|
# Attributes to add to the html root tag of every page.
|
|
|
html_custom_attrs: Optional[Dict[str, str]] = None
|
|
|
|
|
|
- # A component that is present on every page.
|
|
|
- overlay_component: Optional[
|
|
|
- Union[Component, ComponentCallable]
|
|
|
- ] = default_overlay_component
|
|
|
+ # A map from a page route to the component to render. Users should use `add_page`. PRIVATE.
|
|
|
+ pages: Dict[str, Component] = {}
|
|
|
|
|
|
- # Background tasks that are currently running
|
|
|
- background_tasks: Set[asyncio.Task] = set()
|
|
|
+ # The backend API object. PRIVATE.
|
|
|
+ api: FastAPI = None # type: ignore
|
|
|
|
|
|
- # The radix theme for the entire app
|
|
|
- theme: Optional[Component] = themes.theme(accent_color="blue")
|
|
|
+ # The state class to use for the app. PRIVATE.
|
|
|
+ state: Optional[Type[BaseState]] = None
|
|
|
+
|
|
|
+ # Class to manage many client states.
|
|
|
+ _state_manager: Optional[StateManager] = None
|
|
|
+
|
|
|
+ # Middleware to add to the app. Users should use `add_middleware`. PRIVATE.
|
|
|
+ middleware: List[Middleware] = []
|
|
|
+
|
|
|
+ # Mapping from a route to event handlers to trigger when the page loads. PRIVATE.
|
|
|
+ load_events: Dict[str, List[Union[EventHandler, EventSpec]]] = {}
|
|
|
+
|
|
|
+ # Admin dashboard to view and manage the database. PRIVATE.
|
|
|
+ admin_dash: Optional[AdminDash] = None
|
|
|
+
|
|
|
+ # The async server name space. PRIVATE.
|
|
|
+ event_namespace: Optional[EventNamespace] = None
|
|
|
+
|
|
|
+ # Background tasks that are currently running. PRIVATE.
|
|
|
+ background_tasks: Set[asyncio.Task] = set()
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
"""Initialize the app.
|
|
@@ -195,25 +213,25 @@ class App(Base):
|
|
|
|
|
|
# Set up the API.
|
|
|
self.api = FastAPI()
|
|
|
- self.add_cors()
|
|
|
- self.add_default_endpoints()
|
|
|
+ self._add_cors()
|
|
|
+ self._add_default_endpoints()
|
|
|
|
|
|
- self.setup_state()
|
|
|
+ self._setup_state()
|
|
|
|
|
|
# Set up the admin dash.
|
|
|
- self.setup_admin_dash()
|
|
|
+ self._setup_admin_dash()
|
|
|
|
|
|
- def enable_state(self) -> None:
|
|
|
+ def _enable_state(self) -> None:
|
|
|
"""Enable state for the app."""
|
|
|
if not self.state:
|
|
|
self.state = State
|
|
|
- self.setup_state()
|
|
|
+ self._setup_state()
|
|
|
|
|
|
- def setup_state(self) -> None:
|
|
|
+ def _setup_state(self) -> None:
|
|
|
"""Set up the state for the app.
|
|
|
|
|
|
Raises:
|
|
|
- RuntimeError: If custom `sio` does not use `async_mode='asgi'`.
|
|
|
+ RuntimeError: If the socket server is invalid.
|
|
|
"""
|
|
|
if not self.state:
|
|
|
return
|
|
@@ -244,7 +262,6 @@ class App(Base):
|
|
|
|
|
|
# Create the socket app. Note event endpoint constant replaces the default 'socket.io' path.
|
|
|
socket_app = ASGIApp(self.sio, socketio_path="")
|
|
|
-
|
|
|
namespace = config.get_event_namespace()
|
|
|
|
|
|
# Create the event namespace and attach the main app. Not related to any paths.
|
|
@@ -271,12 +288,12 @@ class App(Base):
|
|
|
"""
|
|
|
return self.api
|
|
|
|
|
|
- def add_default_endpoints(self):
|
|
|
+ def _add_default_endpoints(self):
|
|
|
"""Add default api endpoints (ping)."""
|
|
|
# To test the server.
|
|
|
self.api.get(str(constants.Endpoint.PING))(ping)
|
|
|
|
|
|
- def add_optional_endpoints(self):
|
|
|
+ def _add_optional_endpoints(self):
|
|
|
"""Add optional api endpoints (_upload)."""
|
|
|
# To upload files.
|
|
|
if Upload.is_used:
|
|
@@ -289,7 +306,7 @@ class App(Base):
|
|
|
name="uploaded_files",
|
|
|
)
|
|
|
|
|
|
- def add_cors(self):
|
|
|
+ def _add_cors(self):
|
|
|
"""Add CORS middleware to the app."""
|
|
|
self.api.add_middleware(
|
|
|
cors.CORSMiddleware,
|
|
@@ -313,7 +330,7 @@ class App(Base):
|
|
|
raise ValueError("The state manager has not been initialized.")
|
|
|
return self._state_manager
|
|
|
|
|
|
- async def preprocess(self, state: BaseState, event: Event) -> StateUpdate | None:
|
|
|
+ async def _preprocess(self, state: BaseState, event: Event) -> StateUpdate | None:
|
|
|
"""Preprocess the event.
|
|
|
|
|
|
This is where middleware can modify the event before it is processed.
|
|
@@ -337,7 +354,7 @@ class App(Base):
|
|
|
if out is not None:
|
|
|
return out # type: ignore
|
|
|
|
|
|
- async def postprocess(
|
|
|
+ async def _postprocess(
|
|
|
self, state: BaseState, event: Event, update: StateUpdate
|
|
|
) -> StateUpdate:
|
|
|
"""Postprocess the event.
|
|
@@ -468,14 +485,14 @@ class App(Base):
|
|
|
# Ensure state is enabled if this page uses state.
|
|
|
if self.state is None:
|
|
|
if on_load or component._has_event_triggers():
|
|
|
- self.enable_state()
|
|
|
+ self._enable_state()
|
|
|
else:
|
|
|
for var in component._get_vars(include_children=True):
|
|
|
if not var._var_data:
|
|
|
continue
|
|
|
if not var._var_data.state:
|
|
|
continue
|
|
|
- self.enable_state()
|
|
|
+ self._enable_state()
|
|
|
break
|
|
|
|
|
|
component = OverlayFragment.create(component)
|
|
@@ -580,7 +597,7 @@ class App(Base):
|
|
|
"""Define a custom 404 page for any url having no match.
|
|
|
|
|
|
If there is no page defined on 'index' route, add the 404 page to it.
|
|
|
- If there is no global catchall defined, add the 404 page with a catchall
|
|
|
+ If there is no global catchall defined, add the 404 page with a catchall.
|
|
|
|
|
|
Args:
|
|
|
component: The component to display at the page.
|
|
@@ -602,7 +619,7 @@ class App(Base):
|
|
|
meta=meta,
|
|
|
)
|
|
|
|
|
|
- def setup_admin_dash(self):
|
|
|
+ def _setup_admin_dash(self):
|
|
|
"""Setup the admin dash."""
|
|
|
# Get the admin dash.
|
|
|
admin_dash = self.admin_dash
|
|
@@ -625,14 +642,14 @@ class App(Base):
|
|
|
|
|
|
admin.mount_to(self.api)
|
|
|
|
|
|
- def get_frontend_packages(self, imports: Dict[str, set[ImportVar]]):
|
|
|
+ def _get_frontend_packages(self, imports: Dict[str, set[ImportVar]]):
|
|
|
"""Gets the frontend packages to be installed and filters out the unnecessary ones.
|
|
|
|
|
|
Args:
|
|
|
imports: A dictionary containing the imports used in the current page.
|
|
|
|
|
|
Example:
|
|
|
- >>> get_frontend_packages({"react": "16.14.0", "react-dom": "16.14.0"})
|
|
|
+ >>> _get_frontend_packages({"react": "16.14.0", "react-dom": "16.14.0"})
|
|
|
"""
|
|
|
page_imports = {
|
|
|
i
|
|
@@ -715,19 +732,6 @@ class App(Base):
|
|
|
for k, component in self.pages.items():
|
|
|
self.pages[k] = self._add_overlay_to_component(component)
|
|
|
|
|
|
- def compile(self):
|
|
|
- """compile_() is the new function for performing compilation.
|
|
|
- Reflex framework will call it automatically as needed.
|
|
|
- """
|
|
|
- console.deprecate(
|
|
|
- feature_name="app.compile()",
|
|
|
- reason="Explicit calls to app.compile() are not needed."
|
|
|
- " Method will be removed in 0.4.0",
|
|
|
- deprecation_version="0.3.8",
|
|
|
- removal_version="0.5.0",
|
|
|
- )
|
|
|
- return
|
|
|
-
|
|
|
def _apply_decorated_pages(self):
|
|
|
"""Add @rx.page decorated pages to the app.
|
|
|
|
|
@@ -741,7 +745,7 @@ class App(Base):
|
|
|
for render, kwargs in DECORATED_PAGES[get_config().app_name]:
|
|
|
self.add_page(render, **kwargs)
|
|
|
|
|
|
- def compile_(self, export: bool = False):
|
|
|
+ def _compile(self, export: bool = False):
|
|
|
"""Compile the app and output it to the pages folder.
|
|
|
|
|
|
Args:
|
|
@@ -755,7 +759,7 @@ class App(Base):
|
|
|
self.add_custom_404_page()
|
|
|
|
|
|
# Add the optional endpoints (_upload)
|
|
|
- self.add_optional_endpoints()
|
|
|
+ self._add_optional_endpoints()
|
|
|
|
|
|
if not self._should_compile():
|
|
|
return
|
|
@@ -953,7 +957,7 @@ class App(Base):
|
|
|
progress.stop()
|
|
|
|
|
|
# Install frontend packages.
|
|
|
- self.get_frontend_packages(all_imports)
|
|
|
+ self._get_frontend_packages(all_imports)
|
|
|
|
|
|
# Setup the next.config.js
|
|
|
transpile_packages = [
|
|
@@ -1028,7 +1032,7 @@ class App(Base):
|
|
|
handler=handler, state=substate, payload=event.payload
|
|
|
):
|
|
|
# Postprocess the event.
|
|
|
- update = await self.postprocess(state, event, update)
|
|
|
+ update = await self._postprocess(state, event, update)
|
|
|
|
|
|
# Send the update to the client.
|
|
|
await self.event_namespace.emit_update(
|
|
@@ -1079,7 +1083,7 @@ async def process(
|
|
|
state.router = RouterData(router_data)
|
|
|
|
|
|
# Preprocess the event.
|
|
|
- update = await app.preprocess(state, event)
|
|
|
+ update = await app._preprocess(state, event)
|
|
|
|
|
|
# If there was an update, yield it.
|
|
|
if update is not None:
|
|
@@ -1095,7 +1099,7 @@ async def process(
|
|
|
# Process the event synchronously.
|
|
|
async for update in state._process(event):
|
|
|
# Postprocess the event.
|
|
|
- update = await app.postprocess(state, event, update)
|
|
|
+ update = await app._postprocess(state, event, update)
|
|
|
|
|
|
# Yield the update.
|
|
|
yield update
|
|
@@ -1216,7 +1220,7 @@ def upload(app: App):
|
|
|
async with app.state_manager.modify_state(event.substate_token) as state:
|
|
|
async for update in state._process(event):
|
|
|
# Postprocess the event.
|
|
|
- update = await app.postprocess(state, event, update)
|
|
|
+ update = await app._postprocess(state, event, update)
|
|
|
yield update.json() + "\n"
|
|
|
|
|
|
# Stream updates to client
|