Skip to content

chore: remove deprecated Python parser/planner/executor stack#662

Merged
DecisionNerd merged 3 commits into
mainfrom
chore/remove-python-legacy
May 30, 2026
Merged

chore: remove deprecated Python parser/planner/executor stack#662
DecisionNerd merged 3 commits into
mainfrom
chore/remove-python-legacy

Conversation

@DecisionNerd
Copy link
Copy Markdown
Owner

@DecisionNerd DecisionNerd commented May 30, 2026

Summary

Removes the entire Python 0.4.x pipeline. The Rust recursive-descent + Pratt parser is the authoritative implementation; TCK compliance is driven by the Rust cucumber-rs BDD runner in crates/gf-core/tests/bdd/.

Deleted (~117k lines):

  • src/graphforge/{api.py, ast/, parser/, planner/, optimizer/, executor/, storage/, search/, algorithms/, datasets/, procedures/, types/, _compat.py}
  • tests/unit/{parser, ast, planner, optimizer, executor, storage, search, algorithms, datasets, procedures}/
  • Old api test files testing the 0.4.x execution surface
  • tests/tck/ — Python TCK runner (Rust BDD runner is the replacement)
  • tests/parser_parity/ — differential harness (milestone 9 complete)
  • crates/gf-cypher/src/bin/parse_json.rs — only used by parity harness

Kept and promoted:

  • src/graphforge/api_v05.pysrc/graphforge/api.py (v0.5 Arrow-returning stubs)
  • src/graphforge/{exceptions.py, recipes/}
  • tests/unit/api/test_api_v05.py — tests the v0.5 API surface
  • tests/features/ — BDD scenarios that drive the Rust implementation

Result: 241 passing, 41 xfailed (pending Rust implementations), 0 failures.

🤖 Generated with Claude Code

Note

Remove deprecated Python parser/planner/executor stack from graphforge.api

  • Replaces the full parser/planner/executor pipeline in src/graphforge/api.py with a lightweight in-memory stub using NodeHandle and EdgeHandle objects.
  • All analytics methods (execute, rank, cluster, find, schema) now return empty typed pyarrow Tables; execute raises ParseError for unrecognized leading keywords instead of running queries.
  • Transaction support (begin/commit/rollback) is implemented as shallow in-memory snapshots; lifecycle guards raise LifecycleError on misuse.
  • graphforge.recipes.neighbourhood now returns a pyarrow.Table via db.execute() instead of a list[dict] via db.to_dicts().
  • Bumps __version__ to 0.5.0 and removes SearchHit, unwrap, unwrap_results, and unwrap_row from the top-level graphforge package exports.
  • Risk: SearchHit and the unwrap* helpers are no longer importable from graphforge; neighbourhood return type changes from list[dict] to pyarrow.Table; no queries are actually executed against any backend.

Macroscope summarized a270bfa.

Summary by CodeRabbit

  • Major Changes

    • Release v0.5: simplified API surface with lightweight NodeHandle/EdgeHandle and a stubbed GraphForge that returns PyArrow tables for execution and recipe helpers.
  • Removed Features

    • Deprecated: query parser/planner, optimizer, graph algorithms, dataset loaders/exporters, search, storage backends, rich runtime types and many dataset registries.
    • CLI parsing utility removed.
  • Breaking Changes

    • Large API and behavior changes — client code must be updated to the new v0.5 interfaces and Arrow-based results.

Review Change Stack

Delete the entire Python 0.4.x pipeline — the Rust recursive-descent +
Pratt parser is the authoritative implementation going forward, and the
TCK is driven by the Rust cucumber-rs BDD runner.

Removed:
- src/graphforge/{api.py,ast/,parser/,planner/,optimizer/,executor/}
- src/graphforge/{storage/,search/,algorithms/,datasets/,procedures/,types/,_compat.py}
- tests/unit/{parser,ast,planner,optimizer,executor,storage,search,algorithms,datasets,procedures}/
- tests/unit/api/{test_api_coverage,test_bulk_ingest,test_clear,test_cypher_value_conversion,test_json_io,test_add_graph_documents,test_analytics,test_cache,test_clone,test_merge_node,test_schema_introspection,test_set_node_properties}.py
- tests/{tck/,parser_parity/}
- crates/gf-cypher/src/bin/parse_json.rs (parity harness binary)

Kept and promoted:
- src/graphforge/api_v05.py → src/graphforge/api.py (v0.5 Arrow-returning stubs)
- src/graphforge/{exceptions.py,recipes/}
- tests/unit/api/test_api_v05.py → tests the v0.5 surface
- tests/features/ — BDD scenarios driving the Rust implementation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

Walkthrough

