Skip to content

refactor(#1213 sub-PR D+E): drop WhereClause.expr field#1220

Merged
lmeyerov merged 4 commits intomasterfrom
feat/issue-1213-sub-pr-de-field-removal
Apr 26, 2026
Merged

refactor(#1213 sub-PR D+E): drop WhereClause.expr field#1220
lmeyerov merged 4 commits intomasterfrom
feat/issue-1213-sub-pr-de-field-removal

Conversation

@lmeyerov
Copy link
Copy Markdown
Contributor

@lmeyerov lmeyerov commented Apr 26, 2026

Summary

Final slice of #1213 (umbrella #1200). Removes the legacy raw-text WhereClause.expr: Optional[ExpressionText] field now that all production readers consume expr_tree-derived text via boolean_expr_to_text().

Sub-PR D (writers): drop expr= from every WhereClause(...) construction site.

Sub-PR E (field): drop the field declaration from cypher/ast.py. Rewrote the WhereClause docstring to enumerate all 3 observable routing shapes:

  • Structured: predicates populated, expr_tree is None (either WherePredicate entries or a single WherePatternPredicate)
  • Tree: predicates == (), expr_tree populated
  • Mixed: BOTH populated (for WHERE pattern AND expr)

Combined as one PR because the invariant test (test_where_clause_expr_tree_invariant.py, from #1214) becomes vacuous when the field is gone — keeping D + E together avoids landing a degenerate intermediate state.

Test surface:

  • test_where_clause_expr_tree_invariant.py rewritten from the now-vacuous (expr is None) == (expr_tree is None) symmetry to a routing-shape contract (9 query forms across the 3 shapes)
  • 12 raw-text reader assertions across test_parser.py, test_boolean_expr.py, test_ast_normalizer.py, test_where_bool_conformance.py migrated to boolean_expr_to_text(where.expr_tree)
  • Slice-1 backward-compat test (test_expr_tree_and_expr_both_populated_for_compat) deleted as obsolete
  • 3 hand-built fixtures in test_binder_expr_tree.py updated to drop expr= kwarg

Incidental cleanup (commit 44d46ca96): Sub-PR C (#1218) introduced two replace(where, expr=rewritten, expr_tree=new_tree) calls in ast_normalizer.py::_rewrite_where and lowering.py::_rewrite_where_clause_and_resync — valid at C's merge time, broken once D+E removes the field. Drops the expr= arg from both.

ExpressionText itself stays — other AST nodes still use it (ReturnItem, WithItem, OrderByItem, page values, etc.).

Review

Reviewed via the project review skill (~/Work/graphistry/.agents/skills/review/SKILL.md) under the new double-wave protocol (focus + full general, parallel). Findings synthesized in commit a1822b8c4:

  • IMPORTANT: docstring missing "mixed" routing shape (both waves) → fixed
  • IMPORTANT: invariant test missing where_pattern_only_clause coverage (focus) → added
  • SUGGESTION: stale binder comment claiming predicates/expr_tree mutually exclusive (general) → fixed
  • SUGGESTION: CHANGELOG "× 8" wording + public-API note (both) → applied

Adversarial pass cleared: span fidelity, literal-atom caveat, state-loss in replace() drops, test substitution semantics, deleted backward-compat test.

Test plan

  • 1538 / 1538 pass in graphistry/tests/compute/gfql/
  • Production grep clean: no remaining WhereClause.expr accesses (only comments and unrelated plan.expr)
  • CI green

Closes

Refs

🤖 Generated with Claude Code

lmeyerov and others added 4 commits April 25, 2026 19:00
Final slice of #1200 / #1213. Removes the legacy raw-text WHERE field now
that all production readers (binder, lowering, ast_normalizer) consume
expr_tree-derived text via boolean_expr_to_text() per sub-PR B/C.

Sub-PR D — drop expr= from writer construction sites:
  - parser.py: _mixed_where_clause, where_clause non-structured branch,
    generic_where_clause fallback (the three sites that synthesized
    BooleanExpr atoms in #1214); also strip expr=None from the three
    structured-path constructions
  - ast_normalizer.py: drop expr= forwarder + switch gate to expr_tree

Sub-PR E — remove the field:
  - ast.py: drop WhereClause.expr; update docstring to two-shape contract
    (structured vs tree, with mixed pattern+expr as the third combination)
  - test_where_clause_expr_tree_invariant.py: rewrite from the now-vacuous
    (expr is None) == (expr_tree is None) symmetry to the surviving
    routing-shape contract (structured / tree / mixed)
  - test_parser.py, test_boolean_expr.py, test_ast_normalizer.py,
    test_where_bool_conformance.py: replace `where.expr is None/not None`
    with `where.expr_tree is None/not None`; replace `where.expr.text`
    with `boolean_expr_to_text(where.expr_tree)`; drop the slice-1
    backward-compat test that asserted both fields were populated

NOT YET REBASED ONTO POST-C MASTER. This commit produces the expected
93 test failures from lowering/ast_normalizer readers that still access
.expr; sub-PR C migrates them. Rebase onto post-C master before pushing
or running CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…calls

Sub-PR C (#1218) introduced two ``replace(where, expr=rewritten, expr_tree=new_tree)``
calls in ``ast_normalizer.py::_rewrite_where`` and ``lowering.py::_rewrite_where_clause_and_resync``.
These passed both fields because the field still existed at C's merge time.

After this PR removes the field, the ``expr=`` arg becomes invalid keyword
to the dataclass constructor. Drop it from both sites; ``expr_tree`` is the
single source of truth post-D+E.

Also drops the now-unused ``expr=...`` and ``expr=None`` args from three
hand-built ``WhereClause(...)`` test fixtures in ``test_binder_expr_tree.py``
that broke for the same reason, plus the now-unused ``ExpressionText`` import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rage

Two review waves (focus + general per the new review policy) converged on:

1. **WhereClause docstring missing the "mixed" routing shape** (IMPORTANT, both
   waves). The new docstring listed only 2 shapes; reality is 3 (and arguably
   subdivided further by which kind of structured `predicates` populated).
   Rewrote to enumerate all three observable shapes, distinguishing
   `WherePredicate` from `WherePatternPredicate` in the structured case.

2. **Invariant test missing parser construction paths** (IMPORTANT, focus
   wave). Added `MATCH (a) WHERE (a)-[]->(:Admin) RETURN a` covering
   `where_pattern_only_clause` (structured, no AND, single
   `WherePatternPredicate`).  `expr_and_where_pattern_clause` (expr-first
   mixed) doesn't currently parse; `where_clause` _ExpressionSlice branch is
   Lark-internal routing already exercised by integration tests.

3. **Stale binder comment claiming `predicates`/`expr_tree` mutually exclusive**
   (SUGGESTION, general wave).  They aren't — the mixed shape populates
   both — and the binder body correctly handles all three shapes.  Updated
   the comment to match the AST docstring's 3-shape contract.

4. **CHANGELOG polish** (SUGGESTIONS, both waves): clarified count
   ("9 query forms across the 3 shapes" rather than the Cartesian-reading
   "3 × 8"), added an API note that `WhereClause` is exported and external
   constructors must drop `expr=` kwarg.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@lmeyerov lmeyerov marked this pull request as ready for review April 26, 2026 03:33
@lmeyerov lmeyerov merged commit fa300d4 into master Apr 26, 2026
133 checks passed
@lmeyerov lmeyerov deleted the feat/issue-1213-sub-pr-de-field-removal branch April 26, 2026 03:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant