解析字节缓存.pyc文件
项目描述
序列化解析器
marshal
是Python内部对象序列化,用于将代码对象序列化到*.pyc
文件中。
在可预见的未来,这种无用的脑力训练应该能帮助我解决无法重现的.pyc
文件问题。
安装
Marshal解析器可在PyPI上使用
pip install marshalparser
以及作为Fedora RPM包
sudo dnf install python3-marshalparser
解析器操作
以人类可读的方式打印解析内容
当前版本的解析器创建了一个可读的解析对象列表,其中包含对象开始的字节信息以及它们的内容
$ python3 -m marshalparser -p test/pure_marshal/list_of_simple_objects.dat
n=0/0x0 byte=(b'5b', b'[', 0b1011011) TYPE_LIST REF[0]
tuple/list/set size: 4
n=5/0x5 byte=(b'54', b'T', 0b1010100) TYPE_TRUE
result=True, type=<class 'bool'>
n=6/0x6 byte=(b'46', b'F', 0b1000110) TYPE_FALSE
result=False, type=<class 'bool'>
n=7/0x7 byte=(b'4e', b'N', 0b1001110) TYPE_NONE
result=None, type=<class 'NoneType'>
n=8/0x8 byte=(b'2e', b'.', 0b101110) TYPE_ELLIPSIS
result=Ellipsis, type=<class 'ellipsis'>
result=[True, False, None, Ellipsis], type=<class 'list'>
对于 .pyc
文件也是如此,但它们更复杂,因为它们包含代码对象,这些对象被报告为字典
$ python3 -m marshalparser -p test/python_stdlib/3.9/doctest.cpython-39.opt-1.pyc | head -n 30
n=16/0x10 byte=(b'63', b'c', 0b1100011) TYPE_CODE REF[0]
n=41/0x29 byte=(b'73', b's', 0b1110011) TYPE_STRING
result=b'd\x00Z\x00d\x01Z\x01g\x00d\x02\xa2\x01Z\x02d\x03d\x04l\x03Z\x03d\x03d\x04l\x04Z\x04d\x03d\x04l\x05Z\x05d\x03d\x04l\x06Z\x06d\x03d\x04l\x07Z\x07d\x03d\x04l\x08Z\x08d\x03d\x04l\tZ\td\x03d\x04l\nZ\nd\x03d\x04l\x0bZ\x0bd\x03d\x04l\x0cZ\x0cd\x03d\x05l\rm\x0eZ\x0e\x01\x00d\x03d\x06l\x0fm\x10Z\x10\x01\x00e\x10d\x07d\x08\x83\x02Z\x11i\x00Z\x12d\td\n\x84\x00Z\x13e\x13d\x0b\x83\x01Z\x14e\x13d\x0c\x83\x01Z\x15e\x13d\r\x83\x01Z\x16e\x13d\x0e\x83\x01Z\x17e\x13d\x0f\x83\x01Z\x18e\x13d\x10\x83\x01Z\x19e\x14e\x15B\x00e\x16B\x00e\x17B\x00e\x18B\x00e\x19B\x00Z\x1ae\x13d\x11\x83\x01Z\x1be\x13d\x12\x83\x01Z\x1ce\x13d\x13\x83\x01Z\x1de\x13d\x14\x83\x01Z\x1ee\x13d\x15\x83\x01Z\x1fe\x1be\x1cB\x00e\x1dB\x00e\x1eB\x00e\x1fB\x00Z d\x16Z!d\x17Z"d\x18d\x19\x84\x00Z#drd\x1bd\x1c\x84\x01Z$d\x1dd\x1e\x84\x00Z%d\x1fd \x84\x00Z&dsd"d#\x84\x01Z\'d$d%\x84\x00Z(G\x00d&d\'\x84\x00d\'e\x0e\x83\x03Z)d(d)\x84\x00Z*d*d+\x84\x00Z+d,d-\x84\x00Z,G\x00d.d/\x84\x00d/e\x08j-\x83\x03Z.d0d1\x84\x00Z/G\x00d2d3\x84\x00d3\x83\x02Z0G\x00d4d5\x84\x00d5\x83\x02Z1G\x00d6d7\x84\x00d7\x83\x02Z2G\x00d8d9\x84\x00d9\x83\x02Z3G\x00d:d;\x84\x00d;\x83\x02Z4G\x00d<d=\x84\x00d=\x83\x02Z5G\x00d>d?\x84\x00d?e6\x83\x03Z7G\x00d@dA\x84\x00dAe6\x83\x03Z8G\x00dBdC\x84\x00dCe4\x83\x03Z9d\x04a:dtdFdG\x84\x01Z;dDd\x04d\x04d\x04d\x04dDd\x03d\x04dEe2\x83\x00d\x04f\x0bdHdI\x84\x01Z<dudKdL\x84\x01Z=d\x03a>dMdN\x84\x00Z?G\x00dOdP\x84\x00dPe\x0cj@\x83\x03ZAG\x00dQdR\x84\x00dReA\x83\x03ZBG\x00dSdT\x84\x00dTe\x0cjC\x83\x03ZDdvdUdV\x84\x01ZEG\x00dWdX\x84\x00dXeA\x83\x03ZFdDd\x04d\x04e2\x83\x00d\x04f\x05dYdZ\x84\x01ZGd[d\\\x84\x00ZHd]d^\x84\x00ZId_d`\x84\x00ZJdwdadb\x84\x01ZKdxdcdd\x84\x01ZLdydedf\x84\x01ZMG\x00dgdh\x84\x00dh\x83\x02ZNeNdidjdkdldmdn\x9c\x06ZOdodp\x84\x00ZPeQdqk\x02\x90\x03r2e\n\xa0ReP\x83\x00\xa1\x01\x01\x00d\x04S\x00', type=<class 'bytes'>
n=868/0x364 byte=(b'29', b')', 0b101001) TYPE_SMALL_TUPLE
Small tuple size: 122
n=870/0x366 byte=(b'61', b'a', 0b1100001) TYPE_ASCII
result=b'Module doctest -- a framework for running examples in docstrings.\n\nIn simplest use, end each module M to be tested with:\n\ndef _test():\n import doctest\n doctest.testmod()\n\nif __name__ == "__main__":\n _test()\n\nThen running the module as a script will cause the examples in the\ndocstrings to get executed and verified:\n\npython M.py\n\nThis won\'t display anything unless an example fails, in which case the\nfailing example(s) and the cause(s) of the failure(s) are printed to stdout\n(why not stderr? because stderr is a lame hack <0.2 wink>), and the final\nline of output is "Test failed.".\n\nRun it with the -v switch instead:\n\npython M.py -v\n\nand a detailed report of all examples tried is printed to stdout, along\nwith assorted summaries at the end.\n\nYou can force verbose mode by passing "verbose=True" to testmod, or prohibit\nit by passing "verbose=False". In either of those cases, sys.argv is not\nexamined by testmod.\n\nThere are a variety of other ways to run doctests, including integration\nwith the unittest framework, and support for running non-Python text\nfiles containing doctests. There are also many ways to override parts\nof doctest\'s default behaviors. See the Library Reference Manual for\ndetails.\n', type=<class 'bytes'>
n=2096/0x830 byte=(b'7a', b'z', 0b1111010) TYPE_SHORT_ASCII
result=b'reStructuredText en', type=<class 'bytes'>
n=2117/0x845 byte=(b'29', b')', 0b101001) TYPE_SMALL_TUPLE
… etc …
未使用的 FLAG_REF
新版本的解析器还可以生成未使用 FLAG_REF
的列表——具有启用引用可能性的对象,但未使用该功能。
这里我们使用了之前相同的例子,所以你可以尝试手动在本页顶部找到未使用的 FLAG_REF
。
python3 -m marshalparser -u test/pure_marshal/list_of_simple_objects.dat
Unused FLAG_REFs:
0 - Flag_ref(byte=0, type='TYPE_LIST', content=[True, False, None, Ellipsis], usages=0)
如果我们能够检测到它,我们也可以修复它。使用选项 -f
,Marshal解析器生成一个新文件,其中删除了所有未使用的 FLAG_REF
,并重新计算了所有有用的引用。
# Fix it
$ python3 -m marshalparser -f test/pure_marshal/list_of_simple_objects.dat
# Check the fixed file
$ python3 -m marshalparser -u test/pure_marshal/list_of_simple_objects.fixed.dat
# Print it
$ python3 -m marshalparser -p test/pure_marshal/list_of_simple_objects.fixed.dat
n=0/0x0 byte=(b'5b', b'[', 0b1011011) TYPE_LIST
tuple/list/set size: 4
n=5/0x5 byte=(b'54', b'T', 0b1010100) TYPE_TRUE
result=True, type=<class 'bool'>
n=6/0x6 byte=(b'46', b'F', 0b1000110) TYPE_FALSE
result=False, type=<class 'bool'>
n=7/0x7 byte=(b'4e', b'N', 0b1001110) TYPE_NONE
result=None, type=<class 'NoneType'>
n=8/0x8 byte=(b'2e', b'.', 0b101110) TYPE_ELLIPSIS
result=Ellipsis, type=<class 'ellipsis'>
result=[True, False, None, Ellipsis], type=<class 'list'>
也可以使用 -fo
覆盖现有文件。
测试
测试使用 pytest,/test/python_stdlib/3.X
包含大约一百个来自 Python 标准库(python3-libs 或 python36 等)的随机 pyc
文件,这些文件是 Fedora 中每个支持的 Python 版本的 RPM 包。
测试检查解析器是否能够解析/修复 pyc
文件,然后检查解包后的代码对象在两个文件(原始和修复)中相同。
测试确保运行(例如)Python 3.9 的 MarshalParser 能够解析和修复其他支持 Python 版本的 pyc 文件。但为了检查原始和修复的 pyc 文件是否相同,我们需要使用编译文件的 Python 版本运行 marshal_content_check.py
。
Python 支持
代码已在 Python 3.6+ 上进行了测试,并且它还能够修复由 Python 3.6+ 产生的 pyc 文件。Python 3.6 本身需要 dataclasses
。
支持的对象类型
- ✓ TYPE_NULL(作为 TYPE_DICT 的空操作符)
- ✓ TYPE_NONE
- ✓ TYPE_FALSE
- ✓ TYPE_TRUE
- ✓ TYPE_STOPITER
- ✓ TYPE_ELLIPSIS
- ✓ TYPE_INT
- ✘ TYPE_INT64(不再生成)
- ✘ TYPE_FLOAT(仅在 marshal 版本 1 中)
- ✓ TYPE_BINARY_FLOAT
- ✘ TYPE_COMPLEX(仅在 marshal 版本 1 中)
- ✓ TYPE_BINARY_COMPLEX
- ✓ TYPE_LONG(解析为数字,但不会重建为 PyLong)
- ✓ TYPE_STRING
- ✓ TYPE_INTERNED
- ✓ TYPE_REF
- ✓ TYPE_TUPLE
- ✓ TYPE_LIST
- ✓ TYPE_DICT
- ✓ TYPE_CODE
- ✓ TYPE_UNICODE
- ? TYPE_UNKNOWN(不知道如何测试未知字节对象作为字节对象)
- ✓ TYPE_SET
- ✓ TYPE_FROZENSET
- ✓ FLAG_REF(识别为所有复杂类型的标志)
- ✓ TYPE_ASCII
- ✓ TYPE_ASCII_INTERNED
- ✓ TYPE_SMALL_TUPLE
- ✓ TYPE_SHORT_ASCII
- ✓ TYPE_SHORT_ASCII_INTERNED
参考
项目详情
下载文件
下载适合您的平台的文件。如果您不确定要选择哪个,请了解更多关于 安装包 的信息。
源分发
构建分发
marshalparser-0.3.4.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 664e003021b7639d8686ad72ca42e276e8c6891537bfc78333b7ffb3a42c1aa2 |
|
MD5 | 6124375d6e186cb66fc1f28652773435 |
|
BLAKE2b-256 | ba004a41f46b5d8aaa8482408d8c4ba08777f8b4d018b3e2db0e961c560da29d |
marshalparser-0.3.4-py3-none-any.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | a36b4dd7ac83e52adcf364eebdb4ce36909dfd07e8f64807d4e953df54e12d93 |
|
MD5 | 5e0997f7b80b732691320aca4c682e78 |
|
BLAKE2b-256 | d808b416dd105f6e0c724ff61f01f800a26075a20e5b5be4939af44378ba6e13 |