Replaces the production GraphForge (v0.4) with a v0.5 test stub: adds in-memory Node/Edge handles and PyArrow-returning stub methods, and removes parser/planner/optimizer, storage, dataset/format/exporter, algorithm, and many parser-parity tests.

Changes

GraphForge v0.5 Stub Migration

Layer / File(s) Summary
Core API and package restructuring
src/graphforge/__init__.py, src/graphforge/api.py, tests/features/steps/api_steps.py
Introduce v0.5 GraphForge stub (NodeHandle/EdgeHandle), PyArrow-returning execute/rank/cluster/find/schema, in-memory mutation and snapshot transactions, bump version to 0.5.0, reorder exports, and switch BDD imports to graphforge.api.
Parser, planner, and optimizer removal
src/graphforge/parser/*, src/graphforge/planner/*, src/graphforge/optimizer/*
Delete Lark grammar, parser and AST transformer, logical planner operator types and type tracking, and optimizer subsystems (cost model, join reordering, predicate utilities, statistics).
Dataset, formats, and exporter removal
src/graphforge/datasets/*, src/graphforge/datasets/formats/*, src/graphforge/datasets/exporters/*
Remove dataset package initializer, format/exporter re-exports and package-level API wiring; dataset loader/exporter modules are removed elsewhere in the diff.
Parser parity tests and Rust tooling cleanup
crates/gf-cypher/Cargo.toml, crates/gf-cypher/src/bin/parse_json.rs, .claude/worktrees/*, tests/parser_parity/*
Remove parse_json CLI target registration, delete parser-parity corpus/harness/tests, and update subproject worktree pointer.
Recipe signature updates and test fixture tweaks
src/graphforge/recipes/__init__.py, tests/conftest.py
Change neighbourhood recipe to return pyarrow.Table via db.execute(...), update module docs, and remove TCK pytest plugin auto-registration.

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

core

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: removing the deprecated Python parser/planner/executor stack. It is concise, specific, and accurately reflects the primary objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description is comprehensive and well-structured, covering summary, deletions, kept items, and test results, but lacks some checklist items and TCK testing notes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/remove-python-legacy

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread src/graphforge/api.py
Comment on lines +371 to +374
rows = list(records)
except Exception:
rows = []
node_map = {n.id: n for n in self._nodes}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High graphforge/api.py:371

add_edges calls list(records) directly on line 371, which for a pyarrow.Table iterates over column names instead of rows, causing the method to silently produce no edges when passed tabular data. Unlike add_nodes, it doesn't handle pyarrow.Table, pandas DataFrame, or polars DataFrame inputs. Consider adding the same conversion logic used in add_nodes (lines 343-355) to extract rows before processing.

-        try:
-            rows = list(records)
-        except Exception:
-            rows = []
+        try:
+            import pyarrow as pa
+
+            if isinstance(records, pa.Table):
+                rows = records.to_pylist()
+            elif hasattr(records, "to_dict"):
+                rows = records.to_dict("records")  # pandas DataFrame
+            elif hasattr(records, "to_dicts"):
+                rows = records.to_dicts()  # polars DataFrame
+            else:
+                rows = list(records)
+        except Exception:
+            rows = []
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file src/graphforge/api.py around lines 371-374:

`add_edges` calls `list(records)` directly on line 371, which for a `pyarrow.Table` iterates over column names instead of rows, causing the method to silently produce no edges when passed tabular data. Unlike `add_nodes`, it doesn't handle `pyarrow.Table`, pandas DataFrame, or polars DataFrame inputs. Consider adding the same conversion logic used in `add_nodes` (lines 343-355) to extract rows before processing.

Evidence trail:
src/graphforge/api.py lines 338-362 (add_nodes with conversion logic for pyarrow, pandas, polars), lines 364-385 (add_edges without such logic, line 371: `rows = list(records)`). pyarrow.Table.__iter__ yields column names (strings), not row dicts, causing isinstance(row, dict) check at line 376 to fail silently.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/graphforge/__init__.py (1)

17-28: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing SearchHit export/definition breaks from graphforge import SearchHit.

  • tests/integration/test_three_surfaces.py imports SearchHit from the package
  • src/graphforge/__init__.py’s __all__ doesn’t export SearchHit, and there is no SearchHit symbol anywhere under src/graphforge/

Update the package to provide SearchHit (and re-export it from src/graphforge/__init__.py), or adjust the consuming tests to the new result type/location.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/graphforge/__init__.py` around lines 17 - 28, The package is missing the
SearchHit symbol which breaks imports; add a SearchHit definition (e.g., a
lightweight dataclass or namedtuple representing search result fields) in an
appropriate module (for example create or extend a module like results.py or
types.py) and then re-export it from the package root by adding "SearchHit" to
the __all__ list in __init__ and importing it into __init__ (e.g., from .results
import SearchHit). Ensure the new SearchHit type matches how tests use it
(fields/methods) and update any consuming code/tests to import the new location
if you choose to keep it outside the package root.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/graphforge/api.py`:
- Around line 239-242: The current validation in GraphForge.__init__ (checking
self._path and raising StorageError) prevents auto-creation of the workspace
directory and breaks tests; change the logic in the constructor where self._path
is validated so that if self._path is not None and does not exist you create the
directory (use Path.mkdir(parents=True, exist_ok=True)) instead of raising
StorageError, and keep the existing check that raises StorageError if the path
exists but is not a directory; ensure the behavior aligns with prior v0.4
auto-create semantics for GraphForge(path).
- Around line 391-419: The parse-error simulation in execute is incorrectly
treating "NOT" as a valid starting keyword; remove "NOT" from the known_keywords
set (or otherwise exclude it from the accepted openCypher starts) so that
queries like "NOT VALID CYPHER !!!" trigger the intended ParseError; update the
known_keywords set used in execute (and leave _validate_query, ParseError, and
_empty_execute logic unchanged) so first_word checks behave correctly.

In `@src/graphforge/recipes/__init__.py`:
- Around line 9-11: The import typing.Any is unused and causes an F401 lint
error; update the import statement to remove Any so it reads "from typing import
TYPE_CHECKING" (leaving the existing TYPE_CHECKING usage intact) or otherwise
use Any where intended; specifically edit the top-level import line that
currently includes Any to drop that symbol.

---

Outside diff comments:
In `@src/graphforge/__init__.py`:
- Around line 17-28: The package is missing the SearchHit symbol which breaks
imports; add a SearchHit definition (e.g., a lightweight dataclass or namedtuple
representing search result fields) in an appropriate module (for example create
or extend a module like results.py or types.py) and then re-export it from the
package root by adding "SearchHit" to the __all__ list in __init__ and importing
it into __init__ (e.g., from .results import SearchHit). Ensure the new
SearchHit type matches how tests use it (fields/methods) and update any
consuming code/tests to import the new location if you choose to keep it outside
the package root.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d1da3c04-7b0f-4f48-9ba0-ee3cdcbf8232

📥 Commits

Reviewing files that changed from the base of the PR and between 276ca5d and 5f1e4ff.

📒 Files selected for processing (300)
  • .claude/worktrees/agent-a9d3563dc802c115e
  • crates/gf-cypher/Cargo.toml
  • crates/gf-cypher/src/bin/parse_json.rs
  • src/graphforge/__init__.py
  • src/graphforge/_compat.py
  • src/graphforge/algorithms/__init__.py
  • src/graphforge/algorithms/_dispatch.py
  • src/graphforge/algorithms/centrality.py
  • src/graphforge/algorithms/community.py
  • src/graphforge/algorithms/gds.py
  • src/graphforge/algorithms/structural.py
  • src/graphforge/api.py
  • src/graphforge/api_v05.py
  • src/graphforge/ast/__init__.py
  • src/graphforge/ast/clause.py
  • src/graphforge/ast/expression.py
  • src/graphforge/ast/pattern.py
  • src/graphforge/ast/query.py
  • src/graphforge/datasets/__init__.py
  • src/graphforge/datasets/base.py
  • src/graphforge/datasets/data/__init__.py
  • src/graphforge/datasets/data/networkrepository.json
  • src/graphforge/datasets/data/snap.json
  • src/graphforge/datasets/exporters/__init__.py
  • src/graphforge/datasets/exporters/json_graph.py
  • src/graphforge/datasets/formats/__init__.py
  • src/graphforge/datasets/formats/json_graph.py
  • src/graphforge/datasets/loaders/__init__.py
  • src/graphforge/datasets/loaders/compression.py
  • src/graphforge/datasets/loaders/csv.py
  • src/graphforge/datasets/loaders/cypher.py
  • src/graphforge/datasets/loaders/graphml.py
  • src/graphforge/datasets/loaders/json_graph.py
  • src/graphforge/datasets/loaders/ldbc.py
  • src/graphforge/datasets/loaders/ldbc_schema.py
  • src/graphforge/datasets/registry.py
  • src/graphforge/datasets/sources/__init__.py
  • src/graphforge/datasets/sources/graphml.py
  • src/graphforge/datasets/sources/json_graph.py
  • src/graphforge/datasets/sources/ldbc.py
  • src/graphforge/datasets/sources/networkrepository.py
  • src/graphforge/datasets/sources/snap.py
  • src/graphforge/executor/__init__.py
  • src/graphforge/executor/evaluator.py
  • src/graphforge/executor/executor.py
  • src/graphforge/optimizer/__init__.py
  • src/graphforge/optimizer/cost_model.py
  • src/graphforge/optimizer/join_reorder.py
  • src/graphforge/optimizer/optimizer.py
  • src/graphforge/optimizer/predicate_utils.py
  • src/graphforge/optimizer/statistics.py
  • src/graphforge/parser/__init__.py
  • src/graphforge/parser/cypher.lark
  • src/graphforge/parser/parser.py
  • src/graphforge/planner/__init__.py
  • src/graphforge/planner/operators.py
  • src/graphforge/planner/planner.py
  • src/graphforge/planner/types.py
  • src/graphforge/procedures/__init__.py
  • src/graphforge/procedures/registry.py
  • src/graphforge/recipes/__init__.py
  • src/graphforge/search/__init__.py
  • src/graphforge/search/search.py
  • src/graphforge/search/types.py
  • src/graphforge/storage/__init__.py
  • src/graphforge/storage/memory.py
  • src/graphforge/storage/pydantic_serialization.py
  • src/graphforge/storage/serialization.py
  • src/graphforge/storage/sqlite_backend.py
  • src/graphforge/types/__init__.py
  • src/graphforge/types/graph.py
  • src/graphforge/types/values.py
  • tests/conftest.py
  • tests/features/steps/api_steps.py
  • tests/parser_parity/__init__.py
  • tests/parser_parity/corpus.py
  • tests/parser_parity/harness.py
  • tests/parser_parity/test_parity.py
  • tests/tck/__init__.py
  • tests/tck/conftest.py
  • tests/tck/coverage_matrix.json
  • tests/tck/download_tck.py
  • tests/tck/features/Aggregation1_BasicAggregation.feature
  • tests/tck/features/Match1_SimpleMatches.feature
  • tests/tck/features/official/clauses/call/Call1.feature
  • tests/tck/features/official/clauses/call/Call2.feature
  • tests/tck/features/official/clauses/call/Call3.feature
  • tests/tck/features/official/clauses/call/Call4.feature
  • tests/tck/features/official/clauses/call/Call5.feature
  • tests/tck/features/official/clauses/call/Call6.feature
  • tests/tck/features/official/clauses/create/Create1.feature
  • tests/tck/features/official/clauses/create/Create2.feature
  • tests/tck/features/official/clauses/create/Create3.feature
  • tests/tck/features/official/clauses/create/Create4.feature
  • tests/tck/features/official/clauses/create/Create5.feature
  • tests/tck/features/official/clauses/create/Create6.feature
  • tests/tck/features/official/clauses/delete/Delete1.feature
  • tests/tck/features/official/clauses/delete/Delete2.feature
  • tests/tck/features/official/clauses/delete/Delete3.feature
  • tests/tck/features/official/clauses/delete/Delete4.feature
  • tests/tck/features/official/clauses/delete/Delete5.feature
  • tests/tck/features/official/clauses/delete/Delete6.feature
  • tests/tck/features/official/clauses/match-where/MatchWhere1.feature
  • tests/tck/features/official/clauses/match-where/MatchWhere2.feature
  • tests/tck/features/official/clauses/match-where/MatchWhere3.feature
  • tests/tck/features/official/clauses/match-where/MatchWhere4.feature
  • tests/tck/features/official/clauses/match-where/MatchWhere5.feature
  • tests/tck/features/official/clauses/match-where/MatchWhere6.feature
  • tests/tck/features/official/clauses/match/Match1.feature
  • tests/tck/features/official/clauses/match/Match2.feature
  • tests/tck/features/official/clauses/match/Match3.feature
  • tests/tck/features/official/clauses/match/Match4.feature
  • tests/tck/features/official/clauses/match/Match5.feature
  • tests/tck/features/official/clauses/match/Match6.feature
  • tests/tck/features/official/clauses/match/Match7.feature
  • tests/tck/features/official/clauses/match/Match8.feature
  • tests/tck/features/official/clauses/match/Match9.feature
  • tests/tck/features/official/clauses/merge/Merge1.feature
  • tests/tck/features/official/clauses/merge/Merge2.feature
  • tests/tck/features/official/clauses/merge/Merge3.feature
  • tests/tck/features/official/clauses/merge/Merge4.feature
  • tests/tck/features/official/clauses/merge/Merge5.feature
  • tests/tck/features/official/clauses/merge/Merge6.feature
  • tests/tck/features/official/clauses/merge/Merge7.feature
  • tests/tck/features/official/clauses/merge/Merge8.feature
  • tests/tck/features/official/clauses/merge/Merge9.feature
  • tests/tck/features/official/clauses/remove/Remove1.feature
  • tests/tck/features/official/clauses/remove/Remove2.feature
  • tests/tck/features/official/clauses/remove/Remove3.feature
  • tests/tck/features/official/clauses/return-orderby/ReturnOrderBy1.feature
  • tests/tck/features/official/clauses/return-orderby/ReturnOrderBy2.feature
  • tests/tck/features/official/clauses/return-orderby/ReturnOrderBy3.feature
  • tests/tck/features/official/clauses/return-orderby/ReturnOrderBy4.feature
  • tests/tck/features/official/clauses/return-orderby/ReturnOrderBy5.feature
  • tests/tck/features/official/clauses/return-orderby/ReturnOrderBy6.feature
  • tests/tck/features/official/clauses/return-skip-limit/ReturnSkipLimit1.feature
  • tests/tck/features/official/clauses/return-skip-limit/ReturnSkipLimit2.feature
  • tests/tck/features/official/clauses/return-skip-limit/ReturnSkipLimit3.feature
  • tests/tck/features/official/clauses/return/Return1.feature
  • tests/tck/features/official/clauses/return/Return2.feature
  • tests/tck/features/official/clauses/return/Return3.feature
  • tests/tck/features/official/clauses/return/Return4.feature
  • tests/tck/features/official/clauses/return/Return5.feature
  • tests/tck/features/official/clauses/return/Return6.feature
  • tests/tck/features/official/clauses/return/Return7.feature
  • tests/tck/features/official/clauses/return/Return8.feature
  • tests/tck/features/official/clauses/set/Set1.feature
  • tests/tck/features/official/clauses/set/Set2.feature
  • tests/tck/features/official/clauses/set/Set3.feature
  • tests/tck/features/official/clauses/set/Set4.feature
  • tests/tck/features/official/clauses/set/Set5.feature
  • tests/tck/features/official/clauses/set/Set6.feature
  • tests/tck/features/official/clauses/union/Union1.feature
  • tests/tck/features/official/clauses/union/Union2.feature
  • tests/tck/features/official/clauses/union/Union3.feature
  • tests/tck/features/official/clauses/unwind/Unwind1.feature
  • tests/tck/features/official/clauses/with-orderBy/WithOrderBy1.feature
  • tests/tck/features/official/clauses/with-orderBy/WithOrderBy2.feature
  • tests/tck/features/official/clauses/with-orderBy/WithOrderBy3.feature
  • tests/tck/features/official/clauses/with-orderBy/WithOrderBy4.feature
  • tests/tck/features/official/clauses/with-skip-limit/WithSkipLimit1.feature
  • tests/tck/features/official/clauses/with-skip-limit/WithSkipLimit2.feature
  • tests/tck/features/official/clauses/with-skip-limit/WithSkipLimit3.feature
  • tests/tck/features/official/clauses/with-where/WithWhere1.feature
  • tests/tck/features/official/clauses/with-where/WithWhere2.feature
  • tests/tck/features/official/clauses/with-where/WithWhere3.feature
  • tests/tck/features/official/clauses/with-where/WithWhere4.feature
  • tests/tck/features/official/clauses/with-where/WithWhere5.feature
  • tests/tck/features/official/clauses/with-where/WithWhere6.feature
  • tests/tck/features/official/clauses/with-where/WithWhere7.feature
  • tests/tck/features/official/clauses/with/With1.feature
  • tests/tck/features/official/clauses/with/With2.feature
  • tests/tck/features/official/clauses/with/With3.feature
  • tests/tck/features/official/clauses/with/With4.feature
  • tests/tck/features/official/clauses/with/With5.feature
  • tests/tck/features/official/clauses/with/With6.feature
  • tests/tck/features/official/clauses/with/With7.feature
  • tests/tck/features/official/expressions/aggregation/Aggregation1.feature
  • tests/tck/features/official/expressions/aggregation/Aggregation2.feature
  • tests/tck/features/official/expressions/aggregation/Aggregation3.feature
  • tests/tck/features/official/expressions/aggregation/Aggregation4.feature
  • tests/tck/features/official/expressions/aggregation/Aggregation5.feature
  • tests/tck/features/official/expressions/aggregation/Aggregation6.feature
  • tests/tck/features/official/expressions/aggregation/Aggregation7.feature
  • tests/tck/features/official/expressions/aggregation/Aggregation8.feature
  • tests/tck/features/official/expressions/boolean/Boolean1.feature
  • tests/tck/features/official/expressions/boolean/Boolean2.feature
  • tests/tck/features/official/expressions/boolean/Boolean3.feature
  • tests/tck/features/official/expressions/boolean/Boolean4.feature
  • tests/tck/features/official/expressions/boolean/Boolean5.feature
  • tests/tck/features/official/expressions/comparison/Comparison1.feature
  • tests/tck/features/official/expressions/comparison/Comparison2.feature
  • tests/tck/features/official/expressions/comparison/Comparison3.feature
  • tests/tck/features/official/expressions/comparison/Comparison4.feature
  • tests/tck/features/official/expressions/conditional/Conditional1.feature
  • tests/tck/features/official/expressions/conditional/Conditional2.feature
  • tests/tck/features/official/expressions/existentialSubqueries/ExistentialSubquery1.feature
  • tests/tck/features/official/expressions/existentialSubqueries/ExistentialSubquery2.feature
  • tests/tck/features/official/expressions/existentialSubqueries/ExistentialSubquery3.feature
  • tests/tck/features/official/expressions/graph/Graph1.feature
  • tests/tck/features/official/expressions/graph/Graph2.feature
  • tests/tck/features/official/expressions/graph/Graph3.feature
  • tests/tck/features/official/expressions/graph/Graph4.feature
  • tests/tck/features/official/expressions/graph/Graph5.feature
  • tests/tck/features/official/expressions/graph/Graph6.feature
  • tests/tck/features/official/expressions/graph/Graph7.feature
  • tests/tck/features/official/expressions/graph/Graph8.feature
  • tests/tck/features/official/expressions/graph/Graph9.feature
  • tests/tck/features/official/expressions/list/List1.feature
  • tests/tck/features/official/expressions/list/List10.feature
  • tests/tck/features/official/expressions/list/List11.feature
  • tests/tck/features/official/expressions/list/List12.feature
  • tests/tck/features/official/expressions/list/List2.feature
  • tests/tck/features/official/expressions/list/List3.feature
  • tests/tck/features/official/expressions/list/List4.feature
  • tests/tck/features/official/expressions/list/List5.feature
  • tests/tck/features/official/expressions/list/List6.feature
  • tests/tck/features/official/expressions/list/List7.feature
  • tests/tck/features/official/expressions/list/List8.feature
  • tests/tck/features/official/expressions/list/List9.feature
  • tests/tck/features/official/expressions/literals/Literals1.feature
  • tests/tck/features/official/expressions/literals/Literals2.feature
  • tests/tck/features/official/expressions/literals/Literals3.feature
  • tests/tck/features/official/expressions/literals/Literals4.feature
  • tests/tck/features/official/expressions/literals/Literals5.feature
  • tests/tck/features/official/expressions/literals/Literals6.feature
  • tests/tck/features/official/expressions/literals/Literals7.feature
  • tests/tck/features/official/expressions/literals/Literals8.feature
  • tests/tck/features/official/expressions/map/Map1.feature
  • tests/tck/features/official/expressions/map/Map2.feature
  • tests/tck/features/official/expressions/map/Map3.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical1.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical10.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical11.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical12.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical13.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical14.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical15.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical16.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical17.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical2.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical3.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical4.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical5.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical6.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical7.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical8.feature
  • tests/tck/features/official/expressions/mathematical/Mathematical9.feature
  • tests/tck/features/official/expressions/null/Null1.feature
  • tests/tck/features/official/expressions/null/Null2.feature
  • tests/tck/features/official/expressions/null/Null3.feature
  • tests/tck/features/official/expressions/path/Path1.feature
  • tests/tck/features/official/expressions/path/Path2.feature
  • tests/tck/features/official/expressions/path/Path3.feature
  • tests/tck/features/official/expressions/pattern/Pattern1.feature
  • tests/tck/features/official/expressions/pattern/Pattern2.feature
  • tests/tck/features/official/expressions/precedence/Precedence1.feature
  • tests/tck/features/official/expressions/precedence/Precedence2.feature
  • tests/tck/features/official/expressions/precedence/Precedence3.feature
  • tests/tck/features/official/expressions/precedence/Precedence4.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier1.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier10.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier11.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier12.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier2.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier3.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier4.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier5.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier6.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier7.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier8.feature
  • tests/tck/features/official/expressions/quantifier/Quantifier9.feature
  • tests/tck/features/official/expressions/string/String1.feature
  • tests/tck/features/official/expressions/string/String10.feature
  • tests/tck/features/official/expressions/string/String11.feature
  • tests/tck/features/official/expressions/string/String12.feature
  • tests/tck/features/official/expressions/string/String13.feature
  • tests/tck/features/official/expressions/string/String14.feature
  • tests/tck/features/official/expressions/string/String2.feature
  • tests/tck/features/official/expressions/string/String3.feature
  • tests/tck/features/official/expressions/string/String4.feature
  • tests/tck/features/official/expressions/string/String5.feature
  • tests/tck/features/official/expressions/string/String6.feature
  • tests/tck/features/official/expressions/string/String7.feature
  • tests/tck/features/official/expressions/string/String8.feature
  • tests/tck/features/official/expressions/string/String9.feature
  • tests/tck/features/official/expressions/temporal/Temporal1.feature
  • tests/tck/features/official/expressions/temporal/Temporal10.feature
  • tests/tck/features/official/expressions/temporal/Temporal2.feature
  • tests/tck/features/official/expressions/temporal/Temporal3.feature
  • tests/tck/features/official/expressions/temporal/Temporal4.feature
  • tests/tck/features/official/expressions/temporal/Temporal5.feature
  • tests/tck/features/official/expressions/temporal/Temporal6.feature
  • tests/tck/features/official/expressions/temporal/Temporal7.feature
  • tests/tck/features/official/expressions/temporal/Temporal8.feature
  • tests/tck/features/official/expressions/temporal/Temporal9.feature
  • tests/tck/features/official/expressions/typeConversion/TypeConversion1.feature
  • tests/tck/features/official/expressions/typeConversion/TypeConversion2.feature
  • tests/tck/features/official/expressions/typeConversion/TypeConversion3.feature
  • tests/tck/features/official/expressions/typeConversion/TypeConversion4.feature
💤 Files with no reviewable changes (70)
  • crates/gf-cypher/src/bin/parse_json.rs
  • src/graphforge/parser/cypher.lark
  • src/graphforge/datasets/data/init.py
  • src/graphforge/types/values.py
  • src/graphforge/datasets/data/snap.json
  • src/graphforge/_compat.py
  • src/graphforge/algorithms/community.py
  • src/graphforge/storage/memory.py
  • tests/tck/init.py
  • src/graphforge/datasets/loaders/init.py
  • src/graphforge/procedures/init.py
  • src/graphforge/datasets/sources/json_graph.py
  • src/graphforge/datasets/sources/graphml.py
  • tests/parser_parity/test_parity.py
  • src/graphforge/optimizer/statistics.py
  • src/graphforge/algorithms/gds.py
  • src/graphforge/datasets/base.py
  • src/graphforge/storage/serialization.py
  • src/graphforge/datasets/init.py
  • src/graphforge/datasets/formats/json_graph.py
  • src/graphforge/planner/init.py
  • src/graphforge/parser/init.py
  • src/graphforge/datasets/sources/init.py
  • src/graphforge/datasets/sources/ldbc.py
  • src/graphforge/datasets/sources/snap.py
  • src/graphforge/datasets/exporters/json_graph.py
  • src/graphforge/datasets/loaders/compression.py
  • src/graphforge/executor/init.py
  • src/graphforge/ast/init.py
  • src/graphforge/algorithms/structural.py
  • src/graphforge/optimizer/cost_model.py
  • src/graphforge/search/search.py
  • src/graphforge/search/init.py
  • src/graphforge/datasets/loaders/ldbc_schema.py
  • src/graphforge/search/types.py
  • src/graphforge/datasets/loaders/csv.py
  • src/graphforge/datasets/data/networkrepository.json
  • src/graphforge/datasets/loaders/cypher.py
  • src/graphforge/planner/types.py
  • tests/conftest.py
  • src/graphforge/procedures/registry.py
  • tests/parser_parity/corpus.py
  • src/graphforge/ast/clause.py
  • src/graphforge/ast/query.py
  • src/graphforge/types/init.py
  • src/graphforge/datasets/sources/networkrepository.py
  • src/graphforge/datasets/loaders/ldbc.py
  • src/graphforge/ast/pattern.py
  • src/graphforge/storage/sqlite_backend.py
  • src/graphforge/datasets/formats/init.py
  • src/graphforge/storage/pydantic_serialization.py
  • src/graphforge/algorithms/_dispatch.py
  • src/graphforge/types/graph.py
  • src/graphforge/optimizer/predicate_utils.py
  • src/graphforge/datasets/loaders/graphml.py
  • src/graphforge/datasets/registry.py
  • src/graphforge/ast/expression.py
  • crates/gf-cypher/Cargo.toml
  • src/graphforge/datasets/loaders/json_graph.py
  • src/graphforge/algorithms/centrality.py
  • src/graphforge/parser/parser.py
  • src/graphforge/optimizer/join_reorder.py
  • tests/parser_parity/harness.py
  • src/graphforge/planner/operators.py
  • src/graphforge/algorithms/init.py
  • src/graphforge/api_v05.py
  • src/graphforge/optimizer/init.py
  • src/graphforge/storage/init.py
  • src/graphforge/optimizer/optimizer.py
  • src/graphforge/datasets/exporters/init.py

Comment thread src/graphforge/api.py
Comment on lines +239 to +242
if self._path is not None and not self._path.exists():
raise StorageError(f"Path does not exist: {self._path}")
if self._path is not None and self._path.exists() and not self._path.is_dir():
raise StorageError(f"Path must be a directory, not a file: {self._path}")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Path validation prevents auto-creation, breaking existing tests.

The pipeline fails because GraphForge(path) now requires the directory to exist, but tests expect auto-creation. Consider aligning with v0.4 behavior or documenting this breaking change.

🛠️ Option: Create directory if it doesn't exist
         if self._path is not None and not self._path.exists():
-            raise StorageError(f"Path does not exist: {self._path}")
+            try:
+                self._path.mkdir(parents=True, exist_ok=True)
+            except OSError as e:
+                raise StorageError(f"Cannot create path: {self._path}: {e}") from e
         if self._path is not None and self._path.exists() and not self._path.is_dir():
             raise StorageError(f"Path must be a directory, not a file: {self._path}")
🧰 Tools
🪛 GitHub Actions: Test Suite / 19_Coverage Shard 1_4.txt

[error] 240-240: StorageError: Path does not exist: /tmp/tmpe9q8gqng/test.db (raised during GraphForge(db_path) initialization).

🪛 GitHub Actions: Test Suite / Coverage Shard 1_4

[error] 240-240: StorageError: Path does not exist: /tmp/tmpe9q8gqng/test.db (raised in GraphForge init)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/graphforge/api.py` around lines 239 - 242, The current validation in
GraphForge.__init__ (checking self._path and raising StorageError) prevents
auto-creation of the workspace directory and breaks tests; change the logic in
the constructor where self._path is validated so that if self._path is not None
and does not exist you create the directory (use Path.mkdir(parents=True,
exist_ok=True)) instead of raising StorageError, and keep the existing check
that raises StorageError if the path exists but is not a directory; ensure the
behavior aligns with prior v0.4 auto-create semantics for GraphForge(path).

Comment thread src/graphforge/api.py
Comment on lines +391 to +419
def execute(self, query: str, parameters: dict[str, Any] | None = None) -> pa.Table:
self._ensure_open()
if not self._in_transaction:
raise RuntimeError("Not in a transaction. Call begin() first.")
_validate_query(query)
# Stub parse-error simulation: any query that starts with a keyword
# not in the openCypher set raises ParseError.
first_word = query.strip().split()[0].upper()
known_keywords = {
"MATCH",
"CREATE",
"MERGE",
"RETURN",
"WITH",
"WHERE",
"SET",
"DELETE",
"DETACH",
"REMOVE",
"UNWIND",
"CALL",
"OPTIONAL",
"FOREACH",
"LOAD",
"EXPLAIN",
"PROFILE",
"NOT",
}
if first_word not in known_keywords:
raise ParseError(f"Unexpected token: {first_word!r}", span=(0, len(first_word)))
return _empty_execute()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

NOT in known_keywords may break parse-error simulation.

The BDD test execute raises ParseError on invalid Cypher uses "NOT VALID CYPHER !!!". Since "NOT" is in known_keywords, this query won't raise ParseError as expected. NOT isn't a valid openCypher query start.

🐛 Proposed fix
         "OPTIONAL",
         "FOREACH",
         "LOAD",
         "EXPLAIN",
         "PROFILE",
-        "NOT",
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/graphforge/api.py` around lines 391 - 419, The parse-error simulation in
execute is incorrectly treating "NOT" as a valid starting keyword; remove "NOT"
from the known_keywords set (or otherwise exclude it from the accepted
openCypher starts) so that queries like "NOT VALID CYPHER !!!" trigger the
intended ParseError; update the known_keywords set used in execute (and leave
_validate_query, ParseError, and _empty_execute logic unchanged) so first_word
checks behave correctly.

Comment thread src/graphforge/recipes/__init__.py Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented May 30, 2026

Codecov Report

❌ Patch coverage is 96.62447% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.08%. Comparing base (276ca5d) to head (a270bfa).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@             Coverage Diff             @@
##             main     #662       +/-   ##
===========================================
+ Coverage   84.10%   97.08%   +12.97%     
===========================================
  Files          49        2       -47     
  Lines       12942      274    -12668     
  Branches     3632       41     -3591     
===========================================
- Hits        10885      266    -10619     
+ Misses       1272        5     -1267     
+ Partials      785        3      -782     
Flag Coverage Δ
full-coverage 97.08% <96.62%> (+12.97%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
parser ∅ <ø> (∅)
planner ∅ <ø> (∅)
executor ∅ <ø> (∅)
storage ∅ <ø> (∅)
ast ∅ <ø> (∅)
types ∅ <ø> (∅)

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 276ca5d...a270bfa. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@DecisionNerd DecisionNerd merged commit c12fda4 into main May 30, 2026
44 checks passed
@DecisionNerd DecisionNerd deleted the chore/remove-python-legacy branch May 30, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant