Normalize first-party imports to the app. namespace #372
No reviewers
Labels
No labels
Kestra
bug
enhancement
someday
subtask
☁️ api
🎛️ infrastructure
🐞 sentry
📆 2025 Season
📝 pages
allpicks
📝 pages
picks
📝 pages
standings
🚀 performance
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
johnsturgeon/tgfp-web!372
Loading…
Reference in a new issue
No description provided.
Delete branch "normalize-the-import-namespace-371"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
app/ was a package but also sat on sys.path as a root, so its submodules
were imported under two names in the same process. Verified:
dbandapp.dbresolved to the same file yet were distinct entries insys.modules, with distinct module-level state.
The concrete fallout:
engineat module scope. Nine modules usedfrom db import engine; jobs/sync_team_records.py usedfrom app.db import engine-- so that job ran against its ownSQLAlchemy engine and its own connection pool.
os.environ reads, two unrelated Config dataclasses.
Both roots were on the path in every context: PYTHONPATH=/ alongside
WORKDIR /app in compose.prod.yml, the script dir plus PyCharm's content
roots locally, and .pylintrc's init-hook injecting ["." , "app"] -- which
is why lint never flagged it.
Standardize on the app.-qualified form. It was already canonical
(alembic/env.py imports app.models, main.py runs "app.main:app") and it
is the only form that survives becoming an installed package in #343.
51 imports across 15 files gain the prefix; intra-package relative
imports are unchanged.
Drop "app" from the pylint init-hook. That is the regression guard as
much as the cleanup: a bare first-party import now fails as import-error
instead of quietly creating a duplicate module.
Regroup imports in six files. Collapsing the namespaces let pylint see
first-party and third-party imports interleaved -- previously the two
halves looked like separate groups.
Resolve static/ and templates/ from file rather than the working
directory, following the existing pattern in routers/mail.py. The app
now boots from anywhere instead of only from inside app/, which #370
depends on.
No Dockerfile or compose changes; PYTHONPATH=/ is now inert and is
removed in #343.
Verified: pylint 10.00/10, flake8 unchanged at 100, no bare first-party
modules in sys.modules, sync_team_records shares app.db.engine, alembic
current still resolves at a793cd810fd0, and the app serves /ping, /login
and nested static assets when launched from the repo root.
Refs #371
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
This is a clean, well-scoped mechanical refactor. I inspected the surrounding code to verify the claims in the description hold. Here's my review.
Review: Normalize first-party imports to the
app.namespaceThis PR converts all first-party imports to the
app.-qualified form, drops the"app"entry from pylint'sinit-hookas a regression guard, and resolvesstatic//templates/from__file__instead of the CWD. I verified the change is complete and internally consistent — no functional issues found; the notes below are minor.Verification I ran:
grepfor any surviving bare first-party imports (from db,from models,import config, etc.) across all*.py— none remain, matching the "51 imports across 15 files" claim.grepfordirectory="static"/directory="templates"string literals — none remain; all three template/static call sites (app/main.py:144-145,app/routers/admin.py:19,app/routers/mail.py:38) now derive from__file__.compose.prod.ymlmounts./app:/appwithPYTHONPATH: /, soapp.main:appandapp.models(inalembic/env.py:10) resolve — consistent with the newDockerfileENV PYTHONPATH=/..forgejo/workflows/lint.ymlrunspylint $(git ls-files '*.py')from the repo root (CLI infers root, unaffected by the trimmedinit-hook), and pytest is stubbed out (echo "pytest is not enabled yet"), so no test job is impacted.entrypoint.sh'sexec "$@"correctly hands off to the DockerfileCMD, andcompose.prod.ymlsets nocommand:, so theuvicorn app.main:app ... --no-access-logline is what actually runs — this genuinely fixes the drift called out in the Dockerfile comment (prod was logging every request).Nits
app/main.py:38-39uses module constantsSTATIC_FOLDER/TEMPLATE_FOLDER, whileapp/routers/admin.py:18andapp/routers/mail.py:23use lowercasetemplate_folder. Harmless, but worth aligning on one convention since they were touched together.Path(__file__).parent[.parent] / "templates"expression is now duplicated in three modules. Not worth blocking on, but a small shared helper (e.g. inapp/config.py) would prevent the three from drifting the way the two uvicorn definitions did.pytestis a stub in CI), so this import-boundary change — exactly the kind of thing a smoke test would catch — rests entirely on the manual verification in the description. Worth keeping in mind as #343 lands.Nothing here blocks. The change is correct, the commit message documents the reasoning well, and the boundary cleanup (single SQLAlchemy engine, single
Config) is a real fix rather than cosmetic.