8
from typing import Any, IO, BinaryIO, Union
10
__all__ = ["FakeObject", "FakeClass", "DumpUnpickler", "main"]
13
def __init__(self, module, name, args):
21
state_str = "" if self.state is None else f"(state={self.state!r})"
22
return f"{self.module}.{self.name}{self.args!r}{state_str}"
24
def __setstate__(self, state):
28
def pp_format(printer, obj, stream, indent, allowance, context, level):
29
if not obj.args and obj.state is None:
30
stream.write(repr(obj))
33
stream.write(f"{obj.module}.{obj.name}")
34
printer._format(obj.args, stream, indent + 1, allowance + 1, context, level)
37
stream.write(f"{obj.module}.{obj.name}()(state=\n")
38
indent += printer._indent_per_level
39
stream.write(" " * indent)
40
printer._format(obj.state, stream, indent, allowance + 1, context, level + 1)
43
raise Exception("Need to implement")
47
def __init__(self, module, name):
50
self.__new__ = self.fake_new
53
return f"{self.module}.{self.name}"
55
def __call__(self, *args):
56
return FakeObject(self.module, self.name, args)
58
def fake_new(self, *args):
59
return FakeObject(self.module, self.name, args[1:])
62
class DumpUnpickler(pickle._Unpickler):
67
catch_invalid_utf8=False,
69
super().__init__(file, **kwargs)
70
self.catch_invalid_utf8 = catch_invalid_utf8
72
def find_class(self, module, name):
73
return FakeClass(module, name)
75
def persistent_load(self, pid):
76
return FakeObject("pers", "obj", (pid,))
78
dispatch = dict(pickle._Unpickler.dispatch)
84
def load_binunicode(self):
85
strlen, = struct.unpack("<I", self.read(4))
86
if strlen > sys.maxsize:
87
raise Exception("String too long.")
88
str_bytes = self.read(strlen)
91
obj = str(str_bytes, "utf-8", "surrogatepass")
92
except UnicodeDecodeError as exn:
93
if not self.catch_invalid_utf8:
95
obj = FakeObject("builtin", "UnicodeDecodeError", (str(exn),))
97
dispatch[pickle.BINUNICODE[0]] = load_binunicode
100
def dump(cls, in_stream, out_stream):
101
value = cls(in_stream).load()
102
pprint.pprint(value, stream=out_stream)
106
def main(argv, output_stream=None):
109
if output_stream is not None:
110
raise Exception("Pass argv of length 2.")
111
sys.stderr.write("usage: show_pickle PICKLE_FILE\n")
112
sys.stderr.write(" PICKLE_FILE can be any of:\n")
113
sys.stderr.write(" path to a pickle file\n")
114
sys.stderr.write(" file.zip@member.pkl\n")
115
sys.stderr.write(" file.zip@*/pattern.*\n")
116
sys.stderr.write(" (shell glob pattern for members)\n")
117
sys.stderr.write(" (only first match will be shown)\n")
121
handle: Union[IO[bytes], BinaryIO]
123
with open(fname, "rb") as handle:
124
DumpUnpickler.dump(handle, output_stream)
126
zfname, mname = fname.split("@", 1)
127
with zipfile.ZipFile(zfname) as zf:
129
with zf.open(mname) as handle:
130
DumpUnpickler.dump(handle, output_stream)
133
for info in zf.infolist():
134
if fnmatch.fnmatch(info.filename, mname):
135
with zf.open(info) as handle:
136
DumpUnpickler.dump(handle, output_stream)
140
raise Exception(f"Could not find member matching {mname} in {zfname}")
143
if __name__ == "__main__":
148
pprint.PrettyPrinter._dispatch[FakeObject.__repr__] = FakeObject.pp_format
150
sys.exit(main(sys.argv))