Run the fit check as a module, so it works where nothing is installed

CI caught what this machine could not: the hook died with "No module named
'screen_locker'". The package is pip-installed here (editable, pointing at
this checkout), so running the script by path found it anyway; a fresh
checkout has no install, and `python3 scripts/x.py` puts only `scripts/` on
sys.path. `python3 -m scripts.verify_screen_fits` puts the repo root there
instead, which is the checkout in CI and here alike -- verified in a
throwaway worktree, where the module now resolves to the worktree's own
package rather than the installed one.

The xvfb re-exec runs the same way, with an explicit cwd so the child does
not depend on where the parent was invoked from.

pylint had the same blind spot for the same reason: it infers sys.path from
the files it is handed, so this script resolved its imports only when a
package file happened to be in the same batch -- passing or failing by what
else was staged. An init-hook puts the repo root on the path unconditionally.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BsXnTHyuuCUZsqetTaajcF
This commit is contained in:
Krzysztof Rudnicki 2026-08-03 16:34:45 +02:00
parent a79c4fdd05
commit dd233b27aa
3 changed files with 17 additions and 4 deletions

View File

@ -155,7 +155,7 @@ repos:
types: [python]
- id: screens-fit
name: Every lock screen fits 1366x768 (no scrolling required)
entry: python3 scripts/verify_screen_fits.py
entry: python3 -m scripts.verify_screen_fits
language: system
files: '^screen_locker/.*\.py$'
pass_filenames: false

View File

@ -8,7 +8,7 @@ requires-python = ">=3.10"
# also used by diet_guard; here for phone-workout GitHub sync (Workstream C)
dependencies = [
"gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.4.0#subdirectory=gatelock",
"crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.1.0#subdirectory=crdt-sync",
"crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.3.0#subdirectory=crdt-sync",
]
[project.scripts]
@ -152,6 +152,12 @@ color_output = true
exclude = [".venv/"]
[tool.pylint.main]
# The repo root on the path, so a file outside the package (scripts/) can be
# linted on its own. pylint otherwise infers the path from the files it is
# given, so `scripts/verify_screen_fits.py` resolved `screen_locker` only when
# a package file happened to be in the same batch -- passing or failing by
# what else was staged.
init-hook = "import sys; sys.path.insert(0, '.')"
analyse-fallback-blocks = true
persistent = true
jobs = 0

View File

@ -18,7 +18,12 @@ whole ``tk`` module for every test so the suite can never reach a display, and
widget heights can only be measured by really rendering them. Like
``verify_lock_popup_safety.py``, this re-executes itself under Xvfb:
python3 ~/screen-locker/scripts/verify_screen_fits.py
cd ~/screen-locker && python3 -m scripts.verify_screen_fits
Run as a *module*, not a path: ``-m`` puts the repo root on ``sys.path`` so
``screen_locker`` resolves from the checkout. Running it as a path only works
where the package happens to be pip-installed, which is why it passed here and
died in CI.
"""
from __future__ import annotations
@ -88,9 +93,11 @@ def reexec_under_xvfb() -> int:
"-s",
"-screen 0 1600x1200x24",
sys.executable,
str(Path(__file__).resolve()),
"-m",
"scripts.verify_screen_fits",
],
check=False,
cwd=Path(__file__).resolve().parent.parent,
env=env,
).returncode