Conversation
…ree readers (#1213 sub-PR B) First read-side slice of #1213 (umbrella #1200 slice 5). Migrate all text-only readers of `WhereClause.expr.text` to consume `boolean_expr_to_text(WhereClause.expr_tree)` directly, leveraging the parser invariant `(expr is None) == (expr_tree is None)` established by #1214. Sites migrated: - `frontends/cypher/binder.py::_where_predicates` — drop the dead `elif where.expr is not None` fallback. Per #1214's invariant the branch is unreachable for parser-produced WhereClauses; it was defensive code for hand-built fixtures only. - `cypher/lowering.py::_reject_unsupported_where_expr_forms` — gate + `expr.text.strip()` access. - `cypher/lowering.py::_reject_unsupported_variable_length_where_pattern_predicates` — error-reporting `value=expr.text` access. - `cypher/lowering.py::_check_expr` callers (three sites — `query.where`, `reentry_where`, and the post-binding `query.where` re-check) — gate + `expr.text` access. Out of scope (left for sub-PR C): - `ExpressionText` passthrough callers (`rewrite_expr` in lowering at 7515/7533, `_extract_relationship_type_where` at 8131, `pre_join_filters.append` at 7998, `row_where` assignment at 6322-6329, `_rewrite_shortest_path_expr_text` at ast_normalizer.py:432-436). - Span-access readers at lowering.py:8143-8144 (entangled with the passthrough at 8131; migrate together in C). Tests: removed `test_where_predicates_with_expr_tree_none_falls_back_to_expr_text` which exercised the now-dropped binder fallback via a hand-built `WhereClause(expr=..., expr_tree=None)` fixture — that shape violates the parser invariant and is unreachable from any real input. The sibling test `test_where_predicates_with_no_expr_at_all_returns_empty` still covers the legitimate no-expr-tree case. Verified: - 1538/1538 GFQL tests pass (was 1539, minus deleted test). - mypy clean on `cypher/lowering.py` and `frontends/cypher/binder.py`. Coordination: this is sub-PR B per the split agreed on #1213 (reader-side: us; writer-side D+E: colleague who landed #1214). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR Review: #1216 — refactor(gfql/binder + lowering): text-only WhereClause.expr → expr_tree readers (#1213 sub-PR B)Branch: BlockersNone. ImportantNone. Suggestions
(Wave 1 also surfaced a literal-atom fidelity concern and a deleted-test coverage concern; both were rejected/downgraded under adversarial pressure with code-level proof. See Human checks required
Rejected / False positives
MethodologyPer RecommendationApprove and merge with |
Summary
Sub-PR B of #1213 (umbrella #1200 slice 5). Migrate all text-only readers of
WhereClause.expr.textto consumeboolean_expr_to_text(WhereClause.expr_tree)instead, leveraging the parser invariant(expr is None) == (expr_tree is None)established by #1214.Reader-side only. Writer-side (drop
expr=fromWhereClause(...)constructions) and final field removal are sub-PRs D + E, owned by the colleague who landed #1214.Sites migrated
graphistry/compute/gfql/frontends/cypher/binder.py::_where_predicateselif where.expr is not Nonefallback. Per refactor(#1213 sub-PR A): lift boolean_expr_to_text + establish expr_tree invariant #1214's invariant the branch is unreachable for parser-produced WhereClauses; it was defensive code for hand-built fixtures only.graphistry/compute/gfql/cypher/lowering.py::_reject_unsupported_where_expr_forms(gate +.text.strip()).graphistry/compute/gfql/cypher/lowering.py::_reject_unsupported_variable_length_where_pattern_predicates(error-reportingvalue=.text).graphistry/compute/gfql/cypher/lowering.py::_check_exprcallers (3 sites —query.where,reentry_where, and the post-bindingquery.wherere-check).Diff: 17 insertions, 30 deletions across 4 files.
Out of scope (sub-PR C)
ExpressionTextpassthrough callers (rewrite_exprin lowering at L7515/7533;_extract_relationship_type_whereat L8131;pre_join_filters.appendat L7998;row_whereassignment at L6322-6329;_rewrite_shortest_path_expr_textatast_normalizer.py:432-436).lowering.py:8143-8144(entangled with the passthrough at L8131; migrate together in C).Test changes
Removed
test_where_predicates_with_expr_tree_none_falls_back_to_expr_textfromtest_binder_expr_tree.py. That test exercised the now-dropped binder fallback via a hand-builtWhereClause(expr=..., expr_tree=None)fixture — a shape that violates the parser invariant established by #1214 and is unreachable from any real parser output. The sibling testtest_where_predicates_with_no_expr_at_all_returns_emptystill covers the legitimate no-expr-tree case.Test plan
pytest graphistry/tests/compute/gfql/— 1538 passed (was 1539, minus the deleted test); 80 skipped, 15 xfailed.mypy graphistry/compute/gfql/cypher/lowering.py graphistry/compute/gfql/frontends/cypher/binder.py— clean.Coordination
Sub-PR B per the ownership split agreed on #1213:
expr=from constructionsexprfieldC will follow on a fresh branch off this one's merge into master, and will handle the
rewrite_exprintegration plus the latent text/tree staleness bug atlowering.py:7515/7533(wherereplace(where_clause, expr=rewrite_expr(...))currently leavesexpr_treepointing at pre-rewrite text — passes the invariant but is semantically inconsistent).Closes
Partial closure of #1213; full closure deferred until D + E.
Related
WhereClause.exprfield removal)expr_tree)expr_tree, closes gfql/cypher: resolve Lark ambiguity between where_predicates and expr (grammar-level follow-up to #1125) #1194)