尝试使用 pythonnet 从脚本构建 exe 时,py2exe 失败并显示 "No module named 'clr'"

py2exe fails with "No module named 'clr'" when trying to build exe from script using pythonnet

我创建了一个使用 pythonnet 的 python 脚本。该脚本位于名为 main.py 的文件中。当我从命令行 运行 脚本(只需在 Windows 命令提示符下键入 main.py 时),导入的 pythonnet 模块 clr 工作正常。但是当我尝试构建一个 exe 时,我收到一条错误消息:No module named clr.

为了查明原因,我已经验证使用 py2exe 构建可执行文件(在我的例子中是一个简单的 Tkinter 应用程序)是可行的。我只安装了 Python 3.4 并验证 where python 指向 C:\Python34\python.exe.

该错误发生在可执行构建时,似乎是由于在我的 setup.pypy2exe 部分 {"includes":["sip","clr"]}} 中包含 clr 触发的。完整的追溯如下:

Traceback (most recent call last):
  File "setup.py", line 32, in <module>
    windows = [{'script': "main.py"}],
  File "C:\Python34\lib\distutils\core.py", line 148, in setup
    dist.run_commands()
  File "C:\Python34\lib\distutils\dist.py", line 917, in run_commands
    self.run_command(cmd)
  File "C:\Python34\lib\distutils\dist.py", line 936, in run_command
    cmd_obj.run()
  File "C:\Python34\lib\site-packages\py2exe\distutils_buildexe.py", line 188, i
n run
    self._run()
  File "C:\Python34\lib\site-packages\py2exe\distutils_buildexe.py", line 267, i
n _run
    builder.analyze()
  File "C:\Python34\lib\site-packages\py2exe\runtime.py", line 164, in analyze
    mf.import_hook(modname)
  File "C:\Python34\lib\site-packages\py2exe\mf3.py", line 120, in import_hook
    module = self._gcd_import(name)
  File "C:\Python34\lib\site-packages\py2exe\mf3.py", line 273, in _gcd_import
    raise ImportError('No module named {!r}'.format(name), name=name)
ImportError: No module named 'clr'

我也read/tried这些:

https://docs.python.org/2/distutils/setupscript.html
https://pythonhosted.org/setuptools/setuptools.html
http://sourceforge.net/p/py2exe/mailman/message/6937658

引导我将 clr.pydPython.Runtime.dll 移动到不同的位置,包括 main.pyC:\Python34\Lib\site-packages(它们原来所在的位置)和 C:\Python34\Lib\site-packages\py2exe 的位置

None 这些已经奏效,我不知道下一步该尝试什么。我可以看到由于某种原因 py2exe 找不到 clr.pydPython.Runtime.dll 或两者,但不明白为什么。有人有什么想法吗?


代码详情

我的 main.py 脚本如下所示:

import clr
clr.AddReference("name.xxxx")
from name.xxxx import aaa
from clr import System

# All my functioning code, that I've verified works when run from the command line

这是我的 setup.py 文件包含的内容(我留下了一些注释以便您可以看到我尝试过的内容):

from distutils.core import setup
import py2exe, sys, os

mydata_files = []
for files in os.listdir('C:\d\Project\TOOLS\data_acquisition\trunk\DLL'):
    f1 = 'C:\d\Project\TOOLS\data_acquisition\trunk\DLL\' + files
    if os.path.isfile(f1): # skip directories
        f2 = '.', [f1]
        mydata_files.append(f2)

setup(
  data_files=mydata_files,

  # options = {"py2exe" : {"includes" : "module1,module2,module3"}}
  options = {"py2exe": {"includes":["sip", "clr"]}},
  # options = {'py2exe': {'bundle_files': 1 , 'compressed': True,"includes":["sip"]}},
  #python setup.py py2exe
  #CLR.dll and PythonRuntime.dll 
  # options = {'py2exe': {'bundle_files': 1, "skip_archive":1 ,"includes":["sip"]}},
  windows = [{'script': "main.py"}],
  # data_files=mydata_files,
  # zipfile = None
)

如果我将行 options = {"py2exe": {"includes":["sip", "clr"]}}, 更改为 options = {"py2exe": {"includes":["sip"]}},,则 .exe 会构建,但显然无法正常运行。

###安装说明

作为参考,我使用 pip install py2exe 执行了 py2exe 的标准安装。这会将 py2exe 放入您的 python 安装的 Lib\site-packages 中。接下来,我通过下载 .whl from Christoph Gohlke's unofficial Windows binaries page, then using pip install path\to\pythonnet-2.0.0<version_numbers>.whl. This puts clr.pyd and Python.Runtime.dll into Lib\site-packages for your python install. This question and answers 安装了 pythonnet 有更多信息。

###问题

这是 py2exe 的一个相当奇怪的行为,很难调试。我认为这纯粹是该工具中的错误。此外,错误消息没有帮助。

问题是模块 clr 通过其 hooks.py file. It is not clear why. You can see the line that does this exclusion here.

被该工具明确排除

###解决方案

解决方法是从 py2exe 安装中的 windows_excludes 变量 hooks.py 文件中删除单词 clr。假设一切都在其标准位置 - 这意味着删除位于 C:\Python34\Lib\site-packages\py2exe 的文件 hooks.py 中的第 23 行。您还需要确保 Python.Runtime.dll 以某种方式与您的 .exe 打包 - 我通过将它添加到数据文件来测试它。这是我测试和工作的示例 - 我使用了一个非常简单的 main.py 来说明导入并确保程序确实在运行。我让你的 setup.py 尽可能接近你的版本,注释掉不适合我的系统的行

要实际制作 .exe - 使用以下内容(如果 python 是 Python 3 安装的别名,则可能不需要 python.exe 的路径)

C:\python34\python.exe setup.py py2exe

###main.py

import clr
# I import clr, but don't use it as this is not my
# expertise.  The fact it imports without error means
# I'm pretty sure it will work

with open('out.txt','a') as f:
    for i in range(30):
        f.write(str(i))

###setup.py

from distutils.core import setup
import py2exe, sys, os

mydata_files = []

# I had to comment these out as they did not apply to my test environment
# for files in os.listdir('C:\d\Project\TOOLS\data_acquisition\trunk\DLL'):
# f1 = 'C:\d\Project\TOOLS\data_acquisition\trunk\DLL' + files
# if os.path.isfile(f1): # skip directories
    # f2 = 'dll', [f1]
    # mydata_files.append(f2)
    
# It's essential that the Python.Runtime.dll is packaged with main.exe
# This is how I've done it
mydata_files.append(('.',['C:\Python34\Lib\site-packages\Python.Runtime.dll']))

setup(
 data_files=mydata_files,

# I've left all your commented lines in - they weren't necessary for my test
# options = {"py2exe" : {"includes" : "module1,module2,module3"}}

# I haven't included sip as I don't have it installed, but I think it will work
options = {"py2exe": {"includes":["clr"]}},
# options = {'py2exe': {'bundle_files': 1 , 'compressed': True,"includes":["sip"]}},
#python setup.py py2exe
#CLR.dll and PythonRuntime.dll 
# options = {'py2exe': {'bundle_files': 1, "skip_archive":1 ,"includes":["sip"]}},
windows = [{'script': "main.py"}],
# data_files=mydata_files,
# zipfile = None
)

编辑: 对于任何感兴趣的人 - 我在 a chat conversation 中更详细地描述了我是如何隔离和发现这个错误的。