Skip to content

Instantly share code, notes, and snippets.

@nicoddemus
Created May 19, 2015 17:45
Show Gist options
  • Save nicoddemus/ca0acd93a20acbc42d1d to your computer and use it in GitHub Desktop.
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)],
)
@VictorShima
Copy link

[email protected] 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