Skip to content

tooling: explore adding pyright in monitor mode to catch possibly-unbound variables #1075

Description

@lmeyerov

Problem

During #977 (cudf SIGSEGV fix), a real UnboundLocalError in hop.py was missed by both ruff and mypy:

# edge_map assigned only inside this branch:
if g_out._edges is not None and edge_hop_col is not None and ...:
    edge_map = ...

# then used outside it — NameError/UnboundLocalError at runtime:
if missing_mask.any() and edge_hop_col is not None and ...:
    ... safe_map_series(..., edge_map)  # edge_map possibly unbound!

The bug was masked by an upstream SIGSEGV (so it never surfaced in production), and only caught by writing a test that exercised the specific branch combination (`label_node_hops=True, label_edge_hops=None` with a non-empty `missing_mask`).

Why ruff/mypy missed it

  • ruff F821 only catches names with no assignment anywhere in scope — not conditionally-assigned names
  • mypy does not flag "possibly unbound" from conditional branches unless the variable is annotated before the branch (e.g. `edge_map: pd.Series` declared before the `if`)
  • Neither tool does full dataflow / reachability analysis for this pattern

What would catch it

pyright (default mode, not even strict) detects "possibly unbound" variables from conditional assignments and flags them at the use site. This is a well-known difference from mypy.

Proposal

Explore adding pyright in monitor / informational mode to CI as a sequence of stacked PRs:

  1. Observability
  • Run pyright on the `graphistry/` source (excluding tests, matching mypy.ini scope)
  • Enforce in CI any rules that already pass
  • Initially non-blocking (warnings only) for rest to assess the volume of pre-existing issues
  • Clear reporting step in CI for easy auditing
  1. Initial refactor
  • Create and implement on a metaissue for initial target of rules that the repo can enforce
  • There should be a small number of changes that go a long way
  • Promote these to blocking, and leave the rest as warning
  • For local dev flow, add pyright to the mypy steps as a default-on option
  1. Follow-on refactors
  • Track a metaissue prioritized rules that fail
  • Sequence of PRs that fix each one

This does not replace mypy — they catch different things. Pyright's dataflow analysis is complementary.

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions