python3 instalar python ubuntu wxpython ubuntu-14.04 pyinstaller
aquí

instalar - WxPython: PyInstaller falla con ningún módulo llamado_core_



wxpython fedora (2)

Estoy convirtiendo mi aplicación wxpython (3.0.2.0) a los binarios que usan PyInstaller. Los binarios funcionan bien cuando se construyen y ejecutan en Ubuntu 12.04. Sin embargo, si construyo en Ubuntu 14.04, aparece el siguiente error. (La aplicación funciona cuando ejecuto el script python directamente, es decir, python my_application.py incluso en Ubuntu 14.04). ¿Alguna idea de lo que podría faltar al empaquetar la aplicación utilizando 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_**

El archivo de especificaciones de My PyInstaller tiene este aspecto:

... pyz = PYZ(a.pure) exe = EXE(pyz, a.scripts, exclude_binaries=True, name=''my_application'', debug=False, onefile = True, strip=None, upx=True, console=True ) coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=None, upx=True, name=''my_application'')


Si la versión de desarrollo de PyInstaller no es deseada por alguna razón, aquí hay una solución.

La instancia de BuiltinImporter , FrozenImporter y CExtensionImporter de PyInstaller.loader.pyi_importers se anexan a sys.meta_path . Y el método find_module del que se llaman en orden hasta que uno de ellos tenga éxito cuando se importa un módulo.

CExtensionImporter elige solo uno de los muchos sufijos para cargar la extensión C, fe wx._core_.i386-linux-gnu.so . Es por eso que no puede cargar la extensión C wx._core_.so .

Código Buggy;

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

Fijar;

1. Ganchos de tiempo de ejecución
Es posible solucionar el problema sin cambiar el código utilizando los ganchos de tiempo de ejecución. Esta es una solución rápida que soluciona los problemas de ''WxPython''.
Este enganche de tiempo de ejecución cambia algunos atributos privados de instancia de CExtensionImporter . Para usar este gancho, brinde --runtime-hook=wx-run-hook.py a pyinstaller .

wx-run-hook.py

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]

Este segundo gancho de tiempo de ejecución reemplaza completamente el objeto en sys.meta_path[-1] . Por lo tanto, debería funcionar en la mayoría de las situaciones. Usar como pyinstaller --runtime-hook=pyinstaller-run-hook.py application.py

pyinstaller-run-hook.py

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

2. Cambio de código

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)

Como imp.get_suffixes devuelve más de un sufijo para el tipo imp.C_EXTENSION y el derecho no se puede conocer de antemano hasta que se encuentre un módulo, los self._c_ext_tuples todos en una lista self._c_ext_tuples . El sufijo correcto se establece en self._suffix , que se usa junto con self._c_ext_tuple mediante el método load_module , del método find_module si se encuentra el módulo.

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


Fundamentalmente, el problema es con la versión de PyInstaller: debe estar en la versión de develop . Este problema se ha visto y está documentado en un problema de PyInstaller Github .

Para instalar la última versión y rectificar, en el símbolo del sistema escriba:

$ pip install git+https://github.com/pyinstaller/pyinstaller

Esto instala directamente la última versión de pyinstaller de github (esta rama en github .) Hasta hace poco, PyInstaller tenía una rama de python3 separada, pero esto se ha fusionado de nuevo en la rama de develop . Si necesita utilizar Python 3.x, necesitará esta rama - obtén esto agregando @develop al comando de pip install )

El método anterior depende de que hayas instalado git en tu sistema para obtener el código de pyinstaller (creo que es bastante probable para un desarrollador). Si no, puedes

  1. instale git usando apt-get install git (puede que necesite sudo eso)
  2. Descargue el archivo zip de pyinstaller-develop ( aquí ) e instálelo manualmente. Tenga en cuenta que según la wiki a partir de octubre de 2014, esto debería ser compatible con 2.7 y 3.x.

Personalmente, prefiero la opción 1 ya que evita todos los posibles problemas de compilación desde un árbol fuente comprimido.

Pruebas

Probé esto en Ubuntu 14.04, 64 bit, wxpython 3.0.2.0 con python 2.7.6, usando la sencilla aplicación "Hello world" de la página web de wxPython. El problema del OP se reprodujo exactamente antes de instalar la versión de desarrollo de pyinstaller. Después de instalar la versión de desarrollo, la aplicación se compiló correctamente y se ejecutó como un ejecutable.

Documentación sobre el uso de pip con git - https://pip.pypa.io/en/latest/reference/pip_install.html#git

No está claro a partir de su pregunta qué versiones de PyInstaller está utilizando en su instalación Ubuntu 12.04 contra la versión 14.04. Parece que la versión que tiene en 12.04 no presenta el mismo problema que la versión estándar instalada en 14.04.