code cleanup (split constants into a folder) (#1866)
This commit is contained in:
parent
8326abf5d5
commit
dcb17103bb
@ -183,8 +183,8 @@ class App(Base):
|
|||||||
else config.cors_allowed_origins,
|
else config.cors_allowed_origins,
|
||||||
cors_credentials=True,
|
cors_credentials=True,
|
||||||
max_http_buffer_size=constants.POLLING_MAX_HTTP_BUFFER_SIZE,
|
max_http_buffer_size=constants.POLLING_MAX_HTTP_BUFFER_SIZE,
|
||||||
ping_interval=constants.PING_INTERVAL,
|
ping_interval=constants.Ping.INTERVAL,
|
||||||
ping_timeout=constants.PING_TIMEOUT,
|
ping_timeout=constants.Ping.TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 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.
|
||||||
@ -340,14 +340,14 @@ class App(Base):
|
|||||||
self,
|
self,
|
||||||
component: Component | ComponentCallable,
|
component: Component | ComponentCallable,
|
||||||
route: str | None = None,
|
route: str | None = None,
|
||||||
title: str = constants.DEFAULT_TITLE,
|
title: str = constants.DefaultPage.TITLE,
|
||||||
description: str = constants.DEFAULT_DESCRIPTION,
|
description: str = constants.DefaultPage.DESCRIPTION,
|
||||||
image=constants.DEFAULT_IMAGE,
|
image: str = constants.DefaultPage.IMAGE,
|
||||||
on_load: EventHandler
|
on_load: EventHandler
|
||||||
| EventSpec
|
| EventSpec
|
||||||
| list[EventHandler | EventSpec]
|
| list[EventHandler | EventSpec]
|
||||||
| None = None,
|
| None = None,
|
||||||
meta: list[dict[str, str]] = constants.DEFAULT_META_LIST,
|
meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
|
||||||
script_tags: list[Component] | None = None,
|
script_tags: list[Component] | None = None,
|
||||||
):
|
):
|
||||||
"""Add a page to the app.
|
"""Add a page to the app.
|
||||||
@ -433,7 +433,7 @@ class App(Base):
|
|||||||
"""
|
"""
|
||||||
route = route.lstrip("/")
|
route = route.lstrip("/")
|
||||||
if route == "":
|
if route == "":
|
||||||
route = constants.INDEX_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):
|
def _check_routes_conflict(self, new_route: str):
|
||||||
@ -472,14 +472,14 @@ class App(Base):
|
|||||||
def add_custom_404_page(
|
def add_custom_404_page(
|
||||||
self,
|
self,
|
||||||
component: Component | ComponentCallable | None = None,
|
component: Component | ComponentCallable | None = None,
|
||||||
title: str = constants.TITLE_404,
|
title: str = constants.Page404.TITLE,
|
||||||
image: str = constants.FAVICON_404,
|
image: str = constants.Page404.IMAGE,
|
||||||
description: str = constants.DESCRIPTION_404,
|
description: str = constants.Page404.DESCRIPTION,
|
||||||
on_load: EventHandler
|
on_load: EventHandler
|
||||||
| EventSpec
|
| EventSpec
|
||||||
| list[EventHandler | EventSpec]
|
| list[EventHandler | EventSpec]
|
||||||
| None = None,
|
| None = None,
|
||||||
meta: list[dict[str, str]] = constants.DEFAULT_META_LIST,
|
meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
|
||||||
):
|
):
|
||||||
"""Define a custom 404 page for any url having no match.
|
"""Define a custom 404 page for any url having no match.
|
||||||
|
|
||||||
@ -498,10 +498,10 @@ class App(Base):
|
|||||||
component = Default404Page.create()
|
component = Default404Page.create()
|
||||||
self.add_page(
|
self.add_page(
|
||||||
component=wait_for_client_redirect(self._generate_component(component)),
|
component=wait_for_client_redirect(self._generate_component(component)),
|
||||||
route=constants.SLUG_404,
|
route=constants.Page404.SLUG,
|
||||||
title=title or constants.TITLE_404,
|
title=title or constants.Page404.TITLE,
|
||||||
image=image or constants.FAVICON_404,
|
image=image or constants.Page404.IMAGE,
|
||||||
description=description or constants.DESCRIPTION_404,
|
description=description or constants.Page404.DESCRIPTION,
|
||||||
on_load=on_load,
|
on_load=on_load,
|
||||||
meta=meta,
|
meta=meta,
|
||||||
)
|
)
|
||||||
@ -541,11 +541,13 @@ class App(Base):
|
|||||||
page_imports = {
|
page_imports = {
|
||||||
i
|
i
|
||||||
for i, tags in imports.items()
|
for i, tags in imports.items()
|
||||||
if i not in compiler.DEFAULT_IMPORTS.keys()
|
if i
|
||||||
and i != "focus-visible/dist/focus-visible"
|
not in [
|
||||||
and "next" not in i
|
*compiler.DEFAULT_IMPORTS.keys(),
|
||||||
and not i.startswith("/")
|
*constants.PackageJson.DEPENDENCIES.keys(),
|
||||||
and not i.startswith(".")
|
*constants.PackageJson.DEV_DEPENDENCIES.keys(),
|
||||||
|
]
|
||||||
|
and not any(i.startswith(prefix) for prefix in ["/", ".", "next/"])
|
||||||
and i != ""
|
and i != ""
|
||||||
and any(tag.install for tag in tags)
|
and any(tag.install for tag in tags)
|
||||||
}
|
}
|
||||||
@ -581,7 +583,7 @@ class App(Base):
|
|||||||
self.add_page(render, **kwargs)
|
self.add_page(render, **kwargs)
|
||||||
|
|
||||||
# Render a default 404 page if the user didn't supply one
|
# Render a default 404 page if the user didn't supply one
|
||||||
if constants.SLUG_404 not in self.pages:
|
if constants.Page404.SLUG not in self.pages:
|
||||||
self.add_custom_404_page()
|
self.add_custom_404_page()
|
||||||
|
|
||||||
task = progress.add_task("Compiling: ", total=len(self.pages))
|
task = progress.add_task("Compiling: ", total=len(self.pages))
|
||||||
@ -649,7 +651,7 @@ class App(Base):
|
|||||||
# Compile the Tailwind config.
|
# Compile the Tailwind config.
|
||||||
if config.tailwind is not None:
|
if config.tailwind is not None:
|
||||||
config.tailwind["content"] = config.tailwind.get(
|
config.tailwind["content"] = config.tailwind.get(
|
||||||
"content", constants.TAILWIND_CONTENT
|
"content", constants.Tailwind.CONTENT
|
||||||
)
|
)
|
||||||
compile_results.append(compiler.compile_tailwind(config.tailwind))
|
compile_results.append(compiler.compile_tailwind(config.tailwind))
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ DEFAULT_IMPORTS: imports.ImportDict = {
|
|||||||
ImportVar(tag="useContext"),
|
ImportVar(tag="useContext"),
|
||||||
},
|
},
|
||||||
"next/router": {ImportVar(tag="useRouter")},
|
"next/router": {ImportVar(tag="useRouter")},
|
||||||
f"/{constants.STATE_PATH}": {
|
f"/{constants.Dirs.STATE_PATH}": {
|
||||||
ImportVar(tag="uploadFiles"),
|
ImportVar(tag="uploadFiles"),
|
||||||
ImportVar(tag="Event"),
|
ImportVar(tag="Event"),
|
||||||
ImportVar(tag="isTrue"),
|
ImportVar(tag="isTrue"),
|
||||||
@ -40,9 +40,9 @@ DEFAULT_IMPORTS: imports.ImportDict = {
|
|||||||
ImportVar(tag="initialEvents"),
|
ImportVar(tag="initialEvents"),
|
||||||
ImportVar(tag="StateContext"),
|
ImportVar(tag="StateContext"),
|
||||||
},
|
},
|
||||||
"": {ImportVar(tag="focus-visible/dist/focus-visible")},
|
"": {ImportVar(tag="focus-visible/dist/focus-visible", install=False)},
|
||||||
"@chakra-ui/react": {
|
"@chakra-ui/react": {
|
||||||
ImportVar(tag=constants.USE_COLOR_MODE),
|
ImportVar(tag=constants.ColorMode.USE),
|
||||||
ImportVar(tag="Box"),
|
ImportVar(tag="Box"),
|
||||||
ImportVar(tag="Text"),
|
ImportVar(tag="Text"),
|
||||||
},
|
},
|
||||||
@ -151,7 +151,7 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
|
|||||||
"""
|
"""
|
||||||
# Add tailwind css if enabled.
|
# Add tailwind css if enabled.
|
||||||
sheets = (
|
sheets = (
|
||||||
[constants.TAILWIND_ROOT_STYLE_PATH]
|
[constants.Tailwind.ROOT_STYLE_PATH]
|
||||||
if get_config().tailwind is not None
|
if get_config().tailwind is not None
|
||||||
else []
|
else []
|
||||||
)
|
)
|
||||||
@ -159,7 +159,7 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
|
|||||||
if not utils.is_valid_url(stylesheet):
|
if not utils.is_valid_url(stylesheet):
|
||||||
# check if stylesheet provided exists.
|
# check if stylesheet provided exists.
|
||||||
stylesheet_full_path = (
|
stylesheet_full_path = (
|
||||||
Path.cwd() / constants.APP_ASSETS_DIR / stylesheet.strip("/")
|
Path.cwd() / constants.Dirs.APP_ASSETS / stylesheet.strip("/")
|
||||||
)
|
)
|
||||||
if not os.path.exists(stylesheet_full_path):
|
if not os.path.exists(stylesheet_full_path):
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
@ -193,7 +193,7 @@ def _compile_components(components: set[CustomComponent]) -> str:
|
|||||||
"""
|
"""
|
||||||
imports = {
|
imports = {
|
||||||
"react": {ImportVar(tag="memo")},
|
"react": {ImportVar(tag="memo")},
|
||||||
f"/{constants.STATE_PATH}": {ImportVar(tag="E"), ImportVar(tag="isTrue")},
|
f"/{constants.Dirs.STATE_PATH}": {ImportVar(tag="E"), ImportVar(tag="isTrue")},
|
||||||
}
|
}
|
||||||
component_renders = []
|
component_renders = []
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ def compile_document_root(head_components: list[Component]) -> tuple[str, str]:
|
|||||||
The path and code of the compiled document root.
|
The path and code of the compiled document root.
|
||||||
"""
|
"""
|
||||||
# Get the path for the output file.
|
# Get the path for the output file.
|
||||||
output_path = utils.get_page_path(constants.DOCUMENT_ROOT)
|
output_path = utils.get_page_path(constants.PageNames.DOCUMENT_ROOT)
|
||||||
|
|
||||||
# Create the document root.
|
# Create the document root.
|
||||||
document_root = utils.create_document_root(head_components)
|
document_root = utils.create_document_root(head_components)
|
||||||
@ -330,7 +330,7 @@ def compile_tailwind(
|
|||||||
The compiled Tailwind config.
|
The compiled Tailwind config.
|
||||||
"""
|
"""
|
||||||
# Get the path for the output file.
|
# Get the path for the output file.
|
||||||
output_path = constants.TAILWIND_CONFIG
|
output_path = constants.Tailwind.CONFIG
|
||||||
|
|
||||||
# Compile the config.
|
# Compile the config.
|
||||||
code = _compile_tailwind(config)
|
code = _compile_tailwind(config)
|
||||||
@ -339,4 +339,4 @@ def compile_tailwind(
|
|||||||
|
|
||||||
def purge_web_pages_dir():
|
def purge_web_pages_dir():
|
||||||
"""Empty out .web directory."""
|
"""Empty out .web directory."""
|
||||||
utils.empty_dir(constants.WEB_PAGES_DIR, keep_files=["_app.js"])
|
utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"])
|
||||||
|
@ -19,26 +19,26 @@ class ReflexJinjaEnvironment(Environment):
|
|||||||
)
|
)
|
||||||
self.filters["json_dumps"] = json_dumps
|
self.filters["json_dumps"] = json_dumps
|
||||||
self.filters["react_setter"] = lambda state: f"set{state.capitalize()}"
|
self.filters["react_setter"] = lambda state: f"set{state.capitalize()}"
|
||||||
self.loader = FileSystemLoader(constants.JINJA_TEMPLATE_DIR)
|
self.loader = FileSystemLoader(constants.Templates.Dirs.JINJA_TEMPLATE)
|
||||||
self.globals["const"] = {
|
self.globals["const"] = {
|
||||||
"socket": constants.SOCKET,
|
"socket": constants.CompileVars.SOCKET,
|
||||||
"result": constants.RESULT,
|
"result": constants.CompileVars.RESULT,
|
||||||
"router": constants.ROUTER,
|
"router": constants.CompileVars.ROUTER,
|
||||||
"event_endpoint": constants.Endpoint.EVENT.name,
|
"event_endpoint": constants.Endpoint.EVENT.name,
|
||||||
"events": constants.EVENTS,
|
"events": constants.CompileVars.EVENTS,
|
||||||
"state": constants.STATE,
|
"state": constants.CompileVars.STATE,
|
||||||
"final": constants.FINAL,
|
"final": constants.CompileVars.FINAL,
|
||||||
"processing": constants.PROCESSING,
|
"processing": constants.CompileVars.PROCESSING,
|
||||||
"initial_result": {
|
"initial_result": {
|
||||||
constants.STATE: None,
|
constants.CompileVars.STATE: None,
|
||||||
constants.EVENTS: [],
|
constants.CompileVars.EVENTS: [],
|
||||||
constants.FINAL: True,
|
constants.CompileVars.FINAL: True,
|
||||||
constants.PROCESSING: False,
|
constants.CompileVars.PROCESSING: False,
|
||||||
},
|
},
|
||||||
"color_mode": constants.COLOR_MODE,
|
"color_mode": constants.ColorMode.NAME,
|
||||||
"toggle_color_mode": constants.TOGGLE_COLOR_MODE,
|
"toggle_color_mode": constants.ColorMode.TOGGLE,
|
||||||
"use_color_mode": constants.USE_COLOR_MODE,
|
"use_color_mode": constants.ColorMode.USE,
|
||||||
"hydrate": constants.HYDRATE,
|
"hydrate": constants.CompileVars.HYDRATE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ def compile_imports(imports: imports.ImportDict) -> list[dict]:
|
|||||||
default, rest = compile_import_statement(fields)
|
default, rest = compile_import_statement(fields)
|
||||||
|
|
||||||
# prevent lib from being rendered on the page if all imports are non rendered kind
|
# prevent lib from being rendered on the page if all imports are non rendered kind
|
||||||
if all({not f.render for f in fields}): # type: ignore
|
if not any({f.render for f in fields}): # type: ignore
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not lib:
|
if not lib:
|
||||||
@ -104,9 +104,7 @@ def compile_imports(imports: imports.ImportDict) -> list[dict]:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# remove the version before rendering the package imports
|
# remove the version before rendering the package imports
|
||||||
lib, at, version = lib.rpartition("@")
|
lib = format.format_library_name(lib)
|
||||||
if not lib:
|
|
||||||
lib = at + version
|
|
||||||
|
|
||||||
import_dicts.append(get_import_dict(lib, default, rest))
|
import_dicts.append(get_import_dict(lib, default, rest))
|
||||||
return import_dicts
|
return import_dicts
|
||||||
@ -313,7 +311,7 @@ def get_page_path(path: str) -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The path of the compiled JS file.
|
The path of the compiled JS file.
|
||||||
"""
|
"""
|
||||||
return os.path.join(constants.WEB_PAGES_DIR, path + constants.JS_EXT)
|
return os.path.join(constants.Dirs.WEB_PAGES, path + constants.Ext.JS)
|
||||||
|
|
||||||
|
|
||||||
def get_theme_path() -> str:
|
def get_theme_path() -> str:
|
||||||
@ -322,7 +320,9 @@ def get_theme_path() -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The path of the theme style.
|
The path of the theme style.
|
||||||
"""
|
"""
|
||||||
return os.path.join(constants.WEB_UTILS_DIR, constants.THEME + constants.JS_EXT)
|
return os.path.join(
|
||||||
|
constants.Dirs.WEB_UTILS, constants.PageNames.THEME + constants.Ext.JS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_root_stylesheet_path() -> str:
|
def get_root_stylesheet_path() -> str:
|
||||||
@ -332,7 +332,7 @@ def get_root_stylesheet_path() -> str:
|
|||||||
The path of the app root file.
|
The path of the app root file.
|
||||||
"""
|
"""
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
constants.STYLES_DIR, constants.STYLESHEET_ROOT + constants.CSS_EXT
|
constants.STYLES_DIR, constants.PageNames.STYLESHEET_ROOT + constants.Ext.CSS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -342,7 +342,7 @@ def get_context_path() -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The path of the context module.
|
The path of the context module.
|
||||||
"""
|
"""
|
||||||
return os.path.join(constants.WEB_UTILS_DIR, "context" + constants.JS_EXT)
|
return os.path.join(constants.Dirs.WEB_UTILS, "context" + constants.Ext.JS)
|
||||||
|
|
||||||
|
|
||||||
def get_components_path() -> str:
|
def get_components_path() -> str:
|
||||||
@ -351,7 +351,7 @@ def get_components_path() -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The path of the compiled components.
|
The path of the compiled components.
|
||||||
"""
|
"""
|
||||||
return os.path.join(constants.WEB_UTILS_DIR, "components" + constants.JS_EXT)
|
return os.path.join(constants.Dirs.WEB_UTILS, "components" + constants.Ext.JS)
|
||||||
|
|
||||||
|
|
||||||
def get_asset_path(filename: str | None = None) -> str:
|
def get_asset_path(filename: str | None = None) -> str:
|
||||||
@ -364,9 +364,9 @@ def get_asset_path(filename: str | None = None) -> str:
|
|||||||
The path of the asset.
|
The path of the asset.
|
||||||
"""
|
"""
|
||||||
if filename is None:
|
if filename is None:
|
||||||
return constants.WEB_ASSETS_DIR
|
return constants.Dirs.WEB_ASSETS
|
||||||
else:
|
else:
|
||||||
return os.path.join(constants.WEB_ASSETS_DIR, filename)
|
return os.path.join(constants.Dirs.WEB_ASSETS, filename)
|
||||||
|
|
||||||
|
|
||||||
def add_meta(
|
def add_meta(
|
||||||
|
@ -7,10 +7,9 @@ from abc import ABC
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any, Callable, Dict, List, Optional, Set, Type, Union
|
from typing import Any, Callable, Dict, List, Optional, Set, Type, Union
|
||||||
|
|
||||||
from reflex import constants
|
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.components.tags import Tag
|
from reflex.components.tags import Tag
|
||||||
from reflex.constants import EventTriggers
|
from reflex.constants import Dirs, EventTriggers
|
||||||
from reflex.event import (
|
from reflex.event import (
|
||||||
EventChain,
|
EventChain,
|
||||||
EventHandler,
|
EventHandler,
|
||||||
@ -762,7 +761,7 @@ class CustomComponent(Component):
|
|||||||
"""A custom user-defined component."""
|
"""A custom user-defined component."""
|
||||||
|
|
||||||
# Use the components library.
|
# Use the components library.
|
||||||
library = f"/{constants.COMPONENTS_PATH}"
|
library = f"/{Dirs.COMPONENTS_PATH}"
|
||||||
|
|
||||||
# The function that creates the component.
|
# The function that creates the component.
|
||||||
component_fn: Callable[..., Component] = Component.create
|
component_fn: Callable[..., Component] = Component.create
|
||||||
|
@ -163,7 +163,7 @@ class Config(Base):
|
|||||||
telemetry_enabled: bool = True
|
telemetry_enabled: bool = True
|
||||||
|
|
||||||
# The bun path
|
# The bun path
|
||||||
bun_path: str = constants.DEFAULT_BUN_PATH
|
bun_path: str = constants.Bun.DEFAULT_PATH
|
||||||
|
|
||||||
# List of origins that are allowed to connect to the backend API.
|
# List of origins that are allowed to connect to the backend API.
|
||||||
cors_allowed_origins: List[str] = ["*"]
|
cors_allowed_origins: List[str] = ["*"]
|
||||||
@ -284,11 +284,9 @@ def get_config(reload: bool = False) -> Config:
|
|||||||
Returns:
|
Returns:
|
||||||
The app config.
|
The app config.
|
||||||
"""
|
"""
|
||||||
from reflex.config import Config
|
|
||||||
|
|
||||||
sys.path.insert(0, os.getcwd())
|
sys.path.insert(0, os.getcwd())
|
||||||
try:
|
try:
|
||||||
rxconfig = __import__(constants.CONFIG_MODULE)
|
rxconfig = __import__(constants.Config.MODULE)
|
||||||
if reload:
|
if reload:
|
||||||
importlib.reload(rxconfig)
|
importlib.reload(rxconfig)
|
||||||
return rxconfig.config
|
return rxconfig.config
|
||||||
|
@ -1,444 +0,0 @@
|
|||||||
"""Constants used throughout the package."""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import re
|
|
||||||
from enum import Enum
|
|
||||||
from types import SimpleNamespace
|
|
||||||
|
|
||||||
from platformdirs import PlatformDirs
|
|
||||||
|
|
||||||
# importlib is only available for Python 3.8+ so we need the backport for Python 3.7
|
|
||||||
try:
|
|
||||||
from importlib import metadata
|
|
||||||
except ImportError:
|
|
||||||
import importlib_metadata as metadata # pyright: ignore[reportMissingImports]
|
|
||||||
|
|
||||||
IS_WINDOWS = platform.system() == "Windows"
|
|
||||||
|
|
||||||
|
|
||||||
def get_fnm_name() -> str | None:
|
|
||||||
"""Get the appropriate fnm executable name based on the current platform.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The fnm executable name for the current platform.
|
|
||||||
"""
|
|
||||||
platform_os = platform.system()
|
|
||||||
|
|
||||||
if platform_os == "Windows":
|
|
||||||
return "fnm-windows"
|
|
||||||
elif platform_os == "Darwin":
|
|
||||||
return "fnm-macos"
|
|
||||||
elif platform_os == "Linux":
|
|
||||||
machine = platform.machine()
|
|
||||||
if machine == "arm" or machine.startswith("armv7"):
|
|
||||||
return "fnm-arm32"
|
|
||||||
elif machine.startswith("aarch") or machine.startswith("armv8"):
|
|
||||||
return "fnm-arm64"
|
|
||||||
return "fnm-linux"
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
# App names and versions.
|
|
||||||
# The name of the Reflex package.
|
|
||||||
MODULE_NAME = "reflex"
|
|
||||||
# The current version of Reflex.
|
|
||||||
VERSION = metadata.version(MODULE_NAME)
|
|
||||||
|
|
||||||
# Files and directories used to init a new project.
|
|
||||||
# The directory to store reflex dependencies.
|
|
||||||
REFLEX_DIR = (
|
|
||||||
# on windows, we use C:/Users/<username>/AppData/Local/reflex.
|
|
||||||
# on macOS, we use ~/Library/Application Support/reflex.
|
|
||||||
# on linux, we use ~/.local/share/reflex.
|
|
||||||
PlatformDirs(MODULE_NAME, False).user_data_dir
|
|
||||||
)
|
|
||||||
# The root directory of the reflex library.
|
|
||||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
# The name of the assets directory.
|
|
||||||
APP_ASSETS_DIR = "assets"
|
|
||||||
# The template directory used during reflex init.
|
|
||||||
TEMPLATE_DIR = os.path.join(ROOT_DIR, MODULE_NAME, ".templates")
|
|
||||||
# The web subdirectory of the template directory.
|
|
||||||
WEB_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "web")
|
|
||||||
# The assets subdirectory of the template directory.
|
|
||||||
ASSETS_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, APP_ASSETS_DIR)
|
|
||||||
# The jinja template directory.
|
|
||||||
JINJA_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "jinja")
|
|
||||||
|
|
||||||
# Bun config.
|
|
||||||
# The Bun version.
|
|
||||||
BUN_VERSION = "0.7.3"
|
|
||||||
# Min Bun Version
|
|
||||||
MIN_BUN_VERSION = "0.7.0"
|
|
||||||
# The directory to store the bun.
|
|
||||||
BUN_ROOT_PATH = os.path.join(REFLEX_DIR, "bun")
|
|
||||||
# Default bun path.
|
|
||||||
DEFAULT_BUN_PATH = os.path.join(BUN_ROOT_PATH, "bin", "bun")
|
|
||||||
# URL to bun install script.
|
|
||||||
BUN_INSTALL_URL = "https://bun.sh/install"
|
|
||||||
|
|
||||||
# FNM / Node config.
|
|
||||||
# The FNM version.
|
|
||||||
FNM_VERSION = "1.35.1"
|
|
||||||
# The Node version.
|
|
||||||
NODE_VERSION = "18.17.0"
|
|
||||||
# The minimum required node version.
|
|
||||||
NODE_VERSION_MIN = "16.8.0"
|
|
||||||
# The directory to store fnm.
|
|
||||||
FNM_DIR = os.path.join(REFLEX_DIR, "fnm")
|
|
||||||
FNM_FILENAME = get_fnm_name()
|
|
||||||
# The fnm executable binary.
|
|
||||||
FNM_EXE = os.path.join(FNM_DIR, "fnm.exe" if IS_WINDOWS else "fnm")
|
|
||||||
# The node bin path.
|
|
||||||
NODE_BIN_PATH = os.path.join(
|
|
||||||
FNM_DIR,
|
|
||||||
"node-versions",
|
|
||||||
f"v{NODE_VERSION}",
|
|
||||||
"installation",
|
|
||||||
"bin" if not IS_WINDOWS else "",
|
|
||||||
)
|
|
||||||
# The default path where node is installed.
|
|
||||||
NODE_PATH = os.path.join(NODE_BIN_PATH, "node.exe" if IS_WINDOWS else "node")
|
|
||||||
# The default path where npm is installed.
|
|
||||||
NPM_PATH = os.path.join(NODE_BIN_PATH, "npm")
|
|
||||||
# The URL to the fnm release binary
|
|
||||||
FNM_INSTALL_URL = (
|
|
||||||
f"https://github.com/Schniz/fnm/releases/download/v{FNM_VERSION}/{FNM_FILENAME}.zip"
|
|
||||||
)
|
|
||||||
# The frontend directories in a project.
|
|
||||||
# The web folder where the NextJS app is compiled to.
|
|
||||||
WEB_DIR = ".web"
|
|
||||||
# The name of the utils file.
|
|
||||||
UTILS_DIR = "utils"
|
|
||||||
# The name of the output static directory.
|
|
||||||
STATIC_DIR = "_static"
|
|
||||||
# The name of the state file.
|
|
||||||
STATE_PATH = "/".join([UTILS_DIR, "state"])
|
|
||||||
# The name of the components file.
|
|
||||||
COMPONENTS_PATH = "/".join([UTILS_DIR, "components"])
|
|
||||||
# The directory where the app pages are compiled to.
|
|
||||||
WEB_PAGES_DIR = os.path.join(WEB_DIR, "pages")
|
|
||||||
# The directory where the static build is located.
|
|
||||||
WEB_STATIC_DIR = os.path.join(WEB_DIR, STATIC_DIR)
|
|
||||||
# The directory where the utils file is located.
|
|
||||||
WEB_UTILS_DIR = os.path.join(WEB_DIR, UTILS_DIR)
|
|
||||||
# The directory where the assets are located.
|
|
||||||
WEB_ASSETS_DIR = os.path.join(WEB_DIR, "public")
|
|
||||||
# The directory where styles are located.
|
|
||||||
STYLES_DIR = os.path.join(WEB_DIR, "styles")
|
|
||||||
# The Tailwind config.
|
|
||||||
TAILWIND_CONFIG = os.path.join(WEB_DIR, "tailwind.config.js")
|
|
||||||
# Default Tailwind content paths
|
|
||||||
TAILWIND_CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}"]
|
|
||||||
# Relative tailwind style path to root stylesheet in STYLES_DIR.
|
|
||||||
TAILWIND_ROOT_STYLE_PATH = "./tailwind.css"
|
|
||||||
# The Tailwindcss version
|
|
||||||
TAILWIND_VERSION = "tailwindcss@^3.3.2"
|
|
||||||
# The package json file
|
|
||||||
PACKAGE_JSON_PATH = os.path.join(WEB_DIR, "package.json")
|
|
||||||
# The NextJS config file
|
|
||||||
NEXT_CONFIG_FILE = "next.config.js"
|
|
||||||
# The sitemap config file.
|
|
||||||
SITEMAP_CONFIG_FILE = os.path.join(WEB_DIR, "next-sitemap.config.js")
|
|
||||||
# The node modules directory.
|
|
||||||
NODE_MODULES = "node_modules"
|
|
||||||
# The package lock file.
|
|
||||||
PACKAGE_LOCK = "package-lock.json"
|
|
||||||
# The reflex json file.
|
|
||||||
REFLEX_JSON = os.path.join(WEB_DIR, "reflex.json")
|
|
||||||
# The env json file.
|
|
||||||
ENV_JSON = os.path.join(WEB_DIR, "env.json")
|
|
||||||
|
|
||||||
# Compiler variables.
|
|
||||||
# The extension for compiled Javascript files.
|
|
||||||
JS_EXT = ".js"
|
|
||||||
# The extension for python files.
|
|
||||||
PY_EXT = ".py"
|
|
||||||
# The extension for css files.
|
|
||||||
CSS_EXT = ".css"
|
|
||||||
# The expected variable name where the rx.App is stored.
|
|
||||||
APP_VAR = "app"
|
|
||||||
# The expected variable name where the API object is stored for deployment.
|
|
||||||
API_VAR = "api"
|
|
||||||
# The name of the router variable.
|
|
||||||
ROUTER = "router"
|
|
||||||
# The name of the socket variable.
|
|
||||||
SOCKET = "socket"
|
|
||||||
# The name of the variable to hold API results.
|
|
||||||
RESULT = "result"
|
|
||||||
# The name of the final variable.
|
|
||||||
FINAL = "final"
|
|
||||||
# The name of the process variable.
|
|
||||||
PROCESSING = "processing"
|
|
||||||
# The name of the state variable.
|
|
||||||
STATE = "state"
|
|
||||||
# The name of the events variable.
|
|
||||||
EVENTS = "events"
|
|
||||||
# The name of the initial hydrate event.
|
|
||||||
HYDRATE = "hydrate"
|
|
||||||
# The name of the is_hydrated variable.
|
|
||||||
IS_HYDRATED = "is_hydrated"
|
|
||||||
# The name of the index page.
|
|
||||||
INDEX_ROUTE = "index"
|
|
||||||
# The name of the app root page.
|
|
||||||
APP_ROOT = "_app"
|
|
||||||
# The root stylesheet filename.
|
|
||||||
STYLESHEET_ROOT = "styles"
|
|
||||||
# The name of the document root page.
|
|
||||||
DOCUMENT_ROOT = "_document"
|
|
||||||
# The name of the theme page.
|
|
||||||
THEME = "theme"
|
|
||||||
# The prefix used to create setters for state vars.
|
|
||||||
SETTER_PREFIX = "set_"
|
|
||||||
# The name of the frontend zip during deployment.
|
|
||||||
FRONTEND_ZIP = "frontend.zip"
|
|
||||||
# The name of the backend zip during deployment.
|
|
||||||
BACKEND_ZIP = "backend.zip"
|
|
||||||
# The default title to show for Reflex apps.
|
|
||||||
DEFAULT_TITLE = "Reflex App"
|
|
||||||
# The default description to show for Reflex apps.
|
|
||||||
DEFAULT_DESCRIPTION = "A Reflex app."
|
|
||||||
# The default image to show for Reflex apps.
|
|
||||||
DEFAULT_IMAGE = "favicon.ico"
|
|
||||||
# The default meta list to show for Reflex apps.
|
|
||||||
DEFAULT_META_LIST = []
|
|
||||||
|
|
||||||
# The gitignore file.
|
|
||||||
GITIGNORE_FILE = ".gitignore"
|
|
||||||
# Files to gitignore.
|
|
||||||
DEFAULT_GITIGNORE = {WEB_DIR, "*.db", "__pycache__/", "*.py[cod]"}
|
|
||||||
# The name of the reflex config module.
|
|
||||||
CONFIG_MODULE = "rxconfig"
|
|
||||||
# The python config file.
|
|
||||||
CONFIG_FILE = f"{CONFIG_MODULE}{PY_EXT}"
|
|
||||||
# The previous config file.
|
|
||||||
OLD_CONFIG_FILE = f"pcconfig{PY_EXT}"
|
|
||||||
# The deployment URL.
|
|
||||||
PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
|
|
||||||
# Token expiration time in seconds.
|
|
||||||
TOKEN_EXPIRATION = 60 * 60
|
|
||||||
# Maximum time in milliseconds that a state can be locked for exclusive access.
|
|
||||||
LOCK_EXPIRATION = 10000
|
|
||||||
|
|
||||||
# Testing variables.
|
|
||||||
# Testing os env set by pytest when running a test case.
|
|
||||||
PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
|
|
||||||
|
|
||||||
|
|
||||||
# Env modes
|
|
||||||
class Env(str, Enum):
|
|
||||||
"""The environment modes."""
|
|
||||||
|
|
||||||
DEV = "dev"
|
|
||||||
PROD = "prod"
|
|
||||||
|
|
||||||
|
|
||||||
# Log levels
|
|
||||||
class LogLevel(str, Enum):
|
|
||||||
"""The log levels."""
|
|
||||||
|
|
||||||
DEBUG = "debug"
|
|
||||||
INFO = "info"
|
|
||||||
WARNING = "warning"
|
|
||||||
ERROR = "error"
|
|
||||||
CRITICAL = "critical"
|
|
||||||
|
|
||||||
def __le__(self, other: LogLevel) -> bool:
|
|
||||||
"""Compare log levels.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
other: The other log level.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if the log level is less than or equal to the other log level.
|
|
||||||
"""
|
|
||||||
levels = list(LogLevel)
|
|
||||||
return levels.index(self) <= levels.index(other)
|
|
||||||
|
|
||||||
|
|
||||||
# Templates
|
|
||||||
class Template(str, Enum):
|
|
||||||
"""The templates to use for the app."""
|
|
||||||
|
|
||||||
DEFAULT = "default"
|
|
||||||
COUNTER = "counter"
|
|
||||||
|
|
||||||
|
|
||||||
class Endpoint(Enum):
|
|
||||||
"""Endpoints for the reflex backend API."""
|
|
||||||
|
|
||||||
PING = "ping"
|
|
||||||
EVENT = "_event"
|
|
||||||
UPLOAD = "_upload"
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
"""Get the string representation of the endpoint.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The path for the endpoint.
|
|
||||||
"""
|
|
||||||
return f"/{self.value}"
|
|
||||||
|
|
||||||
def get_url(self) -> str:
|
|
||||||
"""Get the URL for the endpoint.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The full URL for the endpoint.
|
|
||||||
"""
|
|
||||||
# Import here to avoid circular imports.
|
|
||||||
from reflex.config import get_config
|
|
||||||
|
|
||||||
# Get the API URL from the config.
|
|
||||||
config = get_config()
|
|
||||||
url = "".join([config.api_url, str(self)])
|
|
||||||
|
|
||||||
# The event endpoint is a websocket.
|
|
||||||
if self == Endpoint.EVENT:
|
|
||||||
# Replace the protocol with ws.
|
|
||||||
url = url.replace("https://", "wss://").replace("http://", "ws://")
|
|
||||||
|
|
||||||
# Return the url.
|
|
||||||
return url
|
|
||||||
|
|
||||||
|
|
||||||
class SocketEvent(Enum):
|
|
||||||
"""Socket events sent by the reflex backend API."""
|
|
||||||
|
|
||||||
PING = "ping"
|
|
||||||
EVENT = "event"
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
"""Get the string representation of the event name.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The event name string.
|
|
||||||
"""
|
|
||||||
return str(self.value)
|
|
||||||
|
|
||||||
|
|
||||||
class RouteArgType(SimpleNamespace):
|
|
||||||
"""Type of dynamic route arg extracted from URI route."""
|
|
||||||
|
|
||||||
# Typecast to str is needed for Enum to work.
|
|
||||||
SINGLE = str("arg_single")
|
|
||||||
LIST = str("arg_list")
|
|
||||||
|
|
||||||
|
|
||||||
# the name of the backend var containing path and client information
|
|
||||||
ROUTER_DATA = "router_data"
|
|
||||||
|
|
||||||
|
|
||||||
class RouteVar(SimpleNamespace):
|
|
||||||
"""Names of variables used in the router_data dict stored in State."""
|
|
||||||
|
|
||||||
CLIENT_IP = "ip"
|
|
||||||
CLIENT_TOKEN = "token"
|
|
||||||
HEADERS = "headers"
|
|
||||||
PATH = "pathname"
|
|
||||||
ORIGIN = "asPath"
|
|
||||||
SESSION_ID = "sid"
|
|
||||||
QUERY = "query"
|
|
||||||
COOKIE = "cookie"
|
|
||||||
|
|
||||||
|
|
||||||
class RouteRegex(SimpleNamespace):
|
|
||||||
"""Regex used for extracting route args in route."""
|
|
||||||
|
|
||||||
ARG = re.compile(r"\[(?!\.)([^\[\]]+)\]")
|
|
||||||
# group return the catchall pattern (i.e. "[[..slug]]")
|
|
||||||
CATCHALL = re.compile(r"(\[?\[\.{3}(?![0-9]).*\]?\])")
|
|
||||||
# group return the arg name (i.e. "slug")
|
|
||||||
STRICT_CATCHALL = re.compile(r"\[\.{3}([a-zA-Z_][\w]*)\]")
|
|
||||||
# group return the arg name (i.e. "slug")
|
|
||||||
OPT_CATCHALL = re.compile(r"\[\[\.{3}([a-zA-Z_][\w]*)\]\]")
|
|
||||||
|
|
||||||
|
|
||||||
class PackageJsonCommands(SimpleNamespace):
|
|
||||||
"""Commands used in package.json file."""
|
|
||||||
|
|
||||||
DEV = "next dev"
|
|
||||||
EXPORT = "next build && next export -o _static"
|
|
||||||
EXPORT_SITEMAP = "next build && next-sitemap && next export -o _static"
|
|
||||||
PROD = "next start"
|
|
||||||
|
|
||||||
|
|
||||||
PACKAGE_DEPENDENCIES = {
|
|
||||||
"@chakra-ui/react": "^2.6.0",
|
|
||||||
"@chakra-ui/system": "^2.5.6",
|
|
||||||
"@emotion/react": "^11.10.6",
|
|
||||||
"@emotion/styled": "^11.10.6",
|
|
||||||
"axios": "^1.4.0",
|
|
||||||
"chakra-react-select": "^4.6.0",
|
|
||||||
"focus-visible": "^5.2.0",
|
|
||||||
"json5": "^2.2.3",
|
|
||||||
"next": "^13.3.1",
|
|
||||||
"next-sitemap": "^4.1.8",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"socket.io-client": "^4.6.1",
|
|
||||||
"universal-cookie": "^4.0.4",
|
|
||||||
}
|
|
||||||
PACKAGE_DEV_DEPENDENCIES = {
|
|
||||||
"autoprefixer": "^10.4.14",
|
|
||||||
"postcss": "^8.4.24",
|
|
||||||
}
|
|
||||||
|
|
||||||
# 404 variables
|
|
||||||
SLUG_404 = "404"
|
|
||||||
TITLE_404 = "404 - Not Found"
|
|
||||||
FAVICON_404 = "favicon.ico"
|
|
||||||
DESCRIPTION_404 = "The page was not found"
|
|
||||||
ROUTE_NOT_FOUND = "routeNotFound"
|
|
||||||
|
|
||||||
# Color mode variables
|
|
||||||
USE_COLOR_MODE = "useColorMode"
|
|
||||||
COLOR_MODE = "colorMode"
|
|
||||||
TOGGLE_COLOR_MODE = "toggleColorMode"
|
|
||||||
|
|
||||||
# Server socket configuration variables
|
|
||||||
POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
|
|
||||||
PING_INTERVAL = 25
|
|
||||||
PING_TIMEOUT = 120
|
|
||||||
|
|
||||||
# Alembic migrations
|
|
||||||
ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini")
|
|
||||||
|
|
||||||
# Keys in the client_side_storage dict
|
|
||||||
COOKIES = "cookies"
|
|
||||||
LOCAL_STORAGE = "local_storage"
|
|
||||||
|
|
||||||
|
|
||||||
class EventTriggers(SimpleNamespace):
|
|
||||||
"""All trigger names used in Reflex."""
|
|
||||||
|
|
||||||
ON_FOCUS = "on_focus"
|
|
||||||
ON_BLUR = "on_blur"
|
|
||||||
ON_CANCEL = "on_cancel"
|
|
||||||
ON_CLICK = "on_click"
|
|
||||||
ON_CHANGE = "on_change"
|
|
||||||
ON_CHANGE_END = "on_change_end"
|
|
||||||
ON_CHANGE_START = "on_change_start"
|
|
||||||
ON_COMPLETE = "on_complete"
|
|
||||||
ON_CONTEXT_MENU = "on_context_menu"
|
|
||||||
ON_DOUBLE_CLICK = "on_double_click"
|
|
||||||
ON_DROP = "on_drop"
|
|
||||||
ON_EDIT = "on_edit"
|
|
||||||
ON_KEY_DOWN = "on_key_down"
|
|
||||||
ON_KEY_UP = "on_key_up"
|
|
||||||
ON_MOUSE_DOWN = "on_mouse_down"
|
|
||||||
ON_MOUSE_ENTER = "on_mouse_enter"
|
|
||||||
ON_MOUSE_LEAVE = "on_mouse_leave"
|
|
||||||
ON_MOUSE_MOVE = "on_mouse_move"
|
|
||||||
ON_MOUSE_OUT = "on_mouse_out"
|
|
||||||
ON_MOUSE_OVER = "on_mouse_over"
|
|
||||||
ON_MOUSE_UP = "on_mouse_up"
|
|
||||||
ON_SCROLL = "on_scroll"
|
|
||||||
ON_SUBMIT = "on_submit"
|
|
||||||
ON_MOUNT = "on_mount"
|
|
||||||
ON_UNMOUNT = "on_unmount"
|
|
||||||
|
|
||||||
|
|
||||||
# If this env var is set to "yes", App.compile will be a no-op
|
|
||||||
SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"
|
|
92
reflex/constants/__init__.py
Normal file
92
reflex/constants/__init__.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
"""The constants package."""
|
||||||
|
|
||||||
|
from .base import (
|
||||||
|
COOKIES,
|
||||||
|
IS_WINDOWS,
|
||||||
|
LOCAL_STORAGE,
|
||||||
|
POLLING_MAX_HTTP_BUFFER_SIZE,
|
||||||
|
PYTEST_CURRENT_TEST,
|
||||||
|
SKIP_COMPILE_ENV_VAR,
|
||||||
|
ColorMode,
|
||||||
|
Dirs,
|
||||||
|
Env,
|
||||||
|
LogLevel,
|
||||||
|
Next,
|
||||||
|
Ping,
|
||||||
|
Reflex,
|
||||||
|
Templates,
|
||||||
|
)
|
||||||
|
from .compiler import (
|
||||||
|
SETTER_PREFIX,
|
||||||
|
CompileVars,
|
||||||
|
ComponentName,
|
||||||
|
Ext,
|
||||||
|
PageNames,
|
||||||
|
)
|
||||||
|
from .config import (
|
||||||
|
ALEMBIC_CONFIG,
|
||||||
|
PRODUCTION_BACKEND_URL,
|
||||||
|
Config,
|
||||||
|
Expiration,
|
||||||
|
GitIgnore,
|
||||||
|
)
|
||||||
|
from .event import Endpoint, EventTriggers, SocketEvent
|
||||||
|
from .installer import (
|
||||||
|
Bun,
|
||||||
|
Fnm,
|
||||||
|
Node,
|
||||||
|
PackageJson,
|
||||||
|
)
|
||||||
|
from .route import (
|
||||||
|
ROUTE_NOT_FOUND,
|
||||||
|
ROUTER_DATA,
|
||||||
|
DefaultPage,
|
||||||
|
Page404,
|
||||||
|
RouteArgType,
|
||||||
|
RouteRegex,
|
||||||
|
RouteVar,
|
||||||
|
)
|
||||||
|
from .style import STYLES_DIR, Tailwind
|
||||||
|
|
||||||
|
__ALL__ = [
|
||||||
|
ALEMBIC_CONFIG,
|
||||||
|
Bun,
|
||||||
|
ColorMode,
|
||||||
|
Config,
|
||||||
|
COOKIES,
|
||||||
|
ComponentName,
|
||||||
|
DefaultPage,
|
||||||
|
Dirs,
|
||||||
|
Endpoint,
|
||||||
|
Env,
|
||||||
|
EventTriggers,
|
||||||
|
Expiration,
|
||||||
|
Ext,
|
||||||
|
Fnm,
|
||||||
|
GitIgnore,
|
||||||
|
IS_WINDOWS,
|
||||||
|
LOCAL_STORAGE,
|
||||||
|
LogLevel,
|
||||||
|
Next,
|
||||||
|
Node,
|
||||||
|
PackageJson,
|
||||||
|
PageNames,
|
||||||
|
Page404,
|
||||||
|
Ping,
|
||||||
|
POLLING_MAX_HTTP_BUFFER_SIZE,
|
||||||
|
PYTEST_CURRENT_TEST,
|
||||||
|
PRODUCTION_BACKEND_URL,
|
||||||
|
Reflex,
|
||||||
|
RouteVar,
|
||||||
|
RouteRegex,
|
||||||
|
RouteArgType,
|
||||||
|
ROUTER_DATA,
|
||||||
|
ROUTE_NOT_FOUND,
|
||||||
|
SETTER_PREFIX,
|
||||||
|
SKIP_COMPILE_ENV_VAR,
|
||||||
|
SocketEvent,
|
||||||
|
STYLES_DIR,
|
||||||
|
Tailwind,
|
||||||
|
Templates,
|
||||||
|
CompileVars,
|
||||||
|
]
|
174
reflex/constants/base.py
Normal file
174
reflex/constants/base.py
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
"""Base file for constants that don't fit any other categories."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
from enum import Enum
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from platformdirs import PlatformDirs
|
||||||
|
|
||||||
|
# importlib is only available for Python 3.8+ so we need the backport for Python 3.7
|
||||||
|
try:
|
||||||
|
from importlib import metadata
|
||||||
|
except ImportError:
|
||||||
|
import importlib_metadata as metadata # pyright: ignore[reportMissingImports]
|
||||||
|
|
||||||
|
|
||||||
|
IS_WINDOWS = platform.system() == "Windows"
|
||||||
|
|
||||||
|
|
||||||
|
class Dirs(SimpleNamespace):
|
||||||
|
"""Various directories/paths used by Reflex."""
|
||||||
|
|
||||||
|
# The frontend directories in a project.
|
||||||
|
# The web folder where the NextJS app is compiled to.
|
||||||
|
WEB = ".web"
|
||||||
|
# The name of the assets directory.
|
||||||
|
APP_ASSETS = "assets"
|
||||||
|
# The name of the utils file.
|
||||||
|
UTILS = "utils"
|
||||||
|
# The name of the output static directory.
|
||||||
|
STATIC = "_static"
|
||||||
|
# The name of the state file.
|
||||||
|
STATE_PATH = "/".join([UTILS, "state"])
|
||||||
|
# The name of the components file.
|
||||||
|
COMPONENTS_PATH = "/".join([UTILS, "components"])
|
||||||
|
# The directory where the app pages are compiled to.
|
||||||
|
WEB_PAGES = os.path.join(WEB, "pages")
|
||||||
|
# The directory where the static build is located.
|
||||||
|
WEB_STATIC = os.path.join(WEB, STATIC)
|
||||||
|
# The directory where the utils file is located.
|
||||||
|
WEB_UTILS = os.path.join(WEB, UTILS)
|
||||||
|
# The directory where the assets are located.
|
||||||
|
WEB_ASSETS = os.path.join(WEB, "public")
|
||||||
|
# The env json file.
|
||||||
|
ENV_JSON = os.path.join(WEB, "env.json")
|
||||||
|
|
||||||
|
|
||||||
|
class Reflex(SimpleNamespace):
|
||||||
|
"""Base constants concerning Reflex."""
|
||||||
|
|
||||||
|
# App names and versions.
|
||||||
|
# The name of the Reflex package.
|
||||||
|
MODULE_NAME = "reflex"
|
||||||
|
# The current version of Reflex.
|
||||||
|
VERSION = metadata.version(MODULE_NAME)
|
||||||
|
|
||||||
|
# The reflex json file.
|
||||||
|
JSON = os.path.join(Dirs.WEB, "reflex.json")
|
||||||
|
|
||||||
|
# Files and directories used to init a new project.
|
||||||
|
# The directory to store reflex dependencies.
|
||||||
|
DIR = (
|
||||||
|
# on windows, we use C:/Users/<username>/AppData/Local/reflex.
|
||||||
|
# on macOS, we use ~/Library/Application Support/reflex.
|
||||||
|
# on linux, we use ~/.local/share/reflex.
|
||||||
|
PlatformDirs(MODULE_NAME, False).user_data_dir
|
||||||
|
)
|
||||||
|
# The root directory of the reflex library.
|
||||||
|
|
||||||
|
ROOT_DIR = os.path.dirname(
|
||||||
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Templates(SimpleNamespace):
|
||||||
|
"""Constants related to Templates."""
|
||||||
|
|
||||||
|
class Kind(str, Enum):
|
||||||
|
"""The templates to use for the app."""
|
||||||
|
|
||||||
|
DEFAULT = "default"
|
||||||
|
COUNTER = "counter"
|
||||||
|
|
||||||
|
class Dirs(SimpleNamespace):
|
||||||
|
"""Folders used by the template system of Reflex."""
|
||||||
|
|
||||||
|
# The template directory used during reflex init.
|
||||||
|
BASE = os.path.join(Reflex.ROOT_DIR, Reflex.MODULE_NAME, ".templates")
|
||||||
|
# The web subdirectory of the template directory.
|
||||||
|
WEB_TEMPLATE = os.path.join(BASE, "web")
|
||||||
|
# The assets subdirectory of the template directory.
|
||||||
|
ASSETS_TEMPLATE = os.path.join(BASE, Dirs.APP_ASSETS)
|
||||||
|
# The jinja template directory.
|
||||||
|
JINJA_TEMPLATE = os.path.join(BASE, "jinja")
|
||||||
|
|
||||||
|
|
||||||
|
class Next(SimpleNamespace):
|
||||||
|
"""Constants related to NextJS."""
|
||||||
|
|
||||||
|
# The NextJS config file
|
||||||
|
CONFIG_FILE = "next.config.js"
|
||||||
|
# The sitemap config file.
|
||||||
|
SITEMAP_CONFIG_FILE = os.path.join(Dirs.WEB, "next-sitemap.config.js")
|
||||||
|
# The node modules directory.
|
||||||
|
NODE_MODULES = "node_modules"
|
||||||
|
# The package lock file.
|
||||||
|
PACKAGE_LOCK = "package-lock.json"
|
||||||
|
|
||||||
|
|
||||||
|
# Color mode variables
|
||||||
|
class ColorMode(SimpleNamespace):
|
||||||
|
"""Constants related to ColorMode."""
|
||||||
|
|
||||||
|
NAME = "colorMode"
|
||||||
|
USE = "useColorMode"
|
||||||
|
TOGGLE = "toggleColorMode"
|
||||||
|
|
||||||
|
|
||||||
|
# Env modes
|
||||||
|
class Env(str, Enum):
|
||||||
|
"""The environment modes."""
|
||||||
|
|
||||||
|
DEV = "dev"
|
||||||
|
PROD = "prod"
|
||||||
|
|
||||||
|
|
||||||
|
# Log levels
|
||||||
|
class LogLevel(str, Enum):
|
||||||
|
"""The log levels."""
|
||||||
|
|
||||||
|
DEBUG = "debug"
|
||||||
|
INFO = "info"
|
||||||
|
WARNING = "warning"
|
||||||
|
ERROR = "error"
|
||||||
|
CRITICAL = "critical"
|
||||||
|
|
||||||
|
def __le__(self, other: LogLevel) -> bool:
|
||||||
|
"""Compare log levels.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
other: The other log level.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the log level is less than or equal to the other log level.
|
||||||
|
"""
|
||||||
|
levels = list(LogLevel)
|
||||||
|
return levels.index(self) <= levels.index(other)
|
||||||
|
|
||||||
|
|
||||||
|
# Server socket configuration variables
|
||||||
|
POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
|
||||||
|
|
||||||
|
|
||||||
|
class Ping(SimpleNamespace):
|
||||||
|
"""PING constants."""
|
||||||
|
|
||||||
|
# The 'ping' interval
|
||||||
|
INTERVAL = 25
|
||||||
|
# The 'ping' timeout
|
||||||
|
TIMEOUT = 120
|
||||||
|
|
||||||
|
|
||||||
|
# Keys in the client_side_storage dict
|
||||||
|
COOKIES = "cookies"
|
||||||
|
LOCAL_STORAGE = "local_storage"
|
||||||
|
|
||||||
|
# If this env var is set to "yes", App.compile will be a no-op
|
||||||
|
SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"
|
||||||
|
|
||||||
|
# Testing variables.
|
||||||
|
# Testing os env set by pytest when running a test case.
|
||||||
|
PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
|
76
reflex/constants/compiler.py
Normal file
76
reflex/constants/compiler.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"""Compiler variables."""
|
||||||
|
from enum import Enum
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
# The prefix used to create setters for state vars.
|
||||||
|
SETTER_PREFIX = "set_"
|
||||||
|
|
||||||
|
|
||||||
|
class Ext(SimpleNamespace):
|
||||||
|
"""Extension used in Reflex."""
|
||||||
|
|
||||||
|
# The extension for JS files.
|
||||||
|
JS = ".js"
|
||||||
|
# The extension for python files.
|
||||||
|
PY = ".py"
|
||||||
|
# The extension for css files.
|
||||||
|
CSS = ".css"
|
||||||
|
# The extension for zip files.
|
||||||
|
ZIP = ".zip"
|
||||||
|
|
||||||
|
|
||||||
|
class CompileVars(SimpleNamespace):
|
||||||
|
"""The variables used during compilation."""
|
||||||
|
|
||||||
|
# The expected variable name where the rx.App is stored.
|
||||||
|
APP = "app"
|
||||||
|
# The expected variable name where the API object is stored for deployment.
|
||||||
|
API = "api"
|
||||||
|
# The name of the router variable.
|
||||||
|
ROUTER = "router"
|
||||||
|
# The name of the socket variable.
|
||||||
|
SOCKET = "socket"
|
||||||
|
# The name of the variable to hold API results.
|
||||||
|
RESULT = "result"
|
||||||
|
# The name of the final variable.
|
||||||
|
FINAL = "final"
|
||||||
|
# The name of the process variable.
|
||||||
|
PROCESSING = "processing"
|
||||||
|
# The name of the state variable.
|
||||||
|
STATE = "state"
|
||||||
|
# The name of the events variable.
|
||||||
|
EVENTS = "events"
|
||||||
|
# The name of the initial hydrate event.
|
||||||
|
HYDRATE = "hydrate"
|
||||||
|
# The name of the is_hydrated variable.
|
||||||
|
IS_HYDRATED = "is_hydrated"
|
||||||
|
|
||||||
|
|
||||||
|
class PageNames(SimpleNamespace):
|
||||||
|
"""The name of basic pages deployed in NextJS."""
|
||||||
|
|
||||||
|
# The name of the index page.
|
||||||
|
INDEX_ROUTE = "index"
|
||||||
|
# The name of the app root page.
|
||||||
|
APP_ROOT = "_app"
|
||||||
|
# The root stylesheet filename.
|
||||||
|
STYLESHEET_ROOT = "styles"
|
||||||
|
# The name of the document root page.
|
||||||
|
DOCUMENT_ROOT = "_document"
|
||||||
|
# The name of the theme page.
|
||||||
|
THEME = "theme"
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentName(Enum):
|
||||||
|
"""Component names."""
|
||||||
|
|
||||||
|
BACKEND = "Backend"
|
||||||
|
FRONTEND = "Frontend"
|
||||||
|
|
||||||
|
def zip(self):
|
||||||
|
"""Give the zip filename for the component.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The lower-case filename with zip extension.
|
||||||
|
"""
|
||||||
|
return self.value.lower() + Ext.ZIP
|
45
reflex/constants/config.py
Normal file
45
reflex/constants/config.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
"""Config constants."""
|
||||||
|
import os
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from reflex.constants.base import Dirs
|
||||||
|
|
||||||
|
from .compiler import Ext
|
||||||
|
|
||||||
|
# Alembic migrations
|
||||||
|
ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini")
|
||||||
|
|
||||||
|
|
||||||
|
class Config(SimpleNamespace):
|
||||||
|
"""Config constants."""
|
||||||
|
|
||||||
|
# The name of the reflex config module.
|
||||||
|
MODULE = "rxconfig"
|
||||||
|
# The python config file.
|
||||||
|
FILE = f"{MODULE}{Ext.PY}"
|
||||||
|
# The previous config file.
|
||||||
|
PREVIOUS_FILE = f"pcconfig{Ext.PY}"
|
||||||
|
|
||||||
|
|
||||||
|
class Expiration(SimpleNamespace):
|
||||||
|
"""Expiration constants."""
|
||||||
|
|
||||||
|
# Token expiration time in seconds
|
||||||
|
TOKEN = 60 * 60
|
||||||
|
# Maximum time in milliseconds that a state can be locked for exclusive access.
|
||||||
|
LOCK = 10000
|
||||||
|
# The PING timeout
|
||||||
|
PING = 120
|
||||||
|
|
||||||
|
|
||||||
|
class GitIgnore(SimpleNamespace):
|
||||||
|
"""Gitignore constants."""
|
||||||
|
|
||||||
|
# The gitignore file.
|
||||||
|
FILE = ".gitignore"
|
||||||
|
# Files to gitignore.
|
||||||
|
DEFAULTS = {Dirs.WEB, "*.db", "__pycache__/", "*.py[cod]"}
|
||||||
|
|
||||||
|
|
||||||
|
# The deployment URL.
|
||||||
|
PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
|
85
reflex/constants/event.py
Normal file
85
reflex/constants/event.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
"""Event-related constants."""
|
||||||
|
from enum import Enum
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
|
||||||
|
class Endpoint(Enum):
|
||||||
|
"""Endpoints for the reflex backend API."""
|
||||||
|
|
||||||
|
PING = "ping"
|
||||||
|
EVENT = "_event"
|
||||||
|
UPLOAD = "_upload"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""Get the string representation of the endpoint.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The path for the endpoint.
|
||||||
|
"""
|
||||||
|
return f"/{self.value}"
|
||||||
|
|
||||||
|
def get_url(self) -> str:
|
||||||
|
"""Get the URL for the endpoint.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The full URL for the endpoint.
|
||||||
|
"""
|
||||||
|
# Import here to avoid circular imports.
|
||||||
|
from reflex.config import get_config
|
||||||
|
|
||||||
|
# Get the API URL from the config.
|
||||||
|
config = get_config()
|
||||||
|
url = "".join([config.api_url, str(self)])
|
||||||
|
|
||||||
|
# The event endpoint is a websocket.
|
||||||
|
if self == Endpoint.EVENT:
|
||||||
|
# Replace the protocol with ws.
|
||||||
|
url = url.replace("https://", "wss://").replace("http://", "ws://")
|
||||||
|
|
||||||
|
# Return the url.
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
class SocketEvent(SimpleNamespace):
|
||||||
|
"""Socket events sent by the reflex backend API."""
|
||||||
|
|
||||||
|
PING = "ping"
|
||||||
|
EVENT = "event"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""Get the string representation of the event name.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The event name string.
|
||||||
|
"""
|
||||||
|
return str(self.value)
|
||||||
|
|
||||||
|
|
||||||
|
class EventTriggers(SimpleNamespace):
|
||||||
|
"""All trigger names used in Reflex."""
|
||||||
|
|
||||||
|
ON_FOCUS = "on_focus"
|
||||||
|
ON_BLUR = "on_blur"
|
||||||
|
ON_CANCEL = "on_cancel"
|
||||||
|
ON_CLICK = "on_click"
|
||||||
|
ON_CHANGE = "on_change"
|
||||||
|
ON_CHANGE_END = "on_change_end"
|
||||||
|
ON_CHANGE_START = "on_change_start"
|
||||||
|
ON_COMPLETE = "on_complete"
|
||||||
|
ON_CONTEXT_MENU = "on_context_menu"
|
||||||
|
ON_DOUBLE_CLICK = "on_double_click"
|
||||||
|
ON_DROP = "on_drop"
|
||||||
|
ON_EDIT = "on_edit"
|
||||||
|
ON_KEY_DOWN = "on_key_down"
|
||||||
|
ON_KEY_UP = "on_key_up"
|
||||||
|
ON_MOUSE_DOWN = "on_mouse_down"
|
||||||
|
ON_MOUSE_ENTER = "on_mouse_enter"
|
||||||
|
ON_MOUSE_LEAVE = "on_mouse_leave"
|
||||||
|
ON_MOUSE_MOVE = "on_mouse_move"
|
||||||
|
ON_MOUSE_OUT = "on_mouse_out"
|
||||||
|
ON_MOUSE_OVER = "on_mouse_over"
|
||||||
|
ON_MOUSE_UP = "on_mouse_up"
|
||||||
|
ON_SCROLL = "on_scroll"
|
||||||
|
ON_SUBMIT = "on_submit"
|
||||||
|
ON_MOUNT = "on_mount"
|
||||||
|
ON_UNMOUNT = "on_unmount"
|
123
reflex/constants/installer.py
Normal file
123
reflex/constants/installer.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
"""File for constants related to the installation process. (Bun/FNM/Node)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from .base import IS_WINDOWS, Dirs, Reflex
|
||||||
|
|
||||||
|
|
||||||
|
def get_fnm_name() -> str | None:
|
||||||
|
"""Get the appropriate fnm executable name based on the current platform.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The fnm executable name for the current platform.
|
||||||
|
"""
|
||||||
|
platform_os = platform.system()
|
||||||
|
|
||||||
|
if platform_os == "Windows":
|
||||||
|
return "fnm-windows"
|
||||||
|
elif platform_os == "Darwin":
|
||||||
|
return "fnm-macos"
|
||||||
|
elif platform_os == "Linux":
|
||||||
|
machine = platform.machine()
|
||||||
|
if machine == "arm" or machine.startswith("armv7"):
|
||||||
|
return "fnm-arm32"
|
||||||
|
elif machine.startswith("aarch") or machine.startswith("armv8"):
|
||||||
|
return "fnm-arm64"
|
||||||
|
return "fnm-linux"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Bun config.
|
||||||
|
class Bun(SimpleNamespace):
|
||||||
|
"""Bun constants."""
|
||||||
|
|
||||||
|
# The Bun version.
|
||||||
|
VERSION = "0.7.3"
|
||||||
|
# Min Bun Version
|
||||||
|
MIN_VERSION = "0.7.0"
|
||||||
|
# The directory to store the bun.
|
||||||
|
ROOT_PATH = os.path.join(Reflex.DIR, "bun")
|
||||||
|
# Default bun path.
|
||||||
|
DEFAULT_PATH = os.path.join(ROOT_PATH, "bin", "bun")
|
||||||
|
# URL to bun install script.
|
||||||
|
INSTALL_URL = "https://bun.sh/install"
|
||||||
|
|
||||||
|
|
||||||
|
# FNM config.
|
||||||
|
class Fnm(SimpleNamespace):
|
||||||
|
"""FNM constants."""
|
||||||
|
|
||||||
|
# The FNM version.
|
||||||
|
VERSION = "1.35.1"
|
||||||
|
# The directory to store fnm.
|
||||||
|
DIR = os.path.join(Reflex.DIR, "fnm")
|
||||||
|
FILENAME = get_fnm_name()
|
||||||
|
# The fnm executable binary.
|
||||||
|
EXE = os.path.join(DIR, "fnm.exe" if IS_WINDOWS else "fnm")
|
||||||
|
|
||||||
|
# The URL to the fnm release binary
|
||||||
|
INSTALL_URL = (
|
||||||
|
f"https://github.com/Schniz/fnm/releases/download/v{VERSION}/{FILENAME}.zip"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Node / NPM config
|
||||||
|
class Node(SimpleNamespace):
|
||||||
|
"""Node/ NPM constants."""
|
||||||
|
|
||||||
|
# The Node version.
|
||||||
|
VERSION = "18.17.0"
|
||||||
|
# The minimum required node version.
|
||||||
|
MIN_VERSION = "16.8.0"
|
||||||
|
|
||||||
|
# The node bin path.
|
||||||
|
BIN_PATH = os.path.join(
|
||||||
|
Fnm.DIR,
|
||||||
|
"node-versions",
|
||||||
|
f"v{VERSION}",
|
||||||
|
"installation",
|
||||||
|
"bin" if not IS_WINDOWS else "",
|
||||||
|
)
|
||||||
|
# The default path where node is installed.
|
||||||
|
PATH = os.path.join(BIN_PATH, "node.exe" if IS_WINDOWS else "node")
|
||||||
|
|
||||||
|
# The default path where npm is installed.
|
||||||
|
NPM_PATH = os.path.join(BIN_PATH, "npm")
|
||||||
|
|
||||||
|
|
||||||
|
class PackageJson(SimpleNamespace):
|
||||||
|
"""Constants used to build the package.json file."""
|
||||||
|
|
||||||
|
class Commands(SimpleNamespace):
|
||||||
|
"""The commands to define in package.json."""
|
||||||
|
|
||||||
|
DEV = "next dev"
|
||||||
|
EXPORT = "next build && next export -o _static"
|
||||||
|
EXPORT_SITEMAP = "next build && next-sitemap && next export -o _static"
|
||||||
|
PROD = "next start"
|
||||||
|
|
||||||
|
PATH = os.path.join(Dirs.WEB, "package.json")
|
||||||
|
|
||||||
|
DEPENDENCIES = {
|
||||||
|
"@chakra-ui/react": "^2.6.0",
|
||||||
|
"@chakra-ui/system": "^2.5.6",
|
||||||
|
"@emotion/react": "^11.10.6",
|
||||||
|
"@emotion/styled": "^11.10.6",
|
||||||
|
"axios": "^1.4.0",
|
||||||
|
"chakra-react-select": "^4.6.0",
|
||||||
|
"focus-visible": "^5.2.0",
|
||||||
|
"json5": "^2.2.3",
|
||||||
|
"next": "^13.3.1",
|
||||||
|
"next-sitemap": "^4.1.8",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"socket.io-client": "^4.6.1",
|
||||||
|
"universal-cookie": "^4.0.4",
|
||||||
|
}
|
||||||
|
DEV_DEPENDENCIES = {
|
||||||
|
"autoprefixer": "^10.4.14",
|
||||||
|
"postcss": "^8.4.24",
|
||||||
|
}
|
67
reflex/constants/route.py
Normal file
67
reflex/constants/route.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
"""Route constants."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
|
||||||
|
class RouteArgType(SimpleNamespace):
|
||||||
|
"""Type of dynamic route arg extracted from URI route."""
|
||||||
|
|
||||||
|
# Typecast to str is needed for Enum to work.
|
||||||
|
SINGLE = str("arg_single")
|
||||||
|
LIST = str("arg_list")
|
||||||
|
|
||||||
|
|
||||||
|
# the name of the backend var containing path and client information
|
||||||
|
ROUTER_DATA = "router_data"
|
||||||
|
|
||||||
|
|
||||||
|
class RouteVar(SimpleNamespace):
|
||||||
|
"""Names of variables used in the router_data dict stored in State."""
|
||||||
|
|
||||||
|
CLIENT_IP = "ip"
|
||||||
|
CLIENT_TOKEN = "token"
|
||||||
|
HEADERS = "headers"
|
||||||
|
PATH = "pathname"
|
||||||
|
ORIGIN = "asPath"
|
||||||
|
SESSION_ID = "sid"
|
||||||
|
QUERY = "query"
|
||||||
|
COOKIE = "cookie"
|
||||||
|
|
||||||
|
|
||||||
|
class RouteRegex(SimpleNamespace):
|
||||||
|
"""Regex used for extracting route args in route."""
|
||||||
|
|
||||||
|
ARG = re.compile(r"\[(?!\.)([^\[\]]+)\]")
|
||||||
|
# group return the catchall pattern (i.e. "[[..slug]]")
|
||||||
|
CATCHALL = re.compile(r"(\[?\[\.{3}(?![0-9]).*\]?\])")
|
||||||
|
# group return the arg name (i.e. "slug")
|
||||||
|
STRICT_CATCHALL = re.compile(r"\[\.{3}([a-zA-Z_][\w]*)\]")
|
||||||
|
# group return the arg name (i.e. "slug") (optional arg can be empty)
|
||||||
|
OPT_CATCHALL = re.compile(r"\[\[\.{3}([a-zA-Z_][\w]*)\]\]")
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultPage(SimpleNamespace):
|
||||||
|
"""Default page constants."""
|
||||||
|
|
||||||
|
# The default title to show for Reflex apps.
|
||||||
|
TITLE = "Reflex App"
|
||||||
|
# The default description to show for Reflex apps.
|
||||||
|
DESCRIPTION = "A Reflex app."
|
||||||
|
# The default image to show for Reflex apps.
|
||||||
|
IMAGE = "favicon.ico"
|
||||||
|
# The default meta list to show for Reflex apps.
|
||||||
|
META_LIST = []
|
||||||
|
|
||||||
|
|
||||||
|
# 404 variables
|
||||||
|
class Page404(SimpleNamespace):
|
||||||
|
"""Page 404 constants."""
|
||||||
|
|
||||||
|
SLUG = "404"
|
||||||
|
TITLE = "404 - Not Found"
|
||||||
|
IMAGE = "favicon.ico"
|
||||||
|
DESCRIPTION = "The page was not found"
|
||||||
|
|
||||||
|
|
||||||
|
ROUTE_NOT_FOUND = "routeNotFound"
|
22
reflex/constants/style.py
Normal file
22
reflex/constants/style.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
"""Style constants."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from reflex.constants.base import Dirs
|
||||||
|
|
||||||
|
# The directory where styles are located.
|
||||||
|
STYLES_DIR = os.path.join(Dirs.WEB, "styles")
|
||||||
|
|
||||||
|
|
||||||
|
class Tailwind(SimpleNamespace):
|
||||||
|
"""Tailwind constants."""
|
||||||
|
|
||||||
|
# The Tailwindcss version
|
||||||
|
VERSION = "tailwindcss@^3.3.2"
|
||||||
|
# The Tailwind config.
|
||||||
|
CONFIG = os.path.join(Dirs.WEB, "tailwind.config.js")
|
||||||
|
# Default Tailwind content paths
|
||||||
|
CONTENT = ["./pages/**/*.{js,ts,jsx,tsx}"]
|
||||||
|
# Relative tailwind style path to root stylesheet in STYLES_DIR.
|
||||||
|
ROOT_STYLE_PATH = "./tailwind.css"
|
@ -33,7 +33,7 @@ def attr_to_prop(attr_name: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
# Names of HTML attributes that are provided by Reflex out of the box.
|
# Names of HTML attributes that are provided by Reflex out of the box.
|
||||||
PYNECONE_PROVIDED_ATTRS = {"class", "id", "style"}
|
REFLEX_PROVIDED_ATTRS = {"class", "id", "style"}
|
||||||
|
|
||||||
# ATTR_TO_ELEMENTS contains HTML attribute names, which might be invalid as
|
# ATTR_TO_ELEMENTS contains HTML attribute names, which might be invalid as
|
||||||
# Reflex prop names. PROP_TO_ELEMENTS contains the corresponding Reflex
|
# Reflex prop names. PROP_TO_ELEMENTS contains the corresponding Reflex
|
||||||
@ -41,7 +41,7 @@ PYNECONE_PROVIDED_ATTRS = {"class", "id", "style"}
|
|||||||
PROP_TO_ELEMENTS = {
|
PROP_TO_ELEMENTS = {
|
||||||
attr_to_prop(attr_name): elements
|
attr_to_prop(attr_name): elements
|
||||||
for attr_name, elements in ATTR_TO_ELEMENTS.items()
|
for attr_name, elements in ATTR_TO_ELEMENTS.items()
|
||||||
if attr_name not in PYNECONE_PROVIDED_ATTRS
|
if attr_name not in REFLEX_PROVIDED_ATTRS
|
||||||
}
|
}
|
||||||
|
|
||||||
# Invert PROP_TO_ELEMENTS to enable easier lookup.
|
# Invert PROP_TO_ELEMENTS to enable easier lookup.
|
||||||
|
@ -192,7 +192,7 @@ class EventChain(Base):
|
|||||||
|
|
||||||
events: List[EventSpec]
|
events: List[EventSpec]
|
||||||
|
|
||||||
args_spec: Optional[ArgsSpec]
|
args_spec: Optional[Callable]
|
||||||
|
|
||||||
|
|
||||||
class Target(Base):
|
class Target(Base):
|
||||||
@ -481,7 +481,7 @@ def get_hydrate_event(state) -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The name of the hydrate event.
|
The name of the hydrate event.
|
||||||
"""
|
"""
|
||||||
return get_event(state, constants.HYDRATE)
|
return get_event(state, constants.CompileVars.HYDRATE)
|
||||||
|
|
||||||
|
|
||||||
def call_event_handler(
|
def call_event_handler(
|
||||||
@ -507,12 +507,12 @@ def call_event_handler(
|
|||||||
|
|
||||||
# handle new API using lambda to define triggers
|
# handle new API using lambda to define triggers
|
||||||
if isinstance(arg_spec, ArgsSpec):
|
if isinstance(arg_spec, ArgsSpec):
|
||||||
parsed_args = parse_args_spec(arg_spec)
|
parsed_args = parse_args_spec(arg_spec) # type: ignore
|
||||||
|
|
||||||
if len(args) == len(["self", *parsed_args]):
|
if len(args) == len(["self", *parsed_args]):
|
||||||
return event_handler(*parsed_args) # type: ignore
|
return event_handler(*parsed_args) # type: ignore
|
||||||
else:
|
else:
|
||||||
source = inspect.getsource(arg_spec)
|
source = inspect.getsource(arg_spec) # type: ignore
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"number of arguments in {event_handler.fn.__name__} "
|
f"number of arguments in {event_handler.fn.__name__} "
|
||||||
f"doesn't match the definition '{source.strip().strip(',')}'"
|
f"doesn't match the definition '{source.strip().strip(',')}'"
|
||||||
@ -524,12 +524,12 @@ def call_event_handler(
|
|||||||
deprecation_version="0.2.8",
|
deprecation_version="0.2.8",
|
||||||
removal_version="0.2.9",
|
removal_version="0.2.9",
|
||||||
)
|
)
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
return event_handler()
|
return event_handler()
|
||||||
assert (
|
assert (
|
||||||
len(args) == 2
|
len(args) == 2
|
||||||
), f"Event handler {event_handler.fn} must have 1 or 2 arguments."
|
), f"Event handler {event_handler.fn} must have 1 or 2 arguments."
|
||||||
return event_handler(arg_spec)
|
return event_handler(arg_spec) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def parse_args_spec(arg_spec: ArgsSpec):
|
def parse_args_spec(arg_spec: ArgsSpec):
|
||||||
@ -578,7 +578,7 @@ def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec]:
|
|||||||
args = inspect.getfullargspec(fn).args
|
args = inspect.getfullargspec(fn).args
|
||||||
|
|
||||||
if isinstance(arg, ArgsSpec):
|
if isinstance(arg, ArgsSpec):
|
||||||
out = fn(*parse_args_spec(arg))
|
out = fn(*parse_args_spec(arg)) # type: ignore
|
||||||
else:
|
else:
|
||||||
# Call the lambda.
|
# Call the lambda.
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
|
@ -13,7 +13,7 @@ if TYPE_CHECKING:
|
|||||||
from reflex.app import App
|
from reflex.app import App
|
||||||
|
|
||||||
|
|
||||||
State.add_var(constants.IS_HYDRATED, type_=bool, default_value=False)
|
State.add_var(constants.CompileVars.IS_HYDRATED, type_=bool, default_value=False)
|
||||||
|
|
||||||
|
|
||||||
class HydrateMiddleware(Middleware):
|
class HydrateMiddleware(Middleware):
|
||||||
@ -40,7 +40,7 @@ class HydrateMiddleware(Middleware):
|
|||||||
state._reset_client_storage()
|
state._reset_client_storage()
|
||||||
|
|
||||||
# Mark state as not hydrated (until on_loads are complete)
|
# Mark state as not hydrated (until on_loads are complete)
|
||||||
setattr(state, constants.IS_HYDRATED, False)
|
setattr(state, constants.CompileVars.IS_HYDRATED, False)
|
||||||
|
|
||||||
# Apply client side storage values to state
|
# Apply client side storage values to state
|
||||||
for storage_type in (constants.COOKIES, constants.LOCAL_STORAGE):
|
for storage_type in (constants.COOKIES, constants.LOCAL_STORAGE):
|
||||||
|
@ -29,7 +29,7 @@ def version(value: bool):
|
|||||||
typer.Exit: If the version flag was passed.
|
typer.Exit: If the version flag was passed.
|
||||||
"""
|
"""
|
||||||
if value:
|
if value:
|
||||||
console.print(constants.VERSION)
|
console.print(constants.Reflex.VERSION)
|
||||||
raise typer.Exit()
|
raise typer.Exit()
|
||||||
|
|
||||||
|
|
||||||
@ -53,8 +53,9 @@ def init(
|
|||||||
name: str = typer.Option(
|
name: str = typer.Option(
|
||||||
None, metavar="APP_NAME", help="The name of the app to initialize."
|
None, metavar="APP_NAME", help="The name of the app to initialize."
|
||||||
),
|
),
|
||||||
template: constants.Template = typer.Option(
|
template: constants.Templates.Kind = typer.Option(
|
||||||
constants.Template.DEFAULT, help="The template to initialize the app with."
|
constants.Templates.Kind.DEFAULT,
|
||||||
|
help="The template to initialize the app with.",
|
||||||
),
|
),
|
||||||
loglevel: constants.LogLevel = typer.Option(
|
loglevel: constants.LogLevel = typer.Option(
|
||||||
config.loglevel, help="The log level to use."
|
config.loglevel, help="The log level to use."
|
||||||
@ -78,7 +79,7 @@ def init(
|
|||||||
prerequisites.migrate_to_reflex()
|
prerequisites.migrate_to_reflex()
|
||||||
|
|
||||||
# Set up the app directory, only if the config doesn't exist.
|
# Set up the app directory, only if the config doesn't exist.
|
||||||
if not os.path.exists(constants.CONFIG_FILE):
|
if not os.path.exists(constants.Config.FILE):
|
||||||
prerequisites.create_config(app_name)
|
prerequisites.create_config(app_name)
|
||||||
prerequisites.initialize_app_directory(app_name, template)
|
prerequisites.initialize_app_directory(app_name, template)
|
||||||
telemetry.send("init")
|
telemetry.send("init")
|
||||||
@ -193,7 +194,7 @@ def run(
|
|||||||
def deploy(
|
def deploy(
|
||||||
dry_run: bool = typer.Option(False, help="Whether to run a dry run."),
|
dry_run: bool = typer.Option(False, help="Whether to run a dry run."),
|
||||||
loglevel: constants.LogLevel = typer.Option(
|
loglevel: constants.LogLevel = typer.Option(
|
||||||
console.LOG_LEVEL, help="The log level to use."
|
console._LOG_LEVEL, help="The log level to use."
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
"""Deploy the app to the Reflex hosting service."""
|
"""Deploy the app to the Reflex hosting service."""
|
||||||
@ -223,10 +224,10 @@ def deploy(
|
|||||||
backend = response["backend_resources_url"]
|
backend = response["backend_resources_url"]
|
||||||
|
|
||||||
# Upload the frontend and backend.
|
# Upload the frontend and backend.
|
||||||
with open(constants.FRONTEND_ZIP, "rb") as f:
|
with open(constants.ComponentName.FRONTEND.zip(), "rb") as f:
|
||||||
httpx.put(frontend, data=f) # type: ignore
|
httpx.put(frontend, data=f) # type: ignore
|
||||||
|
|
||||||
with open(constants.BACKEND_ZIP, "rb") as f:
|
with open(constants.ComponentName.BACKEND.zip(), "rb") as f:
|
||||||
httpx.put(backend, data=f) # type: ignore
|
httpx.put(backend, data=f) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@ -242,7 +243,7 @@ def export(
|
|||||||
True, "--frontend-only", help="Export only frontend.", show_default=False
|
True, "--frontend-only", help="Export only frontend.", show_default=False
|
||||||
),
|
),
|
||||||
loglevel: constants.LogLevel = typer.Option(
|
loglevel: constants.LogLevel = typer.Option(
|
||||||
console.LOG_LEVEL, help="The log level to use."
|
console._LOG_LEVEL, help="The log level to use."
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
"""Export the app to a zip file."""
|
"""Export the app to a zip file."""
|
||||||
|
@ -1102,7 +1102,7 @@ class StateProxy(wrapt.ObjectProxy):
|
|||||||
state_instance: The state instance to proxy.
|
state_instance: The state instance to proxy.
|
||||||
"""
|
"""
|
||||||
super().__init__(state_instance)
|
super().__init__(state_instance)
|
||||||
self._self_app = getattr(prerequisites.get_app(), constants.APP_VAR)
|
self._self_app = getattr(prerequisites.get_app(), constants.CompileVars.APP)
|
||||||
self._self_substate_path = state_instance.get_full_name().split(".")
|
self._self_substate_path = state_instance.get_full_name().split(".")
|
||||||
self._self_actx = None
|
self._self_actx = None
|
||||||
self._self_mutable = False
|
self._self_mutable = False
|
||||||
@ -1355,10 +1355,10 @@ class StateManagerRedis(StateManager):
|
|||||||
redis: Redis
|
redis: Redis
|
||||||
|
|
||||||
# The token expiration time (s).
|
# The token expiration time (s).
|
||||||
token_expiration: int = constants.TOKEN_EXPIRATION
|
token_expiration: int = constants.Expiration.TOKEN
|
||||||
|
|
||||||
# The maximum time to hold a lock (ms).
|
# The maximum time to hold a lock (ms).
|
||||||
lock_expiration: int = constants.LOCK_EXPIRATION
|
lock_expiration: int = constants.Expiration.LOCK
|
||||||
|
|
||||||
# The keyspace subscription string when redis is waiting for lock to be released
|
# The keyspace subscription string when redis is waiting for lock to be released
|
||||||
_redis_notify_keyspace_events: str = (
|
_redis_notify_keyspace_events: str = (
|
||||||
|
@ -7,8 +7,8 @@ from reflex.event import EventChain
|
|||||||
from reflex.utils import format
|
from reflex.utils import format
|
||||||
from reflex.vars import BaseVar, Var
|
from reflex.vars import BaseVar, Var
|
||||||
|
|
||||||
color_mode = BaseVar(name=constants.COLOR_MODE, type_="str")
|
color_mode = BaseVar(name=constants.ColorMode.NAME, type_="str")
|
||||||
toggle_color_mode = BaseVar(name=constants.TOGGLE_COLOR_MODE, type_=EventChain)
|
toggle_color_mode = BaseVar(name=constants.ColorMode.TOGGLE, type_=EventChain)
|
||||||
|
|
||||||
|
|
||||||
def convert(style_dict):
|
def convert(style_dict):
|
||||||
|
@ -154,7 +154,7 @@ class AppHarness:
|
|||||||
with chdir(self.app_path):
|
with chdir(self.app_path):
|
||||||
reflex.reflex.init(
|
reflex.reflex.init(
|
||||||
name=self.app_name,
|
name=self.app_name,
|
||||||
template=reflex.constants.Template.DEFAULT,
|
template=reflex.constants.Templates.Kind.DEFAULT,
|
||||||
loglevel=reflex.constants.LogLevel.INFO,
|
loglevel=reflex.constants.LogLevel.INFO,
|
||||||
)
|
)
|
||||||
self.app_module_path.write_text(source_code)
|
self.app_module_path.write_text(source_code)
|
||||||
@ -211,7 +211,7 @@ class AppHarness:
|
|||||||
# Start the frontend.
|
# Start the frontend.
|
||||||
self.frontend_process = reflex.utils.processes.new_process(
|
self.frontend_process = reflex.utils.processes.new_process(
|
||||||
[reflex.utils.prerequisites.get_package_manager(), "run", "dev"],
|
[reflex.utils.prerequisites.get_package_manager(), "run", "dev"],
|
||||||
cwd=self.app_path / reflex.constants.WEB_DIR,
|
cwd=self.app_path / reflex.constants.Dirs.WEB,
|
||||||
env={"PORT": "0"},
|
env={"PORT": "0"},
|
||||||
**FRONTEND_POPEN_ARGS,
|
**FRONTEND_POPEN_ARGS,
|
||||||
)
|
)
|
||||||
@ -647,7 +647,7 @@ class AppHarnessProd(AppHarness):
|
|||||||
frontend_server: Optional[Subdir404TCPServer] = None
|
frontend_server: Optional[Subdir404TCPServer] = None
|
||||||
|
|
||||||
def _run_frontend(self):
|
def _run_frontend(self):
|
||||||
web_root = self.app_path / reflex.constants.WEB_DIR / "_static"
|
web_root = self.app_path / reflex.constants.Dirs.WEB / "_static"
|
||||||
error_page_map = {
|
error_page_map = {
|
||||||
404: web_root / "404.html",
|
404: web_root / "404.html",
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import json
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import zipfile
|
import zipfile
|
||||||
from enum import Enum
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
||||||
@ -19,7 +18,7 @@ from reflex.utils import console, path_ops, prerequisites, processes
|
|||||||
def set_env_json():
|
def set_env_json():
|
||||||
"""Write the upload url to a REFLEX_JSON."""
|
"""Write the upload url to a REFLEX_JSON."""
|
||||||
path_ops.update_json_file(
|
path_ops.update_json_file(
|
||||||
constants.ENV_JSON,
|
constants.Dirs.ENV_JSON,
|
||||||
{endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint},
|
{endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -52,17 +51,12 @@ def generate_sitemap_config(deploy_url: str):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(constants.SITEMAP_CONFIG_FILE, "w") as f:
|
with open(constants.Next.SITEMAP_CONFIG_FILE, "w") as f:
|
||||||
f.write(templates.SITEMAP_CONFIG(config=config))
|
f.write(templates.SITEMAP_CONFIG(config=config))
|
||||||
|
|
||||||
|
|
||||||
class _ComponentName(Enum):
|
|
||||||
BACKEND = "Backend"
|
|
||||||
FRONTEND = "Frontend"
|
|
||||||
|
|
||||||
|
|
||||||
def _zip(
|
def _zip(
|
||||||
component_name: _ComponentName,
|
component_name: constants.ComponentName,
|
||||||
target: str,
|
target: str,
|
||||||
root_dir: str,
|
root_dir: str,
|
||||||
dirs_to_exclude: set[str] | None = None,
|
dirs_to_exclude: set[str] | None = None,
|
||||||
@ -130,7 +124,7 @@ def export(
|
|||||||
deploy_url: The URL of the deployed app.
|
deploy_url: The URL of the deployed app.
|
||||||
"""
|
"""
|
||||||
# Remove the static folder.
|
# Remove the static folder.
|
||||||
path_ops.rm(constants.WEB_STATIC_DIR)
|
path_ops.rm(constants.Dirs.WEB_STATIC)
|
||||||
|
|
||||||
# The export command to run.
|
# The export command to run.
|
||||||
command = "export"
|
command = "export"
|
||||||
@ -155,25 +149,28 @@ def export(
|
|||||||
# Start the subprocess with the progress bar.
|
# Start the subprocess with the progress bar.
|
||||||
process = processes.new_process(
|
process = processes.new_process(
|
||||||
[prerequisites.get_package_manager(), "run", command],
|
[prerequisites.get_package_manager(), "run", command],
|
||||||
cwd=constants.WEB_DIR,
|
cwd=constants.Dirs.WEB,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
processes.show_progress("Creating Production Build", process, checkpoints)
|
processes.show_progress("Creating Production Build", process, checkpoints)
|
||||||
|
|
||||||
# Zip up the app.
|
# Zip up the app.
|
||||||
if zip:
|
if zip:
|
||||||
files_to_exclude = {constants.FRONTEND_ZIP, constants.BACKEND_ZIP}
|
files_to_exclude = {
|
||||||
|
constants.ComponentName.FRONTEND.zip(),
|
||||||
|
constants.ComponentName.BACKEND.zip(),
|
||||||
|
}
|
||||||
if frontend:
|
if frontend:
|
||||||
_zip(
|
_zip(
|
||||||
component_name=_ComponentName.FRONTEND,
|
component_name=constants.ComponentName.FRONTEND,
|
||||||
target=constants.FRONTEND_ZIP,
|
target=constants.ComponentName.FRONTEND.zip(),
|
||||||
root_dir=".web/_static",
|
root_dir=".web/_static",
|
||||||
files_to_exclude=files_to_exclude,
|
files_to_exclude=files_to_exclude,
|
||||||
)
|
)
|
||||||
if backend:
|
if backend:
|
||||||
_zip(
|
_zip(
|
||||||
component_name=_ComponentName.BACKEND,
|
component_name=constants.ComponentName.BACKEND,
|
||||||
target=constants.BACKEND_ZIP,
|
target=constants.ComponentName.BACKEND.zip(),
|
||||||
root_dir=".",
|
root_dir=".",
|
||||||
dirs_to_exclude={"assets", "__pycache__"},
|
dirs_to_exclude={"assets", "__pycache__"},
|
||||||
files_to_exclude=files_to_exclude,
|
files_to_exclude=files_to_exclude,
|
||||||
@ -192,8 +189,8 @@ def setup_frontend(
|
|||||||
"""
|
"""
|
||||||
# Copy asset files to public folder.
|
# Copy asset files to public folder.
|
||||||
path_ops.cp(
|
path_ops.cp(
|
||||||
src=str(root / constants.APP_ASSETS_DIR),
|
src=str(root / constants.Dirs.APP_ASSETS),
|
||||||
dest=str(root / constants.WEB_ASSETS_DIR),
|
dest=str(root / constants.Dirs.WEB_ASSETS),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set the environment variables in client (env.json).
|
# Set the environment variables in client (env.json).
|
||||||
@ -209,7 +206,7 @@ def setup_frontend(
|
|||||||
"telemetry",
|
"telemetry",
|
||||||
"disable",
|
"disable",
|
||||||
],
|
],
|
||||||
cwd=constants.WEB_DIR,
|
cwd=constants.Dirs.WEB,
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,7 @@ from reflex.constants import LogLevel
|
|||||||
_console = Console()
|
_console = Console()
|
||||||
|
|
||||||
# The current log level.
|
# The current log level.
|
||||||
LOG_LEVEL = LogLevel.INFO
|
_LOG_LEVEL = LogLevel.INFO
|
||||||
|
|
||||||
|
|
||||||
def set_log_level(log_level: LogLevel):
|
def set_log_level(log_level: LogLevel):
|
||||||
@ -23,8 +23,8 @@ def set_log_level(log_level: LogLevel):
|
|||||||
Args:
|
Args:
|
||||||
log_level: The log level to set.
|
log_level: The log level to set.
|
||||||
"""
|
"""
|
||||||
global LOG_LEVEL
|
global _LOG_LEVEL
|
||||||
LOG_LEVEL = log_level
|
_LOG_LEVEL = log_level
|
||||||
|
|
||||||
|
|
||||||
def print(msg: str, **kwargs):
|
def print(msg: str, **kwargs):
|
||||||
@ -44,7 +44,7 @@ def debug(msg: str, **kwargs):
|
|||||||
msg: The debug message.
|
msg: The debug message.
|
||||||
kwargs: Keyword arguments to pass to the print function.
|
kwargs: Keyword arguments to pass to the print function.
|
||||||
"""
|
"""
|
||||||
if LOG_LEVEL <= LogLevel.DEBUG:
|
if _LOG_LEVEL <= LogLevel.DEBUG:
|
||||||
print(f"[blue]Debug: {msg}[/blue]", **kwargs)
|
print(f"[blue]Debug: {msg}[/blue]", **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ def info(msg: str, **kwargs):
|
|||||||
msg: The info message.
|
msg: The info message.
|
||||||
kwargs: Keyword arguments to pass to the print function.
|
kwargs: Keyword arguments to pass to the print function.
|
||||||
"""
|
"""
|
||||||
if LOG_LEVEL <= LogLevel.INFO:
|
if _LOG_LEVEL <= LogLevel.INFO:
|
||||||
print(f"[cyan]Info: {msg}[/cyan]", **kwargs)
|
print(f"[cyan]Info: {msg}[/cyan]", **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ def success(msg: str, **kwargs):
|
|||||||
msg: The success message.
|
msg: The success message.
|
||||||
kwargs: Keyword arguments to pass to the print function.
|
kwargs: Keyword arguments to pass to the print function.
|
||||||
"""
|
"""
|
||||||
if LOG_LEVEL <= LogLevel.INFO:
|
if _LOG_LEVEL <= LogLevel.INFO:
|
||||||
print(f"[green]Success: {msg}[/green]", **kwargs)
|
print(f"[green]Success: {msg}[/green]", **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ def log(msg: str, **kwargs):
|
|||||||
msg: The message to log.
|
msg: The message to log.
|
||||||
kwargs: Keyword arguments to pass to the print function.
|
kwargs: Keyword arguments to pass to the print function.
|
||||||
"""
|
"""
|
||||||
if LOG_LEVEL <= LogLevel.INFO:
|
if _LOG_LEVEL <= LogLevel.INFO:
|
||||||
_console.log(msg, **kwargs)
|
_console.log(msg, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ def warn(msg: str, **kwargs):
|
|||||||
msg: The warning message.
|
msg: The warning message.
|
||||||
kwargs: Keyword arguments to pass to the print function.
|
kwargs: Keyword arguments to pass to the print function.
|
||||||
"""
|
"""
|
||||||
if LOG_LEVEL <= LogLevel.WARNING:
|
if _LOG_LEVEL <= LogLevel.WARNING:
|
||||||
print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
|
print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ def deprecate(
|
|||||||
f"{feature_name} has been deprecated in version {deprecation_version} {reason}. It will be completely "
|
f"{feature_name} has been deprecated in version {deprecation_version} {reason}. It will be completely "
|
||||||
f"removed in {removal_version}"
|
f"removed in {removal_version}"
|
||||||
)
|
)
|
||||||
if LOG_LEVEL <= LogLevel.WARNING:
|
if _LOG_LEVEL <= LogLevel.WARNING:
|
||||||
print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
|
print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ def error(msg: str, **kwargs):
|
|||||||
msg: The error message.
|
msg: The error message.
|
||||||
kwargs: Keyword arguments to pass to the print function.
|
kwargs: Keyword arguments to pass to the print function.
|
||||||
"""
|
"""
|
||||||
if LOG_LEVEL <= LogLevel.ERROR:
|
if _LOG_LEVEL <= LogLevel.ERROR:
|
||||||
print(f"[red]{msg}[/red]", **kwargs)
|
print(f"[red]{msg}[/red]", **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ def run_process_and_launch_url(run_command: list[str]):
|
|||||||
Args:
|
Args:
|
||||||
run_command: The command to run.
|
run_command: The command to run.
|
||||||
"""
|
"""
|
||||||
json_file_path = os.path.join(constants.WEB_DIR, "package.json")
|
json_file_path = os.path.join(constants.Dirs.WEB, "package.json")
|
||||||
last_hash = detect_package_change(json_file_path)
|
last_hash = detect_package_change(json_file_path)
|
||||||
process = None
|
process = None
|
||||||
first_run = True
|
first_run = True
|
||||||
@ -81,7 +81,7 @@ def run_process_and_launch_url(run_command: list[str]):
|
|||||||
while True:
|
while True:
|
||||||
if process is None:
|
if process is None:
|
||||||
process = processes.new_process(
|
process = processes.new_process(
|
||||||
run_command, cwd=constants.WEB_DIR, shell=constants.IS_WINDOWS
|
run_command, cwd=constants.Dirs.WEB, shell=constants.IS_WINDOWS
|
||||||
)
|
)
|
||||||
if process.stdout:
|
if process.stdout:
|
||||||
for line in processes.stream_logs("Starting frontend", process):
|
for line in processes.stream_logs("Starting frontend", process):
|
||||||
@ -153,9 +153,9 @@ def run_backend(
|
|||||||
loglevel: The log level.
|
loglevel: The log level.
|
||||||
"""
|
"""
|
||||||
config = get_config()
|
config = get_config()
|
||||||
app_module = f"{config.app_name}.{config.app_name}:{constants.APP_VAR}"
|
app_module = f"{config.app_name}.{config.app_name}:{constants.CompileVars.APP}"
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
app=f"{app_module}.{constants.API_VAR}",
|
app=f"{app_module}.{constants.CompileVars.API}",
|
||||||
host=host,
|
host=host,
|
||||||
port=port,
|
port=port,
|
||||||
log_level=loglevel.value,
|
log_level=loglevel.value,
|
||||||
@ -180,7 +180,7 @@ def run_backend_prod(
|
|||||||
config = get_config()
|
config = get_config()
|
||||||
RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --preload --timeout {config.timeout} --log-level critical".split()
|
RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --preload --timeout {config.timeout} --log-level critical".split()
|
||||||
RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
|
RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
|
||||||
app_module = f"{config.app_name}.{config.app_name}:{constants.APP_VAR}"
|
app_module = f"{config.app_name}.{config.app_name}:{constants.CompileVars.APP}"
|
||||||
command = (
|
command = (
|
||||||
[
|
[
|
||||||
*RUN_BACKEND_PROD_WINDOWS,
|
*RUN_BACKEND_PROD_WINDOWS,
|
||||||
@ -217,7 +217,7 @@ def run_backend_prod(
|
|||||||
|
|
||||||
def output_system_info():
|
def output_system_info():
|
||||||
"""Show system information if the loglevel is in DEBUG."""
|
"""Show system information if the loglevel is in DEBUG."""
|
||||||
if console.LOG_LEVEL > constants.LogLevel.DEBUG:
|
if console._LOG_LEVEL > constants.LogLevel.DEBUG:
|
||||||
return
|
return
|
||||||
|
|
||||||
config = get_config()
|
config = get_config()
|
||||||
@ -231,8 +231,8 @@ def output_system_info():
|
|||||||
console.debug(f"Config: {config}")
|
console.debug(f"Config: {config}")
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
f"[Reflex {constants.VERSION} with Python {platform.python_version()} (PATH: {sys.executable})]",
|
f"[Reflex {constants.Reflex.VERSION} with Python {platform.python_version()} (PATH: {sys.executable})]",
|
||||||
f"[Node {prerequisites.get_node_version()} (Expected: {constants.NODE_VERSION}) (PATH:{path_ops.get_node_path()})]",
|
f"[Node {prerequisites.get_node_version()} (Expected: {constants.Node.VERSION}) (PATH:{path_ops.get_node_path()})]",
|
||||||
]
|
]
|
||||||
|
|
||||||
system = platform.system()
|
system = platform.system()
|
||||||
@ -240,13 +240,13 @@ def output_system_info():
|
|||||||
if system != "Windows":
|
if system != "Windows":
|
||||||
dependencies.extend(
|
dependencies.extend(
|
||||||
[
|
[
|
||||||
f"[FNM {constants.FNM_VERSION} (Expected: {constants.FNM_VERSION}) (PATH: {constants.FNM_EXE})]",
|
f"[FNM {constants.Fnm.VERSION} (Expected: {constants.Fnm.VERSION}) (PATH: {constants.Fnm.EXE})]",
|
||||||
f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.BUN_VERSION}) (PATH: {config.bun_path})]",
|
f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.Bun.VERSION}) (PATH: {config.bun_path})]",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
dependencies.append(
|
dependencies.append(
|
||||||
f"[FNM {constants.FNM_VERSION} (Expected: {constants.FNM_VERSION}) (PATH: {constants.FNM_EXE})]",
|
f"[FNM {constants.Fnm.VERSION} (Expected: {constants.Fnm.VERSION}) (PATH: {constants.Fnm.EXE})]",
|
||||||
)
|
)
|
||||||
|
|
||||||
if system == "Linux":
|
if system == "Linux":
|
||||||
|
@ -5,7 +5,6 @@ from __future__ import annotations
|
|||||||
import inspect
|
import inspect
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import os.path as op
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from typing import TYPE_CHECKING, Any, Union
|
from typing import TYPE_CHECKING, Any, Union
|
||||||
@ -230,7 +229,7 @@ def format_route(route: str, format_case=True) -> str:
|
|||||||
|
|
||||||
# If the route is empty, return the index route.
|
# If the route is empty, return the index route.
|
||||||
if route == "":
|
if route == "":
|
||||||
return constants.INDEX_ROUTE
|
return constants.PageNames.INDEX_ROUTE
|
||||||
|
|
||||||
return route
|
return route
|
||||||
|
|
||||||
@ -559,11 +558,27 @@ def format_breadcrumbs(route: str) -> list[tuple[str, str]]:
|
|||||||
|
|
||||||
# create and return breadcrumbs
|
# create and return breadcrumbs
|
||||||
return [
|
return [
|
||||||
(part, op.join("/", *route_parts[: i + 1]))
|
(part, "/".join(["", *route_parts[: i + 1]]))
|
||||||
for i, part in enumerate(route_parts)
|
for i, part in enumerate(route_parts)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def format_library_name(library_fullname: str):
|
||||||
|
"""Format the name of a library.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
library_fullname: The fullname of the library.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The name without the @version if it was part of the name
|
||||||
|
"""
|
||||||
|
lib, at, version = library_fullname.rpartition("@")
|
||||||
|
if not lib:
|
||||||
|
lib = at + version
|
||||||
|
|
||||||
|
return lib
|
||||||
|
|
||||||
|
|
||||||
def json_dumps(obj: Any) -> str:
|
def json_dumps(obj: Any) -> str:
|
||||||
"""Takes an object and returns a jsonified string.
|
"""Takes an object and returns a jsonified string.
|
||||||
|
|
||||||
|
@ -118,10 +118,10 @@ def get_node_bin_path() -> str | None:
|
|||||||
Returns:
|
Returns:
|
||||||
The path to the node bin folder.
|
The path to the node bin folder.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(constants.NODE_BIN_PATH):
|
if not os.path.exists(constants.Node.BIN_PATH):
|
||||||
str_path = which("node")
|
str_path = which("node")
|
||||||
return str(Path(str_path).parent) if str_path else str_path
|
return str(Path(str_path).parent) if str_path else str_path
|
||||||
return constants.NODE_BIN_PATH
|
return constants.Node.BIN_PATH
|
||||||
|
|
||||||
|
|
||||||
def get_node_path() -> str | None:
|
def get_node_path() -> str | None:
|
||||||
@ -130,9 +130,9 @@ def get_node_path() -> str | None:
|
|||||||
Returns:
|
Returns:
|
||||||
The path to the node binary file.
|
The path to the node binary file.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(constants.NODE_PATH):
|
if not os.path.exists(constants.Node.PATH):
|
||||||
return which("node")
|
return which("node")
|
||||||
return constants.NODE_PATH
|
return constants.Node.PATH
|
||||||
|
|
||||||
|
|
||||||
def get_npm_path() -> str | None:
|
def get_npm_path() -> str | None:
|
||||||
@ -141,9 +141,9 @@ def get_npm_path() -> str | None:
|
|||||||
Returns:
|
Returns:
|
||||||
The path to the npm binary file.
|
The path to the npm binary file.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(constants.NODE_PATH):
|
if not os.path.exists(constants.Node.PATH):
|
||||||
return which("npm")
|
return which("npm")
|
||||||
return constants.NPM_PATH
|
return constants.Node.NPM_PATH
|
||||||
|
|
||||||
|
|
||||||
def update_json_file(file_path: str, update_dict: dict[str, int | str]):
|
def update_json_file(file_path: str, update_dict: dict[str, int | str]):
|
||||||
|
@ -39,9 +39,9 @@ def check_node_version() -> bool:
|
|||||||
if current_version:
|
if current_version:
|
||||||
# Compare the version numbers
|
# Compare the version numbers
|
||||||
return (
|
return (
|
||||||
current_version >= version.parse(constants.NODE_VERSION_MIN)
|
current_version >= version.parse(constants.Node.MIN_VERSION)
|
||||||
if constants.IS_WINDOWS
|
if constants.IS_WINDOWS
|
||||||
else current_version == version.parse(constants.NODE_VERSION)
|
else current_version == version.parse(constants.Node.VERSION)
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ def get_app(reload: bool = False) -> ModuleType:
|
|||||||
config = get_config()
|
config = get_config()
|
||||||
module = ".".join([config.app_name, config.app_name])
|
module = ".".join([config.app_name, config.app_name])
|
||||||
sys.path.insert(0, os.getcwd())
|
sys.path.insert(0, os.getcwd())
|
||||||
app = __import__(module, fromlist=(constants.APP_VAR,))
|
app = __import__(module, fromlist=(constants.CompileVars.APP,))
|
||||||
if reload:
|
if reload:
|
||||||
importlib.reload(app)
|
importlib.reload(app)
|
||||||
return app
|
return app
|
||||||
@ -160,9 +160,9 @@ def get_default_app_name() -> str:
|
|||||||
app_name = os.getcwd().split(os.path.sep)[-1].replace("-", "_")
|
app_name = os.getcwd().split(os.path.sep)[-1].replace("-", "_")
|
||||||
|
|
||||||
# Make sure the app is not named "reflex".
|
# Make sure the app is not named "reflex".
|
||||||
if app_name == constants.MODULE_NAME:
|
if app_name == constants.Reflex.MODULE_NAME:
|
||||||
console.error(
|
console.error(
|
||||||
f"The app directory cannot be named [bold]{constants.MODULE_NAME}[/bold]."
|
f"The app directory cannot be named [bold]{constants.Reflex.MODULE_NAME}[/bold]."
|
||||||
)
|
)
|
||||||
raise typer.Exit(1)
|
raise typer.Exit(1)
|
||||||
|
|
||||||
@ -179,28 +179,28 @@ def create_config(app_name: str):
|
|||||||
from reflex.compiler import templates
|
from reflex.compiler import templates
|
||||||
|
|
||||||
config_name = f"{re.sub(r'[^a-zA-Z]', '', app_name).capitalize()}Config"
|
config_name = f"{re.sub(r'[^a-zA-Z]', '', app_name).capitalize()}Config"
|
||||||
with open(constants.CONFIG_FILE, "w") as f:
|
with open(constants.Config.FILE, "w") as f:
|
||||||
console.debug(f"Creating {constants.CONFIG_FILE}")
|
console.debug(f"Creating {constants.Config.FILE}")
|
||||||
f.write(templates.RXCONFIG.render(app_name=app_name, config_name=config_name))
|
f.write(templates.RXCONFIG.render(app_name=app_name, config_name=config_name))
|
||||||
|
|
||||||
|
|
||||||
def initialize_gitignore():
|
def initialize_gitignore():
|
||||||
"""Initialize the template .gitignore file."""
|
"""Initialize the template .gitignore file."""
|
||||||
# The files to add to the .gitignore file.
|
# The files to add to the .gitignore file.
|
||||||
files = constants.DEFAULT_GITIGNORE
|
files = constants.GitIgnore.DEFAULTS
|
||||||
|
|
||||||
# Subtract current ignored files.
|
# Subtract current ignored files.
|
||||||
if os.path.exists(constants.GITIGNORE_FILE):
|
if os.path.exists(constants.GitIgnore.FILE):
|
||||||
with open(constants.GITIGNORE_FILE, "r") as f:
|
with open(constants.GitIgnore.FILE, "r") as f:
|
||||||
files |= set([line.strip() for line in f.readlines()])
|
files |= set([line.strip() for line in f.readlines()])
|
||||||
|
|
||||||
# Write files to the .gitignore file.
|
# Write files to the .gitignore file.
|
||||||
with open(constants.GITIGNORE_FILE, "w") as f:
|
with open(constants.GitIgnore.FILE, "w") as f:
|
||||||
console.debug(f"Creating {constants.GITIGNORE_FILE}")
|
console.debug(f"Creating {constants.GitIgnore.FILE}")
|
||||||
f.write(f"{(path_ops.join(sorted(files))).lstrip()}")
|
f.write(f"{(path_ops.join(sorted(files))).lstrip()}")
|
||||||
|
|
||||||
|
|
||||||
def initialize_app_directory(app_name: str, template: constants.Template):
|
def initialize_app_directory(app_name: str, template: constants.Templates.Kind):
|
||||||
"""Initialize the app directory on reflex init.
|
"""Initialize the app directory on reflex init.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -208,26 +208,28 @@ def initialize_app_directory(app_name: str, template: constants.Template):
|
|||||||
template: The template to use.
|
template: The template to use.
|
||||||
"""
|
"""
|
||||||
console.log("Initializing the app directory.")
|
console.log("Initializing the app directory.")
|
||||||
path_ops.cp(os.path.join(constants.TEMPLATE_DIR, "apps", template.value), app_name)
|
path_ops.cp(
|
||||||
|
os.path.join(constants.Templates.Dirs.BASE, "apps", template.value), app_name
|
||||||
|
)
|
||||||
path_ops.mv(
|
path_ops.mv(
|
||||||
os.path.join(app_name, template.value + ".py"),
|
os.path.join(app_name, template.value + ".py"),
|
||||||
os.path.join(app_name, app_name + constants.PY_EXT),
|
os.path.join(app_name, app_name + constants.Ext.PY),
|
||||||
)
|
)
|
||||||
path_ops.cp(constants.ASSETS_TEMPLATE_DIR, constants.APP_ASSETS_DIR)
|
path_ops.cp(constants.Templates.Dirs.ASSETS_TEMPLATE, constants.Dirs.APP_ASSETS)
|
||||||
|
|
||||||
|
|
||||||
def initialize_web_directory():
|
def initialize_web_directory():
|
||||||
"""Initialize the web directory on reflex init."""
|
"""Initialize the web directory on reflex init."""
|
||||||
console.log("Initializing the web directory.")
|
console.log("Initializing the web directory.")
|
||||||
|
|
||||||
path_ops.cp(constants.WEB_TEMPLATE_DIR, constants.WEB_DIR)
|
path_ops.cp(constants.Templates.Dirs.WEB_TEMPLATE, constants.Dirs.WEB)
|
||||||
|
|
||||||
initialize_package_json()
|
initialize_package_json()
|
||||||
|
|
||||||
path_ops.mkdir(constants.WEB_ASSETS_DIR)
|
path_ops.mkdir(constants.Dirs.WEB_ASSETS)
|
||||||
|
|
||||||
# update nextJS config based on rxConfig
|
# update nextJS config based on rxConfig
|
||||||
next_config_file = os.path.join(constants.WEB_DIR, constants.NEXT_CONFIG_FILE)
|
next_config_file = os.path.join(constants.Dirs.WEB, constants.Next.CONFIG_FILE)
|
||||||
|
|
||||||
with open(next_config_file, "r") as file:
|
with open(next_config_file, "r") as file:
|
||||||
next_config = file.read()
|
next_config = file.read()
|
||||||
@ -243,19 +245,19 @@ def initialize_web_directory():
|
|||||||
def _compile_package_json():
|
def _compile_package_json():
|
||||||
return templates.PACKAGE_JSON.render(
|
return templates.PACKAGE_JSON.render(
|
||||||
scripts={
|
scripts={
|
||||||
"dev": constants.PackageJsonCommands.DEV,
|
"dev": constants.PackageJson.Commands.DEV,
|
||||||
"export": constants.PackageJsonCommands.EXPORT,
|
"export": constants.PackageJson.Commands.EXPORT,
|
||||||
"export_sitemap": constants.PackageJsonCommands.EXPORT_SITEMAP,
|
"export_sitemap": constants.PackageJson.Commands.EXPORT_SITEMAP,
|
||||||
"prod": constants.PackageJsonCommands.PROD,
|
"prod": constants.PackageJson.Commands.PROD,
|
||||||
},
|
},
|
||||||
dependencies=constants.PACKAGE_DEPENDENCIES,
|
dependencies=constants.PackageJson.DEPENDENCIES,
|
||||||
dev_dependencies=constants.PACKAGE_DEV_DEPENDENCIES,
|
dev_dependencies=constants.PackageJson.DEV_DEPENDENCIES,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def initialize_package_json():
|
def initialize_package_json():
|
||||||
"""Render and write in .web the package.json file."""
|
"""Render and write in .web the package.json file."""
|
||||||
output_path = constants.PACKAGE_JSON_PATH
|
output_path = constants.PackageJson.PATH
|
||||||
code = _compile_package_json()
|
code = _compile_package_json()
|
||||||
with open(output_path, "w") as file:
|
with open(output_path, "w") as file:
|
||||||
file.write(code)
|
file.write(code)
|
||||||
@ -269,10 +271,10 @@ def init_reflex_json():
|
|||||||
|
|
||||||
# Write the hash and version to the reflex json file.
|
# Write the hash and version to the reflex json file.
|
||||||
reflex_json = {
|
reflex_json = {
|
||||||
"version": constants.VERSION,
|
"version": constants.Reflex.VERSION,
|
||||||
"project_hash": project_hash,
|
"project_hash": project_hash,
|
||||||
}
|
}
|
||||||
path_ops.update_json_file(constants.REFLEX_JSON, reflex_json)
|
path_ops.update_json_file(constants.Reflex.JSON, reflex_json)
|
||||||
|
|
||||||
|
|
||||||
def update_next_config(next_config: str, config: Config) -> str:
|
def update_next_config(next_config: str, config: Config) -> str:
|
||||||
@ -302,7 +304,7 @@ def remove_existing_bun_installation():
|
|||||||
"""Remove existing bun installation."""
|
"""Remove existing bun installation."""
|
||||||
console.debug("Removing existing bun installation.")
|
console.debug("Removing existing bun installation.")
|
||||||
if os.path.exists(get_config().bun_path):
|
if os.path.exists(get_config().bun_path):
|
||||||
path_ops.rm(constants.BUN_ROOT_PATH)
|
path_ops.rm(constants.Bun.ROOT_PATH)
|
||||||
|
|
||||||
|
|
||||||
def download_and_run(url: str, *args, show_status: bool = False, **env):
|
def download_and_run(url: str, *args, show_status: bool = False, **env):
|
||||||
@ -339,9 +341,9 @@ def download_and_extract_fnm_zip():
|
|||||||
Exit: If an error occurs while downloading or extracting the FNM zip.
|
Exit: If an error occurs while downloading or extracting the FNM zip.
|
||||||
"""
|
"""
|
||||||
# Download the zip file
|
# Download the zip file
|
||||||
url = constants.FNM_INSTALL_URL
|
url = constants.Fnm.INSTALL_URL
|
||||||
console.debug(f"Downloading {url}")
|
console.debug(f"Downloading {url}")
|
||||||
fnm_zip_file = os.path.join(constants.FNM_DIR, f"{constants.FNM_FILENAME}.zip")
|
fnm_zip_file = os.path.join(constants.Fnm.DIR, f"{constants.Fnm.FILENAME}.zip")
|
||||||
# Function to download and extract the FNM zip release.
|
# Function to download and extract the FNM zip release.
|
||||||
try:
|
try:
|
||||||
# Download the FNM zip release.
|
# Download the FNM zip release.
|
||||||
@ -354,7 +356,7 @@ def download_and_extract_fnm_zip():
|
|||||||
|
|
||||||
# Extract the downloaded zip file.
|
# Extract the downloaded zip file.
|
||||||
with zipfile.ZipFile(fnm_zip_file, "r") as zip_ref:
|
with zipfile.ZipFile(fnm_zip_file, "r") as zip_ref:
|
||||||
zip_ref.extractall(constants.FNM_DIR)
|
zip_ref.extractall(constants.Fnm.DIR)
|
||||||
|
|
||||||
console.debug("FNM package downloaded and extracted successfully.")
|
console.debug("FNM package downloaded and extracted successfully.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -369,13 +371,13 @@ def install_node():
|
|||||||
"""Install fnm and nodejs for use by Reflex.
|
"""Install fnm and nodejs for use by Reflex.
|
||||||
Independent of any existing system installations.
|
Independent of any existing system installations.
|
||||||
"""
|
"""
|
||||||
if not constants.FNM_FILENAME:
|
if not constants.Fnm.FILENAME:
|
||||||
# fnm only support Linux, macOS and Windows distros.
|
# fnm only support Linux, macOS and Windows distros.
|
||||||
console.debug("")
|
console.debug("")
|
||||||
return
|
return
|
||||||
|
|
||||||
path_ops.mkdir(constants.FNM_DIR)
|
path_ops.mkdir(constants.Fnm.DIR)
|
||||||
if not os.path.exists(constants.FNM_EXE):
|
if not os.path.exists(constants.Fnm.EXE):
|
||||||
download_and_extract_fnm_zip()
|
download_and_extract_fnm_zip()
|
||||||
|
|
||||||
if constants.IS_WINDOWS:
|
if constants.IS_WINDOWS:
|
||||||
@ -384,13 +386,13 @@ def install_node():
|
|||||||
[
|
[
|
||||||
"powershell",
|
"powershell",
|
||||||
"-Command",
|
"-Command",
|
||||||
f'& "{constants.FNM_EXE}" install {constants.NODE_VERSION} --fnm-dir "{constants.FNM_DIR}"',
|
f'& "{constants.Fnm.EXE}" install {constants.Node.VERSION} --fnm-dir "{constants.Fnm.DIR}"',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
else: # All other platforms (Linux, MacOS).
|
else: # All other platforms (Linux, MacOS).
|
||||||
# TODO we can skip installation if check_node_version() checks out
|
# TODO we can skip installation if check_node_version() checks out
|
||||||
# Add execute permissions to fnm executable.
|
# Add execute permissions to fnm executable.
|
||||||
os.chmod(constants.FNM_EXE, stat.S_IXUSR)
|
os.chmod(constants.Fnm.EXE, stat.S_IXUSR)
|
||||||
# Install node.
|
# Install node.
|
||||||
# Specify arm64 arch explicitly for M1s and M2s.
|
# Specify arm64 arch explicitly for M1s and M2s.
|
||||||
architecture_arg = (
|
architecture_arg = (
|
||||||
@ -401,12 +403,12 @@ def install_node():
|
|||||||
|
|
||||||
process = processes.new_process(
|
process = processes.new_process(
|
||||||
[
|
[
|
||||||
constants.FNM_EXE,
|
constants.Fnm.EXE,
|
||||||
"install",
|
"install",
|
||||||
*architecture_arg,
|
*architecture_arg,
|
||||||
constants.NODE_VERSION,
|
constants.Node.VERSION,
|
||||||
"--fnm-dir",
|
"--fnm-dir",
|
||||||
constants.FNM_DIR,
|
constants.Fnm.DIR,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
processes.show_status("Installing node", process)
|
processes.show_status("Installing node", process)
|
||||||
@ -435,9 +437,9 @@ def install_bun():
|
|||||||
|
|
||||||
# Run the bun install script.
|
# Run the bun install script.
|
||||||
download_and_run(
|
download_and_run(
|
||||||
constants.BUN_INSTALL_URL,
|
constants.Bun.INSTALL_URL,
|
||||||
f"bun-v{constants.BUN_VERSION}",
|
f"bun-v{constants.Bun.VERSION}",
|
||||||
BUN_INSTALL=constants.BUN_ROOT_PATH,
|
BUN_INSTALL=constants.Bun.ROOT_PATH,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -453,7 +455,7 @@ def install_frontend_packages(packages: set[str]):
|
|||||||
# Install the base packages.
|
# Install the base packages.
|
||||||
process = processes.new_process(
|
process = processes.new_process(
|
||||||
[get_install_package_manager(), "install", "--loglevel", "silly"],
|
[get_install_package_manager(), "install", "--loglevel", "silly"],
|
||||||
cwd=constants.WEB_DIR,
|
cwd=constants.Dirs.WEB,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -467,10 +469,10 @@ def install_frontend_packages(packages: set[str]):
|
|||||||
get_install_package_manager(),
|
get_install_package_manager(),
|
||||||
"add",
|
"add",
|
||||||
"-d",
|
"-d",
|
||||||
constants.TAILWIND_VERSION,
|
constants.Tailwind.VERSION,
|
||||||
*((config.tailwind or {}).get("plugins", [])),
|
*((config.tailwind or {}).get("plugins", [])),
|
||||||
],
|
],
|
||||||
cwd=constants.WEB_DIR,
|
cwd=constants.Dirs.WEB,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
processes.show_status("Installing tailwind", process)
|
processes.show_status("Installing tailwind", process)
|
||||||
@ -479,7 +481,7 @@ def install_frontend_packages(packages: set[str]):
|
|||||||
if len(packages) > 0:
|
if len(packages) > 0:
|
||||||
process = processes.new_process(
|
process = processes.new_process(
|
||||||
[get_install_package_manager(), "add", *packages],
|
[get_install_package_manager(), "add", *packages],
|
||||||
cwd=constants.WEB_DIR,
|
cwd=constants.Dirs.WEB,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
processes.show_status(
|
processes.show_status(
|
||||||
@ -496,14 +498,14 @@ def check_initialized(frontend: bool = True):
|
|||||||
Raises:
|
Raises:
|
||||||
Exit: If the app is not initialized.
|
Exit: If the app is not initialized.
|
||||||
"""
|
"""
|
||||||
has_config = os.path.exists(constants.CONFIG_FILE)
|
has_config = os.path.exists(constants.Config.FILE)
|
||||||
has_reflex_dir = not frontend or os.path.exists(constants.REFLEX_DIR)
|
has_reflex_dir = not frontend or os.path.exists(constants.Reflex.DIR)
|
||||||
has_web_dir = not frontend or os.path.exists(constants.WEB_DIR)
|
has_web_dir = not frontend or os.path.exists(constants.Dirs.WEB)
|
||||||
|
|
||||||
# Check if the app is initialized.
|
# Check if the app is initialized.
|
||||||
if not (has_config and has_reflex_dir and has_web_dir):
|
if not (has_config and has_reflex_dir and has_web_dir):
|
||||||
console.error(
|
console.error(
|
||||||
f"The app is not initialized. Run [bold]{constants.MODULE_NAME} init[/bold] first."
|
f"The app is not initialized. Run [bold]{constants.Reflex.MODULE_NAME} init[/bold] first."
|
||||||
)
|
)
|
||||||
raise typer.Exit(1)
|
raise typer.Exit(1)
|
||||||
|
|
||||||
@ -527,11 +529,11 @@ def is_latest_template() -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
Whether the app is using the latest template.
|
Whether the app is using the latest template.
|
||||||
"""
|
"""
|
||||||
if not os.path.exists(constants.REFLEX_JSON):
|
if not os.path.exists(constants.Reflex.JSON):
|
||||||
return False
|
return False
|
||||||
with open(constants.REFLEX_JSON) as f: # type: ignore
|
with open(constants.Reflex.JSON) as f: # type: ignore
|
||||||
app_version = json.load(f)["version"]
|
app_version = json.load(f)["version"]
|
||||||
return app_version == constants.VERSION
|
return app_version == constants.Reflex.VERSION
|
||||||
|
|
||||||
|
|
||||||
def validate_bun():
|
def validate_bun():
|
||||||
@ -543,16 +545,16 @@ def validate_bun():
|
|||||||
# if a custom bun path is provided, make sure its valid
|
# if a custom bun path is provided, make sure its valid
|
||||||
# This is specific to non-FHS OS
|
# This is specific to non-FHS OS
|
||||||
bun_path = get_config().bun_path
|
bun_path = get_config().bun_path
|
||||||
if bun_path != constants.DEFAULT_BUN_PATH:
|
if bun_path != constants.Bun.DEFAULT_PATH:
|
||||||
bun_version = get_bun_version()
|
bun_version = get_bun_version()
|
||||||
if not bun_version:
|
if not bun_version:
|
||||||
console.error(
|
console.error(
|
||||||
"Failed to obtain bun version. Make sure the specified bun path in your config is correct."
|
"Failed to obtain bun version. Make sure the specified bun path in your config is correct."
|
||||||
)
|
)
|
||||||
raise typer.Exit(1)
|
raise typer.Exit(1)
|
||||||
elif bun_version < version.parse(constants.MIN_BUN_VERSION):
|
elif bun_version < version.parse(constants.Bun.MIN_VERSION):
|
||||||
console.error(
|
console.error(
|
||||||
f"Reflex requires bun version {constants.BUN_VERSION} or higher to run, but the detected version is "
|
f"Reflex requires bun version {constants.Bun.VERSION} or higher to run, but the detected version is "
|
||||||
f"{bun_version}. If you have specified a custom bun path in your config, make sure to provide one "
|
f"{bun_version}. If you have specified a custom bun path in your config, make sure to provide one "
|
||||||
f"that satisfies the minimum version requirement."
|
f"that satisfies the minimum version requirement."
|
||||||
)
|
)
|
||||||
@ -582,7 +584,7 @@ def validate_frontend_dependencies(init=True):
|
|||||||
if not check_node_version():
|
if not check_node_version():
|
||||||
node_version = get_node_version()
|
node_version = get_node_version()
|
||||||
console.error(
|
console.error(
|
||||||
f"Reflex requires node version {constants.NODE_VERSION_MIN} or higher to run, but the detected version is {node_version}",
|
f"Reflex requires node version {constants.Node.MIN_VERSION} or higher to run, but the detected version is {node_version}",
|
||||||
)
|
)
|
||||||
raise typer.Exit(1)
|
raise typer.Exit(1)
|
||||||
|
|
||||||
@ -597,7 +599,7 @@ def validate_frontend_dependencies(init=True):
|
|||||||
def initialize_frontend_dependencies():
|
def initialize_frontend_dependencies():
|
||||||
"""Initialize all the frontend dependencies."""
|
"""Initialize all the frontend dependencies."""
|
||||||
# Create the reflex directory.
|
# Create the reflex directory.
|
||||||
path_ops.mkdir(constants.REFLEX_DIR)
|
path_ops.mkdir(constants.Reflex.DIR)
|
||||||
# validate dependencies before install
|
# validate dependencies before install
|
||||||
validate_frontend_dependencies()
|
validate_frontend_dependencies()
|
||||||
# Install the frontend dependencies.
|
# Install the frontend dependencies.
|
||||||
@ -644,7 +646,7 @@ def check_schema_up_to_date():
|
|||||||
def migrate_to_reflex():
|
def migrate_to_reflex():
|
||||||
"""Migration from Pynecone to Reflex."""
|
"""Migration from Pynecone to Reflex."""
|
||||||
# Check if the old config file exists.
|
# Check if the old config file exists.
|
||||||
if not os.path.exists(constants.OLD_CONFIG_FILE):
|
if not os.path.exists(constants.Config.PREVIOUS_FILE):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ask the user if they want to migrate.
|
# Ask the user if they want to migrate.
|
||||||
@ -657,16 +659,16 @@ def migrate_to_reflex():
|
|||||||
|
|
||||||
# Rename pcconfig to rxconfig.
|
# Rename pcconfig to rxconfig.
|
||||||
console.log(
|
console.log(
|
||||||
f"[bold]Renaming {constants.OLD_CONFIG_FILE} to {constants.CONFIG_FILE}"
|
f"[bold]Renaming {constants.Config.PREVIOUS_FILE} to {constants.Config.FILE}"
|
||||||
)
|
)
|
||||||
os.rename(constants.OLD_CONFIG_FILE, constants.CONFIG_FILE)
|
os.rename(constants.Config.PREVIOUS_FILE, constants.Config.FILE)
|
||||||
|
|
||||||
# Find all python files in the app directory.
|
# Find all python files in the app directory.
|
||||||
file_pattern = os.path.join(get_config().app_name, "**/*.py")
|
file_pattern = os.path.join(get_config().app_name, "**/*.py")
|
||||||
file_list = glob.glob(file_pattern, recursive=True)
|
file_list = glob.glob(file_pattern, recursive=True)
|
||||||
|
|
||||||
# Add the config file to the list of files to be migrated.
|
# Add the config file to the list of files to be migrated.
|
||||||
file_list.append(constants.CONFIG_FILE)
|
file_list.append(constants.Config.FILE)
|
||||||
|
|
||||||
# Migrate all files.
|
# Migrate all files.
|
||||||
updates = {
|
updates = {
|
||||||
|
@ -39,7 +39,7 @@ def get_reflex_version() -> str:
|
|||||||
Returns:
|
Returns:
|
||||||
The Reflex version.
|
The Reflex version.
|
||||||
"""
|
"""
|
||||||
return constants.VERSION
|
return constants.Reflex.VERSION
|
||||||
|
|
||||||
|
|
||||||
def get_cpu_count() -> int:
|
def get_cpu_count() -> int:
|
||||||
|
@ -4,7 +4,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import typing
|
import typing
|
||||||
from types import LambdaType
|
|
||||||
from typing import Any, Callable, Type, Union, _GenericAlias # type: ignore
|
from typing import Any, Callable, Type, Union, _GenericAlias # type: ignore
|
||||||
|
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
@ -18,7 +17,8 @@ PrimitiveType = Union[int, float, bool, str, list, dict, set, tuple]
|
|||||||
StateVar = Union[PrimitiveType, Base, None]
|
StateVar = Union[PrimitiveType, Base, None]
|
||||||
StateIterVar = Union[list, set, tuple]
|
StateIterVar = Union[list, set, tuple]
|
||||||
|
|
||||||
ArgsSpec = LambdaType
|
# ArgsSpec = Callable[[Var], list[Var]]
|
||||||
|
ArgsSpec = Callable
|
||||||
|
|
||||||
|
|
||||||
def get_args(alias: _GenericAlias) -> tuple[Type, ...]:
|
def get_args(alias: _GenericAlias) -> tuple[Type, ...]:
|
||||||
|
@ -8,7 +8,7 @@ import time
|
|||||||
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
|
|
||||||
from reflex.constants import APP_ASSETS_DIR, WEB_ASSETS_DIR
|
from reflex.constants import Dirs
|
||||||
|
|
||||||
|
|
||||||
class AssetFolderWatch:
|
class AssetFolderWatch:
|
||||||
@ -20,7 +20,7 @@ class AssetFolderWatch:
|
|||||||
Args:
|
Args:
|
||||||
root: root path of the public.
|
root: root path of the public.
|
||||||
"""
|
"""
|
||||||
self.path = str(root / APP_ASSETS_DIR)
|
self.path = str(root / Dirs.APP_ASSETS)
|
||||||
self.event_handler = AssetFolderHandler(root)
|
self.event_handler = AssetFolderHandler(root)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
@ -90,5 +90,5 @@ class AssetFolderHandler(FileSystemEventHandler):
|
|||||||
The public file path.
|
The public file path.
|
||||||
"""
|
"""
|
||||||
return src_path.replace(
|
return src_path.replace(
|
||||||
str(self.root / APP_ASSETS_DIR), str(self.root / WEB_ASSETS_DIR)
|
str(self.root / Dirs.APP_ASSETS), str(self.root / Dirs.WEB_ASSETS)
|
||||||
)
|
)
|
||||||
|
@ -27,8 +27,7 @@ from pydantic.fields import ModelField
|
|||||||
|
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.utils import console, format, types
|
from reflex.utils import console, format, serializers, types
|
||||||
from reflex.utils.serializers import serialize
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from reflex.state import State
|
from reflex.state import State
|
||||||
@ -124,7 +123,7 @@ class Var(ABC):
|
|||||||
|
|
||||||
# Try to serialize the value.
|
# Try to serialize the value.
|
||||||
type_ = type(value)
|
type_ = type(value)
|
||||||
name = serialize(value)
|
name = serializers.serialize(value)
|
||||||
if name is None:
|
if name is None:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"No JSON serializer found for var {value} of type {type_}."
|
f"No JSON serializer found for var {value} of type {type_}."
|
||||||
|
@ -3,7 +3,7 @@ from typing import Any, Dict
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from reflex.app import App
|
from reflex.app import App
|
||||||
from reflex.constants import IS_HYDRATED
|
from reflex.constants import CompileVars
|
||||||
from reflex.middleware.hydrate_middleware import HydrateMiddleware
|
from reflex.middleware.hydrate_middleware import HydrateMiddleware
|
||||||
from reflex.state import State, StateUpdate
|
from reflex.state import State, StateUpdate
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ def exp_is_hydrated(state: State) -> Dict[str, Any]:
|
|||||||
Returns:
|
Returns:
|
||||||
dict similar to that returned by `State.get_delta` with IS_HYDRATED: True
|
dict similar to that returned by `State.get_delta` with IS_HYDRATED: True
|
||||||
"""
|
"""
|
||||||
return {state.get_name(): {IS_HYDRATED: True}}
|
return {state.get_name(): {CompileVars.IS_HYDRATED: True}}
|
||||||
|
|
||||||
|
|
||||||
class TestState(State):
|
class TestState(State):
|
||||||
|
@ -923,7 +923,7 @@ async def test_dynamic_route_var_route_change_completed_on_load(
|
|||||||
state.get_name(): {
|
state.get_name(): {
|
||||||
arg_name: exp_val,
|
arg_name: exp_val,
|
||||||
f"comp_{arg_name}": exp_val,
|
f"comp_{arg_name}": exp_val,
|
||||||
constants.IS_HYDRATED: False,
|
constants.CompileVars.IS_HYDRATED: False,
|
||||||
"loaded": exp_index,
|
"loaded": exp_index,
|
||||||
"counter": exp_index,
|
"counter": exp_index,
|
||||||
# "side_effect_counter": exp_index,
|
# "side_effect_counter": exp_index,
|
||||||
|
@ -15,7 +15,7 @@ from plotly.graph_objects import Figure
|
|||||||
|
|
||||||
import reflex as rx
|
import reflex as rx
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.constants import APP_VAR, IS_HYDRATED, RouteVar, SocketEvent
|
from reflex.constants import CompileVars, RouteVar, SocketEvent
|
||||||
from reflex.event import Event, EventHandler
|
from reflex.event import Event, EventHandler
|
||||||
from reflex.state import (
|
from reflex.state import (
|
||||||
ImmutableStateError,
|
ImmutableStateError,
|
||||||
@ -28,7 +28,7 @@ from reflex.state import (
|
|||||||
StateProxy,
|
StateProxy,
|
||||||
StateUpdate,
|
StateUpdate,
|
||||||
)
|
)
|
||||||
from reflex.utils import format, prerequisites
|
from reflex.utils import prerequisites
|
||||||
from reflex.vars import BaseVar, ComputedVar
|
from reflex.vars import BaseVar, ComputedVar
|
||||||
|
|
||||||
from .states import GenState
|
from .states import GenState
|
||||||
@ -118,6 +118,15 @@ class GrandchildState(ChildState):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DateTimeState(State):
|
||||||
|
"""A State with some datetime fields."""
|
||||||
|
|
||||||
|
d: datetime.date = datetime.date.fromisoformat("1989-11-09")
|
||||||
|
dt: datetime.datetime = datetime.datetime.fromisoformat("1989-11-09T18:53:00+01:00")
|
||||||
|
t: datetime.time = datetime.time.fromisoformat("18:53:00+01:00")
|
||||||
|
td: datetime.timedelta = datetime.timedelta(days=11, minutes=11)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def test_state() -> TestState:
|
def test_state() -> TestState:
|
||||||
"""A state.
|
"""A state.
|
||||||
@ -214,7 +223,7 @@ def test_class_vars(test_state):
|
|||||||
"""
|
"""
|
||||||
cls = type(test_state)
|
cls = type(test_state)
|
||||||
assert set(cls.vars.keys()) == {
|
assert set(cls.vars.keys()) == {
|
||||||
IS_HYDRATED, # added by hydrate_middleware to all State
|
CompileVars.IS_HYDRATED, # added by hydrate_middleware to all State
|
||||||
"num1",
|
"num1",
|
||||||
"num2",
|
"num2",
|
||||||
"key",
|
"key",
|
||||||
@ -292,58 +301,6 @@ def test_dict(test_state):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_format_state(test_state):
|
|
||||||
"""Test that the format state is correct.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
test_state: A state.
|
|
||||||
"""
|
|
||||||
formatted_state = format.format_state(test_state.dict())
|
|
||||||
exp_formatted_state = {
|
|
||||||
"array": [1, 2, 3.14],
|
|
||||||
"child_state": {"count": 23, "grandchild_state": {"value2": ""}, "value": ""},
|
|
||||||
"child_state2": {"value": ""},
|
|
||||||
"complex": {
|
|
||||||
1: {"prop1": 42, "prop2": "hello"},
|
|
||||||
2: {"prop1": 42, "prop2": "hello"},
|
|
||||||
},
|
|
||||||
"dt": "1989-11-09 18:53:00+01:00",
|
|
||||||
"fig": [],
|
|
||||||
"is_hydrated": False,
|
|
||||||
"key": "",
|
|
||||||
"map_key": "a",
|
|
||||||
"mapping": {"a": [1, 2, 3], "b": [4, 5, 6]},
|
|
||||||
"num1": 0,
|
|
||||||
"num2": 3.14,
|
|
||||||
"obj": {"prop1": 42, "prop2": "hello"},
|
|
||||||
"sum": 3.14,
|
|
||||||
"upper": "",
|
|
||||||
}
|
|
||||||
assert formatted_state == exp_formatted_state
|
|
||||||
|
|
||||||
|
|
||||||
def test_format_state_datetime():
|
|
||||||
"""Test that the format state is correct for datetime classes."""
|
|
||||||
|
|
||||||
class DateTimeState(State):
|
|
||||||
d: datetime.date = datetime.date.fromisoformat("1989-11-09")
|
|
||||||
dt: datetime.datetime = datetime.datetime.fromisoformat(
|
|
||||||
"1989-11-09T18:53:00+01:00"
|
|
||||||
)
|
|
||||||
t: datetime.time = datetime.time.fromisoformat("18:53:00+01:00")
|
|
||||||
td: datetime.timedelta = datetime.timedelta(days=11, minutes=11)
|
|
||||||
|
|
||||||
formatted_state = format.format_state(DateTimeState().dict())
|
|
||||||
exp_formatted_state = {
|
|
||||||
"d": "1989-11-09",
|
|
||||||
"dt": "1989-11-09 18:53:00+01:00",
|
|
||||||
"is_hydrated": False,
|
|
||||||
"t": "18:53:00+01:00",
|
|
||||||
"td": "11 days, 0:11:00",
|
|
||||||
}
|
|
||||||
assert formatted_state == exp_formatted_state
|
|
||||||
|
|
||||||
|
|
||||||
def test_default_setters(test_state):
|
def test_default_setters(test_state):
|
||||||
"""Test that we can set default values.
|
"""Test that we can set default values.
|
||||||
|
|
||||||
@ -750,21 +707,6 @@ async def test_process_event_generator():
|
|||||||
assert count == 6
|
assert count == 6
|
||||||
|
|
||||||
|
|
||||||
def test_format_event_handler():
|
|
||||||
"""Test formatting an event handler."""
|
|
||||||
assert (
|
|
||||||
format.format_event_handler(TestState.do_something) == "test_state.do_something" # type: ignore
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
format.format_event_handler(ChildState.change_both) # type: ignore
|
|
||||||
== "test_state.child_state.change_both"
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
format.format_event_handler(GrandchildState.do_nothing) # type: ignore
|
|
||||||
== "test_state.child_state.grandchild_state.do_nothing"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_token(test_state, mocker, router_data):
|
def test_get_token(test_state, mocker, router_data):
|
||||||
"""Test that the token obtained from the router_data is correct.
|
"""Test that the token obtained from the router_data is correct.
|
||||||
|
|
||||||
@ -1184,17 +1126,17 @@ def test_computed_var_depends_on_parent_non_cached():
|
|||||||
assert ps.dict() == {
|
assert ps.dict() == {
|
||||||
cs.get_name(): {"dep_v": 2},
|
cs.get_name(): {"dep_v": 2},
|
||||||
"no_cache_v": 1,
|
"no_cache_v": 1,
|
||||||
IS_HYDRATED: False,
|
CompileVars.IS_HYDRATED: False,
|
||||||
}
|
}
|
||||||
assert ps.dict() == {
|
assert ps.dict() == {
|
||||||
cs.get_name(): {"dep_v": 4},
|
cs.get_name(): {"dep_v": 4},
|
||||||
"no_cache_v": 3,
|
"no_cache_v": 3,
|
||||||
IS_HYDRATED: False,
|
CompileVars.IS_HYDRATED: False,
|
||||||
}
|
}
|
||||||
assert ps.dict() == {
|
assert ps.dict() == {
|
||||||
cs.get_name(): {"dep_v": 6},
|
cs.get_name(): {"dep_v": 6},
|
||||||
"no_cache_v": 5,
|
"no_cache_v": 5,
|
||||||
IS_HYDRATED: False,
|
CompileVars.IS_HYDRATED: False,
|
||||||
}
|
}
|
||||||
assert counter == 6
|
assert counter == 6
|
||||||
|
|
||||||
@ -1595,7 +1537,7 @@ def mock_app(monkeypatch, app: rx.App, state_manager: StateManager) -> rx.App:
|
|||||||
The app, after mocking out prerequisites.get_app()
|
The app, after mocking out prerequisites.get_app()
|
||||||
"""
|
"""
|
||||||
app_module = Mock()
|
app_module = Mock()
|
||||||
setattr(app_module, APP_VAR, app)
|
setattr(app_module, CompileVars.APP, app)
|
||||||
app.state = TestState
|
app.state = TestState
|
||||||
app.state_manager = state_manager
|
app.state_manager = state_manager
|
||||||
assert app.event_namespace is not None
|
assert app.event_namespace is not None
|
||||||
|
578
tests/utils/test_format.py
Normal file
578
tests/utils/test_format.py
Normal file
@ -0,0 +1,578 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from reflex.components.tags.tag import Tag
|
||||||
|
from reflex.event import EVENT_ARG, EventChain, EventHandler, EventSpec
|
||||||
|
from reflex.style import Style
|
||||||
|
from reflex.utils import format
|
||||||
|
from reflex.vars import BaseVar, Var
|
||||||
|
from tests.test_state import ChildState, DateTimeState, GrandchildState, TestState
|
||||||
|
|
||||||
|
|
||||||
|
def mock_event(arg):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
("{", "}"),
|
||||||
|
("(", ")"),
|
||||||
|
("[", "]"),
|
||||||
|
("<", ">"),
|
||||||
|
('"', '"'),
|
||||||
|
("'", "'"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_get_close_char(input: str, output: str):
|
||||||
|
"""Test getting the close character for a given open character.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input: The open character.
|
||||||
|
output: The expected close character.
|
||||||
|
"""
|
||||||
|
assert format.get_close_char(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"text,open,expected",
|
||||||
|
[
|
||||||
|
("", "{", False),
|
||||||
|
("{wrap}", "{", True),
|
||||||
|
("{wrap", "{", False),
|
||||||
|
("{wrap}", "(", False),
|
||||||
|
("(wrap)", "(", True),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_is_wrapped(text: str, open: str, expected: bool):
|
||||||
|
"""Test checking if a string is wrapped in the given open and close characters.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: The text to check.
|
||||||
|
open: The open character.
|
||||||
|
expected: Whether the text is wrapped.
|
||||||
|
"""
|
||||||
|
assert format.is_wrapped(text, open) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"text,open,check_first,num,expected",
|
||||||
|
[
|
||||||
|
("", "{", True, 1, "{}"),
|
||||||
|
("wrap", "{", True, 1, "{wrap}"),
|
||||||
|
("wrap", "(", True, 1, "(wrap)"),
|
||||||
|
("wrap", "(", True, 2, "((wrap))"),
|
||||||
|
("(wrap)", "(", True, 1, "(wrap)"),
|
||||||
|
("{wrap}", "{", True, 2, "{wrap}"),
|
||||||
|
("(wrap)", "{", True, 1, "{(wrap)}"),
|
||||||
|
("(wrap)", "(", False, 1, "((wrap))"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_wrap(text: str, open: str, expected: str, check_first: bool, num: int):
|
||||||
|
"""Test wrapping a string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: The text to wrap.
|
||||||
|
open: The open character.
|
||||||
|
expected: The expected output string.
|
||||||
|
check_first: Whether to check if the text is already wrapped.
|
||||||
|
num: The number of times to wrap the text.
|
||||||
|
"""
|
||||||
|
assert format.wrap(text, open, check_first=check_first, num=num) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"text,indent_level,expected",
|
||||||
|
[
|
||||||
|
("", 2, ""),
|
||||||
|
("hello", 2, "hello"),
|
||||||
|
("hello\nworld", 2, " hello\n world\n"),
|
||||||
|
("hello\nworld", 4, " hello\n world\n"),
|
||||||
|
(" hello\n world", 2, " hello\n world\n"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_indent(text: str, indent_level: int, expected: str, windows_platform: bool):
|
||||||
|
"""Test indenting a string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: The text to indent.
|
||||||
|
indent_level: The number of spaces to indent by.
|
||||||
|
expected: The expected output string.
|
||||||
|
windows_platform: Whether the system is windows.
|
||||||
|
"""
|
||||||
|
assert format.indent(text, indent_level) == (
|
||||||
|
expected.replace("\n", "\r\n") if windows_platform else expected
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
("", ""),
|
||||||
|
("hello", "hello"),
|
||||||
|
("Hello", "hello"),
|
||||||
|
("camelCase", "camel_case"),
|
||||||
|
("camelTwoHumps", "camel_two_humps"),
|
||||||
|
("_start_with_underscore", "_start_with_underscore"),
|
||||||
|
("__start_with_double_underscore", "__start_with_double_underscore"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_to_snake_case(input: str, output: str):
|
||||||
|
"""Test converting strings to snake case.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input: The input string.
|
||||||
|
output: The expected output string.
|
||||||
|
"""
|
||||||
|
assert format.to_snake_case(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
("", ""),
|
||||||
|
("hello", "hello"),
|
||||||
|
("Hello", "Hello"),
|
||||||
|
("snake_case", "snakeCase"),
|
||||||
|
("snake_case_two", "snakeCaseTwo"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_to_camel_case(input: str, output: str):
|
||||||
|
"""Test converting strings to camel case.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input: The input string.
|
||||||
|
output: The expected output string.
|
||||||
|
"""
|
||||||
|
assert format.to_camel_case(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
("", ""),
|
||||||
|
("hello", "Hello"),
|
||||||
|
("Hello", "Hello"),
|
||||||
|
("snake_case", "SnakeCase"),
|
||||||
|
("snake_case_two", "SnakeCaseTwo"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_to_title_case(input: str, output: str):
|
||||||
|
"""Test converting strings to title case.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input: The input string.
|
||||||
|
output: The expected output string.
|
||||||
|
"""
|
||||||
|
assert format.to_title_case(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
("", ""),
|
||||||
|
("hello", "hello"),
|
||||||
|
("Hello", "hello"),
|
||||||
|
("snake_case", "snake-case"),
|
||||||
|
("snake_case_two", "snake-case-two"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_to_kebab_case(input: str, output: str):
|
||||||
|
"""Test converting strings to kebab case.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input: the input string.
|
||||||
|
output: the output string.
|
||||||
|
"""
|
||||||
|
assert format.to_kebab_case(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
("", "{``}"),
|
||||||
|
("hello", "{`hello`}"),
|
||||||
|
("hello world", "{`hello world`}"),
|
||||||
|
("hello=`world`", "{`hello=\\`world\\``}"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_string(input: str, output: str):
|
||||||
|
"""Test formating the input as JS string literal.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input: the input string.
|
||||||
|
output: the output string.
|
||||||
|
"""
|
||||||
|
assert format.format_string(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
(Var.create(value="test"), "{`test`}"),
|
||||||
|
(Var.create(value="test", is_local=True), "{`test`}"),
|
||||||
|
(Var.create(value="test", is_local=False), "{test}"),
|
||||||
|
(Var.create(value="test", is_string=True), "{`test`}"),
|
||||||
|
(Var.create(value="test", is_string=False), "{`test`}"),
|
||||||
|
(Var.create(value="test", is_local=False, is_string=False), "{test}"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_var(input: Var, output: str):
|
||||||
|
assert format.format_var(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"route,format_case,expected",
|
||||||
|
[
|
||||||
|
("", True, "index"),
|
||||||
|
("/", True, "index"),
|
||||||
|
("custom-route", True, "custom-route"),
|
||||||
|
("custom-route", False, "custom-route"),
|
||||||
|
("custom-route/", True, "custom-route"),
|
||||||
|
("custom-route/", False, "custom-route"),
|
||||||
|
("/custom-route", True, "custom-route"),
|
||||||
|
("/custom-route", False, "custom-route"),
|
||||||
|
("/custom_route", True, "custom-route"),
|
||||||
|
("/custom_route", False, "custom_route"),
|
||||||
|
("/CUSTOM_route", True, "custom-route"),
|
||||||
|
("/CUSTOM_route", False, "CUSTOM_route"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_route(route: str, format_case: bool, expected: bool):
|
||||||
|
"""Test formatting a route.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
route: The route to format.
|
||||||
|
format_case: Whether to change casing to snake_case.
|
||||||
|
expected: The expected formatted route.
|
||||||
|
"""
|
||||||
|
assert format.format_route(route, format_case=format_case) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"condition,true_value,false_value,expected",
|
||||||
|
[
|
||||||
|
("cond", "<C1>", '""', '{isTrue(cond) ? <C1> : ""}'),
|
||||||
|
("cond", "<C1>", "<C2>", "{isTrue(cond) ? <C1> : <C2>}"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_cond(condition: str, true_value: str, false_value: str, expected: str):
|
||||||
|
"""Test formatting a cond.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
condition: The condition to check.
|
||||||
|
true_value: The value to return if the condition is true.
|
||||||
|
false_value: The value to return if the condition is false.
|
||||||
|
expected: The expected output string.
|
||||||
|
"""
|
||||||
|
assert format.format_cond(condition, true_value, false_value) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"prop,formatted",
|
||||||
|
[
|
||||||
|
("string", '"string"'),
|
||||||
|
("{wrapped_string}", "{wrapped_string}"),
|
||||||
|
(True, "{true}"),
|
||||||
|
(False, "{false}"),
|
||||||
|
(123, "{123}"),
|
||||||
|
(3.14, "{3.14}"),
|
||||||
|
([1, 2, 3], "{[1, 2, 3]}"),
|
||||||
|
(["a", "b", "c"], '{["a", "b", "c"]}'),
|
||||||
|
({"a": 1, "b": 2, "c": 3}, '{{"a": 1, "b": 2, "c": 3}}'),
|
||||||
|
({"a": 'foo "bar" baz'}, r'{{"a": "foo \"bar\" baz"}}'),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"a": 'foo "{ "bar" }" baz',
|
||||||
|
"b": BaseVar(name="val", type_="str"),
|
||||||
|
},
|
||||||
|
r'{{"a": "foo \"{ \"bar\" }\" baz", "b": val}}',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
EventChain(
|
||||||
|
events=[EventSpec(handler=EventHandler(fn=mock_event))], args_spec=None
|
||||||
|
),
|
||||||
|
'{_e => addEvents([Event("mock_event", {})], _e)}',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
EventChain(
|
||||||
|
events=[
|
||||||
|
EventSpec(
|
||||||
|
handler=EventHandler(fn=mock_event),
|
||||||
|
args=((Var.create_safe("arg"), EVENT_ARG.target.value),),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
args_spec=None,
|
||||||
|
),
|
||||||
|
'{_e => addEvents([Event("mock_event", {arg:_e.target.value})], _e)}',
|
||||||
|
),
|
||||||
|
({"a": "red", "b": "blue"}, '{{"a": "red", "b": "blue"}}'),
|
||||||
|
(BaseVar(name="var", type_="int"), "{var}"),
|
||||||
|
(
|
||||||
|
BaseVar(
|
||||||
|
name="_",
|
||||||
|
type_=Any,
|
||||||
|
state="",
|
||||||
|
is_local=True,
|
||||||
|
is_string=False,
|
||||||
|
),
|
||||||
|
"{_}",
|
||||||
|
),
|
||||||
|
(BaseVar(name='state.colors["a"]', type_="str"), '{state.colors["a"]}'),
|
||||||
|
({"a": BaseVar(name="val", type_="str")}, '{{"a": val}}'),
|
||||||
|
({"a": BaseVar(name='"val"', type_="str")}, '{{"a": "val"}}'),
|
||||||
|
(
|
||||||
|
{"a": BaseVar(name='state.colors["val"]', type_="str")},
|
||||||
|
'{{"a": state.colors["val"]}}',
|
||||||
|
),
|
||||||
|
# tricky real-world case from markdown component
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"h1": f"{{({{node, ...props}}) => <Heading {{...props}} {''.join(Tag(name='', props=Style({'as_': 'h1'})).format_props())} />}}"
|
||||||
|
},
|
||||||
|
'{{"h1": ({node, ...props}) => <Heading {...props} as={`h1`} />}}',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_prop(prop: Var, formatted: str):
|
||||||
|
"""Test that the formatted value of an prop is correct.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prop: The prop to test.
|
||||||
|
formatted: The expected formatted value.
|
||||||
|
"""
|
||||||
|
assert format.format_prop(prop) == formatted
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"single_props,key_value_props,output",
|
||||||
|
[
|
||||||
|
(["string"], {"key": 42}, ["key={42}", "string"]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_props(single_props, key_value_props, output):
|
||||||
|
"""Test the result of formatting a set of props (both single and keyvalue).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
single_props: the list of single props
|
||||||
|
key_value_props: the dict of key value props
|
||||||
|
output: the expected output
|
||||||
|
"""
|
||||||
|
assert format.format_props(*single_props, **key_value_props) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
(EventHandler(fn=mock_event), ("", "mock_event")),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_get_handler_parts(input, output):
|
||||||
|
assert format.get_event_handler_parts(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
(TestState.do_something, "test_state.do_something"),
|
||||||
|
(ChildState.change_both, "test_state.child_state.change_both"),
|
||||||
|
(
|
||||||
|
GrandchildState.do_nothing,
|
||||||
|
"test_state.child_state.grandchild_state.do_nothing",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_event_handler(input, output):
|
||||||
|
"""Test formatting an event handler.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input: The event handler input.
|
||||||
|
output: The expected output.
|
||||||
|
"""
|
||||||
|
assert format.format_event_handler(input) == output # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
(EventSpec(handler=EventHandler(fn=mock_event)), 'Event("mock_event", {})'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_event(input, output):
|
||||||
|
assert format.format_event(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
EventChain(
|
||||||
|
events=[
|
||||||
|
EventSpec(handler=EventHandler(fn=mock_event)),
|
||||||
|
EventSpec(handler=EventHandler(fn=mock_event)),
|
||||||
|
],
|
||||||
|
args_spec=None,
|
||||||
|
),
|
||||||
|
'addEvents([Event("mock_event", {}),Event("mock_event", {})])',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
EventChain(
|
||||||
|
events=[
|
||||||
|
EventSpec(handler=EventHandler(fn=mock_event)),
|
||||||
|
EventSpec(handler=EventHandler(fn=mock_event)),
|
||||||
|
],
|
||||||
|
args_spec=lambda e0: [e0],
|
||||||
|
),
|
||||||
|
'addEvents([Event("mock_event", {}),Event("mock_event", {})])',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_event_chain(input, output):
|
||||||
|
assert format.format_event_chain(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
({"query": {"k1": 1, "k2": 2}}, {"k1": 1, "k2": 2}),
|
||||||
|
({"query": {"k1": 1, "k-2": 2}}, {"k1": 1, "k_2": 2}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_query_params(input, output):
|
||||||
|
assert format.format_query_params(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input, output",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
TestState().dict(), # type: ignore
|
||||||
|
{
|
||||||
|
"array": [1, 2, 3.14],
|
||||||
|
"child_state": {
|
||||||
|
"count": 23,
|
||||||
|
"grandchild_state": {"value2": ""},
|
||||||
|
"value": "",
|
||||||
|
},
|
||||||
|
"child_state2": {"value": ""},
|
||||||
|
"complex": {
|
||||||
|
1: {"prop1": 42, "prop2": "hello"},
|
||||||
|
2: {"prop1": 42, "prop2": "hello"},
|
||||||
|
},
|
||||||
|
"dt": "1989-11-09 18:53:00+01:00",
|
||||||
|
"fig": [],
|
||||||
|
"is_hydrated": False,
|
||||||
|
"key": "",
|
||||||
|
"map_key": "a",
|
||||||
|
"mapping": {"a": [1, 2, 3], "b": [4, 5, 6]},
|
||||||
|
"num1": 0,
|
||||||
|
"num2": 3.14,
|
||||||
|
"obj": {"prop1": 42, "prop2": "hello"},
|
||||||
|
"sum": 3.14,
|
||||||
|
"upper": "",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
DateTimeState().dict(),
|
||||||
|
{
|
||||||
|
"d": "1989-11-09",
|
||||||
|
"dt": "1989-11-09 18:53:00+01:00",
|
||||||
|
"is_hydrated": False,
|
||||||
|
"t": "18:53:00+01:00",
|
||||||
|
"td": "11 days, 0:11:00",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_state(input, output):
|
||||||
|
"""Test that the format state is correct.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input: The state to format.
|
||||||
|
output: The expected formatted state.
|
||||||
|
"""
|
||||||
|
assert format.format_state(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
("input1", "ref_input1"),
|
||||||
|
("input 1", "ref_input_1"),
|
||||||
|
("input-1", "ref_input_1"),
|
||||||
|
("input_1", "ref_input_1"),
|
||||||
|
("a long test?1! name", "ref_a_long_test_1_name"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_ref(input, output):
|
||||||
|
"""Test formatting a ref.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input: The name to format.
|
||||||
|
output: The expected formatted name.
|
||||||
|
"""
|
||||||
|
assert format.format_ref(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
(("my_array", None), "refs_my_array"),
|
||||||
|
(("my_array", Var.create(0)), "refs_my_array[0]"),
|
||||||
|
(("my_array", Var.create(1)), "refs_my_array[1]"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_array_ref(input, output):
|
||||||
|
assert format.format_array_ref(input[0], input[1]) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
("/foo", [("foo", "/foo")]),
|
||||||
|
("/foo/bar", [("foo", "/foo"), ("bar", "/foo/bar")]),
|
||||||
|
(
|
||||||
|
"/foo/bar/baz",
|
||||||
|
[("foo", "/foo"), ("bar", "/foo/bar"), ("baz", "/foo/bar/baz")],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_breadcrumbs(input, output):
|
||||||
|
assert format.format_breadcrumbs(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input, output",
|
||||||
|
[
|
||||||
|
("library@^0.1.2", "library"),
|
||||||
|
("library", "library"),
|
||||||
|
("@library@^0.1.2", "@library"),
|
||||||
|
("@library", "@library"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_format_library_name(input: str, output: str):
|
||||||
|
"""Test formating a library name to remove the @version part.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input: the input string.
|
||||||
|
output: the output string.
|
||||||
|
"""
|
||||||
|
assert format.format_library_name(input) == output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"input,output",
|
||||||
|
[
|
||||||
|
(None, "null"),
|
||||||
|
(True, "true"),
|
||||||
|
(1, "1"),
|
||||||
|
(1.0, "1.0"),
|
||||||
|
([], "[]"),
|
||||||
|
([1, 2, 3], "[1, 2, 3]"),
|
||||||
|
({}, "{}"),
|
||||||
|
({"k1": False, "k2": True}, '{"k1": false, "k2": true}'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_json_dumps(input, output):
|
||||||
|
assert format.json_dumps(input) == output
|
@ -9,20 +9,17 @@ from packaging import version
|
|||||||
|
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.components.tags import Tag
|
from reflex.event import EventHandler
|
||||||
from reflex.event import EVENT_ARG, EventChain, EventHandler, EventSpec
|
|
||||||
from reflex.state import State
|
from reflex.state import State
|
||||||
from reflex.style import Style
|
|
||||||
from reflex.utils import (
|
from reflex.utils import (
|
||||||
build,
|
build,
|
||||||
format,
|
|
||||||
imports,
|
imports,
|
||||||
prerequisites,
|
prerequisites,
|
||||||
types,
|
types,
|
||||||
)
|
)
|
||||||
from reflex.utils import exec as utils_exec
|
from reflex.utils import exec as utils_exec
|
||||||
from reflex.utils.serializers import serialize
|
from reflex.utils.serializers import serialize
|
||||||
from reflex.vars import BaseVar, Var
|
from reflex.vars import Var
|
||||||
|
|
||||||
|
|
||||||
def mock_event(arg):
|
def mock_event(arg):
|
||||||
@ -36,7 +33,7 @@ def get_above_max_version():
|
|||||||
max bun version plus one.
|
max bun version plus one.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
semantic_version_list = constants.BUN_VERSION.split(".")
|
semantic_version_list = constants.Bun.VERSION.split(".")
|
||||||
semantic_version_list[-1] = str(int(semantic_version_list[-1]) + 1) # type: ignore
|
semantic_version_list[-1] = str(int(semantic_version_list[-1]) + 1) # type: ignore
|
||||||
return ".".join(semantic_version_list)
|
return ".".join(semantic_version_list)
|
||||||
|
|
||||||
@ -59,179 +56,6 @@ def test_func():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"input,output",
|
|
||||||
[
|
|
||||||
("", ""),
|
|
||||||
("hello", "hello"),
|
|
||||||
("Hello", "hello"),
|
|
||||||
("camelCase", "camel_case"),
|
|
||||||
("camelTwoHumps", "camel_two_humps"),
|
|
||||||
("_start_with_underscore", "_start_with_underscore"),
|
|
||||||
("__start_with_double_underscore", "__start_with_double_underscore"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_to_snake_case(input: str, output: str):
|
|
||||||
"""Test converting strings to snake case.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
input: The input string.
|
|
||||||
output: The expected output string.
|
|
||||||
"""
|
|
||||||
assert format.to_snake_case(input) == output
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"input,output",
|
|
||||||
[
|
|
||||||
("", ""),
|
|
||||||
("hello", "hello"),
|
|
||||||
("Hello", "Hello"),
|
|
||||||
("snake_case", "snakeCase"),
|
|
||||||
("snake_case_two", "snakeCaseTwo"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_to_camel_case(input: str, output: str):
|
|
||||||
"""Test converting strings to camel case.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
input: The input string.
|
|
||||||
output: The expected output string.
|
|
||||||
"""
|
|
||||||
assert format.to_camel_case(input) == output
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"input,output",
|
|
||||||
[
|
|
||||||
("", ""),
|
|
||||||
("hello", "Hello"),
|
|
||||||
("Hello", "Hello"),
|
|
||||||
("snake_case", "SnakeCase"),
|
|
||||||
("snake_case_two", "SnakeCaseTwo"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_to_title_case(input: str, output: str):
|
|
||||||
"""Test converting strings to title case.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
input: The input string.
|
|
||||||
output: The expected output string.
|
|
||||||
"""
|
|
||||||
assert format.to_title_case(input) == output
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"input,output",
|
|
||||||
[
|
|
||||||
("{", "}"),
|
|
||||||
("(", ")"),
|
|
||||||
("[", "]"),
|
|
||||||
("<", ">"),
|
|
||||||
('"', '"'),
|
|
||||||
("'", "'"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_get_close_char(input: str, output: str):
|
|
||||||
"""Test getting the close character for a given open character.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
input: The open character.
|
|
||||||
output: The expected close character.
|
|
||||||
"""
|
|
||||||
assert format.get_close_char(input) == output
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"text,open,expected",
|
|
||||||
[
|
|
||||||
("", "{", False),
|
|
||||||
("{wrap}", "{", True),
|
|
||||||
("{wrap", "{", False),
|
|
||||||
("{wrap}", "(", False),
|
|
||||||
("(wrap)", "(", True),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_is_wrapped(text: str, open: str, expected: bool):
|
|
||||||
"""Test checking if a string is wrapped in the given open and close characters.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: The text to check.
|
|
||||||
open: The open character.
|
|
||||||
expected: Whether the text is wrapped.
|
|
||||||
"""
|
|
||||||
assert format.is_wrapped(text, open) == expected
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"text,open,check_first,num,expected",
|
|
||||||
[
|
|
||||||
("", "{", True, 1, "{}"),
|
|
||||||
("wrap", "{", True, 1, "{wrap}"),
|
|
||||||
("wrap", "(", True, 1, "(wrap)"),
|
|
||||||
("wrap", "(", True, 2, "((wrap))"),
|
|
||||||
("(wrap)", "(", True, 1, "(wrap)"),
|
|
||||||
("{wrap}", "{", True, 2, "{wrap}"),
|
|
||||||
("(wrap)", "{", True, 1, "{(wrap)}"),
|
|
||||||
("(wrap)", "(", False, 1, "((wrap))"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_wrap(text: str, open: str, expected: str, check_first: bool, num: int):
|
|
||||||
"""Test wrapping a string.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: The text to wrap.
|
|
||||||
open: The open character.
|
|
||||||
expected: The expected output string.
|
|
||||||
check_first: Whether to check if the text is already wrapped.
|
|
||||||
num: The number of times to wrap the text.
|
|
||||||
"""
|
|
||||||
assert format.wrap(text, open, check_first=check_first, num=num) == expected
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"text,indent_level,expected",
|
|
||||||
[
|
|
||||||
("", 2, ""),
|
|
||||||
("hello", 2, "hello"),
|
|
||||||
("hello\nworld", 2, " hello\n world\n"),
|
|
||||||
("hello\nworld", 4, " hello\n world\n"),
|
|
||||||
(" hello\n world", 2, " hello\n world\n"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_indent(text: str, indent_level: int, expected: str, windows_platform: bool):
|
|
||||||
"""Test indenting a string.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: The text to indent.
|
|
||||||
indent_level: The number of spaces to indent by.
|
|
||||||
expected: The expected output string.
|
|
||||||
windows_platform: Whether the system is windows.
|
|
||||||
"""
|
|
||||||
assert format.indent(text, indent_level) == (
|
|
||||||
expected.replace("\n", "\r\n") if windows_platform else expected
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"condition,true_value,false_value,expected",
|
|
||||||
[
|
|
||||||
("cond", "<C1>", '""', '{isTrue(cond) ? <C1> : ""}'),
|
|
||||||
("cond", "<C1>", "<C2>", "{isTrue(cond) ? <C1> : <C2>}"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_format_cond(condition: str, true_value: str, false_value: str, expected: str):
|
|
||||||
"""Test formatting a cond.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
condition: The condition to check.
|
|
||||||
true_value: The value to return if the condition is true.
|
|
||||||
false_value: The value to return if the condition is false.
|
|
||||||
expected: The expected output string.
|
|
||||||
"""
|
|
||||||
assert format.format_cond(condition, true_value, false_value) == expected
|
|
||||||
|
|
||||||
|
|
||||||
def test_merge_imports():
|
def test_merge_imports():
|
||||||
"""Test that imports are merged correctly."""
|
"""Test that imports are merged correctly."""
|
||||||
d1 = {"react": {"Component"}}
|
d1 = {"react": {"Component"}}
|
||||||
@ -263,110 +87,6 @@ def test_is_generic_alias(cls: type, expected: bool):
|
|||||||
assert types.is_generic_alias(cls) == expected
|
assert types.is_generic_alias(cls) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"route,format_case,expected",
|
|
||||||
[
|
|
||||||
("", True, "index"),
|
|
||||||
("/", True, "index"),
|
|
||||||
("custom-route", True, "custom-route"),
|
|
||||||
("custom-route", False, "custom-route"),
|
|
||||||
("custom-route/", True, "custom-route"),
|
|
||||||
("custom-route/", False, "custom-route"),
|
|
||||||
("/custom-route", True, "custom-route"),
|
|
||||||
("/custom-route", False, "custom-route"),
|
|
||||||
("/custom_route", True, "custom-route"),
|
|
||||||
("/custom_route", False, "custom_route"),
|
|
||||||
("/CUSTOM_route", True, "custom-route"),
|
|
||||||
("/CUSTOM_route", False, "CUSTOM_route"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_format_route(route: str, format_case: bool, expected: bool):
|
|
||||||
"""Test formatting a route.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
route: The route to format.
|
|
||||||
format_case: Whether to change casing to snake_case.
|
|
||||||
expected: The expected formatted route.
|
|
||||||
"""
|
|
||||||
assert format.format_route(route, format_case=format_case) == expected
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"prop,formatted",
|
|
||||||
[
|
|
||||||
("string", '"string"'),
|
|
||||||
("{wrapped_string}", "{wrapped_string}"),
|
|
||||||
(True, "{true}"),
|
|
||||||
(False, "{false}"),
|
|
||||||
(123, "{123}"),
|
|
||||||
(3.14, "{3.14}"),
|
|
||||||
([1, 2, 3], "{[1, 2, 3]}"),
|
|
||||||
(["a", "b", "c"], '{["a", "b", "c"]}'),
|
|
||||||
({"a": 1, "b": 2, "c": 3}, '{{"a": 1, "b": 2, "c": 3}}'),
|
|
||||||
({"a": 'foo "bar" baz'}, r'{{"a": "foo \"bar\" baz"}}'),
|
|
||||||
(
|
|
||||||
{
|
|
||||||
"a": 'foo "{ "bar" }" baz',
|
|
||||||
"b": BaseVar(name="val", type_="str"),
|
|
||||||
},
|
|
||||||
r'{{"a": "foo \"{ \"bar\" }\" baz", "b": val}}',
|
|
||||||
),
|
|
||||||
(
|
|
||||||
EventChain(
|
|
||||||
events=[EventSpec(handler=EventHandler(fn=mock_event))], args_spec=None
|
|
||||||
),
|
|
||||||
'{_e => addEvents([Event("mock_event", {})], _e)}',
|
|
||||||
),
|
|
||||||
(
|
|
||||||
EventChain(
|
|
||||||
events=[
|
|
||||||
EventSpec(
|
|
||||||
handler=EventHandler(fn=mock_event),
|
|
||||||
args=((Var.create_safe("arg"), EVENT_ARG.target.value),),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
args_spec=None,
|
|
||||||
),
|
|
||||||
'{_e => addEvents([Event("mock_event", {arg:_e.target.value})], _e)}',
|
|
||||||
),
|
|
||||||
({"a": "red", "b": "blue"}, '{{"a": "red", "b": "blue"}}'),
|
|
||||||
(BaseVar(name="var", type_="int"), "{var}"),
|
|
||||||
(
|
|
||||||
BaseVar(
|
|
||||||
name="_",
|
|
||||||
type_=Any,
|
|
||||||
state="",
|
|
||||||
is_local=True,
|
|
||||||
is_string=False,
|
|
||||||
),
|
|
||||||
"{_}",
|
|
||||||
),
|
|
||||||
(BaseVar(name='state.colors["a"]', type_="str"), '{state.colors["a"]}'),
|
|
||||||
({"a": BaseVar(name="val", type_="str")}, '{{"a": val}}'),
|
|
||||||
({"a": BaseVar(name='"val"', type_="str")}, '{{"a": "val"}}'),
|
|
||||||
(
|
|
||||||
{"a": BaseVar(name='state.colors["val"]', type_="str")},
|
|
||||||
'{{"a": state.colors["val"]}}',
|
|
||||||
),
|
|
||||||
# tricky real-world case from markdown component
|
|
||||||
(
|
|
||||||
{
|
|
||||||
"h1": f"{{({{node, ...props}}) => <Heading {{...props}} {''.join(Tag(name='', props=Style({'as_': 'h1'})).format_props())} />}}"
|
|
||||||
},
|
|
||||||
'{{"h1": ({node, ...props}) => <Heading {...props} as={`h1`} />}}',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_format_prop(prop: Var, formatted: str):
|
|
||||||
"""Test that the formatted value of an prop is correct.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
prop: The prop to test.
|
|
||||||
formatted: The expected formatted value.
|
|
||||||
"""
|
|
||||||
assert format.format_prop(prop) == formatted
|
|
||||||
|
|
||||||
|
|
||||||
def test_validate_invalid_bun_path(mocker):
|
def test_validate_invalid_bun_path(mocker):
|
||||||
"""Test that an error is thrown when a custom specified bun path is not valid
|
"""Test that an error is thrown when a custom specified bun path is not valid
|
||||||
or does not exist.
|
or does not exist.
|
||||||
@ -523,31 +243,11 @@ def test_create_config_e2e(tmp_working_dir):
|
|||||||
app_name = "e2e"
|
app_name = "e2e"
|
||||||
prerequisites.create_config(app_name)
|
prerequisites.create_config(app_name)
|
||||||
eval_globals = {}
|
eval_globals = {}
|
||||||
exec((tmp_working_dir / constants.CONFIG_FILE).read_text(), eval_globals)
|
exec((tmp_working_dir / constants.Config.FILE).read_text(), eval_globals)
|
||||||
config = eval_globals["config"]
|
config = eval_globals["config"]
|
||||||
assert config.app_name == app_name
|
assert config.app_name == app_name
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"name,expected",
|
|
||||||
[
|
|
||||||
("input1", "ref_input1"),
|
|
||||||
("input 1", "ref_input_1"),
|
|
||||||
("input-1", "ref_input_1"),
|
|
||||||
("input_1", "ref_input_1"),
|
|
||||||
("a long test?1! name", "ref_a_long_test_1_name"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_format_ref(name, expected):
|
|
||||||
"""Test formatting a ref.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name: The name to format.
|
|
||||||
expected: The expected formatted name.
|
|
||||||
"""
|
|
||||||
assert format.format_ref(name) == expected
|
|
||||||
|
|
||||||
|
|
||||||
class DataFrame:
|
class DataFrame:
|
||||||
"""A Fake pandas DataFrame class."""
|
"""A Fake pandas DataFrame class."""
|
||||||
|
|
||||||
@ -585,8 +285,8 @@ def test_initialize_non_existent_gitignore(tmp_path, mocker, gitignore_exists):
|
|||||||
mocker: The mock object.
|
mocker: The mock object.
|
||||||
gitignore_exists: Whether a gitignore file exists in the root dir.
|
gitignore_exists: Whether a gitignore file exists in the root dir.
|
||||||
"""
|
"""
|
||||||
expected = constants.DEFAULT_GITIGNORE.copy()
|
expected = constants.GitIgnore.DEFAULTS.copy()
|
||||||
mocker.patch("reflex.constants.GITIGNORE_FILE", tmp_path / ".gitignore")
|
mocker.patch("reflex.constants.GitIgnore.FILE", tmp_path / ".gitignore")
|
||||||
|
|
||||||
gitignore_file = tmp_path / ".gitignore"
|
gitignore_file = tmp_path / ".gitignore"
|
||||||
|
|
||||||
@ -633,8 +333,8 @@ def test_node_install_windows(tmp_path, mocker):
|
|||||||
fnm_root_path = tmp_path / "reflex" / "fnm"
|
fnm_root_path = tmp_path / "reflex" / "fnm"
|
||||||
fnm_exe = fnm_root_path / "fnm.exe"
|
fnm_exe = fnm_root_path / "fnm.exe"
|
||||||
|
|
||||||
mocker.patch("reflex.utils.prerequisites.constants.FNM_DIR", fnm_root_path)
|
mocker.patch("reflex.utils.prerequisites.constants.Fnm.DIR", fnm_root_path)
|
||||||
mocker.patch("reflex.utils.prerequisites.constants.FNM_EXE", fnm_exe)
|
mocker.patch("reflex.utils.prerequisites.constants.Fnm.EXE", fnm_exe)
|
||||||
mocker.patch("reflex.utils.prerequisites.constants.IS_WINDOWS", True)
|
mocker.patch("reflex.utils.prerequisites.constants.IS_WINDOWS", True)
|
||||||
mocker.patch("reflex.utils.processes.new_process")
|
mocker.patch("reflex.utils.processes.new_process")
|
||||||
mocker.patch("reflex.utils.processes.stream_logs")
|
mocker.patch("reflex.utils.processes.stream_logs")
|
||||||
@ -675,8 +375,8 @@ def test_node_install_unix(tmp_path, mocker, machine, system):
|
|||||||
fnm_root_path = tmp_path / "reflex" / "fnm"
|
fnm_root_path = tmp_path / "reflex" / "fnm"
|
||||||
fnm_exe = fnm_root_path / "fnm"
|
fnm_exe = fnm_root_path / "fnm"
|
||||||
|
|
||||||
mocker.patch("reflex.utils.prerequisites.constants.FNM_DIR", fnm_root_path)
|
mocker.patch("reflex.utils.prerequisites.constants.Fnm.DIR", fnm_root_path)
|
||||||
mocker.patch("reflex.utils.prerequisites.constants.FNM_EXE", fnm_exe)
|
mocker.patch("reflex.utils.prerequisites.constants.Fnm.EXE", fnm_exe)
|
||||||
mocker.patch("reflex.utils.prerequisites.constants.IS_WINDOWS", False)
|
mocker.patch("reflex.utils.prerequisites.constants.IS_WINDOWS", False)
|
||||||
mocker.patch("reflex.utils.prerequisites.platform.machine", return_value=machine)
|
mocker.patch("reflex.utils.prerequisites.platform.machine", return_value=machine)
|
||||||
mocker.patch("reflex.utils.prerequisites.platform.system", return_value=system)
|
mocker.patch("reflex.utils.prerequisites.platform.system", return_value=system)
|
||||||
@ -701,14 +401,14 @@ def test_node_install_unix(tmp_path, mocker, machine, system):
|
|||||||
fnm_exe,
|
fnm_exe,
|
||||||
"install",
|
"install",
|
||||||
"--arch=arm64",
|
"--arch=arm64",
|
||||||
constants.NODE_VERSION,
|
constants.Node.VERSION,
|
||||||
"--fnm-dir",
|
"--fnm-dir",
|
||||||
fnm_root_path,
|
fnm_root_path,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
process.assert_called_with(
|
process.assert_called_with(
|
||||||
[fnm_exe, "install", constants.NODE_VERSION, "--fnm-dir", fnm_root_path]
|
[fnm_exe, "install", constants.Node.VERSION, "--fnm-dir", fnm_root_path]
|
||||||
)
|
)
|
||||||
chmod.assert_called_once()
|
chmod.assert_called_once()
|
||||||
|
|
||||||
@ -759,7 +459,7 @@ def test_output_system_info(mocker):
|
|||||||
This test makes no assertions about the output, other than it executes
|
This test makes no assertions about the output, other than it executes
|
||||||
without crashing.
|
without crashing.
|
||||||
"""
|
"""
|
||||||
mocker.patch("reflex.utils.console.LOG_LEVEL", constants.LogLevel.DEBUG)
|
mocker.patch("reflex.utils.console._LOG_LEVEL", constants.LogLevel.DEBUG)
|
||||||
utils_exec.output_system_info()
|
utils_exec.output_system_info()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user