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.17uvicorn: 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.
Summary
A freshly scaffolded app fails on the very first
make devwithModuleNotFoundError: No module named 'routes'. The generatedhost/main.pycallsos.chdir(_REPO_ROOT)at import time beforefrom routes import router, and the generatedMakefileruns the API ascd host && uv run uvicorn main:app. Under uvicorn,sys.path[0]is the empty string''(the current working directory, resolved lazily), so oncemain.pychdirs to the repo root, the siblinghost/routes.pyis 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.17uvicorn: 0.49.0,fastapi: 0.136.3,starlette: 1.2.1Minimal reproduction
Even more minimal (no server needed), from the generated app:
sys.path[0]is''when run this way:Expected vs actual
make devboots the API on :8000 (the CLI even printsmake devas the final quickstart step).dev-apicrashes at import time:Root cause
Generated
host/main.py(verbatim fromsmpy new0.0.17):Generated
Makefile:uvicorn main:appfindsmainbecause cwd (host/) is initially onsys.pathas''. Butmain.pythen chdirs to the repo root, so the same''entry now points at the repo root, whereroutes.pydoes not exist. (Runningpython main.pydirectly would put the absolutehost/dir onsys.path[0]and mask the bug — but--reloadre-imports via theuvicorn main:apppath 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.pywith noos.chdirand no top-levelfrom routes import).Suggested fix
Pin the host directory on
sys.pathas an absolute path before the chdir, sofrom routes importresolves regardless of cwd (and survives the--reloadsubprocess):(Alternatively: keep
routesimport lazy/deferred until after a sys.path fix, or change the Makefile touv run --project host uvicorn host.main:app+from host.routes import router— but the absolute-sys.path.insertkeeps 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))beforeos.chdir(...)inhost/main.py(the suggested fix above).make devthen boots normally.