Repository navigation
Add nodes from dict - #762
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #762 +/- ##
==========================================
+ Coverage 94.53% 94.56% +0.02%
==========================================
Files 63 63
Lines 5146 5170 +24
==========================================
+ Hits 4865 4889 +24
Misses 281 281 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
leotrs
left a comment
There was a problem hiding this comment.
Nice, closes #752 cleanly. Docstring examples in particular are clear and cover the four call shapes explicitly. Approving with a couple of edge cases worth thinking about.
Ambiguity worth flagging
The dispatch heuristic isinstance(n, tuple) and len(n) == 2 and isinstance(n[1], dict) means these two patterns collide:
# Intent A: add a single node whose ID happens to be a 2-tuple with a dict value
H.add_nodes_from([("my_node", {"role": "root"})])
# Intent B: add node "my_node" with attribute {"role": "root"}
H.add_nodes_from([("my_node", {"role": "root"})])They look identical to the caller and now always resolve as Intent B. The old try/except path had the same ambiguity for hashable 2-tuples of the same shape, so this is not a regression, just an inherited limitation. If it bites someone, the workaround is add_node((tuple_id, some_dict)). Worth a line in the docstring so a future reader knows why the heuristic looks the way it does.
Silent misinterpretation when dict values are not dicts
If a caller passes {1: None} (say from {n: metadata.get(n) for n in nodes} where lookups can miss), the code takes the .items() path and gets (1, None) tuples. Since None isn't a dict, each falls into the else branch, and n becomes the whole (1, None) tuple, which then becomes the node ID. So instead of a Hypergraph with node 1, you get a Hypergraph with node (1, None). Same story for any non-dict value.
Two options:
- Silently coerce non-dict values to
{}(accept the sloppy input, add the node without attributes). - Raise a
TypeErrorup front when a dict is passed with any non-dict value, naming the offender.
Either is fine. I'd lean toward the raise for the same "loud failure at the boundary" reason we discussed on the AHORN loader. Not blocking.
Small thing
The test could gain one line for the new dict path receiving pre-existing nodes (e.g., call H.add_nodes_from({0: {"weight": 2}}) after node 0 already exists to verify attribute update behavior). Non-blocking.
This allows users to add nodes from a dict-of-dicts.