Skip to content

fix: vertex.edges() and graph.edges() return a new container, not the internal one - #36

Closed
petercorke wants to merge 2 commits into
mainfrom
fix/edges-return-copies
Closed

petercorke wants to merge 2 commits into
mainfrom
fix/edges-return-copies

Conversation

@petercorke

Copy link
Copy Markdown
Owner

Summary

vertex.edges() and graph.edges() returned the object's own edge list/set, so a caller could corrupt the graph without meaning to:

b.edges().clear()        # empties b's edge list ...
g.ne                     # ... but the graph still counts the edges

and removing edges while iterating v.edges() skipped some, because remove_edge mutates the list being iterated. Both now return a new container (list / set), so a caller can do what it likes with the result.

Audit first

Nothing relied on the aliasing:

  • inside pgraph the only consumer of vertex.edges() is degree (len() only);
  • every call site in RTB (PoseGraph, LatticePlanner), MVTB and spatialmath only iterates the result or takes its length; none mutates it.

Cost

graph.edges() on a 3,000-edge graph is about 9 µs per call; vertex.edges() is about 0.05 µs.

Docs

The BaseVertex.edges() :return: said "edges leaving this vertex" for both graph kinds, which is wrong for an undirected graph (it lists incident edges). The docstring now states which edges are returned for each kind, that an undirected self-loop is listed twice (once per end, the same reason it counts 2 towards the degree), and that the result is a copy.

Tests

tests/test_edges_copy.py, 24 tests across UGraph and DGraph (16 fail on main): type, not the internal object, new object per call, clearing/appending to the result leaves the graph intact, removing while iterating is safe, contents and self-loop listing per graph kind. Full suite 239 passing, mypy and pyflakes clean.

🤖 Generated with Claude Code

petercorke and others added 2 commits October 11, 2026 16:09
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Both returned the object's own edge list/set, so v.edges().clear() emptied the
vertex while the graph still counted the edges, leaving it inconsistent, and
removing edges while iterating the result skipped some. They now return a
copy, so a caller can do what it likes with the result.

Audited first: inside pgraph the only consumer of vertex.edges() is degree
(len only), and every call site in RTB, MVTB and spatialmath only iterates
the result or takes its length; none mutates it.

BaseVertex.edges() docstring now states which edges it returns for each
graph kind (the old :return: said 'leaving' for both), that a self-loop is
listed twice in an undirected graph, and that the result is a copy.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 17 complexity · 3 duplication

Metric Results
Complexity 17
Duplication 3

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@codecov

codecov Bot commented Oct 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.36%. Comparing base (d22408d) to head (9d89f01).

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #36      +/-   ##
==========================================
+ Coverage   93.27%   93.36%   +0.09%     
==========================================
  Files           3        3              
  Lines        1055     1055              
==========================================
+ Hits          984      985       +1     
+ Misses         71       70       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@petercorke

Copy link
Copy Markdown
Owner Author

Superseded by #43 (graph.edges() is now an ordered list, and both edges() methods return copies), which also covers this PR's vertex-side docstring correction and tests. Closing in favour of that.

@petercorke petercorke closed this Oct 11, 2026
@petercorke
petercorke deleted the fix/edges-return-copies branch October 11, 2026 23:54
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