Skip to content

projection_planning.py is invisible to ruff, mypy and pyright #2099

Description

@lmeyerov

graphistry/compute/gfql/cypher/projection_planning.py is 698 lines and 14 top-level functions that no static checker can see.

How

# mypy: ignore-errors
# ruff: noqa: F821
from graphistry.compute.gfql.cypher import lowering as _lowering

globals().update(vars(_lowering))

The module builds its namespace at import time by copying lowering's globals. Roughly fifty names arrive that way — Optional, Mapping, Any, Dict, List, cast, re, pd, plus the domain types (ASTObject, ASTEdge, CypherQuery, ExprNode, GFQLValidationError, _ProjectionPlan, ResultProjectionPlan, …) and helpers (_unsupported, lower_match_clause, _parse_row_expr, …).

No tool can follow that, so each was silenced in turn:

checker how it is silenced cost
ruff # ruff: noqa: F821 file-wide, all rules that depend on name resolution
mypy # mypy: ignore-errors file-wide, every error class
pyright not silenced — reports 195 reportUndefinedVariable baselined in bin/ci/ci_pyright_baseline.json

Those 195 are 195 of the 240 findings currently in the pyright ratchet. One file is 81% of the baseline, and none of its findings are real defects — they are artifacts of a namespace no checker can model.

Why it is shaped this way

lowering.py imports projection_planning lazily from inside five separate functions, which is the usual sign of a circular dependency being worked around. The globals().update(...) is the other half of that workaround: rather than importing what it needs, the module inherits its parent's entire namespace.

Why it is worth fixing

A file with zero static checking is where a typo, an unbound local or a stale import survives review. This is the module that plans Cypher RETURN/ORDER BY projection — not a peripheral one.

Suggested direction

Replace the namespace copy with explicit imports, breaking the cycle properly (a shared module for the types both files need, or moving the planning helpers into lowering if the split no longer earns its keep). Then drop both suppression comments and let the three checkers see it.

The ratchet gives an objective success measure: bin/ci/ci_pyright_guard.py should show this file's reportUndefinedVariable count going to zero, and --strict will lock each improvement as it lands.

Not urgent, but not free

Nothing is broken today. The cost is ongoing: every future change to this file is unreviewed by tooling, and the ratchet's headline number stays dominated by noise.

Context: the pyright gate landed in #2092; the baseline entry for this file was left visible rather than excluded precisely so fixing it shows up as slack under --strict.

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