Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions reflex/utils/misc.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
"""Miscellaneous functions for the experimental package."""

import asyncio
import contextlib
import sys
import threading
from collections.abc import Callable
from pathlib import Path
from typing import Any


Expand All @@ -23,3 +27,27 @@ async def run_in_thread(func: Callable) -> Any:
msg = "func must be a non-async function"
raise ValueError(msg)
return await asyncio.get_event_loop().run_in_executor(None, func)


# Global lock for thread-safe sys.path manipulation
_sys_path_lock = threading.RLock()


@contextlib.contextmanager
def with_cwd_in_syspath():
"""Temporarily add current working directory to sys.path in a thread-safe manner.

This context manager temporarily prepends the current working directory to sys.path,
ensuring that modules in the current directory can be imported. The original sys.path
is restored when exiting the context.

Yields:
None
"""
with _sys_path_lock:
orig_sys_path = sys.path.copy()
sys.path.insert(0, str(Path.cwd()))
try:
yield
finally:
sys.path[:] = orig_sys_path
20 changes: 18 additions & 2 deletions reflex/utils/prerequisites.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,13 +334,14 @@ def npm_escape_hatch() -> bool:


def _check_app_name(config: Config):
"""Check if the app name is set in the config.
"""Check if the app name is valid and matches the folder structure.

Args:
config: The config object.

Raises:
RuntimeError: If the app name is not set in the config.
RuntimeError: If the app name is not set, folder doesn't exist, or doesn't match config.
ModuleNotFoundError: If the app_name is not importable (i.e., not a valid Python package, folder structure being wrong).
"""
if not config.app_name:
msg = (
Expand All @@ -349,6 +350,21 @@ def _check_app_name(config: Config):
)
raise RuntimeError(msg)

from reflex.utils.misc import with_cwd_in_syspath

with with_cwd_in_syspath():
try:
mod_spec = importlib.util.find_spec(config.module)
except ModuleNotFoundError:
mod_spec = None
if mod_spec is None:
msg = f"Module {config.module} not found. "
if config.app_module_import is not None:
msg += f"Ensure app_module_import='{config.app_module_import}' in rxconfig.py matches your folder structure."
else:
msg += f"Ensure app_name='{config.app_name}' in rxconfig.py matches your folder structure."
raise ModuleNotFoundError(msg)


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