Skip to content

Preserve saved globals when copying DelayEvaluator - #3346

Open
Hashim1999164 wants to merge 1 commit into
Netflix:masterfrom
Hashim1999164:fix/delay-evaluator-copy-globals
Open

Preserve saved globals when copying DelayEvaluator#3346
Hashim1999164 wants to merge 1 commit into
Netflix:masterfrom
Hashim1999164:fix/delay-evaluator-copy-globals

Conversation

@Hashim1999164

@Hashim1999164 Hashim1999164 commented Aug 22, 2026

Copy link
Copy Markdown

PR Type

[x] Bug fix
[ ] New feature
[ ] Core Runtime change (higher bar; see CONTRIBUTING.md)
[ ] Docs / tooling
[ ] Refactoring

Summary

Attribute and item access on DelayEvaluator copy the object and previously dropped the caller globals captured by config_expr. Custom functions then raised NameError after any chained access.

Issue

Fixes #3345

Reproduction

Runtime: local

Commands to run:

PYTHONPATH=. pytest test/unit/test_delay_evaluator.py

Where evidence shows up: unit test output

Before (error / log snippet)
After copy or attribute access, DelayEvaluator._globals became None, so eval of config_expr("my_func()") lost my_func and raised NameError.
After (evidence that fix works)
test_copy_preserves_saved_globals PASSED
test_deepcopy_preserves_saved_globals PASSED
test_getattr_preserves_saved_globals PASSED
test_getitem_preserves_saved_globals PASSED

Root Cause

copy and deepcopy constructed DelayEvaluator(self._config_expr) without saved_globals. getattr and getitem call copy, so chaining dropped the globals config_expr had stored.

Why This Fix Is Correct

Pass saved_globals=self._globals into both copy paths so the reference is kept, matching the previous comment that globals stay as a reference. Scope is limited to those two methods plus unit tests.

Failure Modes Considered

  1. Deep copy still keeps globals by reference (intentional; same object identity as before for globals).
  2. Evaluators created without saved_globals still copy None correctly.

Tests

[x] Unit tests added/updated
[ ] Reproduction script provided (required for Core Runtime)
[ ] CI passes
[ ] If tests are impractical: explain why below and provide manual evidence above

Non Goals

No change to eval semantics beyond preserving the globals reference across copy.

AI Tool Usage

[ ] No AI tools were used in this contribution
[x] AI tools were used (describe below)

Cursor assisted with locating the bug from the issue report, applying the saved_globals pass through, and adding unit tests. All code was reviewed and the unit tests were run locally.

Attribute and item access copy the evaluator and previously dropped caller globals, so config_expr with custom functions failed after chaining.
@greptile-apps

greptile-apps Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR preserves the caller globals captured by config_expr when a DelayEvaluator is copied, including copies created during delayed attribute and item access.

  • Passes the existing globals mapping through both shallow and deep-copy operations.
  • Adds unit coverage for shallow copy, deep copy, attribute access, and item access.
  • The tests verify internal state preservation but do not exercise the user-visible evaluation path.

Confidence Score: 4/5

The PR appears safe to merge, with only a non-blocking gap in the regression tests’ coverage of actual delayed evaluation.

The implementation preserves the intended globals reference through every changed copy path, but the new tests would not detect a later failure in expression construction or evaluation because they only inspect private fields.

Files Needing Attention: test/unit/test_delay_evaluator.py

Important Files Changed

Filename Overview
metaflow/user_configs/config_parameters.py Correctly retains the saved globals reference across shallow and deep copies, matching the intended delayed-evaluation semantics.
test/unit/test_delay_evaluator.py Covers all affected copy entry points, but asserts private state instead of reproducing successful delayed expression evaluation.

Reviews (1): Last reviewed commit: "Preserve saved globals when copying Dela..." | Re-trigger Greptile

Comment on lines +29 to +34
def test_getattr_preserves_saved_globals():
saved = _sample_globals()
evaluator = DelayEvaluator("config", saved_globals=saved)
chained = evaluator.project
assert chained._globals is saved
assert chained._access == ["project"]

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.

P2 Chained evaluation remains untested

These regression tests only inspect _globals and _access, so they do not exercise the __call__ evaluation path where the reported NameError occurred. Evaluating a chained expression and asserting its result would ensure future changes cannot preserve these private fields while breaking expression construction or namespace handling.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DelayEvaluator.__copy__/__deepcopy__ drop saved_globals, breaking config_expr() with custom functions

1 participant