Search code examples
pythonpandasexecutablecx-freeze

Error when building executable from Python3.6 script importing pandas using cx_Freeze


I'm trying to create a Python 3.6 executable using cx_Freeze which includes pandas and numpy. I'm using Python 3.6.5 and a virtual env created using virtualenvwrapper. I'm developing on Windows 10. cx_Freeze version is 5.1. Pandas versions is 0.23.4.

My setup.py looks like this:

import os
from cx_Freeze import setup, Executable

PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
os.environ['TCL_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tcl8.6')
os.environ['TK_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tk8.6')

executables = [Executable("main.py", base=base)]

packages = ["idna", "os", "numpy","importlib", "pandas"]
options = {
    'build_exe': {
        'packages':packages,
        'include_files':[
            os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll'),
            os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'),
            os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'sqlite3.dll'),
         ],
    },
}

setup(
    name = "MyScript",
    options = options,
    version = "0.1",
    description = 'Placeholder desc',
    executables = executables
)

Note that I manually set the env variables because they were not being found during the build process and I had to manually move the DLLs to the virtualenv folder.

And the script looks like this:

import numpy as np
import pandas as pd
import tkinter as tk
root = tk.Tk()
root.mainloop()

If I comment out the import pandas as pd, everything works fine. If I add the pandas import I get the following error:

C:\path\to\project\build\exe.win-amd64-3.6>MyScript.exe
Traceback (most recent call last):
  File "C:\path\to\Envs\MyEnv\lib\site-packages\cx_Freeze\initscripts\__startup__.py", line 14, in run
    module.run()
  File "C:\path\to\Envs\MyEnv\lib\site-packages\cx_Freeze\initscripts\Console.py", line 26, in run
    exec(code, m.__dict__)
  File "MyScript.py", line 2, in <module>
  File "C:\path\to\Envs\MyEnv\lib\site-packages\pandas\__init__.py", line 23, in <module>
    from pandas.compat.numpy import *
  File "C:\path\to\Envs\MyEnv\lib\site-packages\pandas\compat\__init__.py", line 32, in <module>
    from distutils.version import LooseVersion
  File "C:\path\to\Envs\MyEnv\lib\distutils\__init__.py", line 17, in <module>
    real_distutils = imp.load_module("_virtualenv_distutils", None, distutils_path, ('', '', imp.PKG_DIRECTORY))
  File "C:\path\to\Envs\MyEnv\lib\imp.py", line 245, in load_module
    return load_package(name, filename)
  File "C:\path\to\Envs\MyEnv\lib\imp.py", line 217, in load_package
    return _load(spec)
  File "<frozen importlib._bootstrap>", line 683, in _load
AttributeError: 'NoneType' object has no attribute 'name'

I've seen a lot of forum and stack overflow questions online (actually I had to fix a lot of errors to get here) but I can't work out what cx_Freeze is missing.

I've uninstalled and reinstalled pandas, I uninstalled cx_Freeze and tried installing a previous version (install failed but that's a different SO question). I actually tried pyinstaller and py2exe but there were so many errors which were less verbose (so harder to fix) that I gave up on anything but cx_Freeze to create the executable.

Can anybody help?


Solution

  • In cx_Freeze version 5.1.1, the included modules are in a subdirectory lib of the build directory. The manually added DLLs apparently need to be moved there as well.

    You can do this with the following modification of your setup.py script:

    options = {
        'build_exe': {
            'packages': packages,
            'include_files': [
                 (os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll'), os.path.join('lib', 'tk86t.dll')),
                 (os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'), os.path.join('lib', 'tcl86t.dll')),
                 (os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'sqlite3.dll'), os.path.join('lib', 'sqlite3.dll'))
            ],
        },
    }
    

    I'm actually not sure whether sqlite3.dll also needs to be moved to lib.

    Your question is interesting in view of the fact that it seems to indicate an import conflict between pandas and tkinter. That's presumably why you get a different error message than reported in this question:
    Getting "ImportError: DLL load failed: The specified module could not be found" when using cx_Freeze even with tcl86t.dll and tk86t.dll added in

    EDIT: I manage to freeze and run the OP's example script main.py without errors using Python 3.6.5 on Windows 7 with the following configuration

    idna 2.7 (installed with pip)
    numpy 1.14.3+mkl (installed using Gohlke's binaries)
    pandas 0.23.4 (installed with pip)
    cx_Freeze 5.1.1 (installed with pip)

    I use the following setup.py script:

    import os
    import sys
    from cx_Freeze import setup, Executable
    
    PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
    os.environ['TCL_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tcl8.6')
    os.environ['TK_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tk8.6')
    
    base = None
    if sys.platform == "win32":
        base = "Win32GUI"
    
    executables = [Executable("main.py", base=base)]
    
    packages = ["idna", "os", "numpy", "importlib", "pandas"]
    options = {
        'build_exe': {
            'packages': packages,
            'include_files': [
                 (os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll'), os.path.join('lib', 'tk86t.dll')),
                 (os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'), os.path.join('lib', 'tcl86t.dll')),
                 (os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'sqlite3.dll'), os.path.join('lib', 'sqlite3.dll'))
            ],
        },
    }
    
    setup(
        name="MyScript",
        options=options,
        version="0.1",
        description='Placeholder desc',
        executables=executables
    )