Open ChrisCScott opened 2 years ago
deepcopy
can't be overloaded in this way (it's a free function and doesn't provide any hooks for overriding its recursion).
The alternative is probably to perform deepcopy
, and then iterate over copied objects and inspect them for function attributes that provide a __closure__
attribute.
Recursion will probably be required, since potentially closure vars could themselves reference closured objects. One option would be to recurse over a reference graph for an object, just like deepcopy
(except that we'd be recursing onto functions too) - start with an object, collect its attributes, and recurse onto them, maintaining a memo of visited objects to avoid infinite recursion. This would probably require using a fresh memo, since we don't want to terminate recursion when visiting objects that were visited by the earlier run of deepcopy
. Consider whether this means we'd need to have two memos, if we need to reference the one produced by deepcopy
; preferably any analysis of the deepcopy
could be avoided, since its documentation discourages this (although its implementation is well-known and straightforward.)
The other option would be to iterate over objects in deepcopy
's memo
, inspect their attributes for objects with __closure__
attributes, mutate those objects by replacing those functions with copies (with mutated closures), and use deepcopy
to perform the copying of the closure vars. One point of complexity in this approach would be that we cannot mutate a dict while iterating over it, so we'd probably need to iterate over a copy and then [recursively?] repeat for any newly-added elements (can do this by finding the difference between keys in the original and mutated dicts; see here). This involves analyzing memo
, but doesn't require any knowledge of what the keys are (any mutation of memo
outside of deepcopy
) - just the knowledge that the values of memo
are the copies produced by deepcopy
. deepcopy
itself does the heavy lifting in terms of copying/mutating.
We ought to replace references to replacement
Scenario
objects (and probably any other replaced objects too) in function closures when copying objects inForecaster.run_forecast
.This will probably require overloading
deepcopy
to recurse onto functions (which ordinarily it ignores and returns the original function) and onto their closures. You can replace the contents of a closure simply by invokingfuncname.__closure__[index].cell_contents = new_value
, but to avoid mutating the original closure you'll need to copy the original closure, whichdeepcopy
does not support.So we will need to:
new_cell = cell(f.__closure__[index].cell_contents)
for each index in the closure)For resources on copying functions, see this answer.