Skip to content

Per-kind gate: queryspy_fail_on = lazy_load,column_load #14

Description

@lbedner

Ask

Let queryspy_fail_on (and/or --queryspy-strict) take a list of detector kinds, so a suite can fail on lazy_load and column_load while only reporting or baselining repeated_statement:

[tool.pytest.ini_options]
queryspy_fail_on = lazy_load,column_load

Today the pytest plugin never looks at Finding.kind: the only levers are per-test markers, queryspy.ignore() in app code, and the baseline. There is no way to say "gate on the unambiguous detectors, collect the heuristic one".

Why

Adopting on a FastAPI + async SQLModel project generator (14 stack variants, 22k tests total), measured on 0.4.1:

  • Merged across every stack: 0 lazy_load, 27 column_load, 346 repeated_statement. The first two were all real and all fixed in an afternoon (one pattern: refresh() after a write with no server_default). None of the 346 was.
  • repeated_statement at threshold 2 is dominated by two things that no application change can remove:
    1. The test fixture hands one AsyncSession to every request in a test (the usual transaction-rollback isolation). Two requests that each legitimately look the same user up once read as "2 identical queries". Switching to per-request sessions removed ~40% of findings but broke 44 tests.
    2. Labels exclude bind parameters by design, so session.get(A) + session.get(B) in a rotation-chain test are "identical".

So a cold --queryspy-strict is unreachable (56 failures on a fresh two-service project, 842 on a real one), while a cold gate on lazy_load + column_load would have been green the same day. We ended up baselining 364 entries to get the gate up; a kind filter would have let us gate on the two detectors immediately and adopt the third with the baseline, which matches the precision ordering the README already describes.

Shape

Purely additive: default stays "all kinds". _resolve_policy would carry a kinds: frozenset[str] and _enforce would filter findings before raising; the marker could accept the same (@pytest.mark.queryspy(fail_on=...)). Happy to send a PR if the shape is agreeable.

Thanks for the tool. The attribution to the ORM call site and the baseline keyed on what does not move are both exactly right.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions