summaryrefslogtreecommitdiff
path: root/bubbob/statesaver.py
diff options
context:
space:
mode:
Diffstat (limited to 'bubbob/statesaver.py')
-rw-r--r--bubbob/statesaver.py135
1 files changed, 135 insertions, 0 deletions
diff --git a/bubbob/statesaver.py b/bubbob/statesaver.py
new file mode 100644
index 0000000..85d3425
--- /dev/null
+++ b/bubbob/statesaver.py
@@ -0,0 +1,135 @@
+"""
+A pure Python implementation of statesaver.c that runs on top of PyPy.
+See description in statesaver.c.
+Difference: this supports new-style instances too.
+Use statesaver.standard_build() as the inst_build() if you want.
+"""
+
+from _pickle_support import generator_new
+import types
+
+def standard_build(self):
+ if type(self) is types.InstanceType:
+ # old-style instance
+ return types.InstanceType(self.__class__)
+ else:
+ # new-style instance
+ return type(self).__new__(type(self))
+
+# ____________________________________________________________
+
+def not_copied(x, memo):
+ return x
+
+def copy_custom_instance(x, memo):
+ try:
+ return memo[id(x)]
+ except KeyError:
+ y = x.inst_build()
+ memo[id(x)] = y
+ for key, value in x.__dict__.items():
+ y.__dict__[key] = copyrec(value, memo)
+ return y
+
+def copy_tuple(x, memo):
+ return tuple([copyrec(item, memo) for item in x])
+
+def copy_list(x, memo):
+ try:
+ return memo[id(x)]
+ except KeyError:
+ y = []
+ memo[id(x)] = y
+ for item in x:
+ y.append(copyrec(item, memo))
+ return y
+
+def copy_dict(x, memo):
+ try:
+ return memo[id(x)]
+ except KeyError:
+ y = {}
+ memo[id(x)] = y
+ for key, value in x.items():
+ y[copyrec(key, memo)] = copyrec(value, memo)
+ return y
+
+def copy_function(x, memo):
+ if not x.func_defaults:
+ return x # not copied
+ try:
+ return memo[id(x)]
+ except KeyError:
+ y = types.FunctionType(x.func_code, x.func_globals, x.func_name)
+ memo[id(x)] = y
+ y.func_defaults = copyrec(x.func_defaults, memo)
+ return y
+
+def copy_method(x, memo):
+ return types.MethodType(copyrec(x.im_func, memo),
+ copyrec(x.im_self, memo),
+ x.im_class)
+
+def copy_generator(x, memo):
+ try:
+ return memo[id(x)]
+ except KeyError:
+ y = generator_new(copyrec(x.gi_frame, memo), x.gi_running)
+ memo[id(x)] = y
+ return y
+
+def copy_frame(x, memo):
+ try:
+ return memo[id(x)]
+ except KeyError:
+ frame_new, args, state = x.__reduce__()
+ y = frame_new(*args)
+ memo[id(x)] = y
+ newstate = []
+ for item in state:
+ if not (item is x.f_globals or item is x.f_builtins):
+ item = copyrec(item, memo)
+ newstate.append(item)
+ y.__setstate__(newstate)
+ return y
+
+def copy_seqiter(x, memo):
+ try:
+ return memo[id(x)]
+ except KeyError:
+ # XXX self-recursion is not correctly handled here
+ seqiter_new, args = x.__reduce__()
+ args = [copyrec(item, memo) for item in args]
+ y = seqiter_new(*args)
+ memo[id(x)] = y
+ return y
+
+# ____________________________________________________________
+
+type_handlers = {tuple: copy_tuple,
+ list: copy_list,
+ dict: copy_dict,
+ types.FunctionType: copy_function,
+ types.MethodType: copy_method,
+ types.GeneratorType: copy_generator,
+ types.FrameType: copy_frame,
+ type(iter([])): copy_seqiter,
+ }
+
+def no_handler_found(x, memo):
+ if hasattr(x, '__dict__') and hasattr(x.__class__, 'inst_build'):
+ handler = copy_custom_instance
+ else:
+ handler = not_copied
+ type_handlers[x.__class__] = handler
+ return handler(x, memo)
+
+def copyrec(x, memo):
+ try:
+ cls = x.__class__
+ except AttributeError:
+ return x # 'cls' is likely an old-style class object
+ return type_handlers.get(cls, no_handler_found)(x, memo)
+
+def copy(x):
+ return copyrec(x, {})