TL;博士; - setuptools/distutils 脚本包装器 .exe entry_points 不会触发 Windows 多处理无限递归。Wheel 的包装器 .exe entry_points 可以。我怎样才能得到以前的行为?
我们中的许多人可能在直接调用模块脚本时遇到了 Python 2.X 的多处理模块和具有无限递归的 Windows 的问题。
当我为我的库创建一个入口点,该入口点导致一个具有多处理功能的函数时,入口点脚本及其包装 .exe 文件在我使用 setuptools 安装它们时都可以在 Windows 上运行,能够直接运行调用 multiprocessing.Pool 的函数和进程对象。这很棒,因为我的库依赖于多处理来加速可耻的并行程序。
事情一直运作良好,直到我尝试使用 bdist_wheel 共享库。虽然构建过程之前在外部添加了 .exe 包装器和脚本,但 .exe 不是同一种包装器。
我的二进制文件格式技术远非好,但我知道有某种 zip 压缩文件到我看到的可执行文件的翻译,所以我去做了唯一合乎逻辑的事情并解压缩了 .exe 文件。我使用了 7zip,因为即使是我的 mingw bash shell 也没有 zip/unzip。从 Wheel 文件安装的 .exe 包装器解压缩到包含简单主.py 脚本的目录中,
# -*- coding: utf-8 -*-
import re
import sys
from example_multiprocessing.runner import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())
它只是一个从 zip 文件运行的 python 脚本。难怪它炸了。
我使用 setup.py 重新安装,添加了旧的包装器 .exe,并尝试解压缩。瞧,它是一个完整的便携式可执行文件,其中 .data、.pdata、.rdata 和 .text 部分通过 7zip 提取到自己的文件中。两个小时后,我翻遍了 distutils 和 setuptools 源代码以找出那里发生了什么,并发现我看到的 .exe 是通过将一些特定于入口点的 python 代码倒入预先制作的启动器 exe 中制作的对于 Windows。伟大的。我比以前知道的更多。但如果区别在于 .exe 包装器,为什么 -script.py entry_point 有效?
__requires__ = 'example-multiprocessing==1.0.2'
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.exit(
load_entry_point('example-multiprocessing==1.0.2', 'console_scripts', 'example-mp-run')()
)
很load_entry_point
特别吗?它是 EntryPoint.load() 的包装器,用于显式调用__import__
:
# From [https://bitbucket.org/pypa/pkg_resources/src/33e56f318f5086158de8bb2827acb55db2dbc153/pkg_resources.py?at=default#cl-2258][1]
def load(self, require=True, env=None, installer=None):
if require: self.require(env, installer)
entry = __import__(self.module_name, globals(),globals(), ['__name__'])
for attr in self.attrs:
try:
entry = getattr(entry,attr)
except AttributeError:
raise ImportError("%r has no %r attribute" % (entry,attr))
return entry
从的globals()
文档中看起来像是死胡同__import__
,但['__name__']
看起来它可能很有用。问题是这__import__
是一个内置的,它是用 C 实现的。我试图阅读发布在GitHub 上的镜像,但目前它超出了我的理解范围,分为三个或更多交互功能,我可以'不跟踪并保持与 fromlist 的连接。__import__
我也可能会因为下一个块尝试访问存储在 中的模块的属性而分心entry
,我推断它是入口点函数本身。
当我运行以下代码时:
getattr(__import__("example_multiprocessing.runner", globals(), globals(), ["__name__"]), "main")()
我确实看到了我希望运行的主要功能,并且没有问题地返回。
真的__import__
在做我需要的工作,__name__
将脚本设置为不包括的内容__main__
吗?为什么runpy,-m背后的机器不工作?