百科.dev
全部条目AI 编程趋势榜开源项目技术资讯提交条目
登录
返回工具页/返回 Issues 列表
#3345·metaflow

DelayEvaluator.__copy__/__deepcopy__ 删除 saved_globals,导致使用自定义函数的 config_expr() 失败

作者: SahilKumar75创建于 2026年8月22日更新于 2026年9月10日

发生了什么事? config_expr() 文档中声明支持在表达式中调用用户自定义的模块级函数,例如 config_expr("my_func()")。它为此目的捕获调用者的全局变量: parent_globals = inspect.currentframe().f_back.f_globals ... saved_globals = parent_globals DelayEvaluator.__copy__ 和 __deepcopy__ 都使用仅包含表达式字符串来构建一个新的 DelayEvaluator,而不转发 saved_globals: def __copy__(self): c = DelayEvaluator(self._config_expr) c._access = self._access.copy() if self._access is not None else None # 全局变量不进行复制 - 总是保留为引用 return c 由于 __init__ 中 saved_globals 默认为 None,因此复制后的 _globals 变量为 None。 __getattr__ 和 __getitem__ 在访问每个属性或项时都调用 self.__copy__(),这是 DelayEvaluator 实际使用的常见方式,例如 config_expr("config").project.name。因此,当任何属性或项链接到使用 config_expr() 构建的 DelayEvaluator 时,捕获的调用者全局变量就会丢失。 您期望发生什么? 在 config_expr() 表达式中引用的自定义函数在返回的 DelayEvaluator 上进行任何属性或项访问后仍然可以正确解析,因为受影响行的上方注释中指出全局变量"总是保留为引用"。 重现问题: # 在一个流的模块中 def my_func(): return "hello" class MyFlow(FlowSpec): config = Config("config") @environment(vars={"foo": config_expr("my_func()")}) @step def start(self): ... 这工作了。但是: @project(name=config_expr("my_func()").upper()) 会引发 NameError: name 'my_func' is not defined,因为 .upper() 触发了 __getattr__,从而调用了 __copy__(),导致 _globals 丢失。 提出的修复 将 self._globals 通过传递到 both __copy__ 和 __deepcopy__ 中: def __copy__(self): c = DelayEvaluator(self._config_expr, saved_globals=self._globals) ... def __deepcopy__(self, memo): c = DelayEvaluator(self._config_expr, saved_globals=self._globals) ... 我已经在本地准备好了此修复以及几个在修复之前和之后重现问题的单元测试。一旦此问题得到确认,我很乐意提交一个 PR。

内容来源: Netflix/metaflow

查看 GitHub 原文在 GitHub 查看讨论