Skip to content

Adopt Python quality gates (ruff, pylint, interrogate, ty) - #435

Merged
leynos merged 12 commits into
mainfrom
python-lint-target
Aug 14, 2026
Merged

Adopt Python quality gates (ruff, pylint, interrogate, ty)#435
leynos merged 12 commits into
mainfrom
python-lint-target

Conversation

@leynos

@leynos leynos commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

This branch imports the Python quality tooling used by leynos/lading and
leynos/prosidy-darn — Ruff, Pylint (PyPy-backed), interrogate, and ty — and
wires it into the Makefile and CI so the repository's Python helper scripts
are held to the same standard as those reference projects. The scripts under
scripts/ and tests/workflow_contracts/ had never been linted as a gate;
this branch both establishes the gates and brings every file into
conformance, with docstring coverage on the shipped helper code raised from
78.5% to 100% and a Pylint rating of 10.00/10.

The work lands as two commits:

  1. Adopt Python quality standards from lading and prosidy-darn
    adds the rule sets and conforms the code.
  2. Wire Python lint, format, and typecheck gates into Make and CI
    turns the gates on.

Review walkthrough

  • Start with pyproject.toml
    for the imported rule sets. It is deliberately config-only (no [project]
    table) so uv keeps treating the helper scripts as non-project scripts; the
    vendored spelling-rollout helper is excluded because
    make spelling-helper-test gates it separately with its own pinned Ruff.
  • Then review the Makefile
    Python tooling block and the lint-python, check-fmt-python, and
    typecheck-python targets. typecheck-python materialises dependencies
    into .venv because ty cannot discover the layered environment that
    uv run --with builds; existing bare uv run invocations gain
    --no-project so the new root pyproject.toml cannot alter their
    semantics.
  • Next, .github/workflows/ci.yml
    adds discrete Python format check and Python lint steps; the existing
    make typecheck step picks up ty automatically. Pylint runs on a
    uv-managed PyPy (downloaded on demand) via the pylint-pypy shim, mirroring
    leynos/lading.
  • The bulk of the diff is conformance:
    scripts/local_k8s/
    and the top-level scripts adopt the import conventions (typ/dc/cabc
    aliases), type-checking blocks, exception-message locals, and missing
    docstrings. Behaviour is unchanged: error message text and CLI flags are
    preserved verbatim.
  • Targeted suppressions, each justified in place rather than by weakening
    the global rule set: E402 in
    test_properties.py
    for the deliberate pytest.importorskip ordering, S404/S603/S607 at
    the intentional subprocess boundaries, and DTZ011 for the wall-clock
    review-date check in
    check_redoc_ignore.py.

Validation

Post-rebase, on this exact tree:

  • make check-fmt: passes (Rust, Biome, ruff format --check).
  • make typecheck: passes (ty over all Python sources, tsc over the TS workspaces).
  • make test: passes — nextest 1438 passed/4 skipped, vitest 90, bun 43,
    15 workflow-contract tests, 100 local-preview script tests.
  • make lint: the Python/architecture/Biome/specs/Makefile/actions tiers all
    pass. lint-whitaker fails on
    backend/src/domain/jobs/generate_route/tests.rs:312
    (no_expect_outside_tests in generate_route_job_strategy). That file is
    byte-identical to the base branch and this PR changes no Rust; Define job structs for GenerateRouteJob and EnrichmentJob (5.2.2) #376's own CI
    fails the same "Whitaker lint" step, so it is pre-existing there and owned by
    that branch.

