summaryrefslogtreecommitdiff
path: root/bubbob/statesaver.py
blob: 72be9cd923d9b62e550f5f078c0f704d260c974d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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 list(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 list(x.items()):
            y[copyrec(key, memo)] = copyrec(value, memo)
        return y

def copy_function(x, memo):
    if not x.__defaults__:
        return x     # not copied
    try:
        return memo[id(x)]
    except KeyError:
        y = types.FunctionType(x.__code__, x.__globals__, x.__name__)
        memo[id(x)] = y
        y.__defaults__ = copyrec(x.__defaults__, memo)
        return y

def copy_method(x, memo):
    return types.MethodType(copyrec(x.__func__, memo),
                            copyrec(x.__self__, memo),
                            x.__self__.__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, {})