Skip to content

chore(types): configure pyright and clear the error backlog - #113

Merged
falbany merged 1 commit into
mainfrom
chore/pyright-configure-and-clean
Nov 4, 2026
Merged

falbany merged 1 commit into
mainfrom
chore/pyright-configure-and-clean

Conversation

@falbany

@falbany falbany commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Fixes #69 — the documented pyright src/profiles gate now works: 81 errors → 0, on top of the Tkinter removal (#112, which already cleared the ~34 gui/ errors).

Configuration

Real type fixes (no suppression)

Cluster Fix
execution.py ×28 (Popen kwargs from dict[str, object]) explicit per-platform Popen calls — same kwargs, no dict
inheritance.py ×13 (untyped pick() closure) typed per-shape helpers (pick_str_list, pick_row_colors, pick_match, pick_scan); resolve_configs now produces the declared list-shaped schema fields — the tests already pinned lists, pydantic was silently coercing the tuples
reader.py ×7 (schema→model drift) explicit conversion (MatchCriteria, tuples) at the boundary instead of relying on __post_init__ coercion
actions.py ×2 (failmode: str) FailMode Literal moved to models.py (bottom of the core graph), re-exported by workflow.py; AppConfig.launch_hook_failmode typed
columns chain ×6 (object-typed dicts) dict[str, ColumnConfiguration] across classifier/scanner/scan route; _iter_columns workaround deleted, its pinned non-dict contract kept via isinstance (test_non_dict_columns_ignored untouched)
tk fallbacks ×3 (tk possibly unbound) restructured into import/display try blocks, identical behavior
matcher.py, schema.py, diagnostics.py, handoff.py, __init__.py, scanner.py, server/app.py, qrcode/zxingcpp one-liners: bool contract, cast in the before-validator, narrowed sys.excepthook params, explicit submodule imports, honest return annotations

Real bugs the gate surfaced (fixed)

  1. app.py headless scan path crashed whenever machine configurations existed — it read cfg.directory, a field MachineConfiguration doesn't have (AttributeError at runtime; the tests only exercised the empty-configurations path). Now iterates cfg.scan as documented ("scan all configured directories").
  2. handoff.py could hit an unbound detail (NameError) when reading the HTTPError body raised inside the suppress block.
  3. matcher.py returned tuple | bool against a -> bool contract; now bool(m.path) and ....

Suppression ledger (9 lines, all commented)

Also

  • The two pylint pragmas with trailing prose (schema.py, interactions.py) moved the prose off the pragma lines — the 7 spurious unknown-option-value (W0012) warnings are gone.

Verification

  • pyright src/profiles: 0 errors, 0 warnings
  • ruff format --check + ruff check: clean
  • pylint src/profiles: 9.88/10 (gate 8.0)
  • pytest: 1490 passed, 1 skipped (pre-existing macOS Finder probe), coverage 88.61% (gate 85%)
  • import profiles.app smoke: clean; pre-commit hooks green

Follow-up

#48's CI quality gate can now include uv run pyright src/profiles — that wiring is left to #48.

A [tool.pyright] section in pyproject.toml (basic mode, .venv env)
makes the documented `pyright src/profiles` gate real: 81 errors → 0.

- declare hatchling in both dev lists so the build-hook import
  resolves (also pre-completes #43's hatchling declaration)
- execution.py: replace the dict[str, object] Popen kwargs with
  explicit per-platform calls (no behavior change)
- inheritance.py: replace the untyped pick() closure with typed
  per-shape helpers; resolve_configs now produces the declared
  list-shaped schema fields (tests already pinned lists)
- reader.py: explicit schema→model conversion (MatchCriteria +
  tuples) instead of relying on __post_init__ coercion
- models.py: FailMode Literal defined here (bottom of the core
  dependency graph) and re-exported by workflow.py;
  launch_hook_failmode is typed FailMode
- columns chain typed dict[str, ColumnConfiguration] across
  classifier/scanner/scan route; the _iter_columns workaround is
  deleted while its non-dict contract is kept via isinstance
  (pinned by test_non_dict_columns_ignored)
- real bugs found by the gate: app.py run_headless accessed
  cfg.directory (a nonexistent field) and crashed whenever machine
  configurations existed — now iterates cfg.scan; handoff.py could
  hit an unbound `detail` when reading the HTTPError body failed;
  matcher.py returned tuple|bool against a bool contract
- tkinter fallbacks (interactions, message_dialog) restructured
  into import/display try blocks (no possibly-unbound tk)
- targeted pyright: ignore only where third-party stubs are absent
  (pyobjc, ctypes.windll, os.startfile, build-hook base union) and
  on the legacy HookSpec/WorkflowStep engine mismatch pending #39
- pylint pragma prose moved off pragma lines (7 spurious W0012)
@falbany
falbany merged commit e05adc8 into main Nov 4, 2026
1 check passed
@falbany
falbany deleted the chore/pyright-configure-and-clean branch November 4, 2026 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore: pyright is prescribed by AGENTS.md but unconfigured — 115 errors on a clean checkout

1 participant