Notes

  • Tool versions are pinned (Ruff 0.15.12 shared with the spelling tier,
    ty 0.0.59, interrogate 1.7.0, and a pinned pylint-pypy shim commit) so
    local and CI runs cannot skew.
  • The branch was rebased onto main after the Podman/kind preview work
    ((7.1) Support Podman and kind previews #378) merged; the conformance commit was re-applied over that final
    merged code, including its new kind.py and session_secret.py modules.

Rebased onto the 5.2.2 job-structs branch

This PR now targets
backend-5-2-2-job-structs-for-generate-route-and-enrichment (#376) rather
than main, and has been rebased onto it.

Notes from that rebase:

  • The audit-remediation commit was dropped as superseded. Define job structs for GenerateRouteJob and EnrichmentJob (5.2.2) #376 had already
    patched the same advisories and gone further: brace-expansion 5.0.9 (vs the
    5.0.7 here), fast-uri 3.1.5 (vs 3.1.4), and an npm-visible resolutions
    block that lets Bun audit consume the patched versions directly — which in
    turn let Define job structs for GenerateRouteJob and EnrichmentJob (5.2.2) #376 retire most of the Bun exception ledger. Every package pinned
    here had an equal-or-newer counterpart there, so this branch no longer
    touches package.json, pnpm-lock.yaml, or security/audit-exceptions.json.
  • Two test files that arrived from Define job structs for GenerateRouteJob and EnrichmentJob (5.2.2) #376 were brought into conformance with
    the Python gates this branch enables: tests/workflow_contracts/ nixie_toolchain_test.py (import conventions, and pytest.fail() in place of
    assert False) and tests/workflow_contracts/makefile_tooling_test.py
    (S404 boundary annotation, formatting). This is the same "bring every file
    into conformance" work the first commit already does.
  • local-k8s-* Make targets now combine both branches' intent:
    $(UV) run --no-project ... keeps Define job structs for GenerateRouteJob and EnrichmentJob (5.2.2) #376's injectable $(UV) (its new
    makefile_tooling_test.py depends on it) alongside the --no-project this
    branch needs so the new root pyproject.toml cannot change uv run
    semantics. The final commit teaches the preview-CLI fake to skip uv run
    options so it tracks the Makefile rather than one fixed argv.

Rust LCOV coverage fix (added after review)

This branch also fixes a coverage regression its own root pyproject.toml
would otherwise cause. The config-only pyproject.toml (no [project] table)
made the shared generate-coverage action's auto-detection classify Wildside
as mixed, and mixed runs only support Cobertura — breaking the Rust-only
lcov.info that CodeScene consumes.

  • Upstream: leynos/shared-actions#373
    (merged, 074f7d8ba75a6e5d18532b72cbe38fccbda4e9c6) adds a
    backwards-compatible language input (auto/rust/python/mixed); the
    forced python/mixed prerequisite matches the action's real
    uv sync --inexact --python contract (a [project] table), so a tooling-only
    pyproject.toml no longer forces a Python run.
  • Here: both Generate Rust coverage steps set language: rust, and only the
    generate-coverage pin is bumped to the merged SHA (the setup-rust and
    upload-codescene-coverage pins stay at 18bed1ca to remain in lockstep with
    audit.yml/mutation-testing.yml). The CI workflow contract now asserts
    language: rust while retaining its LCOV/CodeScene assertions.

References

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Summary

Add Python quality gates with pinned Ruff, Pylint, interrogate, and ty tooling.

  • Configure Python formatting, linting, docstring coverage, and type checking in pyproject.toml.
  • Add Makefile and CI gates for Python sources under scripts/ and tests/workflow_contracts/.
  • Preserve isolated uv run --no-project execution and materialize type-checking dependencies in .venv.
  • Raise helper-script docstring coverage to 100% and achieve a Pylint score of 10.00/10.
  • Set language: rust in Rust coverage workflows to preserve LCOV generation.
  • Add workflow-contract and Makefile tests for Python gates, tool pins, dependency constraints, coverage settings, and uv options.
  • Document the new developer workflows and Rust-only coverage configuration.
  • Update Python helper scripts and tests for Ruff, Pylint, interrogate, and ty compatibility without changing core runtime behaviour.

Walkthrough

Changes

Quality and workflow hardening

Layer / File(s) Summary
Python tooling and quality gates
.gitignore, Makefile, pyproject.toml, docs/developers-guide.md
Add pinned Ruff, Interrogate, Pylint, and ty tooling. Add isolated uv targets and Python quality gates.
Local Kubernetes runtime typing
scripts/local_k8s.py, scripts/local_k8s/*
Move type-only imports behind TYPE_CHECKING. Refine annotations and validation constants. Keep operational behaviour unchanged apart from keyword-only CLI options and improved error logging.
Local Kubernetes test alignment
scripts/local_k8s/unittests/*
Align fixtures and tests with the updated typing, dataclass, formatting, and isolated uv execution patterns.
Script behaviour and documentation
scripts/check_redoc_ignore.py, scripts/rotate_session_key.py, scripts/sync_workspace_members.py, scripts/warm_pg_embedded_cache_test.py
Expand docstrings and diagnostics. Add the replica threshold constant. Strengthen cache-test environment handling and assertions.
CI and workflow contracts
.github/workflows/*, tests/workflow_contracts/*
Add Python CI gates. Pin coverage actions and require Rust-only LCOV generation. Add contract tests for workflows, Makefile tooling, and typed YAML validation.

Possibly related PRs

Suggested reviewers: codescene-access

Poem

Ruff checks march in line,
Rust coverage holds its course,
Types guard every path,
Kubernetes scripts align,
CI records each change.


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error, 4 inconclusive)

Check name Status Explanation Resolution
Testing (Overall) ❌ Error Makefile tests cover standalone Python recipes, but no test guards new aggregate wiring; removing lint-python, typecheck-python, or Ruff from fmt/check-fmt would leave them passing. Add command-level contracts for lint, typecheck, fmt, and check-fmt. Make each contract fail when the Python prerequisites or Ruff commands are removed.
User-Facing Documentation ❓ Inconclusive Need inspect the actual diff and users guide to confirm whether any changed behaviour is user-facing and whether required documentation exists. Inspect the PR diff, public CLI surfaces, and docs/users-guide.md before deciding.
Domain Architecture ❓ Inconclusive Investigation in progress; no verdict evidence submitted yet. Inspect the complete pull-request diff and domain boundaries before deciding.
Security And Privacy ❓ Inconclusive Investigation in progress; the latest commit diff alone does not represent the full pull request. Inspect the pull-request base-to-HEAD diff and all security-sensitive changed paths before deciding.
Concurrency And State ❓ Inconclusive Investigation is still in progress; no verdict submitted yet. Inspect the changed concurrency-sensitive helpers and their tests before deciding.
✅ Passed checks (15 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 94.19% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Developer Documentation ✅ Passed Accept: docs/developers-guide.md documents the new Python tools and Make/CI gates, isolated uv execution, and Rust-only coverage; no related roadmap or locale documentation requires updating.
Module-Level Documentation ✅ Passed Accept the change: tokenisation found a module-level docstring in every Python module under scripts/ and tests/workflow_contracts, including the new coverage contract.
Testing (Unit And Behavioural) ✅ Passed Pass this check: accept the real Make and CLI boundary tests, YAML workflow contracts, and existing validation error-path tests covering the changed gates and command wiring.
Testing (Property / Proof) ✅ Passed The diff adds tooling/workflow contracts and equivalent helper refactors, not a new input/state invariant; existing local_k8s Hypothesis properties remain in place.
Testing (Compile-Time / Ui) ✅ Passed The PR quality-work diff has no Rust or TypeScript files; Python output changes preserve existing text, and focused assertions cover affected CLI and structured workflow behaviour without needing s...
Unit Architecture ✅ Passed The diff adds quality tooling, annotations, docstrings, formatting, and explicit coverage configuration; no changed unit introduces a query-side effect or hides a new fallible dependency.
Observability ✅ Passed Evidence gathering is still in progress.
Performance And Resource Use ✅ Passed Pass this check: the diff adds no new production hot-path loops, retries, or unbounded buffers; format_members stays linear, and the source walk scans 36 Python files.
Architectural Complexity And Maintainability ✅ Passed The PR adds pinned quality-tool configuration and explicit Make/CI gates, not new runtime layers, abstractions, registries, or dependency cycles.
Rust Compiler Lint Integrity ✅ Passed The PR boundary contains no Rust file changes or Rust commits; the existing lint-rust target still runs cargo doc, Clippy, and Whitaker with warnings denied.
Title check ✅ Passed The title clearly describes the main change: adding Python quality gates with Ruff, Pylint, interrogate, and ty.
Description check ✅ Passed The description directly explains the Python tooling, Makefile and CI changes, coverage fix, validation, and rebase context.
✨ Finishing Touches 💡 2
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch python-lint-target
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch python-lint-target

Comment @coderabbitai help to get the list of available commands.

codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Jul 19, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please investigate the cause of the following issue using codegraph exploration and research, identify a fix and provide an AI coding agent prompt for the fix:

Run leynos/shared-actions@927edd4
Run astral-sh/setup-uv@0880764
UV_PYTHON_INSTALL_DIR is already set to /home/runner/.local/share/uv/python
Trying to find version for uv in: /home/runner/work/wildside/wildside/uv.toml
Could not find file: /home/runner/work/wildside/wildside/uv.toml
Trying to find version for uv in: /home/runner/work/wildside/wildside/pyproject.toml
Could not determine uv version from uv.toml or pyproject.toml. Falling back to latest.
Fetching manifest data from https://raw.githubusercontent.com/astral-sh/versions/main/v1/uv.ndjson ...
Found uv in tool-cache for 0.11.29
Added /home/runner/.local/bin to the path
Added /opt/hostedtoolcache/uv/0.11.29/x86_64 to the path
Set UV_PYTHON_INSTALL_DIR to /home/runner/.local/share/uv/python
Added /home/runner/.local/share/uv/python to the path
Successfully installed uv version 0.11.29
Run uv run --script "/home/runner/work/_actions/leynos/shared-actions/927edd45ae77be4251a8a18ca9eb5613a2e32cbd/.github/actions/generate-coverage/scripts/detect.py"
uv run --script "/home/runner/work/_actions/leynos/shared-actions/927edd45ae77be4251a8a18ca9eb5613a2e32cbd/.github/actions/generate-coverage/scripts/detect.py"
shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
env:
PG_VERSION: 16.10.0
RUN_RUST_CARGO_WAIT_TIMEOUT: 5400
CARGO_INCREMENTAL: 0
CARGO_PROFILE_DEV_DEBUG: 0
CARGO_TERM_COLOR: always
RUST_BACKTRACE: short
RUSTFLAGS: -D warnings
CARGO_UNSTABLE_SPARSE_REGISTRY: true
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
UV_PYTHON_INSTALL_DIR: /home/runner/.local/share/uv/python
SCCACHE_PATH: /opt/hostedtoolcache/sccache/0.16.0/x64/sccache
ACTIONS_CACHE_SERVICE_V2: on
ACTIONS_RESULTS_URL: https://results-receiver.actions.githubusercontent.com/
ACTIONS_RUNTIME_TOKEN: ***
GITHUB_TOKEN: ***
POSTGRESQL_VERSION: =16.10.0
POSTGRESQL_RELEASES_URL: https://github.com/theseus-rs/postgresql-binaries
PG_TEST_BACKEND: postgresql_embedded
PG_EMBEDDED_WORKER: /home/runner/work/wildside/wildside/target/pg_worker
INPUT_FORMAT: lcov
INPUT_CARGO_MANIFEST:
Downloading pygments (1.2MiB)
Downloaded pygments
Installed 7 packages in 8ms
Mixed projects only support cobertura format
Error: Process completed with exit code 1.
Node 20 is being deprecated. This workflow is running with Node 24 by default. If you need to temporarily use Node 20, you can set the ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true environment variable. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
Run actions/upload-artifact@v4
(node:19820) [DEP0040] DeprecationWarning: The punycode module is deprecated. Please use a userland alternative instead.
(Use node --trace-deprecation ... to show where the warning was created)
Warning: No files were found with the provided path: lcov.info. No artifacts will be uploaded.

@coderabbitai

This comment was marked as resolved.

@leynos
leynos marked this pull request as ready for review July 20, 2026 22:45
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@lodyai
lodyai Bot force-pushed the python-lint-target branch from 7548d38 to d56c359 Compare July 20, 2026 23:46
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

codescene-access[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the python-lint-target branch from 4dda8eb to a8b24a0 Compare August 4, 2026 23:22
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@lodyai
lodyai Bot force-pushed the python-lint-target branch from a8b24a0 to 119a83f Compare August 5, 2026 11:49
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Aug 6, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Have the following now been resolved?

scripts/rotate_session_key.py (1)

234-240: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Drive the warning from MIN_REPLICAS_FOR_ZERO_DOWNTIME.
The validation now uses the named constant, but the warning still hard-codes 2. Interpolate the constant so the user-facing guidance cannot drift from the actual safety threshold.

Proposed fix
-        "Zero-downtime rotation requires at least 2 replicas.",
+        f"Zero-downtime rotation requires at least "
+        f"{MIN_REPLICAS_FOR_ZERO_DOWNTIME} replicas.",
🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @scripts/rotate_session_key.py around lines 234 - 240, Update the warning in
the replica validation logic to interpolate MIN_REPLICAS_FOR_ZERO_DOWNTIME
instead of hard-coding 2, keeping the message aligned with the threshold used by
the surrounding validation check.

scripts/sync_workspace_members.py (1)

112-131: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Keep private helper docstrings to single-line summaries.
_calculate_bracket_depth_change() and _find_members_array_bounds() are private helpers, but the change expands them into full structured docstrings. Retain a single-line summary for these internal functions and reserve full NumPy sections for public interfaces.
As per path instructions, “Use a single-line summary for private functions and methods, and full structured docs for all public interfaces.”
Also applies to: 135-158

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @scripts/sync_workspace_members.py around lines 112 - 131, The private
helpers _calculate_bracket_depth_change() and _find_members_array_bounds()
should use concise single-line summary docstrings instead of structured NumPy
documentation. Remove their Parameters, Returns, and Examples sections while
preserving each helper’s essential behavior description.

Source: Path instructions

scripts/local_k8s/cluster.py (1)

50-56: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Add slots=True to the internal command specification.
Change the decorator to @dc.dataclass(frozen=True, slots=True); this internal structured record currently retains an unnecessary per-instance __dict__.
As per coding guidelines, use TypedDict or @dataclass(slots=True) for structured data, especially internal-only usage.

Proposed fix
-@dc.dataclass(frozen=True)
+@dc.dataclass(frozen=True, slots=True)
 class _ProviderCommandSpec:
🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @scripts/local_k8s/cluster.py around lines 50 - 56, Update the
_ProviderCommandSpec dataclass decorator to include slots=True while preserving
frozen=True, eliminating per-instance dict storage for this internal command
specification.

Source: Coding guidelines

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '=== File outlines ==='
ast-grep outline scripts/rotate_session_key.py --items all --match 'replica|MIN_REPLICAS' || true
ast-grep outline scripts/sync_workspace_members.py --items all --match '_calculate_bracket_depth_change|_find_members_array_bounds' || true
ast-grep outline scripts/local_k8s/cluster.py --items all --match '_ProviderCommandSpec' || true

printf '%s\n' '=== Replica threshold and warning ==='
rg -n -C 8 'MIN_REPLICAS_FOR_ZERO_DOWNTIME|Zero-downtime rotation requires' scripts/rotate_session_key.py

printf '%s\n' '=== Private helper definitions and docstrings ==='
rg -n -A 55 -B 3 \
  'def _calculate_bracket_depth_change\b|def _find_members_array_bounds\b' \
  scripts/sync_workspace_members.py

printf '%s\n' '=== Provider command specification ==='
rg -n -A 18 -B 4 \
  'class _ProviderCommandSpec\b|`@dc`\.dataclass' \
  scripts/local_k8s/cluster.py

Length of output: 6195


@leynos

Mark these findings as follows:

  • Resolved: scripts/rotate_session_key.py. Line 240 uses MIN_REPLICAS_FOR_ZERO_DOWNTIME in the warning text.
  • Not resolved: scripts/sync_workspace_members.py. _calculate_bracket_depth_change() now has a single-line docstring. _find_members_array_bounds() still has a structured Raises section at Lines 133-139. Replace it with a single-line summary.
  • Resolved: scripts/local_k8s/cluster.py. Line 50 uses @dc.dataclass(frozen=True, slots=True) for _ProviderCommandSpec.

✏️ Learnings added
Learnt from: leynos
URL: https://github.com/leynos/wildside/pull/435

Timestamp: 2026-08-06T14:19:56.995Z
Learning: For Python files in this repository, private functions and methods must use single-line summary docstrings. Public interfaces must use full structured documentation.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos force-pushed the python-lint-target branch from 0c8ba71 to 85cc423 Compare August 13, 2026 23:54
@leynos
leynos changed the base branch from backend-5-2-2-job-structs-for-generate-route-and-enrichment to main August 13, 2026 23:55
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Aug 14, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response. Ensure that this is validated against the current version of the codegraph.

If further refinement to address this finding would be deleterious, please supply a clear explanatory one to two paragraph markdown message I can paste into the CodeScene web ui's diagnostic suppression function so this diagnostic can be silenced.

scripts/warm_pg_embedded_cache_test.py

Comment on file

    ],
    ids=["missing-marker", "non-executable-postgres", "complete-cache"],
)
def test_cache_is_complete(

❌ New issue: Excess Number of Function Arguments
test_cache_is_complete has 5 arguments, max arguments = 4

@coderabbitai

This comment was marked as resolved.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

leynos and others added 12 commits August 14, 2026 18:33
Add a config-only root `pyproject.toml` (no `[project]` table, so uv
keeps treating the helper scripts as non-project scripts) carrying the
Ruff, Pylint, interrogate, and ty rule sets ported from leynos/lading
(primary template) and leynos/prosidy-darn, and bring every Python file
under `scripts/` and `tests/workflow_contracts/` up to those standards:

- Ruff: import conventions (`typ`/`dc`/`cabc` aliases), type-checking
  blocks, exception-message locals (`EM`/`TRY`), docstring layout, line
  length, and formatting across 27 files.
- interrogate: docstring coverage on `scripts/` raised from 78.5% to
  100% (docstrings added to `sync_workspace_members.py`,
  `warm_pg_embedded_cache_test.py`, and friends).
- Pylint (PyPy-shim rule families): implicit-booleaness and
  match-pattern fixes; rating 10.00/10.
- ty: literal narrowing casts in `local_k8s/config.py` and isinstance
  narrowing in the workflow-contract tests.

Targeted suppressions, each justified in place: `E402` in
`test_properties.py` (deliberate `pytest.importorskip` ordering),
`S404`/`S603`/`S607` at the two intentional subprocess boundaries, and
`DTZ011` for the wall-clock review-date check. No global rule was
weakened and no behaviour changed; all 83 script tests and 6 workflow
contract tests pass unchanged.
Mirror the leynos/lading Makefile shapes:

- `lint-python` runs Ruff 0.15.12, interrogate `--fail-under 100` over
  `scripts/`, and Pylint on a uv-managed PyPy via the pylint-pypy shim;
  it joins the `lint` aggregate.
- `fmt` gains `ruff format` and `ruff check --select I --fix`;
  `check-fmt` gains `ruff format --check`, with a standalone
  `check-fmt-python` target for a discrete CI step.
- `typecheck-python` (a `typecheck` prerequisite) materializes the
  script dependencies into `.venv` and runs `ty check
  --python-version 3.13` over all Python sources. A materialized venv
  is required because ty cannot discover the layered environment that
  `uv run --with` builds; `[tool.ty.environment]` supplies the
  `scripts/` search path.
- Existing bare `uv run` invocations gain `--no-project` so the new
  root `pyproject.toml` cannot change their semantics (uv would
  otherwise sync a phantom project and write a `uv.lock`).

CI gets `make check-fmt-python` and `make lint-python` steps; the
existing `make typecheck` step picks up the ty gate automatically. uv
downloads a managed PyPy on demand, so no separate CI install step is
needed. Ignore `.venv/` and `.ruff_cache/`.
The root pyproject.toml added by this branch is configuration-only (Ruff,
Pylint, interrogate, and ty settings, no [project] table). The
generate-coverage action's auto-detection treats any root pyproject.toml as a
Python project, so it classified Wildside as mixed; mixed runs only support
Cobertura, which would break the Rust-only lcov.info that CodeScene consumes.

leynos/shared-actions#373 added a backwards-compatible `language` input to the
action. Set `language: rust` on both `Generate Rust coverage` steps so the
detector emits lang=rust and keeps the lcov path, and bump only the
generate-coverage pin to the merged shared-actions SHA
(074f7d8ba75a6e5d18532b72cbe38fccbda4e9c6) that carries the new input. The
setup-rust and upload-codescene-coverage pins stay at 18bed1ca to remain in
lockstep with audit.yml and mutation-testing.yml, which this change does not
touch.

The ci workflow contract now asserts language: rust in the coverage mapping
while retaining the LCOV/CodeScene assertions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The local-k8s Make targets now pass `uv run --no-project` so the root
pyproject.toml this branch adds cannot change their resolution semantics.
The preview CLI fake matched a fixed argv (`args[:2] == ["run",
"scripts/local_k8s.py"]`), so the extra option made it report an unexpected
command and the make-target smoke tests failed.

Skip leading `uv run` options before matching the script path, mirroring the
existing `_unwrap` handling for `env` and `systemd-run` wrappers, and exec the
resolved script arguments. The fake now tracks the Makefile's invocation
rather than one fixed argv, so future option changes do not break it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Six review findings, each verified against the current tree before acting.

Pin the shared Python test dependencies. pytest, pytest-mock, hypothesis,
cryptography, and tomli were bare package names, so typecheck-python and
test-scripts could resolve different versions from one run to the next. Pin
them with the Makefile's dominant `FOO_VERSION ?=` style at the versions
already resolving today, and factor the shared set into PY_TEST_DEPS used by
both targets — the comment above PY_TYPECHECK_DEPS claims the versions mirror
test-scripts, which only holds if both sites share one definition.

Give _ProviderCommandSpec `slots=True` alongside `frozen=True`. Every other
non-exempt dataclass under scripts/ already pairs them, and no usage relies on
per-instance __dict__.

Interpolate MIN_REPLICAS_FOR_ZERO_DOWNTIME into the replica warning rather
than hard-coding 2, so the message cannot drift from the threshold the
adjacent check enforces.

Document the public helpers in sync_workspace_members.py and
check_redoc_ignore.py with NumPy Examples sections, using doctest skips where
an example would touch the filesystem or manifest state; format_members is
pure, so its example executes. Correct main()'s Returns description in
sync_workspace_members.py: it returns 0 on success but does not always return,
because SystemExit from _find_members_array_bounds propagates through
update_manifest uncaught.

Collapse the two private helpers to one-line summaries, matching the
convention every other private helper under scripts/ follows.
_find_members_array_bounds keeps its Raises section, since dropping it would
discard behaviour callers depend on.

Also widen _assert_command_logged's predicate parameter to Sequence. list is
invariant, so the narrowed list read back out of a log entry did not satisfy
list[object] once this branch's ty gate began covering the file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Python quality gates and the Rust-only coverage selection had no contract
protecting them, so a later edit could drop a gate step or re-enable Python
coverage detection without any test noticing.

Assert in the CI contract that the build job drives each Python gate through
its Makefile target, so CI and local runs cannot diverge onto separate
definitions.

Add a coverage-main contract covering the workflow this branch changed. It
pins the reviewed generate-coverage commit, fixes the full Rust-only ratcheted
LCOV input mapping, and ties the CodeScene upload to the report the generation
step writes, including the immutable upload-codescene-coverage pin.

Extend the Makefile tooling contracts with recipe-level coverage of
check-fmt-python, lint-python, and typecheck-python, and with command-level
coverage of every target that moved to isolated uv execution. The tests reuse
the existing command doubles, so they assert the observable invocations —
which runner, which flags, which targets — without downloading or running the
real tools. The typecheck assertions also require each declared dependency to
carry a version constraint, guarding the pins restored earlier on this branch,
and require the separately gated spelling helper to stay outside the
repository-wide surface.

Document the gates in the developers' guide: what each target runs, that the
aggregate lint and typecheck targets include them, and why the uv invocations
pass --no-project and the coverage workflows pass language: rust. Both follow
from the root pyproject.toml being tooling configuration rather than a managed
uv project, which is the part a future reader is most likely to undo by
accident.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Five review findings, each verified against the current tree first.

Bound both PyYAML declarations to `>=6,<7`. An open-ended floor would admit
PyYAML 7 into the typecheck environment and the workflow-contract runner
without review. The contract test now rejects any unbounded range rather than
merely requiring some constraint, so the next dependency added cannot
reintroduce an open floor.

Pin the interpreter `typecheck-python` builds. An unpinned `uv venv` takes
whichever Python uv resolves first, which on this machine is 3.14 while ty
analyses the sources as 3.13 — so ty resolved a standard library it was not
checking against. Both the venv and ty's `--python-version` now read
PY_TYPECHECK_VERSION, making the match structural rather than a coincidence
of two literals. The contract test asserts that relationship instead of a
version literal, so it keeps holding when the version moves.

The review asked for 3.14 here on the grounds that ty already analyses as that
version. It does not: `--python-version 3.13`, `[tool.ty.environment]`, ruff's
target-version, and requires-python all say 3.13, so 3.14 would have widened
the mismatch rather than closed it.

Correct the `discover_members` docstring example, which showed unsorted output
for a function that returns `sorted(...)`.

Explain the S607 suppression in the Bash test harness. The justification is a
plain comment, not a second `# noqa:` directive, which Ruff would flag as
unused.

Stop the mutation-testing contract normalizing every falsey `workflow_dispatch`
value to an empty mapping. Only `None` is YAML's shorthand for an empty
mapping; treating a list or scalar the same way let malformed workflow input
pass the mapping assertions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bound the workflow-contract runner's pytest requirement, which was the last
open-ended floor in the Makefile.

The review asked for `>=8,<9`. That would have downgraded this runner from
pytest 9.1.1 to 8.4.2 — PYTEST_VERSION is 9.1.1 and every other Python target
resolves 9.x, so `<9` would have left the contract suite alone on pytest 8.
`>=8,<10` closes the unbounded major without moving the resolved version. The
contract test now checks every `--with` requirement, not just the typecheck
dependency set, so an unbounded floor cannot reappear in an ad-hoc runner.

Lower Pylint's max-module-lines from 800 to 400 and split the two modules that
exceeded it. Both were test modules, split along the seams already present in
them:

- scripts/local_k8s/unittests/test_deployment.py (505) keeps the deploy,
  build, and Helm tests; the session-secret tests and their helpers move to
  test_session_secret.py, which is what they actually exercise.
- scripts/warm_pg_embedded_cache_test.py (677) keeps version normalization,
  cache locking, and cache-state tests. The checksum, download, release-URL,
  and end-to-end paths move to warm_pg_embedded_cache_download_test.py, which
  keeps them with the curl stub and archive fixtures they share. The Bash
  harness both modules need (run_bash, result_diagnostics, and the script
  paths) moves to warm_pg_embedded_cache_support.py rather than being
  duplicated.

No test was added, removed, or rewritten: the counts are unchanged at 105 for
the preview suite and 29 for the warm-up suite. The 800-line threshold was a
deliberate choice carried over from leynos/lading, so the earlier round left it
alone; with the split done, 400 holds with the largest remaining module at 395
lines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The upper bounds on pytest and PyYAML read as arbitrary without the reasoning,
and pytest's in particular invites being "corrected" to <9.

Note at the shared-pin block that PyYAML stays a range because no target needs
a particular 6.x, and that the ceiling exists to keep 7.0 out until reviewed.

Note at the workflow-contract runner that it sits outside PY_TEST_DEPS on
purpose, and that pytest's ceiling is <10 rather than <9 because
PYTEST_VERSION pins 9.1.1 everywhere else: <9 would hold this suite on pytest
8 and split the repository across two majors.

Both comments sit above their targets rather than inside a recipe, which
would push the body past checkmake's maxbodylength limit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replaying the Python quality-gates section onto main left a doubled blank line
before each of its three headings, which MD012 rejects. The section reads the
same; only the stray separators go.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The three cache_is_complete tests differed only in whether the .complete
marker existed, the mode on the postgres binary, and the expected result;
each repeated the same six lines of directory setup.

Fold them into one parametrized test with named ids, so the cases read as a
table and a fourth is a row rather than another copy of the setup.

The failure cases now assert the exact return code instead of merely non-zero.
cache_is_complete is a single `[[ -f ... && -x ... ]]` test, so it can only
return 0 or 1; both failure modes were confirmed to return 1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CodeScene flagged test_cache_is_complete for excess arguments: parametrizing
four fields alongside tmp_path put it at five, one over the configured limit.

Carry the four fields in a frozen, slotted _CacheCompletenessCase instead, so
the test takes only tmp_path and case. The three scenarios, their expected exit
statuses, and their pytest ids are unchanged, as is the shell command under
test.

The cases are constructed with keyword arguments. Passing has_complete_marker
positionally trips Ruff's FBT003 boolean-trap rule, and naming the fields also
keeps the table readable now that a row spans several lines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@leynos
leynos force-pushed the python-lint-target branch from 3a7943c to 498c019 Compare August 14, 2026 16:44
codescene-access[bot]

This comment was marked as outdated.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No quality gates enabled for this code.

@leynos
leynos merged commit 6bcf135 into main Aug 14, 2026
8 checks passed
@leynos
leynos deleted the python-lint-target branch August 14, 2026 17:06
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.

2 participants