Skip to content

[smpy][scaffold] make dev fails on fresh app: ModuleNotFoundError: No module named 'routes' (host/main.py chdir invalidates cwd-relative sys.path before importing routes) #194

Description

@antosubash

Summary

A freshly scaffolded app fails on the very first make dev with ModuleNotFoundError: No module named 'routes'. The generated host/main.py calls os.chdir(_REPO_ROOT) at import time beforefrom routes import router, and the generated Makefile runs the API as cd host && uv run uvicorn main:app. Under uvicorn, sys.path[0] is the empty string '' (the current working directory, resolved lazily), so once main.py chdirs to the repo root, the sibling host/routes.py is no longer importable. The blessed quickstart path (smpy new … && make install && make migration && make migrate && make dev) is broken out of the box.

Environment

  • simple_module_cli (smpy): 0.0.17 (PyPI)
  • simple_module_hosting / simple_module_core: 0.0.17
  • uvicorn: 0.49.0, fastapi: 0.136.3, starlette: 1.2.1
  • Python: 3.12.3, uv: 0.11.7
  • OS: Linux (Ubuntu, 6.17 kernel)

Minimal reproduction

uv tool install simple_module_cli==0.0.17
smpy new demo --preset standard --with background_tasks --db sqlite --no-tenancy --yes
cd demo
make install
make migration msg="initial schema"
make migrate
make dev # <-- fails immediately

Even more minimal (no server needed), from the generated app:

cd host
uv run python -c "import main"# ModuleNotFoundError: No module named 'routes'

sys.path[0] is '' when run this way:

cd host && uv run python -c "import sys; print(sys.path[:2])"# ['', '/.../site-packages']

Expected vs actual

  • Expected:make dev boots the API on :8000 (the CLI even prints make dev as the final quickstart step).
  • Actual:dev-api crashes at import time:
 File ".../host/main.py", line 26, in <module>
from routes import router as host_router
ModuleNotFoundError: No module named 'routes'
make[1]: *** [Makefile:13: dev-api] Error 1

Root cause

Generated host/main.py (verbatim from smpy new 0.0.17):

_REPO_ROOT=Path(__file__).resolve().parent.parentos.chdir(_REPO_ROOT) # cwd is now repo rootload_dotenv_into_environ(_REPO_ROOT/".env")
fromsimple_module_hostingimportSettings, create_appfromsimple_module_hosting.loggingimportsetup_loggingfromroutesimportrouterashost_router# <-- resolved against sys.path[0]=='' == repo root

Generated Makefile:

dev-api:
cd host && uv run uvicorn main:app --reload --port 8000

uvicorn main:app finds main because cwd (host/) is initially on sys.path as ''. But main.py then chdirs to the repo root, so the same'' entry now points at the repo root, where routes.py does not exist. (Running python main.py directly would put the absolute host/ dir on sys.path[0] and mask the bug — but --reload re-imports via the uvicorn main:app path in the reloader subprocess, so it breaks there too.)

This is a regression vs. older scaffolds (0.0.11/0.0.12 consumer apps have a host/main.py with no os.chdir and no top-level from routes import).

Suggested fix

Pin the host directory on sys.path as an absolute path before the chdir, so from routes import resolves regardless of cwd (and survives the --reload subprocess):

_HOST_DIR=Path(__file__).resolve().parent_REPO_ROOT=_HOST_DIR.parentifstr(_HOST_DIR) notinsys.path:
sys.path.insert(0, str(_HOST_DIR))
os.chdir(_REPO_ROOT)

(Alternatively: keep routes import lazy/deferred until after a sys.path fix, or change the Makefile to uv run --project host uvicorn host.main:app + from host.routes import router — but the absolute-sys.path.insert keeps the existing chdir/.env behaviour intact.)

Impact + workaround

Blocks first boot of every freshly scaffolded standard/full-preset app. Workaround applied while building: added the absolute sys.path.insert(0, str(_HOST_DIR)) before os.chdir(...) in host/main.py (the suggested fix above). make dev then boots normally.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions