cx_freeze 在 vi​​rtualenv 中缺少 distutils 模块

Posted

技术标签:

【中文标题】cx_freeze 在 vi​​rtualenv 中缺少 distutils 模块【英文标题】:cxfreeze missing distutils module inside virtualenv 【发布时间】:2013-01-09 21:49:24 【问题描述】:

从 python3.2 项目运行 cxfreeze 二进制文件时,出现以下运行时错误:

/project/dist/project/distutils/__init__.py:13: UserWarning: The virtualenv distutils package at %s appears to be in the same location as the system distutils?
Traceback (most recent call last):
  File "/home/chrish/.virtualenvs/project/lib/python3.2/distutils/__init__.py", line 19, in <module>
    import dist
ImportError: No module named dist

相应地,在 cxfreeze 输出的缺失模块部分中有几个 distutils 条目:

? dist imported from distutils
? distutils.ccompiler imported from numpy.distutils.ccompiler
? distutils.cmd imported from setuptools.dist
? distutils.command.build_ext imported from distutils
? distutils.core imported from numpy.distutils.core
...

我尝试强制将 distutils 作为一个模块包含在内,方法是将其导入我的主要 python 文件中并将其添加到 cxfreeze setup.py 为:

options = "build_exe": "packages" : ["distutils"] ,

这两种方法都不起作用。似乎我以某种方式破坏了 virtualenv [因为 distutils 似乎很重要,并且关于 distutils 位置的警告],重复使用干净的 virtualenv 复制了问题。

值得注意的是,我通过运行$VIRTUAL_ENV/build/cx-freeze/setup.py install 安装了 cx-freeze,因为它没有在 pip 中干净地安装。

【问题讨论】:

virtualenv 中的 distutils 不是常规的 distutils,它正在做一些奇怪的事情来使 virtualenv 工作。如果你在 virtualenv 之外冻结它会起作用吗? 为此我需要 python 3。有没有办法以 3 为目标释放而不改变 python 的替代品(我相信这会破坏 ubuntu 中不为人知的事情)。 在 Ubuntu 中,Python 3 是 python3。无需更改任何内容,您可以将它与 Python 2 一起使用。 但我需要安装 cx_freeze 的 python3 版本(如果我希望冻结的二进制文件与 python3 兼容)。而且我无法安装它,因为貌似python3 cx_freeze 需要系统python(即env python 是python3)。 如果你为 cx_Freeze 执行python3 setup.py install,它应该为 Python 3 安装它。 【参考方案1】:

找到另一种解决方法,使您在冻结时仍能使用 virtualenv。

解决方法是排除 distutils 并手动从原始解释器(而不是 virtualenv)添加包。

# 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)],
)

感谢 Bruno Oliveira 在 github 上提供的答案 完整答案:https://gist.github.com/nicoddemus/ca0acd93a20acbc42d1d

【讨论】:

我需要为 lib/distutils_path = os.path.join(os.path.dirname(opcode.__file__), 'lib/distutils') 加上前缀【参考方案2】:

总结我的cmets:

virtualenv 中distutils 的副本正在做一些奇怪的事情,让 cx_Freeze 感到困惑。简单的解决方法是在 virtualenv 之外冻结,以便它使用 distutils 的系统副本。

在 Ubuntu 上,Python 2 和 3 愉快地共存:只需使用 python3 对 Python 3 做任何事情。例如在 Python 3 下安装 cx_Freeze:python3 setup.py install

【讨论】:

您没有其他解决方案吗?我正在使用 Python 2.7.5 运行 OSX 和 venv,并且真的不想污染我的系统 python。 您可以尝试使用 conda envs 代替 virtualenvs。无论 distutils 做些什么来支持 virtualenvs,我敢打赌它不涉及 conda envs。 找到了解决问题的要点:gist.github.com/nicoddemus/ca0acd93a20acbc42d1d【参考方案3】:

我找到了一种解决方法,解决了从virtualenv 冻结时出现的distutils 问题,这可能对其他人有所帮助。

首先确保从您的构建中排除 distutils

build_exe_options = 'excludes': ['distutils']

第二次在你的setup.py文件中声明这个函数:

def copy_distutils_to_build_dir(build_dir):
    # the code below was obtained from the distutils.py file created by
    # virtualenv
    import opcode
    dirname = os.path.dirname
    distutils_path = os.path.join(os.path.dirname(opcode.__file__), 'distutils')
    target_dir = os.path.join(build_dir, 'distutils')
    if os.path.isdir(target_dir):
        shutil.rmtree(target_dir)
    shutil.copytree(distutils_path, target_dir)

最后,在你的setup.py 中调用setup() 之后调用函数:

setup(...)
copy_distutils_to_build_dir(join('build', 'exe.win32-3.4'))

这会将整个 distutils 包从原始解释器复制到包含冻结的可执行文件的目录。

Hackish 和丑陋,但它的工作原理。不过,我很想听听改进的想法。

【讨论】:

【参考方案4】:

一个问题是你的 venv 中的 distutils/__init__.py 试图做一个隐式的相对导入(import dist 而不是正确的 from distutils import dist),所以这是首先要解决的问题。 venv 是如何创建的? distutils/__init__.py 来自哪里?

【讨论】:

venv 是用mkvirtualenv -p python3.2 name 创建的。 import distutils 更改为 from distutils import dist 在该导入行上仍然失败。 distutils/__init__.py 来自$VIRTUAL_ENV/lib/python3.2/distutils/__init__.py【参考方案5】:

现在已经有一段时间了,但我遇到了同样的问题。我能够通过将 distutils 包从本地 Python 库复制到 virtualenv 库来解决它。我还不知道副作用。一切似乎都运行良好。

【讨论】:

以上是关于cx_freeze 在 vi​​rtualenv 中缺少 distutils 模块的主要内容,如果未能解决你的问题,请参考以下文章

从 crontab 在 vi​​rtualenv 中运行 python 方法

未使用 pip 在 vi​​rtualenv 中安装 Python 包

pip install mod_wsgi 在 vi​​rtualenv 中失败

如何在 Python 中安装 gcp?

virtualenvwrapper在macos 10.13.6中的使用

使用 pip 安装软件包时缺少头文件