Skip to content

Issue #79: Add read() support to the Express API - #115

Open
wheevu wants to merge 3 commits into
graph-algorithms:masterfrom
wheevu:fix/79-express-read
Open

wheevu wants to merge 3 commits into
graph-algorithms:masterfrom
wheevu:fix/79-express-read

Conversation

@wheevu

@wheevu wheevu commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Resolves #79

Adds planarity.read(path) to create a graph from a file and PGraph.read(path) to replace an existing graph.
Failed reads leave the existing graph unchanged.

Type of change

  • Bug fix
  • New feature
  • Documentation update

Changes

  • Supports adjacency list, adjacency matrix, graph6, and LEDA input. GraphML remains write-only.
  • Reads into a temporary graph and replaces the original only after success.
  • Adds API docstrings and 25 tests covering formats, replacement, error handling, and memory cleanup.

This also fixes a graph6 reader bug: malformed input could report success and silently replace the graph.
The fix propagates the read failure, so it also affects Graph.gp_Read() in the full API.
I can split that fix into a separate PR if preferred.

Vertex labels

Read graphs use labels 1..N, including for zero-based input.
The reader preserves the zero-based output flag, so writing the graph back can use labels starting at 0.

Would you prefer the in-memory labels to follow the input's numbering instead?

Testing

  • pytest -q: 92 passed on CPython 3.14.7 and 3.11.8.
  • Regression coverage includes malformed graph6, failed-read preservation, and the allocation leak.
  • Documentation builds successfully.

These results cover the submitted branch at c75cc5e.
Integration with the latest master has not yet been tested.

Add planarity.read(path), which returns a new PGraph populated from a
graph file, and PGraph.read(path), which replaces the contents of an
existing graph.

Both read into a temporary C-layer graph and swap it in only after a
successful read, so a failed read leaves the existing graph unchanged.
Supports the adjacency list, adjacency matrix, graph6, and LEDA formats;
GraphML remains write-only.

Vertex labels are the C reader's vertex indexes, so one-based and
zero-based inputs present the same labels, while a zero-based file still
writes back zero-based.

Also adds the cimport cython that Cython needs to recognize the
@cython.embedsignature(False) decorator that keeps the non-public
constructor out of the generated API docs.
_g6_ReadGraphFromStrOrFile() logged the failure from g6_ReadGraph() and
then returned OK, so gp_Read() reported success for a malformed .g6 line
and handed back an empty graph sized to the order the file declared.

Return the reader's status instead, so callers can tell that the read
failed. The Express read() API depends on this: its contract is that a
failed read leaves the existing graph unchanged. Regression coverage for
this lands with the Express read() changes in the following commit.
…ilure paths

_from_path() allocated the C graph before encoding the path and before
creating the Python object that owns it, so a path that raised on
encoding left the graph allocated with nothing to free it.

Encode the path first and create the owner before the allocation, so no
fallible step sits between gp_New() and ownership. Also document that a
path that cannot be encoded raises ValueError.

Extend tests/test_express_api_read.py with invalid graph6 through both
read entry points, which exercises the read-iterator fix in the previous
commit, plus the exception paths that run before any allocation and a
peak-RSS guard against the leak.
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.

Express API: Add read()

1 participant