implement default_factory for EnvVar, improve env_var typing

also migrate environment to be a class singleton to prevent unintended chaos with default factories
This commit is contained in:
Benedikt Bartscher 2024-11-01 22:48:36 +01:00 committed by Masen Furer
parent db2b5b0320
commit d32604713c
No known key found for this signature in database
GPG Key ID: B0008AD22B3B3A95
22 changed files with 193 additions and 144 deletions

View File

@ -67,7 +67,7 @@ from reflex.components.core.client_side_routing import (
)
from reflex.components.core.upload import Upload, get_upload_dir
from reflex.components.radix import themes
from reflex.config import environment, get_config
from reflex.config import EnvironmentVariables, get_config
from reflex.event import (
Event,
EventHandler,
@ -506,7 +506,10 @@ class App(MiddlewareMixin, LifespanMixin):
# Check if the route given is valid
verify_route_validity(route)
if route in self.unevaluated_pages and environment.RELOAD_CONFIG.is_set():
if (
route in self.unevaluated_pages
and EnvironmentVariables.RELOAD_CONFIG.is_set()
):
# when the app is reloaded(typically for app harness tests), we should maintain
# the latest render function of a route.This applies typically to decorated pages
# since they are only added when app._compile is called.
@ -723,7 +726,7 @@ class App(MiddlewareMixin, LifespanMixin):
Whether the app should be compiled.
"""
# Check the environment variable.
if environment.REFLEX_SKIP_COMPILE.get():
if EnvironmentVariables.REFLEX_SKIP_COMPILE.get():
return False
nocompile = prerequisites.get_web_dir() / constants.NOCOMPILE_FILE
@ -946,7 +949,10 @@ class App(MiddlewareMixin, LifespanMixin):
executor = None
if (
platform.system() in ("Linux", "Darwin")
and (number_of_processes := environment.REFLEX_COMPILE_PROCESSES.get())
and (
number_of_processes
:= EnvironmentVariables.REFLEX_COMPILE_PROCESSES.get()
)
is not None
):
executor = concurrent.futures.ProcessPoolExecutor(
@ -955,7 +961,7 @@ class App(MiddlewareMixin, LifespanMixin):
)
else:
executor = concurrent.futures.ThreadPoolExecutor(
max_workers=environment.REFLEX_COMPILE_THREADS.get()
max_workers=EnvironmentVariables.REFLEX_COMPILE_THREADS.get()
)
for route, component in zip(self.pages, page_components):

View File

@ -16,7 +16,7 @@ from reflex.components.component import (
CustomComponent,
StatefulComponent,
)
from reflex.config import environment, get_config
from reflex.config import EnvironmentVariables, get_config
from reflex.state import BaseState
from reflex.style import SYSTEM_COLOR_MODE
from reflex.utils.exec import is_prod_mode
@ -527,7 +527,7 @@ def remove_tailwind_from_postcss() -> tuple[str, str]:
def purge_web_pages_dir():
"""Empty out .web/pages directory."""
if not is_prod_mode() and environment.REFLEX_PERSIST_WEB_DIR.get():
if not is_prod_mode() and EnvironmentVariables.REFLEX_PERSIST_WEB_DIR.get():
# Skip purging the web directory in dev mode if REFLEX_PERSIST_WEB_DIR is set.
return

View File

@ -13,7 +13,7 @@ from reflex.components.component import (
)
from reflex.components.el.elements.forms import Input
from reflex.components.radix.themes.layout.box import Box
from reflex.config import environment
from reflex.config import EnvironmentVariables
from reflex.constants import Dirs
from reflex.constants.compiler import Hooks, Imports
from reflex.event import (
@ -132,7 +132,7 @@ def get_upload_dir() -> Path:
"""
Upload.is_used = True
uploaded_files_dir = environment.REFLEX_UPLOADED_FILES_DIR.get()
uploaded_files_dir = EnvironmentVariables.REFLEX_UPLOADED_FILES_DIR.get()
uploaded_files_dir.mkdir(parents=True, exist_ok=True)
return uploaded_files_dir

View File

@ -13,6 +13,7 @@ from pathlib import Path
from typing import (
TYPE_CHECKING,
Any,
Callable,
Dict,
Generic,
List,
@ -312,26 +313,47 @@ def interpret_env_var_value(
T = TypeVar("T")
ENV_VAR_DEFAULT_FACTORY = Callable[[], T]
class EnvVar(Generic[T]):
"""Environment variable."""
name: str
default: Any
default_factory: Optional[ENV_VAR_DEFAULT_FACTORY]
type_: T
def __init__(self, name: str, default: Any, type_: T) -> None:
def __init__(
self,
name: str,
default: Any,
default_factory: Optional[ENV_VAR_DEFAULT_FACTORY],
type_: T,
) -> None:
"""Initialize the environment variable.
Args:
name: The environment variable name.
default: The default value.
default_factory: The default factory.
type_: The type of the value.
"""
self.name = name
self.default = default
self.default_factory = default_factory
self.type_ = type_
def get_default(self) -> T:
"""Get the default value.
Returns:
The default value.
"""
if self.default_factory is not None:
return self.default_factory()
return self.default
def interpret(self, value: str) -> T:
"""Interpret the environment variable value.
@ -371,7 +393,7 @@ class EnvVar(Generic[T]):
env_value = self.getenv()
if env_value is not None:
return env_value
return self.default
return self.get_default()
def set(self, value: T | None) -> None:
"""Set the environment variable. None unsets the variable.
@ -392,16 +414,24 @@ class env_var: # type: ignore
name: str
default: Any
default_factory: Optional[ENV_VAR_DEFAULT_FACTORY]
internal: bool = False
def __init__(self, default: Any, internal: bool = False) -> None:
def __init__(
self,
default: Any = None,
default_factory: Optional[ENV_VAR_DEFAULT_FACTORY] = None,
internal: bool = False,
) -> None:
"""Initialize the descriptor.
Args:
default: The default value.
default_factory: The default factory.
internal: Whether the environment variable is reflex internal.
"""
self.default = default
self.default_factory = default_factory
self.internal = internal
def __set_name__(self, owner, name):
@ -427,22 +457,30 @@ class env_var: # type: ignore
env_name = self.name
if self.internal:
env_name = f"__{env_name}"
return EnvVar(name=env_name, default=self.default, type_=type_)
return EnvVar(
name=env_name,
default=self.default,
type_=type_,
default_factory=self.default_factory,
)
if TYPE_CHECKING:
if TYPE_CHECKING:
def __new__(
cls,
default: Optional[T] = None,
default_factory: Optional[ENV_VAR_DEFAULT_FACTORY[T]] = None,
internal: bool = False,
) -> EnvVar[T]:
"""Create a new EnvVar instance.
def env_var(default, internal=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
Args:
cls: The class.
default: The default value.
default_factory: The default factory.
internal: Whether the environment variable is reflex internal.
"""
...
class PathExistsFlag:
@ -455,6 +493,16 @@ ExistingPath = Annotated[Path, PathExistsFlag]
class EnvironmentVariables:
"""Environment variables class to instantiate environment variables."""
def __init__(self):
"""Initialize the environment variables.
Raises:
NotImplementedError: Always.
"""
raise NotImplementedError(
f"{type(self).__name__} is a class singleton and not meant to be instantiated."
)
# Whether to use npm over bun to install frontend packages.
REFLEX_USE_NPM: EnvVar[bool] = env_var(False)
@ -545,11 +593,13 @@ class EnvironmentVariables:
# Where to save screenshots when tests fail.
SCREENSHOT_DIR: EnvVar[Optional[Path]] = env_var(None)
# Whether to minify state names.
REFLEX_MINIFY_STATES: EnvVar[Optional[bool]] = env_var(False)
environment = EnvironmentVariables()
# Whether to minify state names. Default to true in prod mode and false otherwise.
REFLEX_MINIFY_STATES: EnvVar[Optional[bool]] = env_var(
default_factory=lambda: (
EnvironmentVariables.REFLEX_ENV_MODE.get() == constants.Env.PROD
)
or False
)
class Config(Base):

View File

@ -109,10 +109,10 @@ class Templates(SimpleNamespace):
Returns:
The URL to redirect to reflex.build.
"""
from reflex.config import environment
from reflex.config import EnvironmentVariables
return (
environment.REFLEX_BUILD_FRONTEND.get()
EnvironmentVariables.REFLEX_BUILD_FRONTEND.get()
+ "/gen?reflex_init_token={reflex_init_token}"
)
@ -124,9 +124,12 @@ class Templates(SimpleNamespace):
Returns:
The URL to poll waiting for the user to select a generation.
"""
from reflex.config import environment
from reflex.config import EnvironmentVariables
return environment.REFLEX_BUILD_BACKEND.get() + "/api/init/{reflex_init_token}"
return (
EnvironmentVariables.REFLEX_BUILD_BACKEND.get()
+ "/api/init/{reflex_init_token}"
)
@classproperty
@classmethod
@ -136,10 +139,10 @@ class Templates(SimpleNamespace):
Returns:
The URL to fetch the generation's reflex code.
"""
from reflex.config import environment
from reflex.config import EnvironmentVariables
return (
environment.REFLEX_BUILD_BACKEND.get()
EnvironmentVariables.REFLEX_BUILD_BACKEND.get()
+ "/api/gen/{generation_hash}/refactored"
)

View File

@ -5,7 +5,7 @@ from enum import Enum
from types import SimpleNamespace
from reflex.base import Base
from reflex.constants import Dirs, Env
from reflex.constants import Dirs
from reflex.utils.imports import ImportVar
# The prefix used to create setters for state vars.
@ -14,9 +14,6 @@ SETTER_PREFIX = "set_"
# The file used to specify no compilation.
NOCOMPILE_FILE = "nocompile"
# The env var to toggle minification of states.
ENV_MINIFY_STATES = "REFLEX_MINIFY_STATES"
class Ext(SimpleNamespace):
"""Extension used in Reflex."""
@ -33,22 +30,6 @@ class Ext(SimpleNamespace):
EXE = ".exe"
def minify_states() -> bool:
"""Whether to minify states.
Returns:
True if states should be minified.
"""
from reflex.config import environment
env = environment.REFLEX_MINIFY_STATES.get()
if env is not None:
return env
# minify states in prod by default
return environment.REFLEX_ENV_MODE.get() == Env.PROD
class CompileVars(SimpleNamespace):
"""The variables used during compilation."""
@ -81,15 +62,6 @@ class CompileVars(SimpleNamespace):
# The name of the function for converting a dict to an event.
TO_EVENT = "Event"
@classmethod
def MINIFY_STATES(cls) -> bool:
"""Whether to minify states.
Returns:
True if states should be minified.
"""
return minify_states()
class PageNames(SimpleNamespace):
"""The name of basic pages deployed in NextJS."""

View File

@ -61,9 +61,9 @@ class Bun(SimpleNamespace):
Returns:
The directory to store the bun.
"""
from reflex.config import environment
from reflex.config import EnvironmentVariables
return environment.REFLEX_DIR.get() / "bun"
return EnvironmentVariables.REFLEX_DIR.get() / "bun"
@classproperty
@classmethod
@ -98,9 +98,9 @@ class Fnm(SimpleNamespace):
Returns:
The directory to store fnm.
"""
from reflex.config import environment
from reflex.config import EnvironmentVariables
return environment.REFLEX_DIR.get() / "fnm"
return EnvironmentVariables.REFLEX_DIR.get() / "fnm"
@classproperty
@classmethod

View File

@ -17,7 +17,7 @@ import typer
from tomlkit.exceptions import TOMLKitError
from reflex import constants
from reflex.config import environment, get_config
from reflex.config import EnvironmentVariables, get_config
from reflex.constants import CustomComponents
from reflex.utils import console
@ -609,14 +609,14 @@ def publish(
help="The API token to use for authentication on python package repository. If token is provided, no username/password should be provided at the same time",
),
username: Optional[str] = typer.Option(
environment.TWINE_USERNAME.get(),
EnvironmentVariables.TWINE_USERNAME.get(),
"-u",
"--username",
show_default="TWINE_USERNAME environment variable value if set",
help="The username to use for authentication on python package repository. Username and password must both be provided.",
),
password: Optional[str] = typer.Option(
environment.TWINE_PASSWORD.get(),
EnvironmentVariables.TWINE_PASSWORD.get(),
"-p",
"--password",
show_default="TWINE_PASSWORD environment variable value if set",

View File

@ -17,7 +17,7 @@ import sqlalchemy.exc
import sqlalchemy.orm
from reflex.base import Base
from reflex.config import environment, get_config
from reflex.config import EnvironmentVariables, get_config
from reflex.utils import console
from reflex.utils.compat import sqlmodel, sqlmodel_field_has_primary_key
@ -38,12 +38,12 @@ def get_engine(url: str | None = None) -> sqlalchemy.engine.Engine:
url = url or conf.db_url
if url is None:
raise ValueError("No database url configured")
if not environment.ALEMBIC_CONFIG.get().exists():
if not EnvironmentVariables.ALEMBIC_CONFIG.get().exists():
console.warn(
"Database is not initialized, run [bold]reflex db init[/bold] first."
)
# Print the SQL queries if the log level is INFO or lower.
echo_db_query = environment.SQLALCHEMY_ECHO.get()
echo_db_query = EnvironmentVariables.SQLALCHEMY_ECHO.get()
# Needed for the admin dash on sqlite.
connect_args = {"check_same_thread": False} if url.startswith("sqlite") else {}
return sqlmodel.create_engine(url, echo=echo_db_query, connect_args=connect_args)
@ -231,7 +231,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
Returns:
tuple of (config, script_directory)
"""
config = alembic.config.Config(environment.ALEMBIC_CONFIG.get())
config = alembic.config.Config(EnvironmentVariables.ALEMBIC_CONFIG.get())
return config, alembic.script.ScriptDirectory(
config.get_main_option("script_location", default="version"),
)
@ -266,8 +266,8 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
def alembic_init(cls):
"""Initialize alembic for the project."""
alembic.command.init(
config=alembic.config.Config(environment.ALEMBIC_CONFIG.get()),
directory=str(environment.ALEMBIC_CONFIG.get().parent / "alembic"),
config=alembic.config.Config(EnvironmentVariables.ALEMBIC_CONFIG.get()),
directory=str(EnvironmentVariables.ALEMBIC_CONFIG.get().parent / "alembic"),
)
@classmethod
@ -287,7 +287,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
Returns:
True when changes have been detected.
"""
if not environment.ALEMBIC_CONFIG.get().exists():
if not EnvironmentVariables.ALEMBIC_CONFIG.get().exists():
return False
config, script_directory = cls._alembic_config()
@ -388,7 +388,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
True - indicating the process was successful.
None - indicating the process was skipped.
"""
if not environment.ALEMBIC_CONFIG.get().exists():
if not EnvironmentVariables.ALEMBIC_CONFIG.get().exists():
return
with cls.get_db_engine().connect() as connection:

View File

@ -13,7 +13,7 @@ from reflex_cli.deployments import deployments_cli
from reflex_cli.utils import dependency
from reflex import constants
from reflex.config import environment, get_config
from reflex.config import EnvironmentVariables, get_config
from reflex.custom_components.custom_components import custom_components_cli
from reflex.state import reset_disk_state_manager
from reflex.utils import console, redir, telemetry
@ -160,7 +160,7 @@ def _run(
console.set_log_level(loglevel)
# Set env mode in the environment
environment.REFLEX_ENV_MODE.set(env)
EnvironmentVariables.REFLEX_ENV_MODE.set(env)
# Show system info
exec.output_system_info()
@ -277,13 +277,13 @@ def run(
False,
"--frontend-only",
help="Execute only frontend.",
envvar=environment.REFLEX_FRONTEND_ONLY.name,
envvar=EnvironmentVariables.REFLEX_FRONTEND_ONLY.name,
),
backend: bool = typer.Option(
False,
"--backend-only",
help="Execute only backend.",
envvar=environment.REFLEX_BACKEND_ONLY.name,
envvar=EnvironmentVariables.REFLEX_BACKEND_ONLY.name,
),
frontend_port: str = typer.Option(
config.frontend_port, help="Specify a different frontend port."
@ -302,8 +302,8 @@ def run(
if frontend and backend:
console.error("Cannot use both --frontend-only and --backend-only options.")
raise typer.Exit(1)
environment.REFLEX_BACKEND_ONLY.set(backend)
environment.REFLEX_FRONTEND_ONLY.set(frontend)
EnvironmentVariables.REFLEX_BACKEND_ONLY.set(backend)
EnvironmentVariables.REFLEX_FRONTEND_ONLY.set(frontend)
_run(env, frontend, backend, frontend_port, backend_port, backend_host, loglevel)
@ -405,7 +405,7 @@ script_cli = typer.Typer()
def _skip_compile():
"""Skip the compile step."""
environment.REFLEX_SKIP_COMPILE.set(True)
EnvironmentVariables.REFLEX_SKIP_COMPILE.set(True)
@db_cli.command(name="init")
@ -420,7 +420,7 @@ def db_init():
return
# Check the alembic config.
if environment.ALEMBIC_CONFIG.get().exists():
if EnvironmentVariables.ALEMBIC_CONFIG.get().exists():
console.error(
"Database is already initialized. Use "
"[bold]reflex db makemigrations[/bold] to create schema change "

View File

@ -69,7 +69,7 @@ from redis.exceptions import ResponseError
import reflex.istate.dynamic
from reflex import constants
from reflex.base import Base
from reflex.config import environment
from reflex.config import EnvironmentVariables
from reflex.event import (
BACKGROUND_TASK_MARKER,
Event,
@ -932,7 +932,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
"""
module = cls.__module__.replace(".", "___")
state_name = format.to_snake_case(f"{module}___{cls.__name__}")
if constants.compiler.CompileVars.MINIFY_STATES():
if EnvironmentVariables.REFLEX_MINIFY_STATES.get():
return get_minified_state_name(state_name)
return state_name
@ -3435,7 +3435,7 @@ class StateManagerRedis(StateManager):
)
except ResponseError:
# Some redis servers only allow out-of-band configuration, so ignore errors here.
if not environment.REFLEX_IGNORE_REDIS_CONFIG_ERROR.get():
if not EnvironmentVariables.REFLEX_IGNORE_REDIS_CONFIG_ERROR.get():
raise
async with self.redis.pubsub() as pubsub:
await pubsub.psubscribe(lock_key_channel)

View File

@ -44,7 +44,7 @@ import reflex.utils.format
import reflex.utils.prerequisites
import reflex.utils.processes
from reflex.components.component import StatefulComponent
from reflex.config import environment
from reflex.config import EnvironmentVariables
from reflex.state import (
BaseState,
State,
@ -199,7 +199,7 @@ class AppHarness:
state_name = reflex.utils.format.to_snake_case(
f"{self.app_name}___{self.app_name}___" + state_cls_name
)
if reflex.constants.CompileVars.MINIFY_STATES():
if EnvironmentVariables.REFLEX_MINIFY_STATES.get():
return minified_state_names.get(state_name, state_name)
return state_name
@ -626,10 +626,10 @@ class AppHarness:
if self.frontend_url is None:
raise RuntimeError("Frontend is not running.")
want_headless = False
if environment.APP_HARNESS_HEADLESS.get():
if EnvironmentVariables.APP_HARNESS_HEADLESS.get():
want_headless = True
if driver_clz is None:
requested_driver = environment.APP_HARNESS_DRIVER.get()
requested_driver = EnvironmentVariables.APP_HARNESS_DRIVER.get()
driver_clz = getattr(webdriver, requested_driver)
if driver_options is None:
driver_options = getattr(webdriver, f"{requested_driver}Options")()
@ -651,7 +651,7 @@ class AppHarness:
driver_options.add_argument("headless")
if driver_options is None:
raise RuntimeError(f"Could not determine options for {driver_clz}")
if args := environment.APP_HARNESS_DRIVER_ARGS.get():
if args := EnvironmentVariables.APP_HARNESS_DRIVER_ARGS.get():
for arg in args.split(","):
driver_options.add_argument(arg)
if driver_option_args is not None:
@ -958,7 +958,7 @@ class AppHarnessProd(AppHarness):
def _start_backend(self):
if self.app_instance is None:
raise RuntimeError("App was not initialized.")
environment.REFLEX_SKIP_COMPILE.set(True)
EnvironmentVariables.REFLEX_SKIP_COMPILE.set(True)
self.backend = uvicorn.Server(
uvicorn.Config(
app=self.app_instance,
@ -976,7 +976,7 @@ class AppHarnessProd(AppHarness):
try:
return super()._poll_for_servers(timeout)
finally:
environment.REFLEX_SKIP_COMPILE.set(None)
EnvironmentVariables.REFLEX_SKIP_COMPILE.set(None)
@override
def start(self) -> AppHarnessProd:
@ -985,7 +985,7 @@ class AppHarnessProd(AppHarness):
Returns:
self
"""
environment.REFLEX_ENV_MODE.set(reflex.constants.base.Env.PROD)
EnvironmentVariables.REFLEX_ENV_MODE.set(reflex.constants.base.Env.PROD)
_ = super().start()
return self
@ -997,4 +997,4 @@ class AppHarnessProd(AppHarness):
self.frontend_server.shutdown()
if self.frontend_thread is not None:
self.frontend_thread.join()
environment.REFLEX_ENV_MODE.set(None)
EnvironmentVariables.REFLEX_ENV_MODE.set(None)

View File

@ -15,7 +15,7 @@ from urllib.parse import urljoin
import psutil
from reflex import constants
from reflex.config import environment, get_config
from reflex.config import EnvironmentVariables, get_config
from reflex.constants.base import LogLevel
from reflex.utils import console, path_ops
from reflex.utils.prerequisites import get_web_dir
@ -184,7 +184,7 @@ def should_use_granian():
Returns:
True if Granian should be used.
"""
return environment.REFLEX_USE_GRANIAN.get()
return EnvironmentVariables.REFLEX_USE_GRANIAN.get()
def get_app_module():
@ -370,7 +370,7 @@ def run_uvicorn_backend_prod(host, port, loglevel):
run=True,
show_logs=True,
env={
environment.REFLEX_SKIP_COMPILE.name: "true"
EnvironmentVariables.REFLEX_SKIP_COMPILE.name: "true"
}, # skip compile for prod backend
)
@ -407,7 +407,7 @@ def run_granian_backend_prod(host, port, loglevel):
run=True,
show_logs=True,
env={
environment.REFLEX_SKIP_COMPILE.name: "true"
EnvironmentVariables.REFLEX_SKIP_COMPILE.name: "true"
}, # skip compile for prod backend
)
except ImportError:
@ -493,7 +493,7 @@ def is_prod_mode() -> bool:
Returns:
True if the app is running in production mode or False if running in dev mode.
"""
current_mode = environment.REFLEX_ENV_MODE.get()
current_mode = EnvironmentVariables.REFLEX_ENV_MODE.get()
return current_mode == constants.Env.PROD
@ -509,7 +509,7 @@ def is_frontend_only() -> bool:
deprecation_version="0.6.5",
removal_version="0.7.0",
)
return environment.REFLEX_FRONTEND_ONLY.get()
return EnvironmentVariables.REFLEX_FRONTEND_ONLY.get()
def is_backend_only() -> bool:
@ -524,7 +524,7 @@ def is_backend_only() -> bool:
deprecation_version="0.6.5",
removal_version="0.7.0",
)
return environment.REFLEX_BACKEND_ONLY.get()
return EnvironmentVariables.REFLEX_BACKEND_ONLY.get()
def should_skip_compile() -> bool:
@ -539,4 +539,4 @@ def should_skip_compile() -> bool:
deprecation_version="0.6.5",
removal_version="0.7.0",
)
return environment.REFLEX_SKIP_COMPILE.get()
return EnvironmentVariables.REFLEX_SKIP_COMPILE.get()

View File

@ -2,7 +2,7 @@
import httpx
from ..config import environment
from ..config import EnvironmentVariables
from . import console
@ -12,7 +12,7 @@ def _httpx_verify_kwarg() -> bool:
Returns:
True if SSL verification is enabled, False otherwise
"""
return not environment.SSL_NO_VERIFY.get()
return not EnvironmentVariables.SSL_NO_VERIFY.get()
def get(url: str, **kwargs) -> httpx.Response:

View File

@ -9,7 +9,7 @@ import shutil
from pathlib import Path
from reflex import constants
from reflex.config import environment
from reflex.config import EnvironmentVariables
# Shorthand for join.
join = os.linesep.join
@ -136,7 +136,7 @@ def use_system_node() -> bool:
Returns:
Whether the system node should be used.
"""
return environment.REFLEX_USE_SYSTEM_NODE.get()
return EnvironmentVariables.REFLEX_USE_SYSTEM_NODE.get()
def use_system_bun() -> bool:
@ -145,7 +145,7 @@ def use_system_bun() -> bool:
Returns:
Whether the system bun should be used.
"""
return environment.REFLEX_USE_SYSTEM_BUN.get()
return EnvironmentVariables.REFLEX_USE_SYSTEM_BUN.get()
def get_node_bin_path() -> Path | None:

View File

@ -33,7 +33,7 @@ from redis.asyncio import Redis
from reflex import constants, model
from reflex.compiler import templates
from reflex.config import Config, environment, get_config
from reflex.config import Config, EnvironmentVariables, get_config
from reflex.utils import console, net, path_ops, processes
from reflex.utils.exceptions import GeneratedCodeHasNoFunctionDefs
from reflex.utils.format import format_library_name
@ -69,7 +69,7 @@ def get_web_dir() -> Path:
Returns:
The working directory.
"""
return environment.REFLEX_WEB_WORKDIR.get()
return EnvironmentVariables.REFLEX_WEB_WORKDIR.get()
def _python_version_check():
@ -260,7 +260,7 @@ def windows_npm_escape_hatch() -> bool:
Returns:
If the user has set REFLEX_USE_NPM.
"""
return environment.REFLEX_USE_NPM.get()
return EnvironmentVariables.REFLEX_USE_NPM.get()
def get_app(reload: bool = False) -> ModuleType:
@ -278,7 +278,7 @@ def get_app(reload: bool = False) -> ModuleType:
from reflex.utils import telemetry
try:
environment.RELOAD_CONFIG.set(reload)
EnvironmentVariables.RELOAD_CONFIG.set(reload)
config = get_config()
if not config.app_name:
raise RuntimeError(
@ -1019,7 +1019,7 @@ def needs_reinit(frontend: bool = True) -> bool:
return False
# Make sure the .reflex directory exists.
if not environment.REFLEX_DIR.get().exists():
if not EnvironmentVariables.REFLEX_DIR.get().exists():
return True
# Make sure the .web directory exists in frontend mode.
@ -1124,7 +1124,7 @@ def ensure_reflex_installation_id() -> Optional[int]:
"""
try:
initialize_reflex_user_directory()
installation_id_file = environment.REFLEX_DIR.get() / "installation_id"
installation_id_file = EnvironmentVariables.REFLEX_DIR.get() / "installation_id"
installation_id = None
if installation_id_file.exists():
@ -1149,7 +1149,7 @@ def ensure_reflex_installation_id() -> Optional[int]:
def initialize_reflex_user_directory():
"""Initialize the reflex user directory."""
# Create the reflex directory.
path_ops.mkdir(environment.REFLEX_DIR.get())
path_ops.mkdir(EnvironmentVariables.REFLEX_DIR.get())
def initialize_frontend_dependencies():
@ -1174,7 +1174,7 @@ def check_db_initialized() -> bool:
"""
if (
get_config().db_url is not None
and not environment.ALEMBIC_CONFIG.get().exists()
and not EnvironmentVariables.ALEMBIC_CONFIG.get().exists()
):
console.error(
"Database is not initialized. Run [bold]reflex db init[/bold] first."
@ -1185,7 +1185,10 @@ def check_db_initialized() -> bool:
def check_schema_up_to_date():
"""Check if the sqlmodel metadata matches the current database schema."""
if get_config().db_url is None or not environment.ALEMBIC_CONFIG.get().exists():
if (
get_config().db_url is None
or not EnvironmentVariables.ALEMBIC_CONFIG.get().exists()
):
return
with model.Model.get_db_engine().connect() as connection:
try:

View File

@ -2,7 +2,7 @@
import httpx
from reflex.config import environment
from reflex.config import EnvironmentVariables
from reflex.utils import console, net
@ -55,4 +55,4 @@ def _get_npm_registry() -> str:
Returns:
str:
"""
return environment.NPM_CONFIG_REGISTRY.get() or get_best_registry()
return EnvironmentVariables.NPM_CONFIG_REGISTRY.get() or get_best_registry()

View File

@ -8,7 +8,7 @@ import multiprocessing
import platform
import warnings
from reflex.config import environment
from reflex.config import EnvironmentVariables
try:
from datetime import UTC, datetime
@ -95,7 +95,7 @@ def _raise_on_missing_project_hash() -> bool:
False when compilation should be skipped (i.e. no .web directory is required).
Otherwise return True.
"""
return not environment.REFLEX_SKIP_COMPILE.get()
return not EnvironmentVariables.REFLEX_SKIP_COMPILE.get()
def _prepare_event(event: str, **kwargs) -> dict:

View File

@ -8,7 +8,7 @@ from typing import Generator, Type
import pytest
import reflex.constants
from reflex.config import environment
from reflex.config import EnvironmentVariables
from reflex.testing import AppHarness, AppHarnessProd
DISPLAY = None
@ -24,7 +24,10 @@ def xvfb():
Yields:
the pyvirtualdisplay object that the browser will be open on
"""
if os.environ.get("GITHUB_ACTIONS") and not environment.APP_HARNESS_HEADLESS.get():
if (
os.environ.get("GITHUB_ACTIONS")
and not EnvironmentVariables.APP_HARNESS_HEADLESS.get()
):
from pyvirtualdisplay.smartdisplay import ( # pyright: ignore [reportMissingImports]
SmartDisplay,
)
@ -45,7 +48,7 @@ def pytest_exception_interact(node, call, report):
call: The pytest call describing when/where the test was invoked.
report: The pytest log report object.
"""
screenshot_dir = environment.SCREENSHOT_DIR.get()
screenshot_dir = EnvironmentVariables.SCREENSHOT_DIR.get()
if DISPLAY is None or screenshot_dir is None:
return
@ -89,7 +92,7 @@ def app_harness_env(
"""
harness: Type[AppHarness] = request.param
if issubclass(harness, AppHarnessProd):
environment.REFLEX_ENV_MODE.set(reflex.constants.Env.PROD)
EnvironmentVariables.REFLEX_ENV_MODE.set(reflex.constants.Env.PROD)
yield harness
if issubclass(harness, AppHarnessProd):
environment.REFLEX_ENV_MODE.set(None)
EnvironmentVariables.REFLEX_ENV_MODE.set(None)

View File

@ -2,7 +2,6 @@
from __future__ import annotations
import os
from functools import partial
from typing import Generator, Optional, Type
@ -10,7 +9,7 @@ import pytest
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webdriver import WebDriver
from reflex.constants.compiler import ENV_MINIFY_STATES
from reflex.config import EnvironmentVariables
from reflex.testing import AppHarness, AppHarnessProd
@ -63,13 +62,9 @@ def minify_state_env(
minify_states: whether to minify state names
"""
minify_states: Optional[bool] = request.param
if minify_states is None:
_ = os.environ.pop(ENV_MINIFY_STATES, None)
else:
os.environ[ENV_MINIFY_STATES] = str(minify_states).lower()
EnvironmentVariables.REFLEX_MINIFY_STATES.set(minify_states)
yield minify_states
if minify_states is not None:
os.environ.pop(ENV_MINIFY_STATES, None)
EnvironmentVariables.REFLEX_MINIFY_STATES.set(None)
@pytest.fixture

View File

@ -8,9 +8,9 @@ import pytest
import reflex as rx
import reflex.config
from reflex.config import (
EnvironmentVariables,
EnvVar,
env_var,
environment,
interpret_boolean_env,
interpret_enum_env,
interpret_int_env,
@ -216,7 +216,7 @@ def test_replace_defaults(
def reflex_dir_constant() -> Path:
return environment.REFLEX_DIR.get()
return EnvironmentVariables.REFLEX_DIR.get()
def test_reflex_dir_env_var(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
@ -253,6 +253,11 @@ def test_env_var():
INTERNAL: EnvVar[str] = env_var("default", internal=True)
BOOLEAN: EnvVar[bool] = env_var(False)
# default_factory with other env_var as fallback
BLUBB_OR_BLA: EnvVar[str] = env_var(
default_factory=lambda: TestEnv.BLUBB.getenv() or "bla"
)
assert TestEnv.BLUBB.get() == "default"
assert TestEnv.BLUBB.name == "BLUBB"
TestEnv.BLUBB.set("new")
@ -280,3 +285,15 @@ def test_env_var():
assert TestEnv.BOOLEAN.get() is False
TestEnv.BOOLEAN.set(None)
assert "BOOLEAN" not in os.environ
assert TestEnv.BLUBB_OR_BLA.get() == "bla"
TestEnv.BLUBB.set("new")
assert TestEnv.BLUBB_OR_BLA.get() == "new"
TestEnv.BLUBB.set(None)
assert TestEnv.BLUBB_OR_BLA.get() == "bla"
TestEnv.BLUBB_OR_BLA.set("test")
assert TestEnv.BLUBB_OR_BLA.get() == "test"
TestEnv.BLUBB.set("other")
assert TestEnv.BLUBB_OR_BLA.get() == "test"
TestEnv.BLUBB_OR_BLA.set(None)
TestEnv.BLUBB.set(None)

View File

@ -10,7 +10,7 @@ from packaging import version
from reflex import constants
from reflex.base import Base
from reflex.config import environment
from reflex.config import EnvironmentVariables
from reflex.event import EventHandler
from reflex.state import BaseState
from reflex.utils import (
@ -598,7 +598,7 @@ def test_style_prop_with_event_handler_value(callable):
def test_is_prod_mode() -> None:
"""Test that the prod mode is correctly determined."""
environment.REFLEX_ENV_MODE.set(constants.Env.PROD)
EnvironmentVariables.REFLEX_ENV_MODE.set(constants.Env.PROD)
assert utils_exec.is_prod_mode()
environment.REFLEX_ENV_MODE.set(None)
EnvironmentVariables.REFLEX_ENV_MODE.set(None)
assert not utils_exec.is_prod_mode()