Creating py2exe assemblies with Python modules containing third-party files
For brevity, we introduce the designation “non-standard” - by this term we will further mean such modules that contain files other than * .py. For example, it can be libraries (* .pyd), pictures, icons, etc.
The first problem is that almost all collectors of binary "distributions" of python-applications, such as py2exe , bbfreeze , cx_Freeze , and others, take only * .py files from such modules. The second problem arises with complex namespace modules, such as ETS - often the collector cannot correctly parse all their internal dependencies.
Specifically, in my case, all ETS modules (mayavi, chaco, etc.) turned out to be stumbling blocksm2crypto , vtk , h5py , matplotlib and several others (in general, as it turned out, there are a lot of such modules).
I tried to test different collectors and at first settled on cx_Freeze, tk. he is the only one who knows how to more or less correctly import ETS “out of the box”. However, it turned out to be insufficient: he could not cope with other non-standard modules, as well as for a number of other reasons (for example, I could not hide the console window, put a custom icon, etc.). Of course, there is a mechanism for “recipes” (not documented at all), which even works, for example, for matplotlib, but I wanted a more universal and simple solution than writing a similar recipe for each module.
In the end, I settled on py2exe, because he managed to solve all of the above problems. Since it took quite a considerable amount of time, I want to share with you - maybe someone will need it too.
While I was still dealing with cx_Freeze, there was an obvious idea - to copy the contents of the module into the distribution folder. Specifically for cx_Freeze, I tried to do this by copying both to the folder itself, and adding the necessary modules to library.zip, into which it collects all the dependent modules, however, all this was unsuccessful.
When I started to understand py2exe, it turned out that with a certain combination of parameters, in which the assembly is created as a folder (compressed: 0, bundle_files: 3), this trick starts to work. It seems that py2exe in this case adds the current application folder to the PYTHONPATH of the built-in interpreter, after which everything copied to it is successfully imported.
Copying a module is most easily done through the copy_tree function from the distutils.dir_util package, and getting the module path is via the module .__ path__ variable.
The following is a working example for ETS, vtk, m5crypto, and h5py:
I will be glad to hear your options for solving this problem, perhaps there is a more elegant method.
PS
I heard that some were able to achieve the same thing when all the modules are available as egg-files, by some py2exe customization.
The first problem is that almost all collectors of binary "distributions" of python-applications, such as py2exe , bbfreeze , cx_Freeze , and others, take only * .py files from such modules. The second problem arises with complex namespace modules, such as ETS - often the collector cannot correctly parse all their internal dependencies.
Specifically, in my case, all ETS modules (mayavi, chaco, etc.) turned out to be stumbling blocksm2crypto , vtk , h5py , matplotlib and several others (in general, as it turned out, there are a lot of such modules).
I tried to test different collectors and at first settled on cx_Freeze, tk. he is the only one who knows how to more or less correctly import ETS “out of the box”. However, it turned out to be insufficient: he could not cope with other non-standard modules, as well as for a number of other reasons (for example, I could not hide the console window, put a custom icon, etc.). Of course, there is a mechanism for “recipes” (not documented at all), which even works, for example, for matplotlib, but I wanted a more universal and simple solution than writing a similar recipe for each module.
In the end, I settled on py2exe, because he managed to solve all of the above problems. Since it took quite a considerable amount of time, I want to share with you - maybe someone will need it too.
While I was still dealing with cx_Freeze, there was an obvious idea - to copy the contents of the module into the distribution folder. Specifically for cx_Freeze, I tried to do this by copying both to the folder itself, and adding the necessary modules to library.zip, into which it collects all the dependent modules, however, all this was unsuccessful.
When I started to understand py2exe, it turned out that with a certain combination of parameters, in which the assembly is created as a folder (compressed: 0, bundle_files: 3), this trick starts to work. It seems that py2exe in this case adds the current application folder to the PYTHONPATH of the built-in interpreter, after which everything copied to it is successfully imported.
Copying a module is most easily done through the copy_tree function from the distutils.dir_util package, and getting the module path is via the module .__ path__ variable.
The following is a working example for ETS, vtk, m5crypto, and h5py:
Copy Source | Copy HTML- from distutils.core import setup
- from distutils.dir_util import copy_tree
- from py2exe.build_exe import py2exe
- import glob
- import os
- import zlib
- import shutil
- import time
- import shutil
- import enthought.tvtk
- import enthought.mayavi
- import vtk
- import sys
- import M2Crypto
- import h5py
-
- ##############
-
- distDit = "my_app_builded_folder"
-
- ##############
-
- class Target(object):
- """ A simple class that holds information on our executable file. """
- def __init__(self, **kw):
- """ Default class constructor. Update as you need. """
- self.__dict__.update(kw)
-
- # ETS and VTK
-
- tvtkPath = os.path.join( os.path.join(distDir, "enthought"), "tvtk")
- copy_tree(enthought.tvtk.__path__[ 0], tvtkPath )
-
- mayaviPath = os.path.join( os.path.join(distDir, "enthought"), "mayavi")
- copy_tree(enthought.mayavi.__path__[ 0], mayaviPath )
-
- vtkPath = os.path.join( distDir, "vtk")
- copy_tree(vtk.__path__[ 0], vtkPath )
-
- # M2Crypto
-
- m2CryptoPath = os.path.join( distDir, "M2Crypto")
- copy_tree(M2Crypto.__path__[ 0], m2CryptoPath )
-
- # h5Py
-
- h5pyPath = os.path.join( distDir, "h5py")
- copy_tree(h5py.__path__[ 0], h5pyPath )
-
-
- includes = ['enthought', 'vtk', 'M2Crypto', 'h5py']
- excludes = ['_gtkagg', '_tkagg', 'bsddb', 'curses', 'email', 'pywin.debugger',
- 'pywin.debugger.dbgcon', 'pywin.dialogs', 'tcl',
- 'Tkconstants', 'Tkinter']
-
- packages = ['enthought', 'vtk']
-
- dll_excludes = ['libgdk-win32-2.0-0.dll', 'libgobject-2.0-0.dll', 'tcl84.dll',
- 'tk84.dll']
-
- data_files = []
- icon_resources = []
- bitmap_resources = []
- other_resources = []
-
-
- MyApp_Target = Target(
- # what to build
- script = "run.py",
- icon_resources = icon_resources,
- bitmap_resources = bitmap_resources,
- other_resources = other_resources,
- dest_base = "my_app",
- version = "0.1.0",
- company_name = "My Company",
- copyright = "My Company",
- name = "My App",
-
- )
-
-
- setup(
- data_files = data_files,
- options = {"py2exe": {"compressed": 0,
- "optimize": 1,
- "includes": includes,
- "excludes": excludes,
- "packages": packages,
- "dll_excludes": dll_excludes,
- "bundle_files": 3,
- "dist_dir": distDir,
- "xref": False,
- "skip_archive": True,
- "ascii": False,
- "custom_boot_script": '',
- }
- },
-
- zipfile = r'library.zip',
- console = [],
- windows = [MyApp_Target],
- service = [],
- com_server = [],
- ctypes_com_server = []
- )
-
I will be glad to hear your options for solving this problem, perhaps there is a more elegant method.
PS
I heard that some were able to achieve the same thing when all the modules are available as egg-files, by some py2exe customization.