Clean up config and app API (#3197)
This commit is contained in:
parent
db47d39979
commit
7903a1020d
@ -237,7 +237,7 @@ def test_app_10_compile_time_cold(benchmark, app_with_10_components):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_10_components.app_path):
|
with chdir(app_with_10_components.app_path):
|
||||||
app_with_10_components.app_instance.compile_()
|
app_with_10_components.app_instance._compile()
|
||||||
|
|
||||||
benchmark.pedantic(benchmark_fn, setup=setup, rounds=10)
|
benchmark.pedantic(benchmark_fn, setup=setup, rounds=10)
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ def test_app_10_compile_time_warm(benchmark, app_with_10_components):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_10_components.app_path):
|
with chdir(app_with_10_components.app_path):
|
||||||
app_with_10_components.app_instance.compile_()
|
app_with_10_components.app_instance._compile()
|
||||||
|
|
||||||
benchmark(benchmark_fn)
|
benchmark(benchmark_fn)
|
||||||
|
|
||||||
@ -290,7 +290,7 @@ def test_app_100_compile_time_cold(benchmark, app_with_100_components):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_100_components.app_path):
|
with chdir(app_with_100_components.app_path):
|
||||||
app_with_100_components.app_instance.compile_()
|
app_with_100_components.app_instance._compile()
|
||||||
|
|
||||||
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
||||||
|
|
||||||
@ -315,7 +315,7 @@ def test_app_100_compile_time_warm(benchmark, app_with_100_components):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_100_components.app_path):
|
with chdir(app_with_100_components.app_path):
|
||||||
app_with_100_components.app_instance.compile_()
|
app_with_100_components.app_instance._compile()
|
||||||
|
|
||||||
benchmark(benchmark_fn)
|
benchmark(benchmark_fn)
|
||||||
|
|
||||||
@ -343,7 +343,7 @@ def test_app_1000_compile_time_cold(benchmark, app_with_1000_components):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_1000_components.app_path):
|
with chdir(app_with_1000_components.app_path):
|
||||||
app_with_1000_components.app_instance.compile_()
|
app_with_1000_components.app_instance._compile()
|
||||||
|
|
||||||
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
||||||
|
|
||||||
@ -368,6 +368,6 @@ def test_app_1000_compile_time_warm(benchmark, app_with_1000_components):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_1000_components.app_path):
|
with chdir(app_with_1000_components.app_path):
|
||||||
app_with_1000_components.app_instance.compile_()
|
app_with_1000_components.app_instance._compile()
|
||||||
|
|
||||||
benchmark(benchmark_fn)
|
benchmark(benchmark_fn)
|
||||||
|
@ -326,7 +326,7 @@ def test_app_1_compile_time_cold(benchmark, app_with_one_page):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_one_page.app_path):
|
with chdir(app_with_one_page.app_path):
|
||||||
app_with_one_page.app_instance.compile_()
|
app_with_one_page.app_instance._compile()
|
||||||
|
|
||||||
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
||||||
app_with_one_page._reload_state_module()
|
app_with_one_page._reload_state_module()
|
||||||
@ -352,7 +352,7 @@ def test_app_1_compile_time_warm(benchmark, app_with_one_page):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_one_page.app_path):
|
with chdir(app_with_one_page.app_path):
|
||||||
app_with_one_page.app_instance.compile_()
|
app_with_one_page.app_instance._compile()
|
||||||
|
|
||||||
benchmark(benchmark_fn)
|
benchmark(benchmark_fn)
|
||||||
app_with_one_page._reload_state_module()
|
app_with_one_page._reload_state_module()
|
||||||
@ -381,7 +381,7 @@ def test_app_10_compile_time_cold(benchmark, app_with_ten_pages):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_ten_pages.app_path):
|
with chdir(app_with_ten_pages.app_path):
|
||||||
app_with_ten_pages.app_instance.compile_()
|
app_with_ten_pages.app_instance._compile()
|
||||||
|
|
||||||
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
||||||
app_with_ten_pages._reload_state_module()
|
app_with_ten_pages._reload_state_module()
|
||||||
@ -407,7 +407,7 @@ def test_app_10_compile_time_warm(benchmark, app_with_ten_pages):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_ten_pages.app_path):
|
with chdir(app_with_ten_pages.app_path):
|
||||||
app_with_ten_pages.app_instance.compile_()
|
app_with_ten_pages.app_instance._compile()
|
||||||
|
|
||||||
benchmark(benchmark_fn)
|
benchmark(benchmark_fn)
|
||||||
app_with_ten_pages._reload_state_module()
|
app_with_ten_pages._reload_state_module()
|
||||||
@ -436,7 +436,7 @@ def test_app_100_compile_time_cold(benchmark, app_with_hundred_pages):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_hundred_pages.app_path):
|
with chdir(app_with_hundred_pages.app_path):
|
||||||
app_with_hundred_pages.app_instance.compile_()
|
app_with_hundred_pages.app_instance._compile()
|
||||||
|
|
||||||
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
||||||
app_with_hundred_pages._reload_state_module()
|
app_with_hundred_pages._reload_state_module()
|
||||||
@ -462,7 +462,7 @@ def test_app_100_compile_time_warm(benchmark, app_with_hundred_pages):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_hundred_pages.app_path):
|
with chdir(app_with_hundred_pages.app_path):
|
||||||
app_with_hundred_pages.app_instance.compile_()
|
app_with_hundred_pages.app_instance._compile()
|
||||||
|
|
||||||
benchmark(benchmark_fn)
|
benchmark(benchmark_fn)
|
||||||
app_with_hundred_pages._reload_state_module()
|
app_with_hundred_pages._reload_state_module()
|
||||||
@ -491,7 +491,7 @@ def test_app_1000_compile_time_cold(benchmark, app_with_thousand_pages):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_thousand_pages.app_path):
|
with chdir(app_with_thousand_pages.app_path):
|
||||||
app_with_thousand_pages.app_instance.compile_()
|
app_with_thousand_pages.app_instance._compile()
|
||||||
|
|
||||||
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
||||||
app_with_thousand_pages._reload_state_module()
|
app_with_thousand_pages._reload_state_module()
|
||||||
@ -517,7 +517,7 @@ def test_app_1000_compile_time_warm(benchmark, app_with_thousand_pages):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_thousand_pages.app_path):
|
with chdir(app_with_thousand_pages.app_path):
|
||||||
app_with_thousand_pages.app_instance.compile_()
|
app_with_thousand_pages.app_instance._compile()
|
||||||
|
|
||||||
benchmark(benchmark_fn)
|
benchmark(benchmark_fn)
|
||||||
app_with_thousand_pages._reload_state_module()
|
app_with_thousand_pages._reload_state_module()
|
||||||
@ -546,7 +546,7 @@ def test_app_10000_compile_time_cold(benchmark, app_with_ten_thousand_pages):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_ten_thousand_pages.app_path):
|
with chdir(app_with_ten_thousand_pages.app_path):
|
||||||
app_with_ten_thousand_pages.app_instance.compile_()
|
app_with_ten_thousand_pages.app_instance._compile()
|
||||||
|
|
||||||
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
|
||||||
app_with_ten_thousand_pages._reload_state_module()
|
app_with_ten_thousand_pages._reload_state_module()
|
||||||
@ -570,7 +570,7 @@ def test_app_10000_compile_time_warm(benchmark, app_with_ten_thousand_pages):
|
|||||||
|
|
||||||
def benchmark_fn():
|
def benchmark_fn():
|
||||||
with chdir(app_with_ten_thousand_pages.app_path):
|
with chdir(app_with_ten_thousand_pages.app_path):
|
||||||
app_with_ten_thousand_pages.app_instance.compile_()
|
app_with_ten_thousand_pages.app_instance._compile()
|
||||||
|
|
||||||
benchmark(benchmark_fn)
|
benchmark(benchmark_fn)
|
||||||
app_with_ten_thousand_pages._reload_state_module()
|
app_with_ten_thousand_pages._reload_state_module()
|
||||||
|
150
reflex/app.py
150
reflex/app.py
@ -102,61 +102,79 @@ class OverlayFragment(Fragment):
|
|||||||
|
|
||||||
|
|
||||||
class App(Base):
|
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.
|
Every Reflex app needs an app defined in its main module.
|
||||||
pages: Dict[str, Component] = {}
|
|
||||||
|
|
||||||
# A list of URLs to stylesheets to include in the app.
|
```python
|
||||||
stylesheets: List[str] = []
|
# app.py
|
||||||
|
import reflex as rx
|
||||||
|
|
||||||
# The backend API object.
|
# Define state and pages
|
||||||
api: FastAPI = None # type: ignore
|
...
|
||||||
|
|
||||||
# The Socket.IO AsyncServer.
|
app = rx.App(
|
||||||
sio: Optional[AsyncServer] = None
|
# Set global level style.
|
||||||
|
style={...},
|
||||||
|
# Set the top level theme.
|
||||||
|
theme=rx.theme(accent_color="blue"),
|
||||||
|
)
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
# The state class to use for the app.
|
# The global [theme](https://reflex.dev/docs/styling/theming/#theme) for the entire app.
|
||||||
state: Optional[Type[BaseState]] = None
|
theme: Optional[Component] = themes.theme(accent_color="blue")
|
||||||
|
|
||||||
# Class to manage many client states.
|
# The [global style](https://reflex.dev/docs/styling/overview/#global-styles}) for the app.
|
||||||
_state_manager: Optional[StateManager] = None
|
|
||||||
|
|
||||||
# The styling to apply to each component.
|
|
||||||
style: ComponentStyle = {}
|
style: ComponentStyle = {}
|
||||||
|
|
||||||
# Middleware to add to the app.
|
# A list of URLs to [stylesheets](https://reflex.dev/docs/styling/custom-stylesheets/) to include in the app.
|
||||||
middleware: List[Middleware] = []
|
stylesheets: List[str] = []
|
||||||
|
|
||||||
# List of event handlers to trigger when a page loads.
|
# A component that is present on every page (defaults to the Connection Error banner).
|
||||||
load_events: Dict[str, List[Union[EventHandler, EventSpec]]] = {}
|
overlay_component: Optional[
|
||||||
|
Union[Component, ComponentCallable]
|
||||||
# Admin dashboard
|
] = default_overlay_component
|
||||||
admin_dash: Optional[AdminDash] = None
|
|
||||||
|
|
||||||
# The async server name space
|
|
||||||
event_namespace: Optional[EventNamespace] = None
|
|
||||||
|
|
||||||
# Components to add to the head of every page.
|
# Components to add to the head of every page.
|
||||||
head_components: List[Component] = []
|
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.
|
# The language to add to the html root tag of every page.
|
||||||
html_lang: Optional[str] = None
|
html_lang: Optional[str] = None
|
||||||
|
|
||||||
# Attributes to add to the html root tag of every page.
|
# Attributes to add to the html root tag of every page.
|
||||||
html_custom_attrs: Optional[Dict[str, str]] = None
|
html_custom_attrs: Optional[Dict[str, str]] = None
|
||||||
|
|
||||||
# A component that is present on every page.
|
# A map from a page route to the component to render. Users should use `add_page`. PRIVATE.
|
||||||
overlay_component: Optional[
|
pages: Dict[str, Component] = {}
|
||||||
Union[Component, ComponentCallable]
|
|
||||||
] = default_overlay_component
|
|
||||||
|
|
||||||
# Background tasks that are currently running
|
# The backend API object. PRIVATE.
|
||||||
|
api: FastAPI = None # type: ignore
|
||||||
|
|
||||||
|
# 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()
|
background_tasks: Set[asyncio.Task] = set()
|
||||||
|
|
||||||
# The radix theme for the entire app
|
|
||||||
theme: Optional[Component] = themes.theme(accent_color="blue")
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""Initialize the app.
|
"""Initialize the app.
|
||||||
|
|
||||||
@ -195,25 +213,25 @@ class App(Base):
|
|||||||
|
|
||||||
# Set up the API.
|
# Set up the API.
|
||||||
self.api = FastAPI()
|
self.api = FastAPI()
|
||||||
self.add_cors()
|
self._add_cors()
|
||||||
self.add_default_endpoints()
|
self._add_default_endpoints()
|
||||||
|
|
||||||
self.setup_state()
|
self._setup_state()
|
||||||
|
|
||||||
# Set up the admin dash.
|
# 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."""
|
"""Enable state for the app."""
|
||||||
if not self.state:
|
if not self.state:
|
||||||
self.state = 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.
|
"""Set up the state for the app.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RuntimeError: If custom `sio` does not use `async_mode='asgi'`.
|
RuntimeError: If the socket server is invalid.
|
||||||
"""
|
"""
|
||||||
if not self.state:
|
if not self.state:
|
||||||
return
|
return
|
||||||
@ -244,7 +262,6 @@ class App(Base):
|
|||||||
|
|
||||||
# Create the socket app. Note event endpoint constant replaces the default 'socket.io' path.
|
# Create the socket app. Note event endpoint constant replaces the default 'socket.io' path.
|
||||||
socket_app = ASGIApp(self.sio, socketio_path="")
|
socket_app = ASGIApp(self.sio, socketio_path="")
|
||||||
|
|
||||||
namespace = config.get_event_namespace()
|
namespace = config.get_event_namespace()
|
||||||
|
|
||||||
# Create the event namespace and attach the main app. Not related to any paths.
|
# Create the event namespace and attach the main app. Not related to any paths.
|
||||||
@ -271,12 +288,12 @@ class App(Base):
|
|||||||
"""
|
"""
|
||||||
return self.api
|
return self.api
|
||||||
|
|
||||||
def add_default_endpoints(self):
|
def _add_default_endpoints(self):
|
||||||
"""Add default api endpoints (ping)."""
|
"""Add default api endpoints (ping)."""
|
||||||
# To test the server.
|
# To test the server.
|
||||||
self.api.get(str(constants.Endpoint.PING))(ping)
|
self.api.get(str(constants.Endpoint.PING))(ping)
|
||||||
|
|
||||||
def add_optional_endpoints(self):
|
def _add_optional_endpoints(self):
|
||||||
"""Add optional api endpoints (_upload)."""
|
"""Add optional api endpoints (_upload)."""
|
||||||
# To upload files.
|
# To upload files.
|
||||||
if Upload.is_used:
|
if Upload.is_used:
|
||||||
@ -289,7 +306,7 @@ class App(Base):
|
|||||||
name="uploaded_files",
|
name="uploaded_files",
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_cors(self):
|
def _add_cors(self):
|
||||||
"""Add CORS middleware to the app."""
|
"""Add CORS middleware to the app."""
|
||||||
self.api.add_middleware(
|
self.api.add_middleware(
|
||||||
cors.CORSMiddleware,
|
cors.CORSMiddleware,
|
||||||
@ -313,7 +330,7 @@ class App(Base):
|
|||||||
raise ValueError("The state manager has not been initialized.")
|
raise ValueError("The state manager has not been initialized.")
|
||||||
return self._state_manager
|
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.
|
"""Preprocess the event.
|
||||||
|
|
||||||
This is where middleware can modify the event before it is processed.
|
This is where middleware can modify the event before it is processed.
|
||||||
@ -337,7 +354,7 @@ class App(Base):
|
|||||||
if out is not None:
|
if out is not None:
|
||||||
return out # type: ignore
|
return out # type: ignore
|
||||||
|
|
||||||
async def postprocess(
|
async def _postprocess(
|
||||||
self, state: BaseState, event: Event, update: StateUpdate
|
self, state: BaseState, event: Event, update: StateUpdate
|
||||||
) -> StateUpdate:
|
) -> StateUpdate:
|
||||||
"""Postprocess the event.
|
"""Postprocess the event.
|
||||||
@ -468,14 +485,14 @@ class App(Base):
|
|||||||
# Ensure state is enabled if this page uses state.
|
# Ensure state is enabled if this page uses state.
|
||||||
if self.state is None:
|
if self.state is None:
|
||||||
if on_load or component._has_event_triggers():
|
if on_load or component._has_event_triggers():
|
||||||
self.enable_state()
|
self._enable_state()
|
||||||
else:
|
else:
|
||||||
for var in component._get_vars(include_children=True):
|
for var in component._get_vars(include_children=True):
|
||||||
if not var._var_data:
|
if not var._var_data:
|
||||||
continue
|
continue
|
||||||
if not var._var_data.state:
|
if not var._var_data.state:
|
||||||
continue
|
continue
|
||||||
self.enable_state()
|
self._enable_state()
|
||||||
break
|
break
|
||||||
|
|
||||||
component = OverlayFragment.create(component)
|
component = OverlayFragment.create(component)
|
||||||
@ -580,7 +597,7 @@ class App(Base):
|
|||||||
"""Define a custom 404 page for any url having no match.
|
"""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 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:
|
Args:
|
||||||
component: The component to display at the page.
|
component: The component to display at the page.
|
||||||
@ -602,7 +619,7 @@ class App(Base):
|
|||||||
meta=meta,
|
meta=meta,
|
||||||
)
|
)
|
||||||
|
|
||||||
def setup_admin_dash(self):
|
def _setup_admin_dash(self):
|
||||||
"""Setup the admin dash."""
|
"""Setup the admin dash."""
|
||||||
# Get the admin dash.
|
# Get the admin dash.
|
||||||
admin_dash = self.admin_dash
|
admin_dash = self.admin_dash
|
||||||
@ -625,14 +642,14 @@ class App(Base):
|
|||||||
|
|
||||||
admin.mount_to(self.api)
|
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.
|
"""Gets the frontend packages to be installed and filters out the unnecessary ones.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
imports: A dictionary containing the imports used in the current page.
|
imports: A dictionary containing the imports used in the current page.
|
||||||
|
|
||||||
Example:
|
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 = {
|
page_imports = {
|
||||||
i
|
i
|
||||||
@ -715,19 +732,6 @@ class App(Base):
|
|||||||
for k, component in self.pages.items():
|
for k, component in self.pages.items():
|
||||||
self.pages[k] = self._add_overlay_to_component(component)
|
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):
|
def _apply_decorated_pages(self):
|
||||||
"""Add @rx.page decorated pages to the app.
|
"""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]:
|
for render, kwargs in DECORATED_PAGES[get_config().app_name]:
|
||||||
self.add_page(render, **kwargs)
|
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.
|
"""Compile the app and output it to the pages folder.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -755,7 +759,7 @@ class App(Base):
|
|||||||
self.add_custom_404_page()
|
self.add_custom_404_page()
|
||||||
|
|
||||||
# Add the optional endpoints (_upload)
|
# Add the optional endpoints (_upload)
|
||||||
self.add_optional_endpoints()
|
self._add_optional_endpoints()
|
||||||
|
|
||||||
if not self._should_compile():
|
if not self._should_compile():
|
||||||
return
|
return
|
||||||
@ -953,7 +957,7 @@ class App(Base):
|
|||||||
progress.stop()
|
progress.stop()
|
||||||
|
|
||||||
# Install frontend packages.
|
# Install frontend packages.
|
||||||
self.get_frontend_packages(all_imports)
|
self._get_frontend_packages(all_imports)
|
||||||
|
|
||||||
# Setup the next.config.js
|
# Setup the next.config.js
|
||||||
transpile_packages = [
|
transpile_packages = [
|
||||||
@ -1028,7 +1032,7 @@ class App(Base):
|
|||||||
handler=handler, state=substate, payload=event.payload
|
handler=handler, state=substate, payload=event.payload
|
||||||
):
|
):
|
||||||
# Postprocess the event.
|
# Postprocess the event.
|
||||||
update = await self.postprocess(state, event, update)
|
update = await self._postprocess(state, event, update)
|
||||||
|
|
||||||
# Send the update to the client.
|
# Send the update to the client.
|
||||||
await self.event_namespace.emit_update(
|
await self.event_namespace.emit_update(
|
||||||
@ -1079,7 +1083,7 @@ async def process(
|
|||||||
state.router = RouterData(router_data)
|
state.router = RouterData(router_data)
|
||||||
|
|
||||||
# Preprocess the event.
|
# Preprocess the event.
|
||||||
update = await app.preprocess(state, event)
|
update = await app._preprocess(state, event)
|
||||||
|
|
||||||
# If there was an update, yield it.
|
# If there was an update, yield it.
|
||||||
if update is not None:
|
if update is not None:
|
||||||
@ -1095,7 +1099,7 @@ async def process(
|
|||||||
# Process the event synchronously.
|
# Process the event synchronously.
|
||||||
async for update in state._process(event):
|
async for update in state._process(event):
|
||||||
# Postprocess the event.
|
# Postprocess the event.
|
||||||
update = await app.postprocess(state, event, update)
|
update = await app._postprocess(state, event, update)
|
||||||
|
|
||||||
# Yield the update.
|
# Yield the update.
|
||||||
yield update
|
yield update
|
||||||
@ -1216,7 +1220,7 @@ def upload(app: App):
|
|||||||
async with app.state_manager.modify_state(event.substate_token) as state:
|
async with app.state_manager.modify_state(event.substate_token) as state:
|
||||||
async for update in state._process(event):
|
async for update in state._process(event):
|
||||||
# Postprocess the event.
|
# Postprocess the event.
|
||||||
update = await app.postprocess(state, event, update)
|
update = await app._postprocess(state, event, update)
|
||||||
yield update.json() + "\n"
|
yield update.json() + "\n"
|
||||||
|
|
||||||
# Stream updates to client
|
# Stream updates to client
|
||||||
|
148
reflex/app.pyi
148
reflex/app.pyi
@ -1,148 +0,0 @@
|
|||||||
""" Generated with stubgen from mypy, then manually edited, do not regen."""
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
from fastapi import FastAPI
|
|
||||||
from fastapi import UploadFile as UploadFile
|
|
||||||
from reflex import constants as constants
|
|
||||||
from reflex.admin import AdminDash as AdminDash
|
|
||||||
from reflex.base import Base as Base
|
|
||||||
from reflex.compiler import compiler as compiler
|
|
||||||
from reflex.components import connection_modal as connection_modal
|
|
||||||
from reflex.components.component import (
|
|
||||||
Component as Component,
|
|
||||||
ComponentStyle as ComponentStyle,
|
|
||||||
)
|
|
||||||
from reflex.components.base.fragment import Fragment as Fragment
|
|
||||||
from reflex.config import get_config as get_config
|
|
||||||
from reflex.event import (
|
|
||||||
Event as Event,
|
|
||||||
EventHandler as EventHandler,
|
|
||||||
EventSpec as EventSpec,
|
|
||||||
)
|
|
||||||
from reflex.middleware import (
|
|
||||||
HydrateMiddleware as HydrateMiddleware,
|
|
||||||
Middleware as Middleware,
|
|
||||||
)
|
|
||||||
from reflex.model import Model as Model
|
|
||||||
from reflex.page import DECORATED_PAGES as DECORATED_PAGES
|
|
||||||
from reflex.route import (
|
|
||||||
catchall_in_route as catchall_in_route,
|
|
||||||
catchall_prefix as catchall_prefix,
|
|
||||||
get_route_args as get_route_args,
|
|
||||||
verify_route_validity as verify_route_validity,
|
|
||||||
)
|
|
||||||
from reflex.state import (
|
|
||||||
State as State,
|
|
||||||
BaseState as BaseState,
|
|
||||||
StateManager as StateManager,
|
|
||||||
StateUpdate as StateUpdate,
|
|
||||||
)
|
|
||||||
from reflex.utils import (
|
|
||||||
console as console,
|
|
||||||
format as format,
|
|
||||||
prerequisites as prerequisites,
|
|
||||||
types as types,
|
|
||||||
)
|
|
||||||
from socketio import ASGIApp, AsyncNamespace, AsyncServer
|
|
||||||
from typing import (
|
|
||||||
Any,
|
|
||||||
AsyncContextManager,
|
|
||||||
AsyncIterator,
|
|
||||||
Callable,
|
|
||||||
Coroutine,
|
|
||||||
Dict,
|
|
||||||
List,
|
|
||||||
Optional,
|
|
||||||
Set,
|
|
||||||
Type,
|
|
||||||
Union,
|
|
||||||
overload,
|
|
||||||
)
|
|
||||||
|
|
||||||
ComponentCallable = Callable[[], Component]
|
|
||||||
Reducer = Callable[[Event], Coroutine[Any, Any, StateUpdate]]
|
|
||||||
|
|
||||||
def default_overlay_component() -> Component: ...
|
|
||||||
|
|
||||||
class OverlayFragment(Fragment):
|
|
||||||
@overload
|
|
||||||
@classmethod
|
|
||||||
def create(cls, *children, **props) -> "OverlayFragment": ... # type: ignore
|
|
||||||
|
|
||||||
class App(Base):
|
|
||||||
pages: Dict[str, Component]
|
|
||||||
stylesheets: List[str]
|
|
||||||
api: FastAPI
|
|
||||||
sio: Optional[AsyncServer]
|
|
||||||
socket_app: Optional[ASGIApp]
|
|
||||||
state: Type[BaseState]
|
|
||||||
state_manager: StateManager
|
|
||||||
style: ComponentStyle
|
|
||||||
middleware: List[Middleware]
|
|
||||||
load_events: Dict[str, List[Union[EventHandler, EventSpec]]]
|
|
||||||
admin_dash: Optional[AdminDash]
|
|
||||||
event_namespace: Optional[AsyncNamespace]
|
|
||||||
overlay_component: Optional[Union[Component, ComponentCallable]]
|
|
||||||
background_tasks: Set[asyncio.Task] = set()
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
stylesheets: Optional[List[str]] = None,
|
|
||||||
style: Optional[ComponentStyle] = None,
|
|
||||||
admin_dash: Optional[AdminDash] = None,
|
|
||||||
overlay_component: Optional[Union[Component, ComponentCallable]] = None,
|
|
||||||
**kwargs
|
|
||||||
) -> None: ...
|
|
||||||
def __call__(self) -> FastAPI: ...
|
|
||||||
def enable_state(self) -> None: ...
|
|
||||||
def add_default_endpoints(self) -> None: ...
|
|
||||||
def add_optional_endpoints(self): ...
|
|
||||||
def add_cors(self) -> None: ...
|
|
||||||
async def preprocess(self, state: State, event: Event) -> StateUpdate | None: ...
|
|
||||||
async def postprocess(
|
|
||||||
self, state: State, event: Event, update: StateUpdate
|
|
||||||
) -> StateUpdate: ...
|
|
||||||
def add_middleware(self, middleware: Middleware, index: int | None = ...): ...
|
|
||||||
def add_page(
|
|
||||||
self,
|
|
||||||
component: Component | ComponentCallable,
|
|
||||||
route: str | None = ...,
|
|
||||||
title: str = ...,
|
|
||||||
description: str = ...,
|
|
||||||
image=...,
|
|
||||||
on_load: EventHandler | EventSpec | list[EventHandler | EventSpec] | None = ...,
|
|
||||||
meta: list[dict[str, str]] = ...,
|
|
||||||
script_tags: list[Component] | None = ...,
|
|
||||||
): ...
|
|
||||||
def get_load_events(self, route: str) -> list[EventHandler | EventSpec]: ...
|
|
||||||
def add_custom_404_page(
|
|
||||||
self,
|
|
||||||
component: Component | ComponentCallable | None = ...,
|
|
||||||
title: str = ...,
|
|
||||||
image: str = ...,
|
|
||||||
description: str = ...,
|
|
||||||
on_load: EventHandler | EventSpec | list[EventHandler | EventSpec] | None = ...,
|
|
||||||
meta: list[dict[str, str]] = ...,
|
|
||||||
): ...
|
|
||||||
def setup_admin_dash(self) -> None: ...
|
|
||||||
def get_frontend_packages(self, imports: Dict[str, str]): ...
|
|
||||||
def compile(self) -> None: ...
|
|
||||||
def compile_(self) -> None: ...
|
|
||||||
def modify_state(self, token: str) -> AsyncContextManager[State]: ...
|
|
||||||
def _setup_overlay_component(self) -> None: ...
|
|
||||||
def _process_background(
|
|
||||||
self, state: State, event: Event
|
|
||||||
) -> asyncio.Task | None: ...
|
|
||||||
|
|
||||||
def process(
|
|
||||||
app: App, event: Event, sid: str, headers: Dict, client_ip: str
|
|
||||||
) -> AsyncIterator[StateUpdate]: ...
|
|
||||||
async def ping() -> str: ...
|
|
||||||
def upload(app: App): ...
|
|
||||||
|
|
||||||
class EventNamespace(AsyncNamespace):
|
|
||||||
app: App
|
|
||||||
def __init__(self, namespace: str, app: App) -> None: ...
|
|
||||||
def on_connect(self, sid, environ) -> None: ...
|
|
||||||
def on_disconnect(self, sid) -> None: ...
|
|
||||||
async def on_event(self, sid, data) -> None: ...
|
|
||||||
async def on_ping(self, sid) -> None: ...
|
|
@ -15,7 +15,7 @@ app = getattr(app_module, constants.CompileVars.APP)
|
|||||||
# For py3.8 and py3.9 compatibility when redis is used, we MUST add any decorator pages
|
# For py3.8 and 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).
|
# before compiling the app in a thread to avoid event loop error (REF-2172).
|
||||||
app._apply_decorated_pages()
|
app._apply_decorated_pages()
|
||||||
compile_future = ThreadPoolExecutor(max_workers=1).submit(app.compile_)
|
compile_future = ThreadPoolExecutor(max_workers=1).submit(app._compile)
|
||||||
compile_future.add_done_callback(
|
compile_future.add_done_callback(
|
||||||
# Force background compile errors to print eagerly
|
# Force background compile errors to print eagerly
|
||||||
lambda f: f.result()
|
lambda f: f.result()
|
||||||
|
@ -135,29 +135,47 @@ class DBConfig(Base):
|
|||||||
|
|
||||||
|
|
||||||
class Config(Base):
|
class Config(Base):
|
||||||
"""A Reflex config."""
|
"""The config defines runtime settings for the app.
|
||||||
|
|
||||||
|
By default, the config is defined in an `rxconfig.py` file in the root of the app.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# rxconfig.py
|
||||||
|
import reflex as rx
|
||||||
|
|
||||||
|
config = rx.Config(
|
||||||
|
app_name="myapp",
|
||||||
|
api_url="http://localhost:8000",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Every config value can be overridden by an environment variable with the same name in uppercase.
|
||||||
|
For example, `db_url` can be overridden by setting the `DB_URL` environment variable.
|
||||||
|
|
||||||
|
See the [configuration](https://reflex.dev/docs/getting-started/configuration/) docs for more info.
|
||||||
|
"""
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
"""Pydantic config for the config."""
|
"""Pydantic config for the config."""
|
||||||
|
|
||||||
validate_assignment = True
|
validate_assignment = True
|
||||||
|
|
||||||
# The name of the app.
|
# The name of the app (should match the name of the app directory).
|
||||||
app_name: str
|
app_name: str
|
||||||
|
|
||||||
# The log level to use.
|
# The log level to use.
|
||||||
loglevel: constants.LogLevel = constants.LogLevel.INFO
|
loglevel: constants.LogLevel = constants.LogLevel.INFO
|
||||||
|
|
||||||
# The port to run the frontend on.
|
# The port to run the frontend on. NOTE: When running in dev mode, the next available port will be used if this is taken.
|
||||||
frontend_port: int = 3000
|
frontend_port: int = 3000
|
||||||
|
|
||||||
# The path to run the frontend on.
|
# The path to run the frontend on. For example, "/app" will run the frontend on http://localhost:3000/app
|
||||||
frontend_path: str = ""
|
frontend_path: str = ""
|
||||||
|
|
||||||
# The port to run the backend on.
|
# The port to run the backend on. NOTE: When running in dev mode, the next available port will be used if this is taken.
|
||||||
backend_port: int = 8000
|
backend_port: int = 8000
|
||||||
|
|
||||||
# The backend url the frontend will connect to.
|
# The backend url the frontend will connect to. This must be updated if the backend is hosted elsewhere, or in production.
|
||||||
api_url: str = f"http://localhost:{backend_port}"
|
api_url: str = f"http://localhost:{backend_port}"
|
||||||
|
|
||||||
# The url the frontend will be hosted on.
|
# The url the frontend will be hosted on.
|
||||||
@ -166,10 +184,10 @@ class Config(Base):
|
|||||||
# The url the backend will be hosted on.
|
# The url the backend will be hosted on.
|
||||||
backend_host: str = "0.0.0.0"
|
backend_host: str = "0.0.0.0"
|
||||||
|
|
||||||
# The database url.
|
# The database url used by rx.Model.
|
||||||
db_url: Optional[str] = "sqlite:///reflex.db"
|
db_url: Optional[str] = "sqlite:///reflex.db"
|
||||||
|
|
||||||
# The redis url.
|
# The redis url
|
||||||
redis_url: Optional[str] = None
|
redis_url: Optional[str] = None
|
||||||
|
|
||||||
# Telemetry opt-in.
|
# Telemetry opt-in.
|
||||||
@ -190,9 +208,6 @@ class Config(Base):
|
|||||||
# Whether to enable or disable nextJS gzip compression.
|
# Whether to enable or disable nextJS gzip compression.
|
||||||
next_compression: bool = True
|
next_compression: bool = True
|
||||||
|
|
||||||
# The event namespace for ws connection
|
|
||||||
event_namespace: Optional[str] = None
|
|
||||||
|
|
||||||
# Additional frontend packages to install.
|
# Additional frontend packages to install.
|
||||||
frontend_packages: List[str] = []
|
frontend_packages: List[str] = []
|
||||||
|
|
||||||
@ -216,9 +231,6 @@ class Config(Base):
|
|||||||
"""
|
"""
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Check for deprecated values.
|
|
||||||
self.check_deprecated_values(**kwargs)
|
|
||||||
|
|
||||||
# Update the config from environment variables.
|
# Update the config from environment variables.
|
||||||
env_kwargs = self.update_from_env()
|
env_kwargs = self.update_from_env()
|
||||||
for key, env_value in env_kwargs.items():
|
for key, env_value in env_kwargs.items():
|
||||||
@ -238,29 +250,8 @@ class Config(Base):
|
|||||||
"""
|
"""
|
||||||
return ".".join([self.app_name, self.app_name])
|
return ".".join([self.app_name, self.app_name])
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_deprecated_values(**kwargs):
|
|
||||||
"""Check for deprecated config values.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
**kwargs: The kwargs passed to the config.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If a deprecated config value is found.
|
|
||||||
"""
|
|
||||||
if "db_config" in kwargs:
|
|
||||||
raise ValueError("db_config is deprecated - use db_url instead")
|
|
||||||
if "admin_dash" in kwargs:
|
|
||||||
raise ValueError(
|
|
||||||
"admin_dash is deprecated in the config - pass it as a param to rx.App instead"
|
|
||||||
)
|
|
||||||
if "env_path" in kwargs:
|
|
||||||
raise ValueError(
|
|
||||||
"env_path is deprecated - use environment variables instead"
|
|
||||||
)
|
|
||||||
|
|
||||||
def update_from_env(self) -> dict[str, Any]:
|
def update_from_env(self) -> dict[str, Any]:
|
||||||
"""Update the config from environment variables.
|
"""Update the config values based on set environment variables.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The updated config values.
|
The updated config values.
|
||||||
@ -300,20 +291,11 @@ class Config(Base):
|
|||||||
return updated_values
|
return updated_values
|
||||||
|
|
||||||
def get_event_namespace(self) -> str:
|
def get_event_namespace(self) -> str:
|
||||||
"""Get the websocket event namespace.
|
"""Get the path that the backend Websocket server lists on.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The namespace for websocket.
|
The namespace for websocket.
|
||||||
"""
|
"""
|
||||||
if self.event_namespace:
|
|
||||||
console.deprecate(
|
|
||||||
feature_name="Passing event_namespace in the config",
|
|
||||||
reason="",
|
|
||||||
deprecation_version="0.3.5",
|
|
||||||
removal_version="0.5.0",
|
|
||||||
)
|
|
||||||
return f'/{self.event_namespace.strip("/")}'
|
|
||||||
|
|
||||||
event_url = constants.Endpoint.EVENT.get_url()
|
event_url = constants.Endpoint.EVENT.get_url()
|
||||||
return urllib.parse.urlsplit(event_url).path
|
return urllib.parse.urlsplit(event_url).path
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ def _run(
|
|||||||
if prerequisites.needs_reinit(frontend=frontend):
|
if prerequisites.needs_reinit(frontend=frontend):
|
||||||
_init(name=config.app_name, loglevel=loglevel)
|
_init(name=config.app_name, loglevel=loglevel)
|
||||||
|
|
||||||
# If something is running on the ports, ask the user if they want to kill or change it.
|
# Find the next available open port.
|
||||||
if frontend and processes.is_process_on_port(frontend_port):
|
if frontend and processes.is_process_on_port(frontend_port):
|
||||||
frontend_port = processes.change_port(frontend_port, "frontend")
|
frontend_port = processes.change_port(frontend_port, "frontend")
|
||||||
|
|
||||||
|
@ -1909,7 +1909,7 @@ class OnLoadInternalState(State):
|
|||||||
Returns:
|
Returns:
|
||||||
The list of events to queue for on load handling.
|
The list of events to queue for on load handling.
|
||||||
"""
|
"""
|
||||||
# Do not app.compile_()! It should be already compiled by now.
|
# Do not app._compile()! It should be already compiled by now.
|
||||||
app = getattr(prerequisites.get_app(), constants.CompileVars.APP)
|
app = getattr(prerequisites.get_app(), constants.CompileVars.APP)
|
||||||
load_events = app.get_load_events(self.router.page.path)
|
load_events = app.get_load_events(self.router.page.path)
|
||||||
if not load_events:
|
if not load_events:
|
||||||
@ -1927,8 +1927,45 @@ class OnLoadInternalState(State):
|
|||||||
|
|
||||||
|
|
||||||
class ComponentState(Base):
|
class ComponentState(Base):
|
||||||
"""The base class for a State that is copied for each Component associated with it."""
|
"""Base class to allow for the creation of a state instance per component.
|
||||||
|
|
||||||
|
This allows for the bundling of UI and state logic into a single class,
|
||||||
|
where each instance has a separate instance of the state.
|
||||||
|
|
||||||
|
Subclass this class and define vars and event handlers in the traditional way.
|
||||||
|
Then define a `get_component` method that returns the UI for the component instance.
|
||||||
|
|
||||||
|
See the full [docs](https://reflex.dev/docs/substates/component-state/) for more.
|
||||||
|
|
||||||
|
Basic example:
|
||||||
|
```python
|
||||||
|
# Subclass ComponentState and define vars and event handlers.
|
||||||
|
class Counter(rx.ComponentState):
|
||||||
|
# Define vars that change.
|
||||||
|
count: int = 0
|
||||||
|
|
||||||
|
# Define event handlers.
|
||||||
|
def increment(self):
|
||||||
|
self.count += 1
|
||||||
|
|
||||||
|
def decrement(self):
|
||||||
|
self.count -= 1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_component(cls, **props):
|
||||||
|
# Access the state vars and event handlers using `cls`.
|
||||||
|
return rx.hstack(
|
||||||
|
rx.button("Decrement", on_click=cls.decrement),
|
||||||
|
rx.text(cls.count),
|
||||||
|
rx.button("Increment", on_click=cls.increment),
|
||||||
|
**props,
|
||||||
|
)
|
||||||
|
|
||||||
|
counter = Counter.create()
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The number of components created from this class.
|
||||||
_per_component_state_instance_count: ClassVar[int] = 0
|
_per_component_state_instance_count: ClassVar[int] = 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -42,7 +42,6 @@ import reflex.utils.prerequisites
|
|||||||
import reflex.utils.processes
|
import reflex.utils.processes
|
||||||
from reflex.state import (
|
from reflex.state import (
|
||||||
BaseState,
|
BaseState,
|
||||||
State,
|
|
||||||
StateManagerMemory,
|
StateManagerMemory,
|
||||||
StateManagerRedis,
|
StateManagerRedis,
|
||||||
reload_state_module,
|
reload_state_module,
|
||||||
@ -598,7 +597,7 @@ class AppHarness:
|
|||||||
await self.state_manager.close()
|
await self.state_manager.close()
|
||||||
|
|
||||||
@contextlib.asynccontextmanager
|
@contextlib.asynccontextmanager
|
||||||
async def modify_state(self, token: str) -> AsyncIterator[State]:
|
async def modify_state(self, token: str) -> AsyncIterator[BaseState]:
|
||||||
"""Modify the state associated with the given token and send update to frontend.
|
"""Modify the state associated with the given token and send update to frontend.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -240,7 +240,7 @@ def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
|
|||||||
# For py3.8 and py3.9 compatibility when redis is used, we MUST add any decorator pages
|
# For py3.8 and 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).
|
# before compiling the app in a thread to avoid event loop error (REF-2172).
|
||||||
app._apply_decorated_pages()
|
app._apply_decorated_pages()
|
||||||
app.compile_(export=export)
|
app._compile(export=export)
|
||||||
return app_module
|
return app_module
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ PWD = Path(".").resolve()
|
|||||||
|
|
||||||
EXCLUDED_FILES = [
|
EXCLUDED_FILES = [
|
||||||
"__init__.py",
|
"__init__.py",
|
||||||
"app.py",
|
# "app.py",
|
||||||
"component.py",
|
"component.py",
|
||||||
"bare.py",
|
"bare.py",
|
||||||
"foreach.py",
|
"foreach.py",
|
||||||
|
@ -260,6 +260,7 @@ def test_add_page_set_route_dynamic(index_page, windows_platform: bool):
|
|||||||
windows_platform: Whether the system is windows.
|
windows_platform: Whether the system is windows.
|
||||||
"""
|
"""
|
||||||
app = App(state=EmptyState)
|
app = App(state=EmptyState)
|
||||||
|
assert app.state is not None
|
||||||
route = "/test/[dynamic]"
|
route = "/test/[dynamic]"
|
||||||
if windows_platform:
|
if windows_platform:
|
||||||
route.lstrip("/").replace("/", "\\")
|
route.lstrip("/").replace("/", "\\")
|
||||||
@ -953,6 +954,7 @@ async def test_dynamic_route_var_route_change_completed_on_load(
|
|||||||
if windows_platform:
|
if windows_platform:
|
||||||
route.lstrip("/").replace("/", "\\")
|
route.lstrip("/").replace("/", "\\")
|
||||||
app = app_module_mock.app = App(state=DynamicState)
|
app = app_module_mock.app = App(state=DynamicState)
|
||||||
|
assert app.state is not None
|
||||||
assert arg_name not in app.state.vars
|
assert arg_name not in app.state.vars
|
||||||
app.add_page(index_page, route=route, on_load=DynamicState.on_load) # type: ignore
|
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.vars
|
||||||
@ -1147,7 +1149,7 @@ async def test_process_events(mocker, token: str):
|
|||||||
"ip": "127.0.0.1",
|
"ip": "127.0.0.1",
|
||||||
}
|
}
|
||||||
app = App(state=GenState)
|
app = App(state=GenState)
|
||||||
mocker.patch.object(app, "postprocess", AsyncMock())
|
mocker.patch.object(app, "_postprocess", AsyncMock())
|
||||||
event = Event(
|
event = Event(
|
||||||
token=token, name="gen_state.go", payload={"c": 5}, router_data=router_data
|
token=token, name="gen_state.go", payload={"c": 5}, router_data=router_data
|
||||||
)
|
)
|
||||||
@ -1156,7 +1158,7 @@ async def test_process_events(mocker, token: str):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
assert (await app.state_manager.get_state(event.substate_token)).value == 5
|
assert (await app.state_manager.get_state(event.substate_token)).value == 5
|
||||||
assert app.postprocess.call_count == 6
|
assert app._postprocess.call_count == 6
|
||||||
|
|
||||||
if isinstance(app.state_manager, StateManagerRedis):
|
if isinstance(app.state_manager, StateManagerRedis):
|
||||||
await app.state_manager.close()
|
await app.state_manager.close()
|
||||||
@ -1236,7 +1238,7 @@ def compilable_app(tmp_path) -> Generator[tuple[App, Path], None, None]:
|
|||||||
web_dir.mkdir(parents=True)
|
web_dir.mkdir(parents=True)
|
||||||
(web_dir / "package.json").touch()
|
(web_dir / "package.json").touch()
|
||||||
app = App(theme=None)
|
app = App(theme=None)
|
||||||
app.get_frontend_packages = unittest.mock.Mock()
|
app._get_frontend_packages = unittest.mock.Mock()
|
||||||
with chdir(app_path):
|
with chdir(app_path):
|
||||||
yield app, web_dir
|
yield app, web_dir
|
||||||
|
|
||||||
@ -1249,7 +1251,7 @@ def test_app_wrap_compile_theme(compilable_app):
|
|||||||
"""
|
"""
|
||||||
app, web_dir = compilable_app
|
app, web_dir = compilable_app
|
||||||
app.theme = rx.theme(accent_color="plum")
|
app.theme = rx.theme(accent_color="plum")
|
||||||
app.compile_()
|
app._compile()
|
||||||
app_js_contents = (web_dir / "pages" / "_app.js").read_text()
|
app_js_contents = (web_dir / "pages" / "_app.js").read_text()
|
||||||
app_js_lines = [
|
app_js_lines = [
|
||||||
line.strip() for line in app_js_contents.splitlines() if line.strip()
|
line.strip() for line in app_js_contents.splitlines() if line.strip()
|
||||||
@ -1299,7 +1301,7 @@ def test_app_wrap_priority(compilable_app):
|
|||||||
return Fragment1.create(Fragment3.create())
|
return Fragment1.create(Fragment3.create())
|
||||||
|
|
||||||
app.add_page(page)
|
app.add_page(page)
|
||||||
app.compile_()
|
app._compile()
|
||||||
app_js_contents = (web_dir / "pages" / "_app.js").read_text()
|
app_js_contents = (web_dir / "pages" / "_app.js").read_text()
|
||||||
app_js_lines = [
|
app_js_lines = [
|
||||||
line.strip() for line in app_js_contents.splitlines() if line.strip()
|
line.strip() for line in app_js_contents.splitlines() if line.strip()
|
||||||
@ -1371,7 +1373,7 @@ def test_raise_on_state():
|
|||||||
"""Test that the state is set."""
|
"""Test that the state is set."""
|
||||||
# state kwargs is deprecated, we just make sure the app is created anyway.
|
# state kwargs is deprecated, we just make sure the app is created anyway.
|
||||||
_app = App(state=State)
|
_app = App(state=State)
|
||||||
print(_app.state)
|
assert _app.state is not None
|
||||||
assert issubclass(_app.state, State)
|
assert issubclass(_app.state, State)
|
||||||
|
|
||||||
|
|
||||||
@ -1387,7 +1389,7 @@ def test_app_with_optional_endpoints():
|
|||||||
|
|
||||||
app = App()
|
app = App()
|
||||||
Upload.is_used = True
|
Upload.is_used = True
|
||||||
app.add_optional_endpoints()
|
app._add_optional_endpoints()
|
||||||
# TODO: verify the availability of the endpoints in app.api
|
# TODO: verify the availability of the endpoints in app.api
|
||||||
|
|
||||||
|
|
||||||
@ -1395,7 +1397,7 @@ def test_app_state_manager():
|
|||||||
app = App()
|
app = App()
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
app.state_manager
|
app.state_manager
|
||||||
app.enable_state()
|
app._enable_state()
|
||||||
assert app.state_manager is not None
|
assert app.state_manager is not None
|
||||||
assert isinstance(app.state_manager, (StateManagerMemory, StateManagerRedis))
|
assert isinstance(app.state_manager, (StateManagerMemory, StateManagerRedis))
|
||||||
|
|
||||||
@ -1479,7 +1481,7 @@ def test_app_with_transpile_packages(compilable_app, export):
|
|||||||
C1.create(), C2.create(), C3.create(), C4.create(), C5.create()
|
C1.create(), C2.create(), C3.create(), C4.create(), C5.create()
|
||||||
)
|
)
|
||||||
app.add_page(page, route="/")
|
app.add_page(page, route="/")
|
||||||
app.compile_(export=export)
|
app._compile(export=export)
|
||||||
|
|
||||||
next_config = (web_dir / "next.config.js").read_text()
|
next_config = (web_dir / "next.config.js").read_text()
|
||||||
transpile_packages_match = re.search(r"transpilePackages: (\[.*?\])", next_config)
|
transpile_packages_match = re.search(r"transpilePackages: (\[.*?\])", next_config)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
from typing import Any, Dict
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -25,25 +24,6 @@ def test_set_app_name(base_config_values):
|
|||||||
assert config.app_name == base_config_values["app_name"]
|
assert config.app_name == base_config_values["app_name"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"param",
|
|
||||||
[
|
|
||||||
"db_config",
|
|
||||||
"admin_dash",
|
|
||||||
"env_path",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_deprecated_params(base_config_values: Dict[str, Any], param):
|
|
||||||
"""Test that deprecated params are removed from the config.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
base_config_values: Config values.
|
|
||||||
param: The deprecated param.
|
|
||||||
"""
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
rx.Config(**base_config_values, **{param: "test"}) # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"env_var, value",
|
"env_var, value",
|
||||||
[
|
[
|
||||||
@ -87,12 +67,6 @@ def test_update_from_env(base_config_values, monkeypatch, env_var, value):
|
|||||||
{"app_name": "test_app", "api_url": "http://example.com/api"},
|
{"app_name": "test_app", "api_url": "http://example.com/api"},
|
||||||
f"/api{Endpoint.EVENT}",
|
f"/api{Endpoint.EVENT}",
|
||||||
),
|
),
|
||||||
({"app_name": "test_app", "event_namespace": "/event"}, f"/event"),
|
|
||||||
({"app_name": "test_app", "event_namespace": "event"}, f"/event"),
|
|
||||||
({"app_name": "test_app", "event_namespace": "event/"}, f"/event"),
|
|
||||||
({"app_name": "test_app", "event_namespace": "/_event"}, f"{Endpoint.EVENT}"),
|
|
||||||
({"app_name": "test_app", "event_namespace": "_event"}, f"{Endpoint.EVENT}"),
|
|
||||||
({"app_name": "test_app", "event_namespace": "_event/"}, f"{Endpoint.EVENT}"),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_event_namespace(mocker, kwargs, expected):
|
def test_event_namespace(mocker, kwargs, expected):
|
||||||
|
@ -24,7 +24,7 @@ def test_app_harness(tmp_path):
|
|||||||
|
|
||||||
app = rx.App(state=State)
|
app = rx.App(state=State)
|
||||||
app.add_page(lambda: rx.text("Basic App"), route="/", title="index")
|
app.add_page(lambda: rx.text("Basic App"), route="/", title="index")
|
||||||
app.compile_()
|
app._compile()
|
||||||
|
|
||||||
with AppHarness.create(
|
with AppHarness.create(
|
||||||
root=tmp_path,
|
root=tmp_path,
|
||||||
|
Loading…
Reference in New Issue
Block a user