Use case - I am taking python code created in another system, and breaking it up into individual functions and connecting them together. The entire point of this work is to break up large python functions that we did not write into smaller python functions for many business reasons.
I COULD take the code, parse for variables, and arbitrarily put them in a dict when doing this, but that is more than a teeny bit of work, and I'd like to run this to ground before I do.
I understand we should almost never but I need to because I am code generating wrappers for functions I did not write, I need to dynamically create variables inside a function. I also can't use exec
because the value could be a complex structure (e.g., a dict).
So, the point of what we're doing is to ask the original authors to make no changes to the incoming code while still executing it across several independent entities.
Just like in the example listed here - we're capturing as much state as we can with the first exit (ideally functions, lambdas and all variables), and re-instating them in the second function so that two functions which formerly had the same scope and context can execute with no changes.
Here is a single block of reproducible code (everything not related to b
is code that I can use to wrap the assignment:
Original:
def original_function():
b = 100
b = b + 20
Resulting generated function:
def fun_1() -> str:
import dill
from base64 import urlsafe_b64decode, urlsafe_b64encode
from types import ModuleType
b = 100
locals_keys = frozenset(locals().keys())
global_keys = frozenset(globals().keys())
__context_export = {}
for val in locals_keys:
if not val.startswith("_") and not isinstance(val, ModuleType):
__context_export[val] = dill.dumps(locals()[val])
for val in global_keys:
if not val.startswith("_") and not isinstance(val, ModuleType):
__context_export[val] = dill.dumps(globals()[val])
b64_string = str(urlsafe_b64encode(dill.dumps(__context_export)), encoding="ascii")
from collections import namedtuple
output = namedtuple("FuncOutput", ["context"])
return output(b64_string)
def fun_2(context):
import dill
from base64 import urlsafe_b64encode, urlsafe_b64decode
from types import ModuleType
__base64_decode = urlsafe_b64decode(context)
__context_import_dict = dill.loads(__base64_decode)
for k in __context_import_dict:
val = dill.loads(__context_import_dict[k])
if globals().get(k) is None and not isinstance(val, ModuleType):
globals()[k] = val
b = b + 20
output = fun_1()
fun_2(output[0])
The error I get when I run this is:
UnboundLocalError: local variable 'b' referenced before assignment
Thank you all for the help!