There's probably a way to use plain old POSIX sed
on the bytecode and that will work assuming the filenames don't appear in the code other than in the co_filename
field of the code object.
However if you want a more bullet-proof solution, here is how you might modify the bytecode and write that back out.
#!/usr/bin python
"""
Renames the co_filename object
"""
import glob, re, types
import os.path as osp
from xasm.misc import write_pycfile
from xasm.assemble import Assembler
from xdis.code import iscode
from xdis.load import load_module
from collections import deque
def change_co_filename(code, new_path):
return types.CodeType(
code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code,
code.co_consts,
code.co_names,
code.co_varnames,
new_path,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
def disco_loop(queue, new_path):
new_code = change_co_filename(queue[0], new_path)
while len(queue) > 0:
co = queue.popleft()
for c in co.co_consts:
if iscode(c):
change_co_filename(c, new_path)
queue.append(c)
pass
pass
return new_code
suffix = "cpython-3.7.pyc"
paths = glob.glob(f"*.{suffix}")
# Modify to figure out what prefix to look for
orig_dirname = "/tmp/pip-unpacked-wheel-2b2_9obo/"
new_dirname = "/tmp/"
for path in paths:
assembler = Assembler("3.7")
version, assembler.timestamp, magic_int, code, is_pypy, assembler.size = load_module(path)
new_path = re.sub(orig_dirname, new_dirname, code.co_filename)
queue = deque([code])
new_code = disco_loop(queue, new_path)
assembler.code_list = [new_code]
write_pycfile(path, assembler)
This relies on my xdis
and xasm
programs that work on bytecode. (I just now had to modify the latter for 3.7 since the code object changed in that version)
If there is interest in this kind of stuff, I can probably modify the above as a standalone routine and distribute that with xasm.
If you want to see what's going on in writing Python bytecode files, see the answer here