Conversation
Co-authored-by: ebhills <53243273+ebhills@users.noreply.github.com>
| "Use export_conflict_policy: overwrite to allow replacing parent values." | ||
| ) | ||
|
|
||
| _recipe._apply_runtime_variable_updates(variables, set_values=exported) |
There was a problem hiding this comment.
@ebhills export_runtime_variables silently fails to propagate when the same recipe:/read:/write: action also passes an explicit variables: override — no error is raised; the parent recipe later fails trying to read a variable that was never actually exported.
For example: import wrangles
recipe = """
run:
on_start:
- variables: {set: {parent_flag: parent}}
- recipe:
variables: {SOME_OVERRIDE: hello} # <- explicit override present
run:
on_start: [{variables: {set: {child_export: exported_value}}}]
export_runtime_variables: [child_export]
wrangles:
- create.column: {output: child_export, value: ${runtime.child_export}}
"""
wrangles.recipe.run(recipe)
Expected: child_export column populated with exported_value. Actual: ValueError: create.column (line 8) - Runtime variable '${runtime.child_export}' was not found. Removing the variables: {SOME_OVERRIDE: hello} line makes the export work correctly — confirming the override is what breaks propagation.
| _validate_runtime_variable_name(key) | ||
| prepared_assignments[key] = _clone_runtime_value(value) | ||
|
|
||
| if update_values is not None: |
There was a problem hiding this comment.
@ebhills set + update on the same key in one variables: action either raises a spurious "not found for update" error or silently drops the set value — the merge logic validates/reads against pre-action state instead of the in-flight set values.
import wrangles
recipe = """
run:
on_start:
- variables:
set: {counter: {a: 1}}
update: {counter: {b: 2}}
wrangles:
- create.column: {output: out, value: ${runtime.counter}}
"""
wrangles.recipe.run(recipe)
Expected: counter == {a: 1, b: 2}. Actual: ValueError: variables (line 4) - Runtime variable 'counter' was not found for update. (If counter already existed before this action, the error doesn't fire, but the set value is silently discarded — update merges onto the stale pre-action value instead.)
| try: | ||
| if params is None: | ||
| params = {} | ||
| params = _resolve_runtime_references( |
There was a problem hiding this comment.
@ebhills if: gates are evaluated after eager resolution of all other parameters, across all four execution paths (_run_actions, _read_data, _execute_wrangles, _write_data). A step that should be skipped can still raise ValueError from an unrelated, not-yet-set runtime variable referenced elsewhere in the same action. This is the exact pattern used in the README's own worked example.
import wrangles
recipe = """
run:
on_start:
- variables: {set: {enabled: false}}
wrangles:
- create.column:
if: ${runtime.enabled} == True
output: out
value: ${runtime.not_set_variable}
"""
wrangles.recipe.run(recipe)
Expected: step skipped (condition is false), no error. Actual: ValueError: create.column (line 8) - Runtime variable '${runtime.not_set_variable}' was not found. This is exactly the "declare a variable only inside the branch that needs it" pattern used in the feature's own README example — so the flagship example is expected to fail the same way. Same root cause reproduces in _read_data, _execute_wrangles, and _write_data (just substitute a read:/run:/write: step with the same if:/unrelated-${runtime.X} shape).
Linked issue
Implements general-purpose mutable runtime variables in a separate PR from #1179; this branch does not include or depend on attachment/feature-branch changes from that draft.
What changes
Adds an opt-in runtime variable system so recipes can set/mutate values during execution, bind action return values, and consume current values in later steps (including
on_start -> read/wrangles/write) without changing existing static${...}behavior.Runtime variable model
${runtime.name}and${runtime.name.path}\${runtime...})Mutation + result binding
variablesrun action:set(atomic assignment)update(deep merge for dict values)mark_secretinspect/max_itemsresult_variableto capture connector/action return values into runtime state (excluded from connector args)Isolation and export semantics
recipe.run()invocationexport_runtime_variablesonrecipeconnectorexport_conflict_policy: error|overwriterow_count,column_count,columns,df,recipe_variables,applied_permission_group, internal runtime keys)Inspection/logging safety
logwrangle supportsruntime_variablesandruntime_max_itemsSchema/docs/tests
result_variablevariablesHow it was verified
Focused suites covering runtime behavior and regressions:
tests/recipes/test_run.pytests/recipes/test_variables.pytests/connectors/test_recipe.pytests/connectors/test_concurrent.pytests/connectors/test_matrix.pytests/recipes/wrangles/test_main.pylog/python/matrix casesparallel_validationrun (CodeQL clean; code-review subtool unavailable in this environment)Compatibility and risk
${variable}remains load-time/static; mutable behavior is opt-in via${runtime...}andvariablesaction.recipe.run()calls unless explicitly exported.Ready-for-review checklist
mainand has no merge conflictsSee the pull request workflow.