[MAINTENANCE] Type-check great_expectations/render/renderer/

Author: joshua-staufferCreated Sep 14, 2026Updated Sep 14, 2026
Labelshelp wantedmaintenanceblocked

Part of #12188.

What to do

Bring the six excluded modules under great_expectations/render/renderer/ into the type-check: remove their six exclude patterns from pyproject.toml, fix the errors mypy then reports, regenerate the relaxation inventory in the same change, and prove that no rendered byte changed.

These modules build every Data Docs page and Slack payload a user sees, and their test coverage is thin precisely because they were never checked. So the bar is higher than "mypy is green": fix each error at its cause, widen a declaration rather than narrow it, and if the only fix you can see changes what a renderer emits, comment here before writing it.

One error is dead code rather than a typing gap: _build_run_time_block in slack_renderer.py guards if run_id is not None, then reads formatted_run_time outside the guard. The None case cannot arrive — CheckpointResult.run_id is a required RunIdentifier, and Checkpoint.run mints one when the caller passes none. Remove the guard so the name is bound on the one real path; do not widen the type to admit a value that never comes. Say that reasoning in the commit.

Where

  • render/renderer/content_block/content_block.py 12, site_builder.py 11, page_renderer.py 8, content_block/exception_list_content_block.py 6, slack_renderer.py 3, profiling_results_overview_section_renderer.py 2 — 42 errors after #12189 merges (51 before it). By kind: 15 union-attr, 5 misc, 4 override, 4 attr-defined, and a tail.
  • pyproject.toml — remove the six exclude entries under render/renderer/.
  • scripts/mypy_relaxation_inventory.json — regenerate with the guard's emit mode, never by hand.

How to verify

Files named on mypy's command line are checked even while an exclude pattern covers them, so this is red today and green only when both the errors and the patterns are gone (--cache-dir=/dev/null keeps the count independent of earlier runs):

bash
mypy --cache-dir=/dev/null --warn-unused-ignores --disallow-untyped-decorators great_expectations/render/renderer/*.py great_expectations/render/renderer/content_block/*.py && ! grep -q "'render/renderer/" pyproject.toml

Then python scripts/mypy_config_guard.py --emit-inventory > scripts/mypy_relaxation_inventory.json, invoke type-check --ci, and pytest tests/render tests/actions tests/checkpoint.

Then the rendered-output check: build Data Docs twice from the same fixed input — an ephemeral context, one small pandas DataFrame, a suite that exercises the expectations whose renderers these modules drive, one checkpoint run, context.build_data_docs() — once at your merge-base and once on your branch, and diff the two site directories after stripping run ids and timestamps. Before trusting a clean diff, prove it can fail: change one rendered string locally, see the diff flag it, revert. Put the procedure and the result in the PR.

Before: 42 errors in six files with #12189 merged, 51 without it (at 3798c021b). invoke type-check --ci checks 939 files with #12190 merged. After: the fence exits 0; invoke type-check --ci reports zero errors over 945 files; the three test directories pass as they do today; an empty normalized Data Docs diff.

Requirements

  1. When the type-check runs on the merged change, it reports zero errors and no exclude pattern naming a path under render/renderer/ remains.
  2. pytest tests/render tests/actions tests/checkpoint must pass with the same outcomes as before, with no expected value, assertion, skip, or xfail changed or added.
  3. Data Docs built from identical input before and after the change must be byte-identical apart from run ids and timestamps, and the PR must say how that was checked.
  4. The slack_renderer.py possibly-undefined error is resolved by removing the dead guard, with its output on every supported path unchanged. If your own call-path analysis finds a path where run_id is None, comment here and do not remove the guard.
  5. No public signature narrows, renames, or changes a default; a new # type: ignore[code] is acceptable only where every code-level fix would change output or break a public surface, and each one is single-line, commented, and listed in the PR.
  6. The inventory must be regenerated by python scripts/mypy_config_guard.py --emit-inventory in the same change, and the guard must pass.

Out of scope: the follow_imports = 'silent' block (#12192); great_expectations/render/ components.py's constructor contracts and the renderer configuration/result nullability convention, settled in #12116 — comment here if an error traces to them; anything under tests/ (#12128).

Order

  • Blocked by #12190 — the two lifts edit the same lines of pyproject.toml and the inventory, and this issue's error count is only meaningful against the merged state of that one.
  • Blocks #12192 — the silent-imports block must outlive every library exclusion.

Notes

  • page_renderer.py's _parse_run_values carried a second guard of the same shape until #12098 found a real None reaching it. That is why the Slack site gets its own call-path check before anything is deleted.
  • If a genuine runtime bug surfaces while you type a module, open a separate issue for it.

Source: fivetran/great_expectations