问题描述
我有一个独特的问题 - 我的解决方案,我希望使用 pyinstaller 打包,在启动时基于 sys.argv 对一些内容进行 JIT。在 Windows 上将 multiprocessing
与 freeze_support
一起使用时,多处理需要传入不同的参数来初始化新进程。原始 sys.argv 最终在调用目标函数时设置。如何在调用目标函数之前获取原始的 sys.argv?
import sys
import multiprocessing
print('ArgV:',sys.argv)
def print_argv():
print(sys.argv)
if __name__ == '__main__':
multiprocessing.freeze_support()
print_argv()
p = multiprocessing.Process(target=print_argv)
p.start()
p.join()
当用 pyinstaller 打包并用 --hello=True
运行时,产生:
ArgV: ['scratch.exe','--hello=True']
['scratch.exe','--hello=True']
ArgV: ['scratch.exe','--multiprocessing-fork','parent_pid=16096','pipe_handle=380']
['scratch.exe','--hello=True']
我想要一些神奇的代码,当sys.argv
设置为--hello=True
时,它可以提供我原来的sys.argv
,即--multiprocessing-fork...
解决方法
我从未广泛使用冻结可执行文件,但我有几个想法......
看一下 multiprocessing.spawn._main()
,复制原始 sys.argv
发生在这里:
preparation_data = reduction.pickle.load(from_parent)
prepare(preparation_data)
如果您覆盖 Process.__new__
,您应该能够在 _bootstrap
之前运行代码(最终在进程对象上调用 run
),但在收到 sys.argv
之后。
import sys
import multiprocessing
print('ArgV:',sys.argv)
def print_argv():
print(sys.argv)
class myProcess(multiprocessing.Process):
def __new__(cls,*args,**kwargs):
if __name__ == "__mp_main__":
print("hook",sys.argv)
instance = super(myProcess,cls).__new__(cls)
instance.__init__(*args,**kwargs)
return instance
if __name__ == '__main__':
multiprocessing.freeze_support()
print_argv()
p = myProcess(target=print_argv)
p.start()
p.join()
另一个想法是通过覆盖 __getstate__
和 __setstate__
来挂钩 unpickle 过程。
class myProcess(multiprocessing.Process):
def __getstate__(self):
return self.__dict__.copy()
def __setstate__(self,state):
print("hook",sys.argv)
self.__dict__.update(state)
最后你可以钩住pickle寻找自定义类来解开时生成的审计事件:
class myProcess(multiprocessing.Process):
pass
def hook(event_name,args):
if "pickle.find_class" in event_name:
if args[1] == myProcess.__name__:
print("hook",sys.argv)
sys.addaudithook(hook)
所有这些在加载新进程的过程中大致同时发生,我不能说哪个是最健壮的...