Skip to content

Commit e9c2e4b

Browse files
authored
reload env file on hot reload in granian (#5510)
1 parent 5128f44 commit e9c2e4b

5 files changed

Lines changed: 141 additions & 106 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ requires-python = ">=3.10,<4.0"
2222
dependencies = [
2323
"alembic >=1.15.2,<2.0",
2424
"fastapi >=0.115.0",
25-
"granian[reload] >=2.2.5",
25+
"granian[reload] >=2.4.0",
2626
"httpx >=0.28.0,<1.0",
2727
"jinja2 >=3.1.2,<4.0",
2828
"packaging >=24.2,<26",

reflex/config.py

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,48 +19,18 @@
1919
from reflex.constants.base import LogLevel
2020
from reflex.environment import EnvironmentVariables as EnvironmentVariables
2121
from reflex.environment import EnvVar as EnvVar
22-
from reflex.environment import ExistingPath, interpret_env_var_value
22+
from reflex.environment import (
23+
ExistingPath,
24+
_load_dotenv_from_str,
25+
interpret_env_var_value,
26+
)
2327
from reflex.environment import env_var as env_var
2428
from reflex.environment import environment as environment
2529
from reflex.plugins import Plugin
2630
from reflex.utils import console
2731
from reflex.utils.exceptions import ConfigError
2832
from reflex.utils.types import true_type_for_pydantic_field
2933

30-
try:
31-
from dotenv import load_dotenv
32-
except ImportError:
33-
load_dotenv = None
34-
35-
36-
def _load_dotenv_from_str(env_files: str) -> None:
37-
if not env_files:
38-
return
39-
40-
if load_dotenv is None:
41-
console.error(
42-
"""The `python-dotenv` package is required to load environment variables from a file. Run `pip install "python-dotenv>=1.1.0"`."""
43-
)
44-
return
45-
46-
# load env files in reverse order if they exist
47-
for env_file_path in [
48-
Path(p) for s in reversed(env_files.split(os.pathsep)) if (p := s.strip())
49-
]:
50-
if env_file_path.exists():
51-
load_dotenv(env_file_path, override=True)
52-
53-
54-
def _load_dotenv_from_env():
55-
"""Load environment variables from paths specified in REFLEX_ENV_FILE."""
56-
env_env_file = os.environ.get("REFLEX_ENV_FILE")
57-
if env_env_file:
58-
_load_dotenv_from_str(env_env_file)
59-
60-
61-
# Load the env files at import time if they are set in the ENV_FILE environment variable.
62-
_load_dotenv_from_env()
63-
6434

6535
class DBConfig(Base):
6636
"""Database config."""

reflex/environment.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,3 +606,39 @@ class EnvironmentVariables:
606606

607607

608608
environment = EnvironmentVariables()
609+
610+
try:
611+
from dotenv import load_dotenv
612+
except ImportError:
613+
load_dotenv = None
614+
615+
616+
def _load_dotenv_from_str(env_files: str) -> None:
617+
from reflex.utils import console
618+
619+
if not env_files:
620+
return
621+
622+
if load_dotenv is None:
623+
console.error(
624+
"""The `python-dotenv` package is required to load environment variables from a file. Run `pip install "python-dotenv>=1.1.0"`."""
625+
)
626+
return
627+
628+
# load env files in reverse order if they exist
629+
for env_file_path in [
630+
Path(p) for s in reversed(env_files.split(os.pathsep)) if (p := s.strip())
631+
]:
632+
if env_file_path.exists():
633+
load_dotenv(env_file_path, override=True)
634+
635+
636+
def _load_dotenv_from_env():
637+
"""Load environment variables from paths specified in REFLEX_ENV_FILE."""
638+
env_env_file = os.environ.get("REFLEX_ENV_FILE")
639+
if env_env_file:
640+
_load_dotenv_from_str(env_env_file)
641+
642+
643+
# Load the env files at import time if they are set in the ENV_FILE environment variable.
644+
_load_dotenv_from_env()

reflex/utils/exec.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,13 @@ def run_uvicorn_backend(host: str, port: int, loglevel: LogLevel):
502502
)
503503

504504

505+
def _reload_hook():
506+
"""Hook to load environment variables from .env file."""
507+
from reflex.environment import _load_dotenv_from_env
508+
509+
_load_dotenv_from_env()
510+
511+
505512
def run_granian_backend(host: str, port: int, loglevel: LogLevel):
506513
"""Run the backend in development mode using Granian.
507514
@@ -521,7 +528,7 @@ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
521528
from granian.log import LogLevels
522529
from granian.server import MPServer as Granian
523530

524-
Granian(
531+
granian_app = Granian(
525532
target=get_app_instance_from_file(),
526533
factory=True,
527534
address=host,
@@ -534,7 +541,11 @@ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
534541
reload_ignore_patterns=HOTRELOAD_IGNORE_PATTERNS,
535542
reload_tick=100,
536543
workers_kill_timeout=2,
537-
).serve()
544+
)
545+
546+
granian_app.on_reload(_reload_hook)
547+
548+
granian_app.serve()
538549

539550

540551
def run_backend_prod(

0 commit comments

Comments
 (0)