
* Reuse the sqlalchemy engine once it's created * Implement `rx.asession` for async database support Requires setting `async_db_url` to the same database as `db_url`, except using an async driver; this will vary by database. * resolve the url first, so the key into _ENGINE is correct * Ping db connections before returning them from pool Move connect engine kwargs to a separate function * the param is `echo` * sanity check that config db_url and async_db_url are the same throw a warning if the part following the `://` differs between these two * create_async_engine: use sqlalchemy async API update types * redact ASYNC_DB_URL similarly to DB_URL when overridden in config * update rx.asession docstring * use async_sessionmaker * Redact sensitive env vars instead of hiding them
386 lines
10 KiB
Python
386 lines
10 KiB
Python
"""Import all classes and functions the end user will need to make an app.
|
|
|
|
Anything imported here will be available in the default Reflex import as `rx.*`.
|
|
|
|
Dynamic Imports
|
|
---------------
|
|
Reflex utilizes dynamic imports, or lazy loading, to reduce startup/import times.
|
|
With this approach, imports are delayed until they are actually needed. We use
|
|
the `lazy_loader` library(https://github.com/scientific-python/lazy_loader) to achieve this.
|
|
|
|
How it works
|
|
--------------
|
|
`lazy_loader.attach` takes two optional arguments: `submodules` and `submod_attrs`.
|
|
- `submodules` typically points to directories or files to be accessed.
|
|
- `submod_attrs` defines a mapping of directory or file names as keys with a list
|
|
of attributes or modules to access.
|
|
|
|
Example directory structure:
|
|
|
|
reflex/
|
|
|_ components/
|
|
|_ radix/
|
|
|_ themes/
|
|
|_ components/
|
|
|_ box.py
|
|
|
|
To add `box` under the `rx` namespace (`rx.box`), add the relative path to `submod_attrs` in
|
|
`reflex/__init__.py` (this file):
|
|
|
|
```python
|
|
lazy_loader.attach(
|
|
submodules={"components"},
|
|
submod_attrs={
|
|
"components.radix.themes.components.box": ["box"]
|
|
}
|
|
)
|
|
```
|
|
|
|
This implies that `box` will be imported from `reflex/components/radix/themes/components/box.py`.
|
|
|
|
To add box under the `rx.radix` namespace (`rx.radix.box`), add the relative path to the
|
|
submod_attrs argument in `reflex/components/radix/__init__.py`:
|
|
|
|
```python
|
|
lazy_loader.attach(
|
|
submodules = {"themes"},
|
|
submod_attrs = {
|
|
"themes.components.box": ["box"]
|
|
}
|
|
)
|
|
```
|
|
|
|
Note: It is important to specify the immediate submodules of a directory in the submodules
|
|
argument to ensure they are registered at runtime. For example, 'components' for reflex,
|
|
'radix' for components, 'themes' for radix, etc.
|
|
|
|
|
|
Pyi_generator
|
|
--------------
|
|
To generate `.pyi` files for `__init__.py` files, we read the `_SUBMODULES` and `_SUBMOD_ATTRS`
|
|
attributes to generate the import statements. It is highly recommended to define these with
|
|
the provided annotations to facilitate their generation.
|
|
|
|
|
|
Aliases
|
|
------------
|
|
This is a special case to specify an alias for a component.
|
|
As an example, we use this typically for `rx.list` where defining `list` attribute in the list.py
|
|
overshadows python's list object which messes up the pyi generation for `list.pyi`. As a result, aliases
|
|
should be used for similar cases like this. Note that this logic is employed to fix the pyi generation and alias
|
|
should still be defined or accessible. Check out the __getattr__ logic in `reflex/components/radix/themes/layouts/list.py`
|
|
|
|
```python
|
|
lazy_loader.attach(
|
|
submodules={"components"},
|
|
submod_attrs={
|
|
"components.radix.themes.layouts": [("list_ns", "list")]
|
|
}
|
|
)
|
|
```
|
|
|
|
In the example above, you will be able to do `rx.list`
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from reflex.utils import (
|
|
compat, # for side-effects
|
|
lazy_loader,
|
|
)
|
|
|
|
from .event import event as event
|
|
|
|
# import this here explicitly to avoid returning the page module since page attr has the
|
|
# same name as page module(page.py)
|
|
from .page import page as page
|
|
|
|
# Remove the `compat` name from the namespace, it was imported for side-effects only.
|
|
del compat
|
|
|
|
RADIX_THEMES_MAPPING: dict = {
|
|
"components.radix.themes.base": ["color_mode", "theme", "theme_panel"],
|
|
"components.radix.themes.color_mode": ["color_mode"],
|
|
}
|
|
RADIX_THEMES_COMPONENTS_MAPPING: dict = {
|
|
**{
|
|
f"components.radix.themes.components.{mod}": [mod]
|
|
for mod in [
|
|
"alert_dialog",
|
|
"aspect_ratio",
|
|
"avatar",
|
|
"badge",
|
|
"button",
|
|
"callout",
|
|
"card",
|
|
"checkbox",
|
|
"context_menu",
|
|
"data_list",
|
|
"dialog",
|
|
"hover_card",
|
|
"icon_button",
|
|
"input",
|
|
"inset",
|
|
"popover",
|
|
"scroll_area",
|
|
"select",
|
|
"skeleton",
|
|
"slider",
|
|
"spinner",
|
|
"switch",
|
|
"table",
|
|
"tabs",
|
|
"text_area",
|
|
"tooltip",
|
|
"segmented_control",
|
|
"radio_cards",
|
|
"checkbox_cards",
|
|
"checkbox_group",
|
|
]
|
|
},
|
|
"components.radix.themes.components.text_field": ["text_field", "input"],
|
|
"components.radix.themes.components.radio_group": ["radio", "radio_group"],
|
|
"components.radix.themes.components.dropdown_menu": ["menu", "dropdown_menu"],
|
|
"components.radix.themes.components.separator": ["divider", "separator"],
|
|
"components.radix.themes.components.progress": ["progress"],
|
|
}
|
|
|
|
RADIX_THEMES_LAYOUT_MAPPING: dict = {
|
|
"components.radix.themes.layout.box": [
|
|
"box",
|
|
],
|
|
"components.radix.themes.layout.center": [
|
|
"center",
|
|
],
|
|
"components.radix.themes.layout.container": [
|
|
"container",
|
|
],
|
|
"components.radix.themes.layout.flex": [
|
|
"flex",
|
|
],
|
|
"components.radix.themes.layout.grid": [
|
|
"grid",
|
|
],
|
|
"components.radix.themes.layout.section": [
|
|
"section",
|
|
],
|
|
"components.radix.themes.layout.spacer": [
|
|
"spacer",
|
|
],
|
|
"components.radix.themes.layout.stack": [
|
|
"stack",
|
|
"hstack",
|
|
"vstack",
|
|
],
|
|
"components.radix.themes.layout.list": [
|
|
("list_ns", "list"),
|
|
"list_item",
|
|
"ordered_list",
|
|
"unordered_list",
|
|
],
|
|
}
|
|
|
|
RADIX_THEMES_TYPOGRAPHY_MAPPING: dict = {
|
|
"components.radix.themes.typography.blockquote": [
|
|
"blockquote",
|
|
],
|
|
"components.radix.themes.typography.code": [
|
|
"code",
|
|
],
|
|
"components.radix.themes.typography.heading": [
|
|
"heading",
|
|
],
|
|
"components.radix.themes.typography.link": [
|
|
"link",
|
|
],
|
|
"components.radix.themes.typography.text": [
|
|
"text",
|
|
],
|
|
}
|
|
|
|
RADIX_PRIMITIVES_MAPPING: dict = {
|
|
"components.radix.primitives.accordion": [
|
|
"accordion",
|
|
],
|
|
"components.radix.primitives.drawer": [
|
|
"drawer",
|
|
],
|
|
"components.radix.primitives.form": [
|
|
"form",
|
|
],
|
|
"components.radix.primitives.progress": [
|
|
"progress",
|
|
],
|
|
}
|
|
|
|
RADIX_PRIMITIVES_SHORTCUT_MAPPING: dict = {
|
|
k: v for k, v in RADIX_PRIMITIVES_MAPPING.items() if "progress" not in k
|
|
}
|
|
|
|
COMPONENTS_CORE_MAPPING: dict = {
|
|
"components.core.banner": [
|
|
"connection_banner",
|
|
"connection_modal",
|
|
],
|
|
"components.core.cond": ["cond", "color_mode_cond"],
|
|
"components.core.foreach": ["foreach"],
|
|
"components.core.debounce": ["debounce_input"],
|
|
"components.core.html": ["html"],
|
|
"components.core.match": ["match"],
|
|
"components.core.clipboard": ["clipboard"],
|
|
"components.core.colors": ["color"],
|
|
"components.core.breakpoints": ["breakpoints"],
|
|
"components.core.responsive": [
|
|
"desktop_only",
|
|
"mobile_and_tablet",
|
|
"mobile_only",
|
|
"tablet_and_desktop",
|
|
"tablet_only",
|
|
],
|
|
"components.core.upload": [
|
|
"cancel_upload",
|
|
"clear_selected_files",
|
|
"get_upload_dir",
|
|
"get_upload_url",
|
|
"selected_files",
|
|
"upload",
|
|
],
|
|
}
|
|
|
|
COMPONENTS_BASE_MAPPING: dict = {
|
|
"components.base.fragment": ["fragment", "Fragment"],
|
|
"components.base.script": ["script", "Script"],
|
|
}
|
|
|
|
RADIX_MAPPING: dict = {
|
|
**RADIX_THEMES_MAPPING,
|
|
**RADIX_THEMES_COMPONENTS_MAPPING,
|
|
**RADIX_THEMES_TYPOGRAPHY_MAPPING,
|
|
**RADIX_THEMES_LAYOUT_MAPPING,
|
|
**RADIX_PRIMITIVES_SHORTCUT_MAPPING,
|
|
}
|
|
|
|
_MAPPING: dict = {
|
|
"experimental": ["_x"],
|
|
"admin": ["AdminDash"],
|
|
"app": ["App", "UploadFile"],
|
|
"assets": ["asset"],
|
|
"base": ["Base"],
|
|
"components.component": [
|
|
"Component",
|
|
"NoSSRComponent",
|
|
"memo",
|
|
"ComponentNamespace",
|
|
],
|
|
"components.el.elements.media": ["image"],
|
|
"components.lucide": ["icon"],
|
|
**COMPONENTS_BASE_MAPPING,
|
|
"components.suneditor": [
|
|
"editor",
|
|
"EditorButtonList",
|
|
"EditorOptions",
|
|
],
|
|
"components": ["el", "radix", "lucide", "recharts", "next"],
|
|
"components.markdown": ["markdown"],
|
|
**RADIX_MAPPING,
|
|
"components.plotly": ["plotly"],
|
|
"components.react_player": ["audio", "video"],
|
|
**COMPONENTS_CORE_MAPPING,
|
|
"components.datadisplay.code": [
|
|
"code_block",
|
|
],
|
|
"components.datadisplay.dataeditor": [
|
|
"data_editor",
|
|
"data_editor_theme",
|
|
],
|
|
"components.sonner.toast": ["toast"],
|
|
"components.datadisplay.logo": ["logo"],
|
|
"components.gridjs": ["data_table"],
|
|
"components.moment": ["MomentDelta", "moment"],
|
|
"config": ["Config", "DBConfig"],
|
|
"constants": ["Env"],
|
|
"constants.colors": ["Color"],
|
|
"event": [
|
|
"EventChain",
|
|
"EventHandler",
|
|
"background",
|
|
"call_script",
|
|
"call_function",
|
|
"run_script",
|
|
"clear_local_storage",
|
|
"clear_session_storage",
|
|
"console_log",
|
|
"download",
|
|
"noop",
|
|
"prevent_default",
|
|
"redirect",
|
|
"remove_cookie",
|
|
"remove_local_storage",
|
|
"remove_session_storage",
|
|
"set_clipboard",
|
|
"set_focus",
|
|
"scroll_to",
|
|
"set_value",
|
|
"stop_propagation",
|
|
"upload_files",
|
|
"window_alert",
|
|
],
|
|
"istate.storage": [
|
|
"Cookie",
|
|
"LocalStorage",
|
|
"SessionStorage",
|
|
],
|
|
"middleware": ["middleware", "Middleware"],
|
|
"model": ["asession", "session", "Model"],
|
|
"state": [
|
|
"var",
|
|
"ComponentState",
|
|
"State",
|
|
"dynamic",
|
|
],
|
|
"istate.wrappers": ["get_state"],
|
|
"style": ["Style", "toggle_color_mode"],
|
|
"utils.imports": ["ImportDict", "ImportVar"],
|
|
"utils.serializers": ["serializer"],
|
|
"vars": ["Var", "field", "Field"],
|
|
}
|
|
|
|
_SUBMODULES: set[str] = {
|
|
"components",
|
|
"app",
|
|
"style",
|
|
"admin",
|
|
"base",
|
|
"model",
|
|
"testing",
|
|
"utils",
|
|
"vars",
|
|
"config",
|
|
"compiler",
|
|
}
|
|
_SUBMOD_ATTRS: dict = _MAPPING
|
|
getattr, __dir__, __all__ = lazy_loader.attach(
|
|
__name__,
|
|
submodules=_SUBMODULES,
|
|
submod_attrs=_SUBMOD_ATTRS,
|
|
)
|
|
|
|
|
|
def __getattr__(name):
|
|
if name == "chakra":
|
|
from reflex.utils import console
|
|
|
|
console.deprecate(
|
|
"rx.chakra",
|
|
reason="and moved to a separate package. "
|
|
"To continue using Chakra UI components, install the `reflex-chakra` package via `pip install "
|
|
"reflex-chakra`.",
|
|
deprecation_version="0.6.0",
|
|
removal_version="0.7.0",
|
|
dedupe=True,
|
|
)
|
|
import reflex_chakra as rc
|
|
|
|
return rc
|
|
return getattr(name)
|