parent
4e08b32d10
commit
6e58b77376
@ -1,114 +0,0 @@
|
||||
from enum import Enum, auto
|
||||
import inspect
|
||||
|
||||
class RehydrateMethod(Enum):
|
||||
DIRECT = auto()
|
||||
INIT = auto()
|
||||
CLASS_METHOD = auto()
|
||||
|
||||
#freezedry, for when pickling is just a bit too intense
|
||||
|
||||
# The class key is a reserved dict key used to flag that the dict should be unpacked back out to a class instance
|
||||
class_key = "<freezedried>"
|
||||
# The Pack module stores some state from init to keep a list of valid packable classes
|
||||
freezedryables = {}
|
||||
|
||||
# Decorator to mark class as packable and keep track of associated names and classes. When packed, the
|
||||
# special key string "<packable>" indicates what class the current dict should be unpacked to
|
||||
|
||||
# name argument is the string that will identify this class in a packed dict
|
||||
def freezedryable(cls, rehydrate_method=RehydrateMethod.DIRECT, name=None):
|
||||
if name is None:
|
||||
cls._freezedry_name = cls.__name__
|
||||
else:
|
||||
if isinstance(name, str):
|
||||
raise Exception("freezedryable name must be a string")
|
||||
cls._freezedry_name = name
|
||||
cls._rehydrate_method = rehydrate_method
|
||||
|
||||
if cls._freezedry_name in freezedryables:
|
||||
raise Exception("Duplicate freezedryable class name "+cls._freezedry_name)
|
||||
freezedryables[cls._freezedry_name] = cls
|
||||
|
||||
def _freezedry(self):
|
||||
dried_dict=_freezedry_dict(vars(self))
|
||||
dried_dict[class_key]=self._freezedry_name
|
||||
return dried_dict
|
||||
|
||||
cls.freezedry=_freezedry
|
||||
#setattr(cls, "freezedry", freezedry)
|
||||
return cls
|
||||
|
||||
|
||||
def freezedry(hydrated_obj):
|
||||
# If it's a primitive, store it. If it's a dict or list, recursively freezedry that.
|
||||
# If it's an instance of another freezedryable class, call its .freezedry() method.
|
||||
if isinstance(hydrated_obj, (str, int, float, bool, type(None))):
|
||||
return hydrated_obj
|
||||
elif isinstance(hydrated_obj, dict):
|
||||
return _freezedry_dict(hydrated_obj)
|
||||
elif isinstance(hydrated_obj, list):
|
||||
dried_list = []
|
||||
for val in hydrated_obj:
|
||||
dried_list.append(freezedry(val))
|
||||
return dried_list
|
||||
elif hasattr(hydrated_obj, "_freezedry_name"):
|
||||
return hydrated_obj.freezedry()
|
||||
else:
|
||||
raise Exception("Object "+str(hydrated_obj)+" is not freezedryable")
|
||||
|
||||
def _freezedry_dict(hydrated_dict):
|
||||
dried_dict = {}
|
||||
for k,val in hydrated_dict.items():
|
||||
if not isinstance(k,str):
|
||||
raise Exception("Non-string dictionary keys are not freezedryable")
|
||||
if k == class_key:
|
||||
raise Exception("Key "+class_key+" is reserved for internal freezedry use")
|
||||
dried_dict[k]=freezedry(val)
|
||||
return dried_dict
|
||||
|
||||
def rehydrate(dried_obj):
|
||||
if isinstance(dried_obj, (str, int, float, bool, type(None))):
|
||||
return dried_obj
|
||||
elif isinstance(dried_obj, dict):
|
||||
return _rehydrate_dict(dried_obj)
|
||||
elif isinstance(dried_obj, list):
|
||||
hydrated_list = []
|
||||
for val in dried_obj:
|
||||
hydrated_list.append(rehydrate(val))
|
||||
return hydrated_list
|
||||
else:
|
||||
raise Exception("Object "+str(dried_obj)+" is not rehydrateable")
|
||||
|
||||
def _rehydrate_dict(dried_dict):
|
||||
hydrated_dict = {}
|
||||
for k,val in dried_dict.items():
|
||||
if not isinstance(k,str):
|
||||
raise Exception("Non-string dictionary keys are not rehydrateable")
|
||||
if k != class_key:
|
||||
hydrated_dict[k]=rehydrate(val)
|
||||
|
||||
# Check if this is an object that needs to be unpacked back to an instance
|
||||
if class_key in dried_dict:
|
||||
if dried_dict[class_key] not in freezedryables:
|
||||
raise Exception("Class "+dried_dict[class_key]+" has not been decorated as freezedryable")
|
||||
f_class=freezedryables[dried_dict[class_key]]
|
||||
# If DIRECT, skip __init__ and set attributes back directly
|
||||
if f_class._rehydrate_method == RehydrateMethod.DIRECT:
|
||||
hydrated_instance = f_class.__new__(f_class)
|
||||
hydrated_instance.__dict__.update(hydrated_dict)
|
||||
#if INIT, pass all attributes as keywords to __init__ method
|
||||
elif f_class._rehydrate_method == RehydrateMethod.INIT:
|
||||
hydrated_instance = f_class(**hydrated_dict)
|
||||
# IF CLASS_METHOD, pass all attributes as keyword aguments to classmethod "unpack()"
|
||||
elif f_class._rehydrate_method == RehydrateMethod.CLASS_METHOD:
|
||||
if inspect.ismethod(getattr(f_class, "rehydrate", None)):
|
||||
hydrated_instance = f_class.rehydrate(**hydrated_dict)
|
||||
else:
|
||||
raise Exception("Class "+str(f_class)+" does not have classmethod 'rehydrate()'")
|
||||
else:
|
||||
raise Exception("Class _rehydrate_method "+str(f_class._rehydrate_method)+" is not supported")
|
||||
|
||||
return hydrated_instance
|
||||
else:
|
||||
return hydrated_dict
|
||||
@ -0,0 +1,113 @@
|
||||
from enum import Enum, auto
|
||||
import inspect
|
||||
|
||||
class RestoreMethod(Enum):
|
||||
DIRECT = auto()
|
||||
INIT = auto()
|
||||
CLASS_METHOD = auto()
|
||||
|
||||
#preserve, for when pickling is just a bit too intense
|
||||
|
||||
# The class key is a reserved dict key used to flag that the dict should be unpacked back out to a class instance
|
||||
class_key = "<_jam>"
|
||||
# The Preserve module stores some state from init to keep a list of valid packable classes
|
||||
preservables = {}
|
||||
|
||||
# Decorator to mark class as packable and keep track of associated names and classes. When packed, the
|
||||
# special key string "<packable>" indicates what class the current dict should be unpacked to
|
||||
|
||||
# name argument is the string that will identify this class in a packed dict
|
||||
def preservable(cls, restore_method=RestoreMethod.DIRECT, name=None):
|
||||
if name is None:
|
||||
cls._preserve_name = cls.__name__
|
||||
else:
|
||||
if isinstance(name, str):
|
||||
raise Exception("preservable name must be a string")
|
||||
cls._preserve_name = name
|
||||
cls._restore_method = restore_method
|
||||
|
||||
if cls._preserve_name in preservables:
|
||||
raise Exception("Duplicate preservable class name "+cls._preserve_name)
|
||||
preservables[cls._preserve_name] = cls
|
||||
|
||||
def _preserve(self):
|
||||
dict_jam=_preserve_dict(vars(self))
|
||||
dict_jam[class_key]=self._preserve_name
|
||||
return dict_jam
|
||||
|
||||
cls.preserve=_preserve
|
||||
return cls
|
||||
|
||||
|
||||
def preserve(target_obj):
|
||||
# If it's a primitive, store it. If it's a dict or list, recursively preserve that.
|
||||
# If it's an instance of another preservable class, call its .preserve() method.
|
||||
if isinstance(target_obj, (str, int, float, bool, type(None))):
|
||||
return target_obj
|
||||
elif isinstance(target_obj, dict):
|
||||
return _preserve_dict(target_obj)
|
||||
elif isinstance(target_obj, list):
|
||||
list_jam = []
|
||||
for val in target_obj:
|
||||
list_jam.append(preserve(val))
|
||||
return list_jam
|
||||
elif hasattr(target_obj, "_preserve_name"):
|
||||
return target_obj.preserve()
|
||||
else:
|
||||
raise Exception("Object "+str(target_obj)+" is not preservable")
|
||||
|
||||
def _preserve_dict(target_dict):
|
||||
dict_jam = {}
|
||||
for k,val in target_dict.items():
|
||||
if not isinstance(k,str):
|
||||
raise Exception("Non-string dictionary keys are not preservable")
|
||||
if k == class_key:
|
||||
raise Exception("Key "+class_key+" is reserved for internal use")
|
||||
dict_jam[k]=preserve(val)
|
||||
return dict_jam
|
||||
|
||||
def restore(obj_jam):
|
||||
if isinstance(obj_jam, (str, int, float, bool, type(None))):
|
||||
return obj_jam
|
||||
elif isinstance(obj_jam, dict):
|
||||
return _restore_dict(obj_jam)
|
||||
elif isinstance(obj_jam, list):
|
||||
restored_list = []
|
||||
for val in obj_jam:
|
||||
restored_list.append(restore(val))
|
||||
return restored_list
|
||||
else:
|
||||
raise Exception("Object "+str(obj_jam)+" is not restorable")
|
||||
|
||||
def _restore_dict(dict_jam):
|
||||
restored_dict = {}
|
||||
for k,val in dict_jam.items():
|
||||
if not isinstance(k,str):
|
||||
raise Exception("Non-string dictionary keys are not restorable")
|
||||
if k != class_key:
|
||||
restored_dict[k]=restore(val)
|
||||
|
||||
# Check if this is an object that needs to be restored back to a class instance
|
||||
if class_key in dict_jam:
|
||||
if dict_jam[class_key] not in preservables:
|
||||
raise Exception("Class "+dict_jam[class_key]+" has not been decorated as preservable")
|
||||
f_class=preservables[dict_jam[class_key]]
|
||||
# If DIRECT, skip __init__ and set attributes back directly
|
||||
if f_class._restore_method == RestoreMethod.DIRECT:
|
||||
restored_instance = f_class.__new__(f_class)
|
||||
restored_instance.__dict__.update(restored_dict)
|
||||
#if INIT, pass all attributes as keywords to __init__ method
|
||||
elif f_class._restore_method == RestoreMethod.INIT:
|
||||
restored_instance = f_class(**restored_dict)
|
||||
# IF CLASS_METHOD, pass all attributes as keyword aguments to classmethod "unpack()"
|
||||
elif f_class._restore_method == RestoreMethod.CLASS_METHOD:
|
||||
if inspect.ismethod(getattr(f_class, "restore", None)):
|
||||
restored_instance = f_class.restore(**restored_dict)
|
||||
else:
|
||||
raise Exception("Class "+str(f_class)+" does not have classmethod 'restore()'")
|
||||
else:
|
||||
raise Exception("Class _restore_method "+str(f_class._restore_method)+" is not supported")
|
||||
|
||||
return restored_instance
|
||||
else:
|
||||
return restored_dict
|
||||
Loading…
Reference in new issue