Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Problem trying to freeze an executable which imports distutils modules (directly or indirectly)

cxFreeze and distutils

Problem

When trying to freeze a script which imports any distutils module (for example, distutils.dist), the script fails at runtime with an exception like this:

>   from distutils import dist, sysconfig
E   ImportError: cannot import name dist

(In my case, the error was raised by matplotlib, which tries to import distutils.dist).

The problem is easy to reproduce:

$ virtualenv myenv
$ source myenv/bin/activate
$ pip install cx_freeze
# contents of setup.py
from cx_Freeze import setup, Executable

build_exe_options = {}

setup(
    name="foo",
    version="0.1",
    description="My app",
    options={"build_exe": build_exe_options},
    executables=[Executable("foo_main.py", base=None)],
)
# contents of foo_main.py
import distutils.dist
$ python setup.py build

Running the produced executable should raise the same error (tested with cx-freeze 4.3.4).

This problem happens because distutils does not install all its modules into the virtualenv, it only creates a package with some magic code in the __init__ file to import its submodules dynamically. This is a problem to cx-freeze's static module analysis, which complains during the build command that it can't find distutils modules.

Workaround

The workaround used was to tell cx-freeze to exclude distutils and add the package from the original interpreter (not from the virtualenv) manually.

# contents of setup.py
from cx_Freeze import setup, Executable

import distutils
import opcode
import os

# opcode is not a virtualenv module, so we can use it to find the stdlib; this is the same
# trick used by distutils itself it installs itself into the virtualenv
distutils_path = os.path.join(os.path.dirname(opcode.__file__), 'distutils')
build_exe_options = {'include_files': [(distutils_path, 'distutils')], "excludes": ["distutils"]}

setup(
    name="foo",
    version="0.1",
    description="My app",
    options={"build_exe": build_exe_options},
    executables=[Executable("foo_main.py", base=None)],
)
@noplay

This comment has been minimized.

Copy link

commented Sep 11, 2015

THANKS A LOT !

@onsmribah

This comment has been minimized.

Copy link

commented Oct 7, 2015

Thanks, this was very helpful !

@frenchbeast

This comment has been minimized.

Copy link

commented Mar 16, 2017

Thanks!! That helped as well

@bobatsar

This comment has been minimized.

Copy link

commented Aug 30, 2017

Just wanted to note that with cx_freeze 5.0.2 I had to change the destination path to 'lib/distutils' on windows. But then it worked again.
For OSX it has to stay at "distutils"

@bobatsar

This comment has been minimized.

Copy link

commented Aug 31, 2017

Sorry, my last comment was wrong. It has to be "lib/distutils" on both platforms.

@tim-edelmann

This comment has been minimized.

Copy link

commented Nov 29, 2017

@nicoddemus && @bobatsar:

thanks for the hint. That helped me alot +1

for completeness' sake..
This is the setup.py I used:

import sys
from cx_Freeze import setup, Executable

#stuff to use distutils from outside the virtual environment
import distutils
import opcode
import os
distutils_path = os.path.join(os.path.dirname(opcode.file), 'distutils')
#end

base = None
if sys.platform == 'win32':
base = 'Win32GUI'

#with the tag 'include_files' we tell cx_freeze to include the distutils from outside our python environment
#with the tag 'exclude' we explicitly exclude the distutils inside our python environment
options = {
'build_exe': {
'includes': 'atexit',
'include_files': [(distutils_path, 'lib/distutils')],
'packages': ['pkg_resources._vendor.packaging', 'idna', 'dumbdbm', 'ftrack_action_handler'],
'excludes': ['distutils']
}
}

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

setup(name='ftrack-connect',
version='0.1',
description='ftrack-connect client to communicate with FTrack-Server',
options=options,
executables=executables
)

@Kamforka

This comment has been minimized.

Copy link

commented Jul 10, 2018

Thanks for this beautiful workaround! Saved me days of debugging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.