Conversation
node-graph's socket spec records structured_type extras for enum-typed sockets so coerce_inputs_from_spec can rebuild the member before a task body runs - the declared contract is that the serialized form is the bare value. The AiiDA adapter never honoured it: raw Enum instances reached aiida-pythonjob's general_serializer, which has no serializer for them and failed the whole submission. Flatten enums (recursively, including dict keys/values and list items) before serialize_ports. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
node-graph's socket spec records structured_type extras for enum-typed sockets so coerce_inputs_from_spec can rebuild the member before a task body runs - the declared contract is that the serialized form is the bare value. The AiiDA adapter never honoured it: raw Enum instances reached aiida-pythonjob's general_serializer, which has no serializer for them and failed the whole submission. Flatten enums (recursively, including dict keys/values and list items) before serialize_ports. Add unit tests in tests/test_serializer.py covering _flatten_enums (bare/IntEnum/str-Enum members, dict keys and values, list/tuple, mixed nesting, enum-free passthrough, wrapt-proxied members) plus one end-to-end serialize_ports check that an enum-valued entry serializes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ation The read side does not round-trip Enums: node-graph's coerce_inputs_from_spec records structured_type extras only for dataclass/pydantic/TypedDict, never for Enum sockets, so a task body declaring a plain Enum input receives the bare value (isinstance False, x == Color.RED False), not the member. Correct the _flatten_enums docstring (was claiming a round-trip that does not happen) and add test_body_receives_bare_value_not_member, which drives a real wg.run() and asserts the body sees the bare value - it flips loudly if node-graph later adds enum reconstruction. set/frozenset are not descended into: a set fails in general_serializer regardless of contents (not JSON-serializable, no registered serializer), so flattening enums inside one would not help. Document this and pin it with test_flatten_leaves_sets_untouched. Guard dict-key flattening: two distinct keys collapsing to the same flattened value (an Enum member and its bare value, or two members sharing a .value) now raises instead of silently dropping an entry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3a86513 to
e53a7ce
Compare
test_body_receives_bare_value_not_member asserted that a task body declaring a plain Enum input receives the bare value. That outcome is not this package's to fix: it depends on whether the installed node-graph reconstructs Enum sockets, so the test reports red on a dependency swap rather than on a defect, and its premise was wrong for @task.graph bodies, which receive the stored orm.Str, not a bare value. - Assert the invariant instead: whatever the boundary delivers, Color(c) rebuilds the member that was passed, in both a function task's body and a @task.graph body. - Keep the difference pinned with a second test asserting that what arrives agrees with what the installed node-graph advertises, so a reconstruction that silently stops working still fails here. - Reword the _flatten_enums docstring, which stated the bare value as fact, to state the rule and the portable idiom. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughEnum values now flatten recursively in dictionaries, lists, and tuples before port serialization. Unchanged containers retain their instances. Dictionary key collisions still raise ChangesEnum serialization
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to The change recursively converts enum values during serialization and adds coverage for supported input forms; no actionable merge-blocking risk remains after normal checks and review. Sequence Diagram(s)sequenceDiagram
participant WorkGraphTask
participant serialization.serialize
participant _flatten_enums
participant serialization.serialize_ports
WorkGraphTask->>serialization.serialize: provide Enum input
serialization.serialize->>_flatten_enums: flatten nested Enum values
_flatten_enums-->>serialization.serialize: return converted or original container
serialization.serialize->>serialization.serialize_ports: pass flattened payload
serialization.serialize_ports-->>WorkGraphTask: provide serialized port data
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #800 +/- ##
==========================================
+ Coverage 90.18% 91.02% +0.85%
==========================================
Files 44 46 +2
Lines 2991 3184 +193
==========================================
+ Hits 2697 2898 +201
+ Misses 294 286 -8 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
test_enum_input_rebuilds_to_the_member_that_was_passed failed in the @task.graph body when the installed node-graph reconstructs the Enum member before the body runs. - Rebuild via Color(getattr(c, 'value', c)) in both observer bodies, so the assertion holds for every form the boundary delivers. - Point the _flatten_enums docstring and the test docstring at that idiom, which they previously gave as Color(c). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_flatten_enums rebuilt every dict/list/tuple unconditionally, so an enum-free namedtuple came back as a plain tuple and an OrderedDict or defaultdict came back as a plain dict, even though nothing needed flattening. Return the original object when no element changed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two tests reconstructed an Enum member by hand inside a task body (Color(getattr(c, 'value', c))) to work around the two node-graph arrival forms, contradicting the settled contract that a task body only ever uses the annotation. - observe_enum reports type_name, is_member, equals_member, equals_value; no constructor call on the received value. - observe_enum_in_graph uses the member directly (c.name), matching what a @task.graph body actually receives. - test_enum_arrival_follows_the_node_graph_capability asserts the is_member/equals_* combination for both arrival forms. - test_enum_input_arrives_as_the_member_in_a_graph_body replaces the old rebuild-parity test, skipping (with a stated reason) when the installed node_graph does not reconstruct Enum sockets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Problem
node-graph's socket spec records
structured_typeextras for anEnum-typed socket so its own coercion can rebuild the member before a task body runs — the declared contract is that the serialized form is the bare value. This package's AiiDA adapter never honoured that on the way out: a rawEnuminstance reached aiida-pythonjob'sgeneral_serializer, which has no serializer registered for it, and failed the whole submission.Changes
_flatten_enumswalks a value recursively — dict keys and values, list/tuple items, nested structures — and replaces everyEnummember with its bare value beforeserialize_portshands the payload to aiida-pythonjob.set/frozensetare left alone and documented as out of scope: a set failsgeneral_serializerregardless of its contents (no registered serializer, not JSON-safe), so flattening inside one wouldn't help.namedtupleto a plaintupleand anOrderedDict/defaultdictto a plaindicteven when nothing needed flattening. A container that does carry anEnumstill flattens as before.Enummember and its own bare value, or two members sharing a.value) now raises instead of silently dropping an entry.Literal[...]not supported scinode/node-graph#175 and answers Change of syntaxWorkGraph.add_link#176 for annotated sockets). With node-graph 0.6.5 anEnum-typed input arrives as the flattened value; with Change the syntax forwait#178 it arrives as the member. A body relies on its annotation either way and never rebuilds a member by hand from what arrived. The tests here pin that agreement: what arrives matches what the installed node-graph advertises, so a reconstruction that silently regresses fails here rather than passing vacuously.Testing
tests/test_serializer.py:_flatten_enumsunit coverage (bare/IntEnum/str-Enummembers, dict keys and values, list/tuple, mixed nesting, enum-free passthrough,namedtuple/OrderedDict/defaultdicttype preservation on the enum-free path and continued flattening when they do carry anEnum,wrapt-proxied members) plus an end-to-endserialize_portscheck that an enum-valued entry serializes; a livewg.run()exercising a function task with anEnuminput, asserting the arriving form against the installed node-graph's capability, plus a@task.graphbody using the member as annotated (skipped where node-graph does not reconstruct).tests/test_serializer.pyagainst node-graph 0.6.5 (the pinned release): 16 passed, 1 skipped — the@task.graphmember test skips because 0.6.5 does not reconstructEnumsockets, and the arrival test asserts the flattened form.PYTHONPATHshadow of the paired node-graph branch (verified vianode_graph.__file__): 17 passed, 0 skipped — the member arrives and the graph body usesc.nameas annotated. The pair of runs is the discriminating check: the same tests assert opposite arrival forms and pass under each node-graph exactly as its capability predicts.