Skip to content

fix: dynamic var spec serialization, JSON indexes, and StepMutator sequencing#3285

Open
npow wants to merge 3 commits into
romain/dynamic-vars-2.19.30from
npow/fix-dynamic-var-spec-serialization
Open

fix: dynamic var spec serialization, JSON indexes, and StepMutator sequencing#3285
npow wants to merge 3 commits into
romain/dynamic-vars-2.19.30from
npow/fix-dynamic-var-spec-serialization

Conversation

@npow

@npow npow commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Three bug fixes for the dynamic decorator values framework from #2970, found during integration testing with the Maestro orchestrator.

1. Preserve dynamic var spec metadata (df8dc15f)

Decorator.translate_decorator_spec was reconstructing DynamicVar objects from the __dynvar__: sentinel but losing pertask and default metadata. Fixed by base64-encoding a JSON payload into the sentinel so all fields survive the spec → string → spec roundtrip.

2. Handle JSON stringified dynamic var indexes (d55d75e4)

Foreach split indexes stored as Python int keys ({0: "val"}) become string keys ({"0": "val"}) when serialized through JSON (e.g., via Maestro). resolve_dynamic_vars_from_store now accepts both integer and stringified integer keys.

3. Fix StepMutator pre-mutate sequencing (2bc95e1e)

FlowSpec._pre_step_decorator_hooks was calling step_decorator.pre_mutate_step for every step on every step_decorator, but StepMutator.pre_mutate_step should only be called on the step that owns the mutator. Fixes incorrect dynamic var resolution when multiple steps have mutators.

PR Type

  • Bug fix

Test plan

  • Unit tests added for each fix (test/unit/test_dynamic_var_decorator_spec.py)
  • Integration tested against Netflix Maestro with foreach, switch, and task-process decorator patterns

Fixes bugs in #2970.

@greptile-apps

greptile-apps Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes several dynamic decorator value roundtrip issues. The main changes are:

  • Dynamic var decorator specs now carry pertask and default metadata.
  • Foreach dynamic var lookup now accepts JSON-stringified split indexes.
  • StepMutator pre-mutation now runs on the mutator-owning step path.
  • Unit tests cover the new decorator spec and index behavior.

Confidence Score: 4/5

The dynamic-var spec serialization path needs fixes before merging.

  • Non-JSON Python defaults can now fail during task launch or deployment.
  • Legacy dynamic var names beginning with json: can be parsed as the new encoded format and raise instead of roundtripping.
  • The split-index and StepMutator sequencing changes otherwise match the surrounding contracts.

metaflow/decorators.py

Important Files Changed

Filename Overview
metaflow/decorators.py Adds JSON/base64 dynamic-var sentinel encoding and decoding, but the new format can crash on non-JSON defaults and misread legacy names beginning with json:.
metaflow/dynamic_var.py Adds stringified split-index fallback for pertask dict resolution while preserving integer-key precedence.
metaflow/flowspec.py Runs StepMutator pre-mutation over config_decorators and removes the duplicate pre-mutate pass.
test/unit/test_dynamic_var_decorator_spec.py Adds tests for legacy sentinel parsing, metadata preservation, nested dynamic vars, and stringified split indexes.

Reviews (1): Last reviewed commit: "Fix StepMutator pre-mutate sequencing fo..." | Re-trigger Greptile

Comment thread metaflow/decorators.py
Comment on lines +251 to +253
payload["default"] = value.default
encoded = base64.urlsafe_b64encode(
json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Default Serialization Can Crash

When a decorator dynamic var uses a valid Python default such as datetime, bytes, an enum, or a custom object, this path stores that object in the JSON payload and json.dumps(payload) raises during make_decorator_spec(). The resolver accepts defaults as Python values and returns them directly, so preserving the metadata this way can turn task launch or deployment into a serialization failure.

Comment thread metaflow/decorators.py
Comment on lines +259 to +263
if payload.startswith(_DYNAMIC_VAR_JSON_PREFIX):
data = json.loads(
base64.urlsafe_b64decode(
payload[len(_DYNAMIC_VAR_JSON_PREFIX) :].encode("ascii")
).decode("utf-8")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Legacy Json Names Misdecode

An existing legacy sentinel for a variable whose name starts with json:, such as __dynvar__:json:foo, now enters the new base64 decode branch instead of the legacy fallback. That makes parsing the decorator spec raise from urlsafe_b64decode rather than reconstructing DynamicVar("json:foo"), breaking backward compatibility for those saved specs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant