Flatten Enum members to bare values on the serialize path#800
Open
elinscott wants to merge 3 commits into
Open
Flatten Enum members to bare values on the serialize path#800elinscott wants to merge 3 commits into
elinscott wants to merge 3 commits into
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>
elinscott
marked this pull request as ready for review
July 8, 2026 12:46
…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>
elinscott
force-pushed
the
serialize-enum-flatten
branch
from
July 17, 2026 09:41
3a86513 to
e53a7ce
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
aiida-pythonjob'sgeneral_serializerhas no serializer forEnummembers. A rawEnuminstance (or a dict/list containing one, e.g. a parameters dict with an enum-valued entry) flows straight intoserialize_ports, which hands it togeneral_serializer, and the whole submission fails at submit time with an opaque serialization error.Change
A small
_flatten_enumshelper replacesEnummembers with their.value, recursively through dicts (keys and values), lists and tuples, and is applied in bothAiidaSerializationAdapter.serializeandAiidaSerializationAdapter.serialize_portsimmediately afterresolve_tagged_values. Theisinstance(value, Enum)check also matches wrapt proxies (TaggedValue) wrapping enum members, so tagged graph inputs are handled by the same path.This is a one-way flattening on the write path, not a round-trip. Under the pinned
node-graph(0.6.5) the read side (coerce_inputs_from_spec) reconstructs only structured models — dataclass/pydantic/TypedDict — and records nostructured_typeextra forEnumsockets, so it never rebuilds the member. Task bodies therefore receive the bare.value, not theEnumthey declared. This is harmless forIntEnum/str, Enumconsumed by value or equality, but a body that testsx is Color.RED/x == Color.REDagainst a plainEnumwill seeFalse. True reconstruction would need an enum branch in node-graph'sstructured_type_info/coerce_structured_valueand is left as follow-up there.Two edge cases are handled explicitly:
set/frozensetare left untouched (a set fails ingeneral_serializerregardless of its contents — not JSON-serializable, no registered serializer — so descending into one to flatten enums would not make it serializable; documented as a limitation), and dict-key flattening now raises on a collision (two distinct keys collapsing to one, e.g. anEnummember and its bare value) rather than silently dropping an entry.Testing
New unit tests in
tests/test_serializer.py, includingtest_body_receives_bare_value_not_member, which drives a realwg.run()and asserts a plain-Enuminput reaches the body as the bare value — pinning the actual (non-round-trip) behaviour so it flips loudly if node-graph later adds enum reconstruction — plus tests for the set passthrough and the key-collision guard.Exercised end-to-end by the downstream Koopmans package, whose workgraphs pass enum-valued settings (e.g. functional and spin-treatment enums) through
@task.graphinputs: before the change every such submission failed ingeneral_serializer; after it, the graphs submit (the bodies receive the bare enum value, per the note above).