Skip to content

Commit 90a888b

Browse files
authored
add a check if app_name is wrong in rxconfig (#5410)
* add a check if manual renaming was done wrong * refacto/condense * handle testing env * skip check in pytest * fix everything * better handling, don't exclude app_module_import * remove comment
1 parent a4fac5d commit 90a888b

File tree

2 files changed

+46
-2
lines changed

2 files changed

+46
-2
lines changed

reflex/utils/misc.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
"""Miscellaneous functions for the experimental package."""
22

33
import asyncio
4+
import contextlib
5+
import sys
6+
import threading
47
from collections.abc import Callable
8+
from pathlib import Path
59
from typing import Any
610

711

@@ -23,3 +27,27 @@ async def run_in_thread(func: Callable) -> Any:
2327
msg = "func must be a non-async function"
2428
raise ValueError(msg)
2529
return await asyncio.get_event_loop().run_in_executor(None, func)
30+
31+
32+
# Global lock for thread-safe sys.path manipulation
33+
_sys_path_lock = threading.RLock()
34+
35+
36+
@contextlib.contextmanager
37+
def with_cwd_in_syspath():
38+
"""Temporarily add current working directory to sys.path in a thread-safe manner.
39+
40+
This context manager temporarily prepends the current working directory to sys.path,
41+
ensuring that modules in the current directory can be imported. The original sys.path
42+
is restored when exiting the context.
43+
44+
Yields:
45+
None
46+
"""
47+
with _sys_path_lock:
48+
orig_sys_path = sys.path.copy()
49+
sys.path.insert(0, str(Path.cwd()))
50+
try:
51+
yield
52+
finally:
53+
sys.path[:] = orig_sys_path

reflex/utils/prerequisites.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,13 +334,14 @@ def npm_escape_hatch() -> bool:
334334

335335

336336
def _check_app_name(config: Config):
337-
"""Check if the app name is set in the config.
337+
"""Check if the app name is valid and matches the folder structure.
338338
339339
Args:
340340
config: The config object.
341341
342342
Raises:
343-
RuntimeError: If the app name is not set in the config.
343+
RuntimeError: If the app name is not set, folder doesn't exist, or doesn't match config.
344+
ModuleNotFoundError: If the app_name is not importable (i.e., not a valid Python package, folder structure being wrong).
344345
"""
345346
if not config.app_name:
346347
msg = (
@@ -349,6 +350,21 @@ def _check_app_name(config: Config):
349350
)
350351
raise RuntimeError(msg)
351352

353+
from reflex.utils.misc import with_cwd_in_syspath
354+
355+
with with_cwd_in_syspath():
356+
try:
357+
mod_spec = importlib.util.find_spec(config.module)
358+
except ModuleNotFoundError:
359+
mod_spec = None
360+
if mod_spec is None:
361+
msg = f"Module {config.module} not found. "
362+
if config.app_module_import is not None:
363+
msg += f"Ensure app_module_import='{config.app_module_import}' in rxconfig.py matches your folder structure."
364+
else:
365+
msg += f"Ensure app_name='{config.app_name}' in rxconfig.py matches your folder structure."
366+
raise ModuleNotFoundError(msg)
367+
352368

353369
def get_app(reload: bool = False) -> ModuleType:
354370
"""Get the app module based on the default config.

0 commit comments

Comments
 (0)