Skip to content

Commit b644380

Browse files
adhami3310masenf
authored andcommitted
avoid side effects of checking app module existing (#5480)
1 parent 861ec4e commit b644380

3 files changed

Lines changed: 56 additions & 21 deletions

File tree

reflex/utils/exec.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from reflex.environment import environment
2424
from reflex.utils import console, path_ops
2525
from reflex.utils.decorator import once
26+
from reflex.utils.misc import get_module_path
2627
from reflex.utils.prerequisites import get_web_dir
2728

2829
# For uvicorn windows bug fix (#2335)
@@ -323,15 +324,12 @@ def get_app_file() -> Path:
323324
if current_working_dir not in sys.path:
324325
# Add the current working directory to sys.path
325326
sys.path.insert(0, current_working_dir)
326-
module_spec = importlib.util.find_spec(get_app_module())
327-
if module_spec is None:
328-
msg = f"Module {get_app_module()} not found. Make sure the module is installed."
327+
app_module = get_app_module()
328+
module_path = get_module_path(app_module)
329+
if module_path is None:
330+
msg = f"Module {app_module} not found. Make sure the module is installed."
329331
raise ImportError(msg)
330-
file_name = module_spec.origin
331-
if file_name is None:
332-
msg = f"Module {get_app_module()} not found. Make sure the module is installed."
333-
raise ImportError(msg)
334-
return Path(file_name).resolve()
332+
return module_path
335333

336334

337335
def get_app_instance_from_file() -> str:
@@ -396,8 +394,10 @@ def get_reload_paths() -> Sequence[Path]:
396394
"""
397395
config = get_config()
398396
reload_paths = [Path.cwd()]
399-
if (spec := importlib.util.find_spec(config.module)) is not None and spec.origin:
400-
module_path = Path(spec.origin).resolve().parent
397+
app_module = config.module
398+
module_path = get_module_path(app_module)
399+
if module_path is not None:
400+
module_path = module_path.parent
401401

402402
while module_path.parent.name and _has_child_file(module_path, "__init__.py"):
403403
if _has_child_file(module_path, "rxconfig.py"):

reflex/utils/misc.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,46 @@
99
from typing import Any
1010

1111

12+
def get_module_path(module_name: str) -> Path | None:
13+
"""Check if a module exists and return its path.
14+
15+
This function searches for a module by navigating through the module hierarchy
16+
in each path of sys.path, checking for both .py files and packages with __init__.py.
17+
18+
Args:
19+
module_name: The name of the module to search for (e.g., "package.submodule").
20+
21+
Returns:
22+
The path to the module file if found, None otherwise.
23+
"""
24+
parts = module_name.split(".")
25+
26+
# Check each path in sys.path
27+
for path in sys.path:
28+
current_path = Path(path)
29+
30+
# Navigate through the module hierarchy
31+
for i, part in enumerate(parts):
32+
potential_file = current_path / (part + ".py")
33+
potential_dir = current_path / part
34+
potential_init = current_path / part / "__init__.py"
35+
36+
if potential_file.is_file():
37+
# We encountered a file, but we can't continue deeper
38+
if i == len(parts) - 1:
39+
return potential_file
40+
return None # Can't continue deeper
41+
if potential_dir.is_dir() and potential_init.is_file():
42+
# It's a package, so we can continue deeper
43+
current_path = potential_dir
44+
else:
45+
break # Path doesn't exist, break out of the loop
46+
else:
47+
return current_path / "__init__.py" # Made it through all parts
48+
49+
return None
50+
51+
1252
async def run_in_thread(func: Callable) -> Any:
1353
"""Run a function in a separate thread.
1454

reflex/utils/prerequisites.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from reflex.environment import environment
4141
from reflex.utils import console, net, path_ops, processes, redir
4242
from reflex.utils.exceptions import SystemPackageMissingError
43+
from reflex.utils.misc import get_module_path
4344
from reflex.utils.registry import get_npm_registry
4445

4546
if typing.TYPE_CHECKING:
@@ -348,14 +349,11 @@ def _check_app_name(config: Config):
348349
)
349350
raise RuntimeError(msg)
350351

351-
from reflex.utils.misc import with_cwd_in_syspath
352+
from reflex.utils.misc import get_module_path, with_cwd_in_syspath
352353

353354
with with_cwd_in_syspath():
354-
try:
355-
mod_spec = importlib.util.find_spec(config.module)
356-
except ModuleNotFoundError:
357-
mod_spec = None
358-
if mod_spec is None:
355+
module_path = get_module_path(config.module)
356+
if module_path is None:
359357
msg = f"Module {config.module} not found. "
360358
if config.app_module_import is not None:
361359
msg += f"Ensure app_module_import='{config.app_module_import}' in rxconfig.py matches your folder structure."
@@ -740,14 +738,11 @@ def rename_app(new_app_name: str, loglevel: constants.LogLevel):
740738
sys.path.insert(0, str(Path.cwd()))
741739

742740
config = get_config()
743-
module_path = importlib.util.find_spec(config.module)
741+
module_path = get_module_path(config.module)
744742
if module_path is None:
745743
console.error(f"Could not find module {config.module}.")
746744
raise click.exceptions.Exit(1)
747745

748-
if not module_path.origin:
749-
console.error(f"Could not find origin for module {config.module}.")
750-
raise click.exceptions.Exit(1)
751746
console.info(f"Renaming app directory to {new_app_name}.")
752747
process_directory(
753748
Path.cwd(),
@@ -756,7 +751,7 @@ def rename_app(new_app_name: str, loglevel: constants.LogLevel):
756751
exclude_dirs=[constants.Dirs.WEB, constants.Dirs.APP_ASSETS],
757752
)
758753

759-
rename_path_up_tree(Path(module_path.origin), config.app_name, new_app_name)
754+
rename_path_up_tree(module_path, config.app_name, new_app_name)
760755

761756
console.success(f"App directory renamed to [bold]{new_app_name}[/bold].")
762757

0 commit comments

Comments
 (0)