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

noplay commented Sep 11, 2015

THANKS A LOT !

@onsmribah

This comment has been minimized.

Copy link

onsmribah commented Oct 7, 2015

Thanks, this was very helpful !

@frenchbeast

This comment has been minimized.

Copy link

frenchbeast commented Mar 16, 2017

Thanks!! That helped as well

@bobatsar

This comment has been minimized.

Copy link

bobatsar 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

bobatsar 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

tim-edelmann 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

Kamforka commented Jul 10, 2018

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

@marcelotduarte

This comment has been minimized.

Copy link

marcelotduarte commented Sep 4, 2019

cx_Freeze 6 has been release. The workaround is no longer needed either.

@jeffrothkirch

This comment has been minimized.

Copy link

jeffrothkirch commented Sep 10, 2019

Confirming that I have cx_freexe 6, and I still needed this code! Thanks!

@NeroVanbiervliet

This comment has been minimized.

Copy link

NeroVanbiervliet commented Jan 21, 2020

Same here! (version 6.2)

@VictoryShima

This comment has been minimized.

Copy link

VictoryShima commented Feb 19, 2020

cx-Freeze@6.1 with python3.6 in a virtualenv still required the workaround.

@marcelotduarte

This comment has been minimized.

Copy link

marcelotduarte commented Feb 23, 2020

@jeffrothkirch @NeroVanbiervliet @VictoryShima
You can take a look in my comment at: anthony-tuininga/cx_Freeze#443
And suggest any modification to sample to test it. I'll try to solve this for 6.2 release.

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.