我尝试将python代码编译为PyCodeObject并编组为pyc文件。但是当我导入我的pyc文件时,它失败并且回溯是“ValueError:错误的编组数据(未知类型代码)”这是我的代码
import struct
import marshal
import time
import imp
import sys
def dump(co, filename):
magic = imp.get_magic()
time_string = struct.pack('L', int(time.time()))
f = open("merge.pyc", "wb")
f.write(magic)
f.write(time_string)
marshal.dump(dco, f)
f.close()
demo = open("demo.py").read()
dco = compile(demo, "demo.py", "exec")
dump(dco, "dco.pyc")
我想ValueError
引发的是因为时间戳的类型不匹配。您应该确保fmt
中的L
参数(struct.pack
使用,标准大小为4个字节)与python可执行文件具有相同的长度。
由于时间戳的值不会影响最终结果,因此在这里使类型合适就足够了。如果您使用64位版本的CPython,请尝试使用Q
而不是L
来确保8个字节。
===更新开始===
对不起,我在前面的描述中犯了一个严重的错误。
没有任何前缀的L
意味着native,并且大小和字节顺序与机器的本机格式和字节顺序相同(在64位CPython中,它将是8个字节)。但是,写入pyc
文件的时间戳在Python 2和3中始终为4个字节(小端)。
如果使用的是Python2,我们应该使用<L
而不是L
,它具有标准大小,4个字节。
如果使用的是Python3,除了timestamp
之外,我们应该添加另外4个字节的字段source size
,如下面的代码所示(importlib / _bootstrap_external.py,Python3.6.4)
def _code_to_bytecode(code, mtime=0, source_size=0):
"""Compile a code object into bytecode for writing out to a byte-compiled
file."""
data = bytearray(MAGIC_NUMBER)
data.extend(_w_long(mtime))
data.extend(_w_long(source_size))
data.extend(marshal.dumps(code))
return data
最后,您可以参考pkgutil.read_code()
,这是编写代码的相反顺序。
===更新结束===
顺便说一下,你的代码中有一些缺陷:没有在co
函数中使用filename
和dump
参数