我需要在基本上像这样的系统上进行一些维护:
(复杂的旧版Python程序)->二进制pickle文件->(另一个复杂的旧版Python程序)
需要准确找出中间的腌制文件中的内容。我怀疑文件格式比生成和使用它的代码简单得多,如果我可以通过盯着文件本身而不用弄清楚所有代码的作用来验证这一点,那将很有帮助。
是否有一种方法来获取二进制pickle文件并将其转换,而不是转换为内存中的活动对象(这是我用Google搜索可以找到的每个页面都认为“不挑剔”的意思)而是某种可读的文本格式? JSON,XML,无论如何,我对确切的格式都不是很挑剔,只要它是可以在文本编辑器中加载并查看的内容的完整且可读的表示形式,任何操作都可以。
答案 0 :(得分:2)
如果应用程序足够老,则可能会使用人类易于阅读的pickle protocol 0。
您可以尝试在Python 3.2+中找到的pickletools
模块。
使用python3 -m pickletools <file>
将为您“取消”泡菜文件。
或者,您可以尝试使用data = pickle.load()
加载数据,然后立即使用print(json.dumps(data))
转储数据。请注意,这可能会失败,因为泡菜比JSON可以代表更多的东西。
答案 1 :(得分:1)
Python本机类型足够可读。您遇到的困难是,取消选择将自动尝试为您文件中代码中定义的任何实例的类导入任何模块。
幸运的是,Python具有足够的灵活性,可以临时破解导入机制,以欺骗取消腌制并为其提供 false 类以填充腌制的属性。
然后,这是将以这种方式取消选择的实例的字典转换回人类可读的问题。
幸运的是,我维护了一个宠物项目,该项目执行此“临时导入系统黑客攻击”,因此我可以从那里借几行代码来在此处做同样的事情。
为了测试这件事,我最终创建了一个独立脚本。正如它的注释所拼写的:不要试图将其合并到更大的程序中-通过创建伪造的模块,它会破坏正在运行的Python程序-但对于您而言,可视化其中的内容应该足够了-尽管不可能匹配所有可能存在的极端情况-您将不得不从这里开始工作,主要是在下面的“ pythonize”函数上进行:
import re, pickle, pprint, sys
from types import ModuleType
from collections.abc import Sequence, Mapping, Set
from contextlib import contextmanager
def pythonize(obj):
if isinstance(obj, (str, bytes)):
return obj
if isinstance(obj, (Sequence, Set)):
container = []
for element in obj:
container.append(pythonize(element))
return container
elif isinstance(obj, Mapping):
container = {}
else:
container = {"$CLS": obj.__class__.__qualname__}
if not hasattr(obj, "__dict__"):
return repr(obj)
obj = obj.__dict__
for key, value in obj.items():
container[key] = pythonize(value)
return container
class FakeModule:
def __getattr__(self, attr):
cls = type(attr, (), {})
setattr(self, attr, cls)
return cls
def fake_importer(name, globals, locals, fromlist, level):
module = sys.modules[name] = FakeModule()
return module
@contextmanager
def fake_import_system():
# With code lifted from https://github.com/jsbueno/extradict - MapGetter functionality
builtins = __builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__
original_import = builtins["__import__"]
builtins["__import__"] = fake_importer
yield None
builtins["__import__"] = original_import
def unpickle_to_text(stream: bytes):
# WARNING: this example will wreck havoc in loaded modules!
# do not use as part of a complex system!!
action_log = []
with fake_import_system():
result = pickle.loads(stream)
pythonized = pythonize(result)
return pprint.pformat(pythonized)
if __name__ == "__main__":
print(unpickle_to_text(open(sys.argv[1], "rb").read()))
更新:由于这可能对更多人有用,所以我只是根据这些代码总结了要点。甚至更值得:https://gist.github.com/jsbueno/b72a20cba121926bec19163780390b92