Skip to content

Instantly share code, notes, and snippets.

@nicoddemus
Created May 19, 2015 17:45
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save nicoddemus/ca0acd93a20acbc42d1d to your computer and use it in GitHub Desktop.
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)],
)
@Kamforka
Copy link

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

@marcelotduarte
Copy link

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

@jeffrothkirch
Copy link

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

@NeroVanbiervliet
Copy link

NeroVanbiervliet commented Jan 21, 2020

Same here! (version 6.2)

@VictorShima
Copy link

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

@marcelotduarte
Copy link

marcelotduarte commented Feb 23, 2020

@jeffrothkirch @NeroVanbiervliet @VictoryShima
You can take a look in my comment at: marcelotduarte/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