WxPython:PyInstaller失败,没有名为_core_的模块

时间:2015-08-22 10:05:36

标签: python ubuntu wxpython ubuntu-14.04 pyinstaller

我正在使用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_**

2 个答案:

答案 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代码(这些天很可能是开发人员,我猜)。如果没有,你可以

  1. 使用apt-get install git安装git(您可能需要sudo那个)
  2. 下载pyinstaller-develop zip文件(here)并手动安装。请注意wiki as of Oct 2014,这应支持2.7和3.x。
  3. 个人 - 我更喜欢选项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}},FrozenImporterCExtensionImporter的实例会附加到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