diff --git a/pyi_hashes.json b/pyi_hashes.json index 2548b3a9ed7..e8d1534be47 100644 --- a/pyi_hashes.json +++ b/pyi_hashes.json @@ -14,13 +14,13 @@ "reflex/components/base/strict_mode.pyi": "13d91eb9ac0a097c20f95cd3aaa2e703", "reflex/components/core/__init__.pyi": "44bcee7bc4e27e2f4f4707b843acf291", "reflex/components/core/auto_scroll.pyi": "6f8a2089b8810e08ff1609c616b9e1b1", - "reflex/components/core/banner.pyi": "be040b74446dbeee79849cdd4abfb472", + "reflex/components/core/banner.pyi": "1608a30ac68b749c13ba38d8088b3d1b", "reflex/components/core/client_side_routing.pyi": "dd0bfc63117ef5f214e3c66dfc4ff1a2", "reflex/components/core/clipboard.pyi": "8ef2e904caeceb9ce386f9938ab00269", "reflex/components/core/debounce.pyi": "a6416b52e2857994861f6f90e2f29391", "reflex/components/core/html.pyi": "d49588c2f04191cbbf1b4ac1c0bf855d", "reflex/components/core/sticky.pyi": "6c7d85617f8e85f2aacc6aef90b553a5", - "reflex/components/core/upload.pyi": "794d5e242e8506b68934b1dc2bf02c64", + "reflex/components/core/upload.pyi": "aae4f9a95ad5b6f8c596508145839473", "reflex/components/datadisplay/__init__.pyi": "cf087efa8b3960decc6b231cc986cfa9", "reflex/components/datadisplay/code.pyi": "651fc3d417b998eb1c3d072328f505d0", "reflex/components/datadisplay/dataeditor.pyi": "601c59f3ced6ab94fcf5527b90472a4f", diff --git a/reflex/app.py b/reflex/app.py index bee5e864ee9..243fbc864c2 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -68,7 +68,8 @@ from reflex.components.core.upload import Upload, get_upload_dir from reflex.components.radix import themes from reflex.components.sonner.toast import toast -from reflex.config import ExecutorType, environment, get_config +from reflex.config import get_config +from reflex.environment import ExecutorType, environment from reflex.event import ( _EVENT_FIELDS, Event, diff --git a/reflex/assets.py b/reflex/assets.py index 05b00f13290..693041b7a18 100644 --- a/reflex/assets.py +++ b/reflex/assets.py @@ -4,7 +4,7 @@ from pathlib import Path from reflex import constants -from reflex.config import EnvironmentVariables +from reflex.environment import EnvironmentVariables def asset( diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index 3833e26cb3e..cebf1069cc2 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -19,8 +19,9 @@ CustomComponent, StatefulComponent, ) -from reflex.config import environment, get_config +from reflex.config import get_config from reflex.constants.compiler import PageNames +from reflex.environment import environment from reflex.state import BaseState from reflex.style import SYSTEM_COLOR_MODE from reflex.utils import console, path_ops diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index 94244fd004a..5baff2d52db 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -8,7 +8,7 @@ from reflex.components.component import BaseComponent, Component, ComponentStyle from reflex.components.tags import Tag from reflex.components.tags.tagless import Tagless -from reflex.config import PerformanceMode, environment +from reflex.environment import PerformanceMode, environment from reflex.utils import console from reflex.utils.decorator import once from reflex.utils.imports import ParsedImportDict diff --git a/reflex/components/core/banner.py b/reflex/components/core/banner.py index 5c562c1adad..fb6569fa4c7 100644 --- a/reflex/components/core/banner.py +++ b/reflex/components/core/banner.py @@ -16,9 +16,9 @@ from reflex.components.radix.themes.layout.flex import Flex from reflex.components.radix.themes.typography.text import Text from reflex.components.sonner.toast import ToastProps, toast_ref -from reflex.config import environment from reflex.constants import Dirs, Hooks, Imports from reflex.constants.compiler import CompileVars +from reflex.environment import environment from reflex.utils.imports import ImportVar from reflex.vars import VarData from reflex.vars.base import LiteralVar, Var diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py index 0e75bcb3ea1..89c9dcb3f0c 100644 --- a/reflex/components/core/upload.py +++ b/reflex/components/core/upload.py @@ -16,9 +16,9 @@ from reflex.components.core.cond import cond from reflex.components.el.elements.forms import Input from reflex.components.radix.themes.layout.box import Box -from reflex.config import environment from reflex.constants import Dirs from reflex.constants.compiler import Hooks, Imports +from reflex.environment import environment from reflex.event import ( CallableEventSpec, EventChain, diff --git a/reflex/config.py b/reflex/config.py index c222b242fcc..9c340bc0c9f 100644 --- a/reflex/config.py +++ b/reflex/config.py @@ -2,48 +2,30 @@ from __future__ import annotations -import concurrent.futures -import dataclasses -import enum import importlib -import inspect -import multiprocessing import os -import platform import sys import threading import urllib.parse -from collections.abc import Callable -from functools import lru_cache from importlib.util import find_spec from pathlib import Path from types import ModuleType -from typing import ( - TYPE_CHECKING, - Annotated, - Any, - ClassVar, - Generic, - TypeVar, - get_args, - get_origin, - get_type_hints, -) +from typing import Any, ClassVar import pydantic.v1 as pydantic from reflex import constants from reflex.base import Base from reflex.constants.base import LogLevel +from reflex.environment import EnvironmentVariables as EnvironmentVariables +from reflex.environment import EnvVar as EnvVar +from reflex.environment import ExistingPath, interpret_env_var_value +from reflex.environment import env_var as env_var +from reflex.environment import environment as environment from reflex.plugins import Plugin, TailwindV3Plugin, TailwindV4Plugin from reflex.utils import console -from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError -from reflex.utils.types import ( - GenericType, - is_union, - true_type_for_pydantic_field, - value_inside_optional, -) +from reflex.utils.exceptions import ConfigError +from reflex.utils.types import true_type_for_pydantic_field try: from dotenv import load_dotenv @@ -200,582 +182,6 @@ def get_url(self) -> str: return f"{self.engine}://{path}/{self.database}" -def get_default_value_for_field(field: dataclasses.Field) -> Any: - """Get the default value for a field. - - Args: - field: The field. - - Returns: - The default value. - - Raises: - ValueError: If no default value is found. - """ - if field.default != dataclasses.MISSING: - return field.default - if field.default_factory != dataclasses.MISSING: - return field.default_factory() - msg = f"Missing value for environment variable {field.name} and no default value found" - raise ValueError(msg) - - -# TODO: Change all interpret_.* signatures to value: str, field: dataclasses.Field once we migrate rx.Config to dataclasses -def interpret_boolean_env(value: str, field_name: str) -> bool: - """Interpret a boolean environment variable value. - - Args: - value: The environment variable value. - field_name: The field name. - - Returns: - The interpreted value. - - Raises: - EnvironmentVarValueError: If the value is invalid. - """ - true_values = ["true", "1", "yes", "y"] - false_values = ["false", "0", "no", "n"] - - if value.lower() in true_values: - return True - if value.lower() in false_values: - return False - msg = f"Invalid boolean value: {value} for {field_name}" - raise EnvironmentVarValueError(msg) - - -def interpret_int_env(value: str, field_name: str) -> int: - """Interpret an integer environment variable value. - - Args: - value: The environment variable value. - field_name: The field name. - - Returns: - The interpreted value. - - Raises: - EnvironmentVarValueError: If the value is invalid. - """ - try: - return int(value) - except ValueError as ve: - msg = f"Invalid integer value: {value} for {field_name}" - raise EnvironmentVarValueError(msg) from ve - - -def interpret_existing_path_env(value: str, field_name: str) -> ExistingPath: - """Interpret a path environment variable value as an existing path. - - Args: - value: The environment variable value. - field_name: The field name. - - Returns: - The interpreted value. - - Raises: - EnvironmentVarValueError: If the path does not exist. - """ - path = Path(value) - if not path.exists(): - msg = f"Path does not exist: {path} for {field_name}" - raise EnvironmentVarValueError(msg) - return path - - -def interpret_path_env(value: str, field_name: str) -> Path: - """Interpret a path environment variable value. - - Args: - value: The environment variable value. - field_name: The field name. - - Returns: - The interpreted value. - """ - return Path(value) - - -def interpret_enum_env(value: str, field_type: GenericType, field_name: str) -> Any: - """Interpret an enum environment variable value. - - Args: - value: The environment variable value. - field_type: The field type. - field_name: The field name. - - Returns: - The interpreted value. - - Raises: - EnvironmentVarValueError: If the value is invalid. - """ - try: - return field_type(value) - except ValueError as ve: - msg = f"Invalid enum value: {value} for {field_name}" - raise EnvironmentVarValueError(msg) from ve - - -def interpret_env_var_value( - value: str, field_type: GenericType, field_name: str -) -> Any: - """Interpret an environment variable value based on the field type. - - Args: - value: The environment variable value. - field_type: The field type. - field_name: The field name. - - Returns: - The interpreted value. - - Raises: - ValueError: If the value is invalid. - """ - field_type = value_inside_optional(field_type) - - if is_union(field_type): - msg = f"Union types are not supported for environment variables: {field_name}." - raise ValueError(msg) - - if field_type is bool: - return interpret_boolean_env(value, field_name) - if field_type is str: - return value - if field_type is int: - return interpret_int_env(value, field_name) - if field_type is Path: - return interpret_path_env(value, field_name) - if field_type is ExistingPath: - return interpret_existing_path_env(value, field_name) - if get_origin(field_type) is list: - return [ - interpret_env_var_value( - v, - get_args(field_type)[0], - f"{field_name}[{i}]", - ) - for i, v in enumerate(value.split(":")) - ] - if inspect.isclass(field_type) and issubclass(field_type, enum.Enum): - return interpret_enum_env(value, field_type, field_name) - - msg = f"Invalid type for environment variable {field_name}: {field_type}. This is probably an issue in Reflex." - raise ValueError(msg) - - -T = TypeVar("T") - - -class EnvVar(Generic[T]): - """Environment variable.""" - - name: str - default: Any - type_: T - - def __init__(self, name: str, default: Any, type_: T) -> None: - """Initialize the environment variable. - - Args: - name: The environment variable name. - default: The default value. - type_: The type of the value. - """ - self.name = name - self.default = default - self.type_ = type_ - - def interpret(self, value: str) -> T: - """Interpret the environment variable value. - - Args: - value: The environment variable value. - - Returns: - The interpreted value. - """ - return interpret_env_var_value(value, self.type_, self.name) - - def getenv(self) -> T | None: - """Get the interpreted environment variable value. - - Returns: - The environment variable value. - """ - env_value = os.getenv(self.name, None) - if env_value and env_value.strip(): - return self.interpret(env_value) - return None - - def is_set(self) -> bool: - """Check if the environment variable is set. - - Returns: - True if the environment variable is set. - """ - return bool(os.getenv(self.name, "").strip()) - - def get(self) -> T: - """Get the interpreted environment variable value or the default value if not set. - - Returns: - The interpreted value. - """ - env_value = self.getenv() - if env_value is not None: - return env_value - return self.default - - def set(self, value: T | None) -> None: - """Set the environment variable. None unsets the variable. - - Args: - value: The value to set. - """ - if value is None: - _ = os.environ.pop(self.name, None) - else: - if isinstance(value, enum.Enum): - value = value.value - if isinstance(value, list): - str_value = ":".join(str(v) for v in value) - else: - str_value = str(value) - os.environ[self.name] = str_value - - -@lru_cache -def get_type_hints_environment(cls: type) -> dict[str, Any]: - """Get the type hints for the environment variables. - - Args: - cls: The class. - - Returns: - The type hints. - """ - return get_type_hints(cls) - - -class env_var: # noqa: N801 # pyright: ignore [reportRedeclaration] - """Descriptor for environment variables.""" - - name: str - default: Any - internal: bool = False - - def __init__(self, default: Any, internal: bool = False) -> None: - """Initialize the descriptor. - - Args: - default: The default value. - internal: Whether the environment variable is reflex internal. - """ - self.default = default - self.internal = internal - - def __set_name__(self, owner: Any, name: str): - """Set the name of the descriptor. - - Args: - owner: The owner class. - name: The name of the descriptor. - """ - self.name = name - - def __get__( - self, instance: EnvironmentVariables, owner: type[EnvironmentVariables] - ): - """Get the EnvVar instance. - - Args: - instance: The instance. - owner: The owner class. - - Returns: - The EnvVar instance. - """ - type_ = get_args(get_type_hints_environment(owner)[self.name])[0] - env_name = self.name - if self.internal: - env_name = f"__{env_name}" - return EnvVar(name=env_name, default=self.default, type_=type_) - - -if TYPE_CHECKING: - - def env_var(default: Any, internal: bool = False) -> EnvVar: - """Typing helper for the env_var descriptor. - - Args: - default: The default value. - internal: Whether the environment variable is reflex internal. - - Returns: - The EnvVar instance. - """ - return default - - -class PathExistsFlag: - """Flag to indicate that a path must exist.""" - - -ExistingPath = Annotated[Path, PathExistsFlag] - - -class PerformanceMode(enum.Enum): - """Performance mode for the app.""" - - WARN = "warn" - RAISE = "raise" - OFF = "off" - - -class ExecutorType(enum.Enum): - """Executor for compiling the frontend.""" - - THREAD = "thread" - PROCESS = "process" - MAIN_THREAD = "main_thread" - - @classmethod - def get_executor_from_environment(cls): - """Get the executor based on the environment variables. - - Returns: - The executor. - """ - executor_type = environment.REFLEX_COMPILE_EXECUTOR.get() - - reflex_compile_processes = environment.REFLEX_COMPILE_PROCESSES.get() - reflex_compile_threads = environment.REFLEX_COMPILE_THREADS.get() - # By default, use the main thread. Unless the user has specified a different executor. - # Using a process pool is much faster, but not supported on all platforms. It's gated behind a flag. - if executor_type is None: - if ( - platform.system() not in ("Linux", "Darwin") - and reflex_compile_processes is not None - ): - console.warn("Multiprocessing is only supported on Linux and MacOS.") - - if ( - platform.system() in ("Linux", "Darwin") - and reflex_compile_processes is not None - ): - if reflex_compile_processes == 0: - console.warn( - "Number of processes must be greater than 0. If you want to use the default number of processes, set REFLEX_COMPILE_EXECUTOR to 'process'. Defaulting to None." - ) - reflex_compile_processes = None - elif reflex_compile_processes < 0: - console.warn( - "Number of processes must be greater than 0. Defaulting to None." - ) - reflex_compile_processes = None - executor_type = ExecutorType.PROCESS - elif reflex_compile_threads is not None: - if reflex_compile_threads == 0: - console.warn( - "Number of threads must be greater than 0. If you want to use the default number of threads, set REFLEX_COMPILE_EXECUTOR to 'thread'. Defaulting to None." - ) - reflex_compile_threads = None - elif reflex_compile_threads < 0: - console.warn( - "Number of threads must be greater than 0. Defaulting to None." - ) - reflex_compile_threads = None - executor_type = ExecutorType.THREAD - else: - executor_type = ExecutorType.MAIN_THREAD - - match executor_type: - case ExecutorType.PROCESS: - executor = concurrent.futures.ProcessPoolExecutor( - max_workers=reflex_compile_processes, - mp_context=multiprocessing.get_context("fork"), - ) - case ExecutorType.THREAD: - executor = concurrent.futures.ThreadPoolExecutor( - max_workers=reflex_compile_threads - ) - case ExecutorType.MAIN_THREAD: - FUTURE_RESULT_TYPE = TypeVar("FUTURE_RESULT_TYPE") - - class MainThreadExecutor: - def __enter__(self): - return self - - def __exit__(self, *args): - pass - - def submit( - self, fn: Callable[..., FUTURE_RESULT_TYPE], *args, **kwargs - ) -> concurrent.futures.Future[FUTURE_RESULT_TYPE]: - future_job = concurrent.futures.Future() - future_job.set_result(fn(*args, **kwargs)) - return future_job - - executor = MainThreadExecutor() - - return executor - - -class EnvironmentVariables: - """Environment variables class to instantiate environment variables.""" - - # Indicate the current command that was invoked in the reflex CLI. - REFLEX_COMPILE_CONTEXT: EnvVar[constants.CompileContext] = env_var( - constants.CompileContext.UNDEFINED, internal=True - ) - - # Whether to use npm over bun to install and run the frontend. - REFLEX_USE_NPM: EnvVar[bool] = env_var(False) - - # The npm registry to use. - NPM_CONFIG_REGISTRY: EnvVar[str | None] = env_var(None) - - # Whether to use Granian for the backend. By default, the backend uses Uvicorn if available. - REFLEX_USE_GRANIAN: EnvVar[bool] = env_var(False) - - # Whether to use the system installed bun. If set to false, bun will be bundled with the app. - REFLEX_USE_SYSTEM_BUN: EnvVar[bool] = env_var(False) - - # The working directory for the next.js commands. - REFLEX_WEB_WORKDIR: EnvVar[Path] = env_var(Path(constants.Dirs.WEB)) - - # The working directory for the states directory. - REFLEX_STATES_WORKDIR: EnvVar[Path] = env_var(Path(constants.Dirs.STATES)) - - # Path to the alembic config file - ALEMBIC_CONFIG: EnvVar[ExistingPath] = env_var(Path(constants.ALEMBIC_CONFIG)) - - # Disable SSL verification for HTTPX requests. - SSL_NO_VERIFY: EnvVar[bool] = env_var(False) - - # The directory to store uploaded files. - REFLEX_UPLOADED_FILES_DIR: EnvVar[Path] = env_var( - Path(constants.Dirs.UPLOADED_FILES) - ) - - REFLEX_COMPILE_EXECUTOR: EnvVar[ExecutorType | None] = env_var(None) - - # Whether to use separate processes to compile the frontend and how many. If not set, defaults to thread executor. - REFLEX_COMPILE_PROCESSES: EnvVar[int | None] = env_var(None) - - # Whether to use separate threads to compile the frontend and how many. Defaults to `min(32, os.cpu_count() + 4)`. - REFLEX_COMPILE_THREADS: EnvVar[int | None] = env_var(None) - - # The directory to store reflex dependencies. - REFLEX_DIR: EnvVar[Path] = env_var(constants.Reflex.DIR) - - # Whether to print the SQL queries if the log level is INFO or lower. - SQLALCHEMY_ECHO: EnvVar[bool] = env_var(False) - - # Whether to check db connections before using them. - SQLALCHEMY_POOL_PRE_PING: EnvVar[bool] = env_var(True) - - # Whether to ignore the redis config error. Some redis servers only allow out-of-band configuration. - REFLEX_IGNORE_REDIS_CONFIG_ERROR: EnvVar[bool] = env_var(False) - - # Whether to skip purging the web directory in dev mode. - REFLEX_PERSIST_WEB_DIR: EnvVar[bool] = env_var(False) - - # The reflex.build frontend host. - REFLEX_BUILD_FRONTEND: EnvVar[str] = env_var( - constants.Templates.REFLEX_BUILD_FRONTEND - ) - - # The reflex.build backend host. - REFLEX_BUILD_BACKEND: EnvVar[str] = env_var( - constants.Templates.REFLEX_BUILD_BACKEND - ) - - # This env var stores the execution mode of the app - REFLEX_ENV_MODE: EnvVar[constants.Env] = env_var(constants.Env.DEV) - - # Whether to run the backend only. Exclusive with REFLEX_FRONTEND_ONLY. - REFLEX_BACKEND_ONLY: EnvVar[bool] = env_var(False) - - # Whether to run the frontend only. Exclusive with REFLEX_BACKEND_ONLY. - REFLEX_FRONTEND_ONLY: EnvVar[bool] = env_var(False) - - # The port to run the frontend on. - REFLEX_FRONTEND_PORT: EnvVar[int | None] = env_var(None) - - # The port to run the backend on. - REFLEX_BACKEND_PORT: EnvVar[int | None] = env_var(None) - - # If this env var is set to "yes", App.compile will be a no-op - REFLEX_SKIP_COMPILE: EnvVar[bool] = env_var(False, internal=True) - - # Whether to run app harness tests in headless mode. - APP_HARNESS_HEADLESS: EnvVar[bool] = env_var(False) - - # Which app harness driver to use. - APP_HARNESS_DRIVER: EnvVar[str] = env_var("Chrome") - - # Arguments to pass to the app harness driver. - APP_HARNESS_DRIVER_ARGS: EnvVar[str] = env_var("") - - # Whether to check for outdated package versions. - REFLEX_CHECK_LATEST_VERSION: EnvVar[bool] = env_var(True) - - # In which performance mode to run the app. - REFLEX_PERF_MODE: EnvVar[PerformanceMode] = env_var(PerformanceMode.WARN) - - # The maximum size of the reflex state in kilobytes. - REFLEX_STATE_SIZE_LIMIT: EnvVar[int] = env_var(1000) - - # Whether to use the turbopack bundler. - REFLEX_USE_TURBOPACK: EnvVar[bool] = env_var(False) - - # Additional paths to include in the hot reload. Separated by a colon. - REFLEX_HOT_RELOAD_INCLUDE_PATHS: EnvVar[list[Path]] = env_var([]) - - # Paths to exclude from the hot reload. Takes precedence over include paths. Separated by a colon. - REFLEX_HOT_RELOAD_EXCLUDE_PATHS: EnvVar[list[Path]] = env_var([]) - - # Enables different behavior for when the backend would do a cold start if it was inactive. - REFLEX_DOES_BACKEND_COLD_START: EnvVar[bool] = env_var(False) - - # The timeout for the backend to do a cold start in seconds. - REFLEX_BACKEND_COLD_START_TIMEOUT: EnvVar[int] = env_var(10) - - # Used by flexgen to enumerate the pages. - REFLEX_ADD_ALL_ROUTES_ENDPOINT: EnvVar[bool] = env_var(False) - - # The address to bind the HTTP client to. You can set this to "::" to enable IPv6. - REFLEX_HTTP_CLIENT_BIND_ADDRESS: EnvVar[str | None] = env_var(None) - - # Maximum size of the message in the websocket server in bytes. - REFLEX_SOCKET_MAX_HTTP_BUFFER_SIZE: EnvVar[int] = env_var( - constants.POLLING_MAX_HTTP_BUFFER_SIZE - ) - - # The interval to send a ping to the websocket server in seconds. - REFLEX_SOCKET_INTERVAL: EnvVar[int] = env_var(constants.Ping.INTERVAL) - - # The timeout to wait for a pong from the websocket server in seconds. - REFLEX_SOCKET_TIMEOUT: EnvVar[int] = env_var(constants.Ping.TIMEOUT) - - # Whether to run Granian in a spawn process. This enables Reflex to pick up on environment variable changes between hot reloads. - REFLEX_STRICT_HOT_RELOAD: EnvVar[bool] = env_var(False) - - # The path to the reflex log file. If not set, the log file will be stored in the reflex user directory. - REFLEX_LOG_FILE: EnvVar[Path | None] = env_var(None) - - # Enable full logging of debug messages to reflex user directory. - REFLEX_ENABLE_FULL_LOGGING: EnvVar[bool] = env_var(False) - - -environment = EnvironmentVariables() - - # These vars are not logged because they may contain sensitive information. _sensitive_env_vars = {"DB_URL", "ASYNC_DB_URL", "REDIS_URL"} @@ -1025,11 +431,11 @@ def update_from_env(self) -> dict[str, Any]: for key, field in self.__fields__.items(): # The env var name is the key in uppercase. for prefix in self._prefixes: - if env_var := os.environ.get(f"{prefix}{key.upper()}"): + if environment_variable := os.environ.get(f"{prefix}{key.upper()}"): break else: # Default to non-prefixed env var if other are not found. - if env_var := os.environ.get(key.upper()): + if environment_variable := os.environ.get(key.upper()): console.deprecate( f"Usage of deprecated {key.upper()} env var detected.", reason=f"Prefer `{self._prefixes[0]}` prefix when setting env vars.", @@ -1038,21 +444,23 @@ def update_from_env(self) -> dict[str, Any]: ) # If the env var is set, override the config value. - if env_var and env_var.strip(): + if environment_variable and environment_variable.strip(): # Interpret the value. value = interpret_env_var_value( - env_var, true_type_for_pydantic_field(field), field.name + environment_variable, + true_type_for_pydantic_field(field), + field.name, ) # Set the value. updated_values[key] = value if key.upper() in _sensitive_env_vars: - env_var = "***" + environment_variable = "***" if value != getattr(self, key): console.debug( - f"Overriding config value {key} with env var {key.upper()}={env_var}", + f"Overriding config value {key} with env var {key.upper()}={environment_variable}", dedupe=True, ) return updated_values diff --git a/reflex/constants/base.py b/reflex/constants/base.py index 7d896aca2a2..6660ac92df8 100644 --- a/reflex/constants/base.py +++ b/reflex/constants/base.py @@ -147,7 +147,7 @@ def REFLEX_BUILD_URL(cls): Returns: The URL to redirect to reflex.build. """ - from reflex.config import environment + from reflex.environment import environment return ( environment.REFLEX_BUILD_FRONTEND.get() @@ -162,7 +162,7 @@ def REFLEX_BUILD_POLL_URL(cls): Returns: The URL to poll waiting for the user to select a generation. """ - from reflex.config import environment + from reflex.environment import environment return environment.REFLEX_BUILD_BACKEND.get() + "/api/init/{reflex_init_token}" @@ -174,7 +174,7 @@ def REFLEX_BUILD_CODE_URL(cls): Returns: The URL to fetch the generation's reflex code. """ - from reflex.config import environment + from reflex.environment import environment return ( environment.REFLEX_BUILD_BACKEND.get() diff --git a/reflex/constants/installer.py b/reflex/constants/installer.py index fbb3fba66e0..d0602d2b2a0 100644 --- a/reflex/constants/installer.py +++ b/reflex/constants/installer.py @@ -38,7 +38,7 @@ def ROOT_PATH(cls): Returns: The directory to store the bun. """ - from reflex.config import environment + from reflex.environment import environment return environment.REFLEX_DIR.get() / "bun" diff --git a/reflex/environment.py b/reflex/environment.py new file mode 100644 index 00000000000..cfcae6b68c8 --- /dev/null +++ b/reflex/environment.py @@ -0,0 +1,606 @@ +"""Environment variable management.""" + +from __future__ import annotations + +import concurrent.futures +import dataclasses +import enum +import inspect +import multiprocessing +import os +import platform +from collections.abc import Callable +from functools import lru_cache +from pathlib import Path +from typing import ( + TYPE_CHECKING, + Annotated, + Any, + Generic, + TypeVar, + get_args, + get_origin, + get_type_hints, +) + +from reflex import constants +from reflex.utils.exceptions import EnvironmentVarValueError +from reflex.utils.types import GenericType, is_union, value_inside_optional + + +def get_default_value_for_field(field: dataclasses.Field) -> Any: + """Get the default value for a field. + + Args: + field: The field. + + Returns: + The default value. + + Raises: + ValueError: If no default value is found. + """ + if field.default != dataclasses.MISSING: + return field.default + if field.default_factory != dataclasses.MISSING: + return field.default_factory() + msg = f"Missing value for environment variable {field.name} and no default value found" + raise ValueError(msg) + + +# TODO: Change all interpret_.* signatures to value: str, field: dataclasses.Field once we migrate rx.Config to dataclasses +def interpret_boolean_env(value: str, field_name: str) -> bool: + """Interpret a boolean environment variable value. + + Args: + value: The environment variable value. + field_name: The field name. + + Returns: + The interpreted value. + + Raises: + EnvironmentVarValueError: If the value is invalid. + """ + true_values = ["true", "1", "yes", "y"] + false_values = ["false", "0", "no", "n"] + + if value.lower() in true_values: + return True + if value.lower() in false_values: + return False + msg = f"Invalid boolean value: {value} for {field_name}" + raise EnvironmentVarValueError(msg) + + +def interpret_int_env(value: str, field_name: str) -> int: + """Interpret an integer environment variable value. + + Args: + value: The environment variable value. + field_name: The field name. + + Returns: + The interpreted value. + + Raises: + EnvironmentVarValueError: If the value is invalid. + """ + try: + return int(value) + except ValueError as ve: + msg = f"Invalid integer value: {value} for {field_name}" + raise EnvironmentVarValueError(msg) from ve + + +def interpret_existing_path_env(value: str, field_name: str) -> ExistingPath: + """Interpret a path environment variable value as an existing path. + + Args: + value: The environment variable value. + field_name: The field name. + + Returns: + The interpreted value. + + Raises: + EnvironmentVarValueError: If the path does not exist. + """ + path = Path(value) + if not path.exists(): + msg = f"Path does not exist: {path} for {field_name}" + raise EnvironmentVarValueError(msg) + return path + + +def interpret_path_env(value: str, field_name: str) -> Path: + """Interpret a path environment variable value. + + Args: + value: The environment variable value. + field_name: The field name. + + Returns: + The interpreted value. + """ + return Path(value) + + +def interpret_enum_env(value: str, field_type: GenericType, field_name: str) -> Any: + """Interpret an enum environment variable value. + + Args: + value: The environment variable value. + field_type: The field type. + field_name: The field name. + + Returns: + The interpreted value. + + Raises: + EnvironmentVarValueError: If the value is invalid. + """ + try: + return field_type(value) + except ValueError as ve: + msg = f"Invalid enum value: {value} for {field_name}" + raise EnvironmentVarValueError(msg) from ve + + +def interpret_env_var_value( + value: str, field_type: GenericType, field_name: str +) -> Any: + """Interpret an environment variable value based on the field type. + + Args: + value: The environment variable value. + field_type: The field type. + field_name: The field name. + + Returns: + The interpreted value. + + Raises: + ValueError: If the value is invalid. + """ + field_type = value_inside_optional(field_type) + + if is_union(field_type): + msg = f"Union types are not supported for environment variables: {field_name}." + raise ValueError(msg) + + if field_type is bool: + return interpret_boolean_env(value, field_name) + if field_type is str: + return value + if field_type is int: + return interpret_int_env(value, field_name) + if field_type is Path: + return interpret_path_env(value, field_name) + if field_type is ExistingPath: + return interpret_existing_path_env(value, field_name) + if get_origin(field_type) is list: + return [ + interpret_env_var_value( + v, + get_args(field_type)[0], + f"{field_name}[{i}]", + ) + for i, v in enumerate(value.split(":")) + ] + if inspect.isclass(field_type) and issubclass(field_type, enum.Enum): + return interpret_enum_env(value, field_type, field_name) + + msg = f"Invalid type for environment variable {field_name}: {field_type}. This is probably an issue in Reflex." + raise ValueError(msg) + + +T = TypeVar("T") + + +class EnvVar(Generic[T]): + """Environment variable.""" + + name: str + default: Any + type_: T + + def __init__(self, name: str, default: Any, type_: T) -> None: + """Initialize the environment variable. + + Args: + name: The environment variable name. + default: The default value. + type_: The type of the value. + """ + self.name = name + self.default = default + self.type_ = type_ + + def interpret(self, value: str) -> T: + """Interpret the environment variable value. + + Args: + value: The environment variable value. + + Returns: + The interpreted value. + """ + return interpret_env_var_value(value, self.type_, self.name) + + def getenv(self) -> T | None: + """Get the interpreted environment variable value. + + Returns: + The environment variable value. + """ + env_value = os.getenv(self.name, None) + if env_value and env_value.strip(): + return self.interpret(env_value) + return None + + def is_set(self) -> bool: + """Check if the environment variable is set. + + Returns: + True if the environment variable is set. + """ + return bool(os.getenv(self.name, "").strip()) + + def get(self) -> T: + """Get the interpreted environment variable value or the default value if not set. + + Returns: + The interpreted value. + """ + env_value = self.getenv() + if env_value is not None: + return env_value + return self.default + + def set(self, value: T | None) -> None: + """Set the environment variable. None unsets the variable. + + Args: + value: The value to set. + """ + if value is None: + _ = os.environ.pop(self.name, None) + else: + if isinstance(value, enum.Enum): + value = value.value + if isinstance(value, list): + str_value = ":".join(str(v) for v in value) + else: + str_value = str(value) + os.environ[self.name] = str_value + + +@lru_cache +def get_type_hints_environment(cls: type) -> dict[str, Any]: + """Get the type hints for the environment variables. + + Args: + cls: The class. + + Returns: + The type hints. + """ + return get_type_hints(cls) + + +class env_var: # noqa: N801 # pyright: ignore [reportRedeclaration] + """Descriptor for environment variables.""" + + name: str + default: Any + internal: bool = False + + def __init__(self, default: Any, internal: bool = False) -> None: + """Initialize the descriptor. + + Args: + default: The default value. + internal: Whether the environment variable is reflex internal. + """ + self.default = default + self.internal = internal + + def __set_name__(self, owner: Any, name: str): + """Set the name of the descriptor. + + Args: + owner: The owner class. + name: The name of the descriptor. + """ + self.name = name + + def __get__( + self, instance: EnvironmentVariables, owner: type[EnvironmentVariables] + ): + """Get the EnvVar instance. + + Args: + instance: The instance. + owner: The owner class. + + Returns: + The EnvVar instance. + """ + type_ = get_args(get_type_hints_environment(owner)[self.name])[0] + env_name = self.name + if self.internal: + env_name = f"__{env_name}" + return EnvVar(name=env_name, default=self.default, type_=type_) + + +if TYPE_CHECKING: + + def env_var(default: Any, internal: bool = False) -> EnvVar: + """Typing helper for the env_var descriptor. + + Args: + default: The default value. + internal: Whether the environment variable is reflex internal. + + Returns: + The EnvVar instance. + """ + return default + + +class PathExistsFlag: + """Flag to indicate that a path must exist.""" + + +ExistingPath = Annotated[Path, PathExistsFlag] + + +class PerformanceMode(enum.Enum): + """Performance mode for the app.""" + + WARN = "warn" + RAISE = "raise" + OFF = "off" + + +class ExecutorType(enum.Enum): + """Executor for compiling the frontend.""" + + THREAD = "thread" + PROCESS = "process" + MAIN_THREAD = "main_thread" + + @classmethod + def get_executor_from_environment(cls): + """Get the executor based on the environment variables. + + Returns: + The executor. + """ + from reflex.utils import console + + executor_type = environment.REFLEX_COMPILE_EXECUTOR.get() + + reflex_compile_processes = environment.REFLEX_COMPILE_PROCESSES.get() + reflex_compile_threads = environment.REFLEX_COMPILE_THREADS.get() + # By default, use the main thread. Unless the user has specified a different executor. + # Using a process pool is much faster, but not supported on all platforms. It's gated behind a flag. + if executor_type is None: + if ( + platform.system() not in ("Linux", "Darwin") + and reflex_compile_processes is not None + ): + console.warn("Multiprocessing is only supported on Linux and MacOS.") + + if ( + platform.system() in ("Linux", "Darwin") + and reflex_compile_processes is not None + ): + if reflex_compile_processes == 0: + console.warn( + "Number of processes must be greater than 0. If you want to use the default number of processes, set REFLEX_COMPILE_EXECUTOR to 'process'. Defaulting to None." + ) + reflex_compile_processes = None + elif reflex_compile_processes < 0: + console.warn( + "Number of processes must be greater than 0. Defaulting to None." + ) + reflex_compile_processes = None + executor_type = ExecutorType.PROCESS + elif reflex_compile_threads is not None: + if reflex_compile_threads == 0: + console.warn( + "Number of threads must be greater than 0. If you want to use the default number of threads, set REFLEX_COMPILE_EXECUTOR to 'thread'. Defaulting to None." + ) + reflex_compile_threads = None + elif reflex_compile_threads < 0: + console.warn( + "Number of threads must be greater than 0. Defaulting to None." + ) + reflex_compile_threads = None + executor_type = ExecutorType.THREAD + else: + executor_type = ExecutorType.MAIN_THREAD + + match executor_type: + case ExecutorType.PROCESS: + executor = concurrent.futures.ProcessPoolExecutor( + max_workers=reflex_compile_processes, + mp_context=multiprocessing.get_context("fork"), + ) + case ExecutorType.THREAD: + executor = concurrent.futures.ThreadPoolExecutor( + max_workers=reflex_compile_threads + ) + case ExecutorType.MAIN_THREAD: + FUTURE_RESULT_TYPE = TypeVar("FUTURE_RESULT_TYPE") + + class MainThreadExecutor: + def __enter__(self): + return self + + def __exit__(self, *args): + pass + + def submit( + self, fn: Callable[..., FUTURE_RESULT_TYPE], *args, **kwargs + ) -> concurrent.futures.Future[FUTURE_RESULT_TYPE]: + future_job = concurrent.futures.Future() + future_job.set_result(fn(*args, **kwargs)) + return future_job + + executor = MainThreadExecutor() + + return executor + + +class EnvironmentVariables: + """Environment variables class to instantiate environment variables.""" + + # Indicate the current command that was invoked in the reflex CLI. + REFLEX_COMPILE_CONTEXT: EnvVar[constants.CompileContext] = env_var( + constants.CompileContext.UNDEFINED, internal=True + ) + + # Whether to use npm over bun to install and run the frontend. + REFLEX_USE_NPM: EnvVar[bool] = env_var(False) + + # The npm registry to use. + NPM_CONFIG_REGISTRY: EnvVar[str | None] = env_var(None) + + # Whether to use Granian for the backend. By default, the backend uses Uvicorn if available. + REFLEX_USE_GRANIAN: EnvVar[bool] = env_var(False) + + # Whether to use the system installed bun. If set to false, bun will be bundled with the app. + REFLEX_USE_SYSTEM_BUN: EnvVar[bool] = env_var(False) + + # The working directory for the next.js commands. + REFLEX_WEB_WORKDIR: EnvVar[Path] = env_var(Path(constants.Dirs.WEB)) + + # The working directory for the states directory. + REFLEX_STATES_WORKDIR: EnvVar[Path] = env_var(Path(constants.Dirs.STATES)) + + # Path to the alembic config file + ALEMBIC_CONFIG: EnvVar[ExistingPath] = env_var(Path(constants.ALEMBIC_CONFIG)) + + # Disable SSL verification for HTTPX requests. + SSL_NO_VERIFY: EnvVar[bool] = env_var(False) + + # The directory to store uploaded files. + REFLEX_UPLOADED_FILES_DIR: EnvVar[Path] = env_var( + Path(constants.Dirs.UPLOADED_FILES) + ) + + REFLEX_COMPILE_EXECUTOR: EnvVar[ExecutorType | None] = env_var(None) + + # Whether to use separate processes to compile the frontend and how many. If not set, defaults to thread executor. + REFLEX_COMPILE_PROCESSES: EnvVar[int | None] = env_var(None) + + # Whether to use separate threads to compile the frontend and how many. Defaults to `min(32, os.cpu_count() + 4)`. + REFLEX_COMPILE_THREADS: EnvVar[int | None] = env_var(None) + + # The directory to store reflex dependencies. + REFLEX_DIR: EnvVar[Path] = env_var(constants.Reflex.DIR) + + # Whether to print the SQL queries if the log level is INFO or lower. + SQLALCHEMY_ECHO: EnvVar[bool] = env_var(False) + + # Whether to check db connections before using them. + SQLALCHEMY_POOL_PRE_PING: EnvVar[bool] = env_var(True) + + # Whether to ignore the redis config error. Some redis servers only allow out-of-band configuration. + REFLEX_IGNORE_REDIS_CONFIG_ERROR: EnvVar[bool] = env_var(False) + + # Whether to skip purging the web directory in dev mode. + REFLEX_PERSIST_WEB_DIR: EnvVar[bool] = env_var(False) + + # The reflex.build frontend host. + REFLEX_BUILD_FRONTEND: EnvVar[str] = env_var( + constants.Templates.REFLEX_BUILD_FRONTEND + ) + + # The reflex.build backend host. + REFLEX_BUILD_BACKEND: EnvVar[str] = env_var( + constants.Templates.REFLEX_BUILD_BACKEND + ) + + # This env var stores the execution mode of the app + REFLEX_ENV_MODE: EnvVar[constants.Env] = env_var(constants.Env.DEV) + + # Whether to run the backend only. Exclusive with REFLEX_FRONTEND_ONLY. + REFLEX_BACKEND_ONLY: EnvVar[bool] = env_var(False) + + # Whether to run the frontend only. Exclusive with REFLEX_BACKEND_ONLY. + REFLEX_FRONTEND_ONLY: EnvVar[bool] = env_var(False) + + # The port to run the frontend on. + REFLEX_FRONTEND_PORT: EnvVar[int | None] = env_var(None) + + # The port to run the backend on. + REFLEX_BACKEND_PORT: EnvVar[int | None] = env_var(None) + + # If this env var is set to "yes", App.compile will be a no-op + REFLEX_SKIP_COMPILE: EnvVar[bool] = env_var(False, internal=True) + + # Whether to run app harness tests in headless mode. + APP_HARNESS_HEADLESS: EnvVar[bool] = env_var(False) + + # Which app harness driver to use. + APP_HARNESS_DRIVER: EnvVar[str] = env_var("Chrome") + + # Arguments to pass to the app harness driver. + APP_HARNESS_DRIVER_ARGS: EnvVar[str] = env_var("") + + # Whether to check for outdated package versions. + REFLEX_CHECK_LATEST_VERSION: EnvVar[bool] = env_var(True) + + # In which performance mode to run the app. + REFLEX_PERF_MODE: EnvVar[PerformanceMode] = env_var(PerformanceMode.WARN) + + # The maximum size of the reflex state in kilobytes. + REFLEX_STATE_SIZE_LIMIT: EnvVar[int] = env_var(1000) + + # Whether to use the turbopack bundler. + REFLEX_USE_TURBOPACK: EnvVar[bool] = env_var(False) + + # Additional paths to include in the hot reload. Separated by a colon. + REFLEX_HOT_RELOAD_INCLUDE_PATHS: EnvVar[list[Path]] = env_var([]) + + # Paths to exclude from the hot reload. Takes precedence over include paths. Separated by a colon. + REFLEX_HOT_RELOAD_EXCLUDE_PATHS: EnvVar[list[Path]] = env_var([]) + + # Enables different behavior for when the backend would do a cold start if it was inactive. + REFLEX_DOES_BACKEND_COLD_START: EnvVar[bool] = env_var(False) + + # The timeout for the backend to do a cold start in seconds. + REFLEX_BACKEND_COLD_START_TIMEOUT: EnvVar[int] = env_var(10) + + # Used by flexgen to enumerate the pages. + REFLEX_ADD_ALL_ROUTES_ENDPOINT: EnvVar[bool] = env_var(False) + + # The address to bind the HTTP client to. You can set this to "::" to enable IPv6. + REFLEX_HTTP_CLIENT_BIND_ADDRESS: EnvVar[str | None] = env_var(None) + + # Maximum size of the message in the websocket server in bytes. + REFLEX_SOCKET_MAX_HTTP_BUFFER_SIZE: EnvVar[int] = env_var( + constants.POLLING_MAX_HTTP_BUFFER_SIZE + ) + + # The interval to send a ping to the websocket server in seconds. + REFLEX_SOCKET_INTERVAL: EnvVar[int] = env_var(constants.Ping.INTERVAL) + + # The timeout to wait for a pong from the websocket server in seconds. + REFLEX_SOCKET_TIMEOUT: EnvVar[int] = env_var(constants.Ping.TIMEOUT) + + # Whether to run Granian in a spawn process. This enables Reflex to pick up on environment variable changes between hot reloads. + REFLEX_STRICT_HOT_RELOAD: EnvVar[bool] = env_var(False) + + # The path to the reflex log file. If not set, the log file will be stored in the reflex user directory. + REFLEX_LOG_FILE: EnvVar[Path | None] = env_var(None) + + # Enable full logging of debug messages to reflex user directory. + REFLEX_ENABLE_FULL_LOGGING: EnvVar[bool] = env_var(False) + + +environment = EnvironmentVariables() diff --git a/reflex/istate/manager.py b/reflex/istate/manager.py index dfe10cef333..d543331549f 100644 --- a/reflex/istate/manager.py +++ b/reflex/istate/manager.py @@ -17,7 +17,8 @@ from typing_extensions import override from reflex import constants -from reflex.config import environment, get_config +from reflex.config import get_config +from reflex.environment import environment from reflex.state import BaseState, _split_substate_key, _substate_key from reflex.utils import console, path_ops, prerequisites from reflex.utils.exceptions import ( diff --git a/reflex/model.py b/reflex/model.py index 6c82b199a22..b93e7a56143 100644 --- a/reflex/model.py +++ b/reflex/model.py @@ -21,7 +21,8 @@ from alembic.runtime.migration import MigrationContext from reflex.base import Base -from reflex.config import environment, get_config +from reflex.config import get_config +from reflex.environment import environment from reflex.utils import console from reflex.utils.compat import sqlmodel, sqlmodel_field_has_primary_key diff --git a/reflex/reflex.py b/reflex/reflex.py index 69653b05cf8..f587d3a20e1 100644 --- a/reflex/reflex.py +++ b/reflex/reflex.py @@ -11,9 +11,10 @@ from reflex_cli.v2.deployments import hosting_cli from reflex import constants -from reflex.config import environment, get_config +from reflex.config import get_config from reflex.constants.base import LITERAL_ENV from reflex.custom_components.custom_components import custom_components_cli +from reflex.environment import environment from reflex.state import reset_disk_state_manager from reflex.utils import console, redir, telemetry from reflex.utils.exec import should_use_granian diff --git a/reflex/state.py b/reflex/state.py index e2c516e390a..4e407c1303a 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -38,7 +38,7 @@ import reflex.istate.dynamic from reflex import constants, event from reflex.base import Base -from reflex.config import PerformanceMode, environment +from reflex.environment import PerformanceMode, environment from reflex.event import ( BACKGROUND_TASK_MARKER, Event, diff --git a/reflex/testing.py b/reflex/testing.py index ac8dd0bbf04..0540ea67d30 100644 --- a/reflex/testing.py +++ b/reflex/testing.py @@ -28,6 +28,7 @@ import uvicorn import reflex +import reflex.environment import reflex.reflex import reflex.utils.build import reflex.utils.exec @@ -35,7 +36,8 @@ import reflex.utils.prerequisites import reflex.utils.processes from reflex.components.component import CustomComponent -from reflex.config import environment, get_config +from reflex.config import get_config +from reflex.environment import environment from reflex.state import ( BaseState, StateManager, diff --git a/reflex/utils/console.py b/reflex/utils/console.py index f317f481ca4..fa98ab2917e 100644 --- a/reflex/utils/console.py +++ b/reflex/utils/console.py @@ -101,7 +101,7 @@ def log_file_console(): Returns: A Console object that logs to a file. """ - from reflex.config import environment + from reflex.environment import environment if not (env_log_file := environment.REFLEX_LOG_FILE.get()): subseconds = int((time.time() % 1) * 1000) @@ -123,7 +123,7 @@ def should_use_log_file_console() -> bool: Returns: True if the log file console should be used, False otherwise. """ - from reflex.config import environment + from reflex.environment import environment return environment.REFLEX_ENABLE_FULL_LOGGING.get() diff --git a/reflex/utils/exec.py b/reflex/utils/exec.py index b7317061382..b5a9dd4c58b 100644 --- a/reflex/utils/exec.py +++ b/reflex/utils/exec.py @@ -18,8 +18,9 @@ import psutil from reflex import constants -from reflex.config import environment, get_config +from reflex.config import get_config from reflex.constants.base import LogLevel +from reflex.environment import environment from reflex.utils import console, path_ops from reflex.utils.decorator import once from reflex.utils.prerequisites import get_web_dir diff --git a/reflex/utils/export.py b/reflex/utils/export.py index 38493076cb7..0a557e9e996 100644 --- a/reflex/utils/export.py +++ b/reflex/utils/export.py @@ -3,7 +3,8 @@ from pathlib import Path from reflex import constants -from reflex.config import environment, get_config +from reflex.config import get_config +from reflex.environment import environment from reflex.utils import build, console, exec, prerequisites, telemetry diff --git a/reflex/utils/net.py b/reflex/utils/net.py index f90190867e6..67475f97b19 100644 --- a/reflex/utils/net.py +++ b/reflex/utils/net.py @@ -19,7 +19,7 @@ def _httpx_verify_kwarg() -> bool: Returns: True if SSL verification is enabled, False otherwise """ - from reflex.config import environment + from reflex.environment import environment return not environment.SSL_NO_VERIFY.get() @@ -131,7 +131,7 @@ def _httpx_local_address_kwarg() -> str: Returns: The local address to bind to """ - from reflex.config import environment + from reflex.environment import environment return environment.REFLEX_HTTP_CLIENT_BIND_ADDRESS.get() or ( "::" if _should_use_ipv6() else "0.0.0.0" diff --git a/reflex/utils/path_ops.py b/reflex/utils/path_ops.py index c8cd8315fcc..152fac596fa 100644 --- a/reflex/utils/path_ops.py +++ b/reflex/utils/path_ops.py @@ -9,7 +9,8 @@ import stat from pathlib import Path -from reflex.config import environment, get_config +from reflex.config import get_config +from reflex.environment import environment # Shorthand for join. join = os.linesep.join diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index b24e0c20c4a..0c6da4b51be 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -36,7 +36,8 @@ from reflex import constants, model from reflex.compiler import templates -from reflex.config import Config, environment, get_config +from reflex.config import Config, get_config +from reflex.environment import environment from reflex.utils import console, net, path_ops, processes, redir from reflex.utils.decorator import once from reflex.utils.exceptions import SystemPackageMissingError diff --git a/reflex/utils/processes.py b/reflex/utils/processes.py index 24bc6099846..8f98741331a 100644 --- a/reflex/utils/processes.py +++ b/reflex/utils/processes.py @@ -19,7 +19,7 @@ from rich.progress import Progress from reflex import constants -from reflex.config import environment +from reflex.environment import environment from reflex.utils import console, path_ops, prerequisites from reflex.utils.registry import get_npm_registry diff --git a/reflex/utils/registry.py b/reflex/utils/registry.py index a67a394ae6d..287cd3cfa62 100644 --- a/reflex/utils/registry.py +++ b/reflex/utils/registry.py @@ -2,7 +2,7 @@ import httpx -from reflex.config import environment +from reflex.environment import environment from reflex.utils import console, net from reflex.utils.decorator import once diff --git a/reflex/utils/telemetry.py b/reflex/utils/telemetry.py index 325cdd74618..7501093e4eb 100644 --- a/reflex/utils/telemetry.py +++ b/reflex/utils/telemetry.py @@ -16,7 +16,7 @@ import psutil from reflex import constants -from reflex.config import environment +from reflex.environment import environment from reflex.utils import console from reflex.utils.decorator import once_unless_none from reflex.utils.exceptions import ReflexError diff --git a/tests/integration/test_connection_banner.py b/tests/integration/test_connection_banner.py index 4f87bef6c9a..b852d43ca71 100644 --- a/tests/integration/test_connection_banner.py +++ b/tests/integration/test_connection_banner.py @@ -7,7 +7,7 @@ from selenium.webdriver.common.by import By from reflex import constants -from reflex.config import environment +from reflex.environment import environment from reflex.testing import AppHarness, WebDriver from .utils import SessionStorage diff --git a/tests/units/test_config.py b/tests/units/test_config.py index 1ad3002e9ad..3bfcf147be1 100644 --- a/tests/units/test_config.py +++ b/tests/units/test_config.py @@ -8,7 +8,8 @@ import reflex as rx import reflex.config -from reflex.config import ( +from reflex.constants import Endpoint, Env +from reflex.environment import ( EnvVar, env_var, environment, @@ -16,7 +17,6 @@ interpret_enum_env, interpret_int_env, ) -from reflex.constants import Endpoint, Env def test_requires_app_name(): diff --git a/tests/units/test_var.py b/tests/units/test_var.py index f7562c7acf7..de2896311bd 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -11,8 +11,8 @@ import reflex as rx from reflex.base import Base -from reflex.config import PerformanceMode from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG +from reflex.environment import PerformanceMode from reflex.state import BaseState from reflex.utils.exceptions import ( PrimitiveUnserializableToJSONError, diff --git a/tests/units/utils/test_utils.py b/tests/units/utils/test_utils.py index 216f900d5d5..a1398fdd686 100644 --- a/tests/units/utils/test_utils.py +++ b/tests/units/utils/test_utils.py @@ -11,7 +11,7 @@ from pytest_mock import MockerFixture from reflex import constants -from reflex.config import environment +from reflex.environment import environment from reflex.event import EventHandler from reflex.state import BaseState from reflex.utils import build, prerequisites, types