'Re-serializing values and functions into a new environment in Python
I'm interested in moving a "session" across a boundary where I can only pass text.
I've been using Dill and this ALMOST seems to work.
import dill
from base64 import urlsafe_b64encode
def fun1():
value_one = "abcdef"
def sub_fun_one(x):
return x * x
__context_export = {}
for val in globals():
if not val.startswith("_"):
__context_export[val] = dill.dumps(globals()[val])
for val in locals():
if not val.startswith("_"):
__context_export[val] = dill.dumps(locals()[val])
# __context_export["globals"] = dill.dumps(globals())
# __context_export["locals"] = dill.dumps(locals())
b64_string = str(urlsafe_b64encode(dill.dumps(__context_export)), encoding="ascii")
print(b64_string)
fun1()
This outputs gASVHAQAAAAAAAB9lCiMBGRpbGyUQzeABJUsAAAAAAAAAIwKZGlsbC5fZGlsbJSMDl9pbXBvcnRfbW9kdWxllJOUjARkaWxslIWUUpQulIwRdXJsc2FmZV9iNjRlbmNvZGWUQyuABJUgAAAAAAAAAIwGYmFzZTY0lIwRdXJsc2FmZV9iNjRlbmNvZGWUk5QulIwEZnVuMZRCYwIAAIAElVgCAAAAAAAAjApkaWxsLl9kaWxslIwQX2NyZWF0ZV9mdW5jdGlvbpSTlChoAIwMX2NyZWF0ZV9jb2RllJOUKEsASwBLAEsFSwVLQ0OGZAF9AGQCZAOEAH0BaQB9AnQAgwBEAF0ifQN8A6ABZAShAXMWdAKgA3QAgwB8AxkAoQF8AnwDPABxFnQEgwBEAF0ifQN8A6ABZAShAXNAdAKgA3QEgwB8AxkAoQF8AnwDPABxQHQFdAZ0AqADfAKhAYMBZAVkBo0CfQR0B3wEgwEBAGQAUwCUKE6MBmFiY2RlZpRoBChLAUsASwBLAUsCS1NDCHwAfAAUAFMAlE6FlCmMAXiUhZSMMS9ob21lL2RhYXJvbmNoL2NvZGUvc2FtZS1jbGkvZXhwZXJpbWVudGFsL2Z1bjEucHmUjAtzdWJfZnVuX29uZZRLCEMCAAGUKSl0lFKUjBlmdW4xLjxsb2NhbHM-LnN1Yl9mdW5fb25llIwBX5SMBWFzY2lplIwIZW5jb2RpbmeUhZR0lCiMB2dsb2JhbHOUjApzdGFydHN3aXRolIwEZGlsbJSMBWR1bXBzlIwGbG9jYWxzlIwDc3RylIwRdXJsc2FmZV9iNjRlbmNvZGWUjAVwcmludJR0lCiMCXZhbHVlX29uZZRoDIwQX19jb250ZXh0X2V4cG9ydJSMA3ZhbJSMCmI2NF9zdHJpbmeUdJRoC4wEZnVuMZRLBUMWAAEEAggDBAEKAQoBFgIKAQoBFgQWApQpKXSUUpRjX19idWlsdGluX18KX19tYWluX18KaCROTn2UTnSUUpQulIwJdmFsdWVfb25llEMVgASVCgAAAAAAAACMBmFiY2RlZpQulIwLc3ViX2Z1bl9vbmWUQ9SABJXJAAAAAAAAAIwKZGlsbC5fZGlsbJSMEF9jcmVhdGVfZnVuY3Rpb26Uk5QoaACMDF9jcmVhdGVfY29kZZSTlChLAUsASwBLAUsCS1NDCHwAfAAUAFMAlE6FlCmMAXiUhZSMMS9ob21lL2RhYXJvbmNoL2NvZGUvc2FtZS1jbGkvZXhwZXJpbWVudGFsL2Z1bjEucHmUjAtzdWJfZnVuX29uZZRLCEMCAAGUKSl0lFKUY19fYnVpbHRpbl9fCl9fbWFpbl9fCmgKTk59lE50lFKULpSMA3ZhbJRDEoAElQcAAAAAAAAAjAN2YWyULpR1Lg=="
as expected.
But in the second function - mocked up here - "value_one" doesn't seem to work at all.
import dill
from base64 import urlsafe_b64decode
def fun2(import_string):
__base64_decode = urlsafe_b64decode(import_string)
__context_import_dict = dill.loads(__base64_decode)
# __globals_import = dill.loads(__context_import_dict["globals"])
# __locals_import = dill.loads(__context_import_dict["locals"])
print(__context_import_dict)
for k in __context_import_dict:
print(f"local = {k}")
if locals().get(k) is None:
g = dill.loads(__context_import_dict[k])
locals()[k] = g
print(value_one)
print(f"Square value: {sub_fun_one(5)}")
import_value = "gASVHAQAAAAAAAB9lCiMBGRpbGyUQzeABJUsAAAAAAAAAIwKZGlsbC5fZGlsbJSMDl9pbXBvcnRfbW9kdWxllJOUjARkaWxslIWUUpQulIwRdXJsc2FmZV9iNjRlbmNvZGWUQyuABJUgAAAAAAAAAIwGYmFzZTY0lIwRdXJsc2FmZV9iNjRlbmNvZGWUk5QulIwEZnVuMZRCYwIAAIAElVgCAAAAAAAAjApkaWxsLl9kaWxslIwQX2NyZWF0ZV9mdW5jdGlvbpSTlChoAIwMX2NyZWF0ZV9jb2RllJOUKEsASwBLAEsFSwVLQ0OGZAF9AGQCZAOEAH0BaQB9AnQAgwBEAF0ifQN8A6ABZAShAXMWdAKgA3QAgwB8AxkAoQF8AnwDPABxFnQEgwBEAF0ifQN8A6ABZAShAXNAdAKgA3QEgwB8AxkAoQF8AnwDPABxQHQFdAZ0AqADfAKhAYMBZAVkBo0CfQR0B3wEgwEBAGQAUwCUKE6MBmFiY2RlZpRoBChLAUsASwBLAUsCS1NDCHwAfAAUAFMAlE6FlCmMAXiUhZSMMS9ob21lL2RhYXJvbmNoL2NvZGUvc2FtZS1jbGkvZXhwZXJpbWVudGFsL2Z1bjEucHmUjAtzdWJfZnVuX29uZZRLCEMCAAGUKSl0lFKUjBlmdW4xLjxsb2NhbHM-LnN1Yl9mdW5fb25llIwBX5SMBWFzY2lplIwIZW5jb2RpbmeUhZR0lCiMB2dsb2JhbHOUjApzdGFydHN3aXRolIwEZGlsbJSMBWR1bXBzlIwGbG9jYWxzlIwDc3RylIwRdXJsc2FmZV9iNjRlbmNvZGWUjAVwcmludJR0lCiMCXZhbHVlX29uZZRoDIwQX19jb250ZXh0X2V4cG9ydJSMA3ZhbJSMCmI2NF9zdHJpbmeUdJRoC4wEZnVuMZRLBUMWAAEEAggDBAEKAQoBFgIKAQoBFgQWApQpKXSUUpRjX19idWlsdGluX18KX19tYWluX18KaCROTn2UTnSUUpQulIwJdmFsdWVfb25llEMVgASVCgAAAAAAAACMBmFiY2RlZpQulIwLc3ViX2Z1bl9vbmWUQ9SABJXJAAAAAAAAAIwKZGlsbC5fZGlsbJSMEF9jcmVhdGVfZnVuY3Rpb26Uk5QoaACMDF9jcmVhdGVfY29kZZSTlChLAUsASwBLAUsCS1NDCHwAfAAUAFMAlE6FlCmMAXiUhZSMMS9ob21lL2RhYXJvbmNoL2NvZGUvc2FtZS1jbGkvZXhwZXJpbWVudGFsL2Z1bjEucHmUjAtzdWJfZnVuX29uZZRLCEMCAAGUKSl0lFKUY19fYnVpbHRpbl9fCl9fbWFpbl9fCmgKTk59lE50lFKULpSMA3ZhbJRDEoAElQcAAAAAAAAAjAN2YWyULpR1Lg=="
fun2(import_value)
I'd prefer to do this more elegantly than eval 'k = v'
because that will be hard to reserialize functions and dictionaries into.
Is this possible?
To be clear, I've read about the dangers of modifying locals() and I do NOT want to do this. But I also cannot rewrite the code that I'm accessing elsewhere to use a custom dict.
E.g. in fun2, I can NOT change print(value_one)
to print(my_dict['value_one'])
Solution 1:[1]
Ok, so it APPEARS that doing the following works:
for k in __context_import_dict:
if globals().get(k) is None:
globals()[k] = dill.loads(__context_import_dict[k])
What kind of pain am i signing myself up for?
Solution 2:[2]
From the Python documentation about locals()
:
Note: The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.
Function namespaces are not commmon dictionaries like module namespaces for performance reasons. I was bitten by this same quirk just yesterday.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | aronchick |
Solution 2 | leogama |