/
zodiac.py
129 lines (103 loc) · 2.9 KB
/
zodiac.py
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
import sys
try:
import builtins
except ImportError:
builtins = __builtins__
import types
import imp
_get_default = object()
def _get(obj, name, default=_get_default):
if isinstance(obj, dict):
if default is _get_default:
return obj[name]
else:
return obj.get(name, default)
else:
if default is _get_default:
return getattr(obj, name)
else:
return getattr(obj, name, default)
def _set(obj, name, val):
if isinstance(obj, dict):
obj[name] = val
else:
return setattr(obj, name, val)
def _create_closure_cell(obj):
def ret(): obj
return ret.__closure__[0]
def rebase_function(f, target, new_name=None, ns=None):
if not new_name:
new_name = f.__name__
ns = ns or dict()
if f.__closure__:
new_closure = []
for c in f.__closure__:
name = _get(c.cell_contents, '__name__', False)
if name and name in ns:
new_closure.append(_create_closure_cell(ns[name]))
else:
new_closure.append(c)
new_closure = tuple(new_closure)
else:
new_closure = f.__closure__
new_f = types.FunctionType(
f.__code__,
ns,
new_name,
f.__defaults__,
new_closure
)
_set(target, new_name, new_f)
def rebase_class(cls, target, new_name=None, ns=None):
if not new_name:
new_name = cls.__name__
ns = ns or dict()
new_bases = []
for base in cls.__bases__:
new_base = _get(target, base.__name__, False)
if new_base and isinstance(new_base, type):
new_bases.append(new_base)
else:
new_bases.append(base)
new_bases = tuple(new_bases)
new_cls = type(new_name, new_bases, dict())
ns[new_name] = new_cls
new_cls._my_class = new_cls
for name, item in cls.__dict__.items():
if name in ('__dict__', '__slots__', '__bases__', '__weakref__', '__name__', '__module__', '__doc__'): continue
if isinstance(item, types.MemberDescriptorType): continue
rebase(item, new_cls, name, ns)
_set(target, new_name, new_cls)
def rebase(obj, target, new_name=None, ns=None):
if isinstance(obj, type):
rebase_class(obj, target, new_name, ns)
elif isinstance(obj, types.FunctionType):
rebase_function(obj, target, new_name, ns)
else:
_set(target, new_name, obj)
def build_patch(original_module, patch_module):
original = __import__(original_module)
patch = __import__(patch_module)
mod = imp.new_module(patch_module)
mod.__builtins__ = builtins
for name in patch.__dict__:
if name.startswith('__'): continue
setattr(mod, name, getattr(patch, name))
for name in original.__dict__:
if name.startswith('__') or name in patch.__dict__:
continue
val = getattr(original, name)
rebase(val, mod, name, mod.__dict__)
return mod
hidden_modules = {}
def replace_module(name, replacement):
real = sys.modules.get(name, False)
if real:
hidden_modules[name] = real
sys.modules[name] = replacement
def restore_module(name):
sys.modules[name] = hidden_modules[name]
del hidden_modules[name]
def monkeypatch(dest, source):
patch = build_patch(dest, source)
replace_module(dest, patch)