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.
graphistry/compute/gfql/cypher/projection_planning.pyis 698 lines and 14 top-level functions that no static checker can see.How
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:
# ruff: noqa: F821# mypy: ignore-errorsreportUndefinedVariablebin/ci/ci_pyright_baseline.jsonThose 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.pyimportsprojection_planninglazily from inside five separate functions, which is the usual sign of a circular dependency being worked around. Theglobals().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 BYprojection — 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
loweringif 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.pyshould show this file'sreportUndefinedVariablecount going to zero, and--strictwill 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.