我正在使用PyInstaller将我的wxpython(3.0.2.0)应用程序转换为二进制文件。在Ubuntu 12.04上构建和执行时,二进制文件工作正常。但是,如果我在Ubuntu 14.04上构建,我会收到以下错误。 (该应用程序在我直接启动python脚本时工作,即即使在Ubuntu 14.04中也是python my_application.py)。知道在使用PyInstaller打包应用程序时可能会遗漏什么?
parseInt()
我的PyInstaller规范文件如下所示:
$ ./my_application
Traceback (most recent call last):
File "<string>", line 22, in <module>
File "/usr/local/lib/python2.7/dist-packages/PyInstaller/loader/pyi_importers.py", line 270, in load_module
exec(bytecode, module.__dict__)
File "/local/workspace/my_application/out00-PYZ.pyz/wx", line 45, in <module>
File "/usr/local/lib/python2.7/dist-packages/PyInstaller/loader/pyi_importers.py", line 270, in load_module
exec(bytecode, module.__dict__)
File "/local/workspace/my_application/out00-PYZ.pyz/wx._core", line 4, in <module>
**ImportError: No module named _core_**
答案 0 :(得分:8)
根本问题在于PyInstaller版本 - 您需要使用develop
版本。已经看到此问题,并在PyInstaller Github issue上记录。
要安装最新版本并进行纠正 - 在命令提示符下键入:
$ pip install git+https://github.com/pyinstaller/pyinstaller
这直接从github安装了最新版本的pyinstaller(这个branch on github。直到最近,PyInstaller还有一个单独的python3
分支,但这已经是merged back into the develop
branch。如果你需要使用Python 3.x,您需要this branch - 通过将@develop
附加到pip install
命令来获取此内容
上面的方法依赖于你在你的系统上安装git
来获取pyinstaller代码(这些天很可能是开发人员,我猜)。如果没有,你可以
apt-get install git
安装git(您可能需要sudo
那个)个人 - 我更喜欢选项1,因为你自己避免了从压缩源树构建的所有潜在问题。
我在wbuPython网页上使用简单的"Hello world" app在Ubuntu 14.04,64 bit,wxpython 3.0.2.0和python 2.7.6上进行了测试。在安装pyinstaller开发版之前,OP的问题正好重现了。安装开发版本后,应用程序正确构建并作为可执行文件运行。
与git一起使用pip的文档 - https://pip.pypa.io/en/latest/reference/pip_install.html#git
从您的问题中不清楚您在Ubuntu 12.04安装版和14.04版上使用的PyInstaller版本。您在12.04上看到的版本似乎与14.04上安装的标准版本没有相同的问题。
答案 1 :(得分:4)
如果由于某种原因不希望PyInstaller开发版本,这里有一些修复。
来自BuiltinImporter
的{{1}},FrozenImporter
和CExtensionImporter
的实例会附加到PyInstaller.loader.pyi_importers
。并且按顺序调用sys.meta_path
方法,直到其中一个方法在导入模块时成功。
find_module
只选择加载C扩展名的许多后缀中的一个,例如: CExtensionImporter
。这就是为什么它无法加载C扩展wx._core_.i386-linux-gnu.so
。
越野车代码;
wx._core_.so
修复;
<强> 1。运行时挂钩
使用运行时挂钩可以在不更改代码的情况下修复问题。这是一个解决'WxPython'问题的快速修复方法
此运行时挂钩会更改class CExtensionImporter(object):
def __init__(self):
# Find the platform specific suffix. On Windows it is .pyd, on Linux/Unix .so.
for ext, mode, typ in imp.get_suffixes():
if typ == imp.C_EXTENSION:
self._c_ext_tuple = (ext, mode, typ)
self._suffix = ext # Just string like .pyd or .so
break
实例的某些私有属性。要使用此挂钩,请将CExtensionImporter
提交给--runtime-hook=wx-run-hook.py
。
wx-run-hook.py
pyinstaller
第二个运行时挂钩完全替换了import sys
import imp
sys.meta_path[-1]._c_ext_tuple = imp.get_suffixes()[1]
sys.meta_path[-1]._suffix = sys.meta_path[-1]._c_ext_tuple[0]
中的对象。所以它应该适用于大多数情况。用作sys.meta_path[-1]
。
pyinstaller-run-hook.py
pyinstaller --runtime-hook=pyinstaller-run-hook.py application.py
<强> 2。代码更改
import sys
import imp
from PyInstaller.loader import pyi_os_path
class CExtensionImporter(object):
"""
PEP-302 hook for sys.meta_path to load Python C extension modules.
C extension modules are present on the sys.prefix as filenames:
full.module.name.pyd
full.module.name.so
"""
def __init__(self):
# TODO cache directory content for faster module lookup without file system access.
# Find the platform specific suffix. On Windows it is .pyd, on Linux/Unix .so.
self._c_ext_tuples = [(ext, mode, typ) for ext, mode, typ in imp.get_suffixes() if typ == imp.C_EXTENSION]
# Create hashmap of directory content for better performance.
files = pyi_os_path.os_listdir(sys.prefix)
self._file_cache = set(files)
def find_module(self, fullname, path=None):
imp.acquire_lock()
module_loader = None # None means - no module found by this importer.
# Look in the file list of sys.prefix path (alias PYTHONHOME).
for ext, mode, typ in self._c_ext_tuples:
if fullname + ext in self._file_cache:
module_loader = self
self._suffix = ext
self._c_ext_tuple = (ext, mode, typ)
break
imp.release_lock()
return module_loader
def load_module(self, fullname, path=None):
imp.acquire_lock()
try:
# PEP302 If there is an existing module object named 'fullname'
# in sys.modules, the loader must use that existing module.
module = sys.modules.get(fullname)
if module is None:
filename = pyi_os_path.os_path_join(sys.prefix, fullname + self._suffix)
fp = open(filename, 'rb')
module = imp.load_module(fullname, fp, filename, self._c_ext_tuple)
# Set __file__ attribute.
if hasattr(module, '__setattr__'):
module.__file__ = filename
else:
# Some modules (eg: Python for .NET) have no __setattr__
# and dict entry have to be set.
module.__dict__['__file__'] = filename
except Exception:
# Remove 'fullname' from sys.modules if it was appended there.
if fullname in sys.modules:
sys.modules.pop(fullname)
# Release the interpreter's import lock.
imp.release_lock()
raise # Raise the same exception again.
# Release the interpreter's import lock.
imp.release_lock()
return module
### Optional Extensions to the PEP302 Importer Protocol
def is_package(self, fullname):
"""
Return always False since C extension modules are never packages.
"""
return False
def get_code(self, fullname):
"""
Return None for a C extension module.
"""
if fullname + self._suffix in self._file_cache:
return None
else:
# ImportError should be raised if module not found.
raise ImportError('No module named ' + fullname)
def get_source(self, fullname):
"""
Return None for a C extension module.
"""
if fullname + self._suffix in self._file_cache:
return None
else:
# ImportError should be raised if module not found.
raise ImportError('No module named ' + fullname)
def get_data(self, path):
"""
This returns the data as a string, or raise IOError if the "file"
wasn't found. The data is always returned as if "binary" mode was used.
The 'path' argument is a path that can be constructed by munging
module.__file__ (or pkg.__path__ items)
"""
# Since __file__ attribute works properly just try to open and read it.
fp = open(path, 'rb')
content = fp.read()
fp.close()
return content
# TODO Do we really need to implement this method?
def get_filename(self, fullname):
"""
This method should return the value that __file__ would be set to
if the named module was loaded. If the module is not found, then
ImportError should be raised.
"""
if fullname + self._suffix in self._file_cache:
return pyi_os_path.os_path_join(sys.prefix, fullname + self._suffix)
else:
# ImportError should be raised if module not found.
raise ImportError('No module named ' + fullname)
#This may overwrite some other object
#sys.meta_path[-1] = CExtensionImporter()
#isinstance(object, CExtensionImporter)
#type(object) == CExtensioImporter
#the above two doesn't work here
#grab the index of instance of CExtensionImporter
for i, obj in enumerate(sys.meta_path):
if obj.__class__.__name__ == CExtensionImporter.__name__:
sys.meta_path[i] = CExtensionImporter()
break
因为class CExtensionImporter(object):
def __init__(self):
# Find the platform specific suffix. On Windows it is .pyd, on Linux/Unix .so.
self._c_ext_tuples = [(ext, mode, typ) for ext, mode, typ in imp.get_suffixes() if typ == imp.C_EXTENSION]
files = pyi_os_path.os_listdir(sys.prefix)
self._file_cache = set(files)
为类型imp.get_suffixes
返回多个后缀,并且在找到模块之前无法预先知道正确的后缀,所以我将所有这些后缀存储在列表{{1}中}。右后缀在imp.C_EXTENSION
中设置,如果找到模块,则self._c_ext_tuples
方法与self._suffix
方法一起使用self._c_ext_tuple
方法。
load_module