当尝试使用pythonnet从脚本构建exe时,py2exe失败并显示“没有名为'clr'的模块”

时间:2015-05-12 10:31:35

标签: python .net python-3.4 py2exe python.net

我创建了一个使用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

错误发生在可执行构建时,似乎是通过在clr {"includes":["sip","clr"]}}setup.py部分py2exe中包含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' 来触发的。完整的追溯如下:

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

我也阅读/试过这些:

clr.pyd

引导我将Python.Runtime.dllmain.py移动到各个位置,包括C:\Python34\Lib\site-packagesC:\Python34\Lib\site-packages\py2exe(原来的位置)和py2exe

这些都没有奏效,我不知道下一步该尝试什么。我可以看到clr.pyd因某些原因无法找到Python.Runtime.dllmain.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,则构建 package com.example.bilal.aaa; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends ActionBarActivity { EditText t,t2; TextView t3; Button b1,b2,b3,b4,b6; @Override protected void onCreate(Bundle savedInstanceState) { // super.onCreate(savedInstanceState); // setContentView(R.layout.home); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); t = (EditText)findViewById(R.id.edittext); t2 = (EditText)findViewById(R.id.edittext2); t3 = (TextView)findViewById(R.id.edittext3); Button b1 = (Button) findViewById(R.id.button1); b1.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { create(v); } }); Button b6 = (Button) findViewById(R.id.button6); Button b2 = (Button) findViewById(R.id.button2); b2.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { create(v); } }); Button b3 = (Button) findViewById(R.id.button3); b3.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { create(v); } }); Button b4 = (Button) findViewById(R.id.button4); b4.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { create(v); } }); try{ b1.setOnClickListener((View.OnClickListener) this); b2.setOnClickListener((View.OnClickListener) this); b3.setOnClickListener((View.OnClickListener) this); b4.setOnClickListener((View.OnClickListener) this); b6.setOnClickListener((View.OnClickListener)this); } catch(Exception e){ Toast.makeText(getApplicationContext(), "Please Add the Number", Toast.LENGTH_LONG).show(); } } String a,b; Double A1,B1; Double result; int AA1,BB1; public void create(View v) { switch (v.getId()) { case R.id.button1: a = t.getText().toString(); b=t2.getText().toString(); A1=Double.parseDouble(a); B1=Double.parseDouble(b); if (a.equals(null)) { Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show(); // create(v); } else if (b.equals(null)) { Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show(); // create(v); } else if (A1.equals(null)) { Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show(); // create(v); } else if (B1.equals(null)) { Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show(); // create(v); } else { result=A1/B1; answer(v); } // Log.e("EXCEPTION", a); // Log.e("EXCEPTION",b);} // else{ // // // Toast.makeText(getBaseContext(), "value"+A1, Toast.LENGTH_LONG).show(); // Toast.makeText(getBaseContext(), "value"+B1, Toast.LENGTH_LONG).show(); // result=A1/B1; // answer(v); // Log.e("EXCEPTION","A is Null"); // } // if(a.equals(null)){ // Log.e("EXCEPTION",a); // Log.e("EXCEPTION",b); // Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show(); // create(v); // // // } // else{ // if(b.equals(null)){ // Log.e("EXCEPTION","A is Null"); // Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show(); // create(v); // // } // else { //// A1=Double.parseDouble(a); //// B1=Double.parseDouble(b); // // Toast.makeText(getBaseContext(), "value"+A1, Toast.LENGTH_LONG).show(); // Toast.makeText(getBaseContext(), "value"+B1, Toast.LENGTH_LONG).show(); // result=A1/B1; // answer(v); // // } // // } break; case R.id.button2: a = t.getText().toString(); b=t2.getText().toString(); A1=Double.parseDouble(a); B1=Double.parseDouble(b); if(a!=null){ if(b!=null){ result=A1*B1; answer(v); } else { Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show(); } } else { Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show(); } { Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show(); } break; case R.id.button3: a = t.getText().toString(); b=t2.getText().toString(); A1=Double.parseDouble(a); B1=Double.parseDouble(b); if(A1 != 0){ if(B1 != 0){ result=A1+B1; answer(v); } else { Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show(); } } else { Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show(); } break; case R.id.button4: a = t.getText().toString(); b=t2.getText().toString(); A1=Double.parseDouble(a); B1=Double.parseDouble(b); if(A1 != 0){ if(B1 != 0){ result=A1-B1; answer(v); } else { Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show(); } } else { Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show(); } break; default: Toast.makeText(this, "INVALID OPTION", Toast.LENGTH_SHORT).show(); break; } } public void clear(View v) { t.setText(""); t2.setText(""); } public void answer(View v) { String tmpstring = Double.toString(result); t3.setText(tmpstring); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } } ,但显然无法正常运行。

1 个答案:

答案 0 :(得分:4)

安装说明

作为参考,我使用py2exe执行了pip install py2exe的标准安装。这会将py2exe放入Lib\site-packages以进行python安装。接下来,我通过从Christoph Gohlke .whl下载unofficial Windows binaries page,然后使用pythonnet安装pip install path\to\pythonnet-2.0.0<version_numbers>.whl。这会将clr.pydPython.Runtime.dll放入Lib\site-packages进行python安装。 This question and answers还有更多信息。

问题

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

问题是该工具通过其hooks.py文件明确排除了模块clr。目前尚不清楚为什么。您可以看到line that does this exclusion here

解决方案

解决方法是从py2exe安装中的clr变量windows_excludes文件中删除单词hooks.py。假设一切都在标准位置 - 这意味着删除位于hooks.py的文件C:\Python34\Lib\site-packages\py2exe中的第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中详细介绍了我是如何隔离并发现此错误的。