1"""Python implementation of Programs/_freeze_module.c 2 3The pure Python implementation uses same functions and arguments as the C 4implementation. 5 6The generated byte code is slightly different because 7compile() sets the PyCF_SOURCE_IS_UTF8 flag and objects have a 8reference count > 1. Marshal adds the `FLAG_REF` flag and creates a 9reference `hashtable`. 10""" 11 12import marshal 13import sys 14 15header = "/* Auto-generated by Programs/_freeze_module.py */" 16 17 18def read_text(inpath: str) -> bytes: 19 with open(inpath, "rb") as f: 20 return f.read() 21 22 23def compile_and_marshal(name: str, text: bytes) -> bytes: 24 filename = f"<frozen {name}>" 25 # exec == Py_file_input 26 code = compile(text, filename, "exec", optimize=0, dont_inherit=True) 27 return marshal.dumps(code) 28 29 30def get_varname(name: str, prefix: str) -> str: 31 return f"{prefix}{name.replace('.', '_')}" 32 33 34def write_code(outfile, marshalled: bytes, varname: str) -> None: 35 data_size = len(marshalled) 36 37 outfile.write(f"const unsigned char {varname}[] = {{\n") 38 39 for n in range(0, data_size, 16): 40 outfile.write(" ") 41 outfile.write(",".join(str(i) for i in marshalled[n : n + 16])) 42 outfile.write(",\n") 43 outfile.write("};\n") 44 45 46def write_frozen(outpath: str, inpath: str, name: str, marshalled: bytes) -> None: 47 with open(outpath, "w") as outfile: 48 outfile.write(header) 49 outfile.write("\n") 50 arrayname = get_varname(name, "_Py_M__") 51 write_code(outfile, marshalled, arrayname) 52 53 54def main(): 55 if len(sys.argv) != 4: 56 sys.exit("need to specify the name, input and output paths\n") 57 58 name = sys.argv[1] 59 inpath = sys.argv[2] 60 outpath = sys.argv[3] 61 62 text = read_text(inpath) 63 marshalled = compile_and_marshal(name, text) 64 write_frozen(outpath, inpath, name, marshalled) 65 66 67if __name__ == "__main__": 68 main() 69