Virtualenv: 在子进程下以错误的shebang安装的脚本

创建于 2016-01-20  ·  27评论  ·  资料来源: pypa/virtualenv

在 Python 3.5.1 而不是 2.7.10 上,当我通过 virtualenv 中的子进程调用 pip 时,脚本是使用错误的 shebang 创建的,因此无法运行:

$ cat > invoke.py
import sys
import subprocess

subprocess.Popen(sys.argv[1:]).wait()
$ rm -Rf env
$ python -m virtualenv --version
14.0.0
$ python --version
Python 3.5.1
$ python -m virtualenv env
Using base prefix '/Library/Frameworks/Python.framework/Versions/3.5'
New python executable in /Users/jaraco/Dropbox/code/public/aspen/env/bin/python3
Also creating executable in /Users/jaraco/Dropbox/code/public/aspen/env/bin/python
Installing setuptools, pip, wheel...done.
$ python invoke.py env/bin/pip install pytest
Collecting pytest
  Using cached pytest-2.8.5-py2.py3-none-any.whl
Collecting py>=1.4.29 (from pytest)
  Using cached py-1.4.31-py2.py3-none-any.whl
Installing collected packages: py, pytest
Successfully installed py-1.4.31 pytest-2.8.5
$ head -n 1 env/bin/py.test
#!/Library/Frameworks/Python.framework/Versions/3.5/bin/python3

人们会期望 py.test 脚本中有一个带有 'env' 的 shebang。

改用 pyvenv 不受此问题的影响。 在启动子进程之前删除__PYVENV_LAUNCHER__环境变量可以解决这个问题,如这里

这种行为是预期的吗? 如果是这样,删除环境变量是否适合父进程解决此问题?

最有用的评论

我只是被这个确切的错误咬了一口。 在我看来,这是一个相当严重的错误,主要是因为它非常令人困惑,并且很难弄清楚它何时发生。

所有27条评论

$ env/bin/pip uninstall pytest
$ env/bin/pip install pytest
$ head -n 1 env/bin/py.test

给出相同或不同的结果?

和同样的问题

$ env/bin/python -m pip install pytest

?

可能在https://github.com/pypa/virtualenv/issues/322 / https://github.com/pypa/virtualenv/pull/541上以某种方式回归

在这种情况下,你能测试一下

subprocess.Popen(sys.argv[1:], env={}).wait()

有没有帮助?

嗯,pip / distlib 中有代码可以解决这个问题,但它在一年前被注释掉了-

https://github.com/pypa/pip/blob/7cea748d0fb2e8980c2676304607426585550b41/pip/_vendor/distlib/util.py#L157 -L165

给出相同或不同的结果?

在这两种情况下,直接调用 pip 都会导致正确的shebang:

$ env/bin/pip install pytest
Collecting pytest
  Using cached pytest-2.8.5-py2.py3-none-any.whl
Requirement already satisfied (use --upgrade to upgrade): py>=1.4.29 in ./env/lib/python3.5/site-packages (from pytest)
Installing collected packages: pytest
Successfully installed pytest-2.8.5
$ head -n 1 env/bin/py.test
#!/Users/jaraco/Dropbox/code/public/aspen/env/bin/python3
$ env/bin/pip uninstall -y pytest
Uninstalling pytest-2.8.5:
  Successfully uninstalled pytest-2.8.5
$ env/bin/python -m pip install pytest
Collecting pytest
  Using cached pytest-2.8.5-py2.py3-none-any.whl
Requirement already satisfied (use --upgrade to upgrade): py>=1.4.29 in ./env/lib/python3.5/site-packages (from pytest)
Installing collected packages: pytest
Successfully installed pytest-2.8.5
$ head -n 1 env/bin/py.test
#!/Users/jaraco/Dropbox/code/public/aspen/env/bin/python

此外,使用空 env 调用也有效:

$ cat invoke.py 
import sys
import os
import subprocess

env = dict(os.environ)
env.pop('__PYVENV_LAUNCHER__')
env = {}

subprocess.Popen(sys.argv[1:], env=env).wait()
$ python -m virtualenv env
Using base prefix '/Library/Frameworks/Python.framework/Versions/3.5'
New python executable in /Users/jaraco/Dropbox/code/public/aspen/env/bin/python3
Also creating executable in /Users/jaraco/Dropbox/code/public/aspen/env/bin/python
Installing setuptools, pip, wheel...done.
$ python invoke.py env/bin/pip install pytest
Collecting pytest
  Using cached pytest-2.8.5-py2.py3-none-any.whl
Collecting py>=1.4.29 (from pytest)
  Using cached py-1.4.31-py2.py3-none-any.whl
Installing collected packages: py, pytest
Successfully installed py-1.4.31 pytest-2.8.5
$ head -n 1 env/bin/py.test
#!/Users/jaraco/Dropbox/code/public/aspen/env/bin/python3

根据提交,该解决方法被评论为包含在 pip 6.0 中发布的 distlib 0.2.0。

该提交引用了pip 2031

另外,我已经从 python.org Mac 安装程序安装了 Python,而不是通过自制软件或其他方法。

此提交中的代码

我想这是“预期的”,因为您最初的$ python正在设置__VENV_LAUNCHER__并随后愚弄其他脚本/可执行文件。 不幸的是,我没有 OS X 来调试“愚弄”过程究竟是如何进行的。

也可能与#620 中报告的问题类似/相同

@jaraco可能值得在 virtualenv 中修改pip以取消对提到的 distlib 行的注释,看看这是否可以解决 pip 选择错误的 Python 来编写的行为

我将那个 venv 检测代码追溯到这个 commit ,不幸的是它没有阐明这个想法的起源。

我找到了这个解释

我的倾向与罗纳德在这里建议的相似。

@vsajip你能给点建议吗?

一些想法:

  • __PYVENV_LAUNCHER__是因为对于早期版本的 Python(< 3.3,根据 Ronald)但不是对于更高版本,OS X 上的 Python 的site.py需要区分存根启动器和框架可执行文件的位置。 根据 Ned Deily 的评论sys.executable现在指向存根启动器,这就是distlib停止使用__PYVENV_LAUNCHER__ 。 它现在唯一的用法是在site.py - 如果可用,在 OS X 上,它用于定位pyvenv.cfg文件。
  • 可能值得检查一下在例如 Python 3.4 中发生了什么,看看是否有某种回归,或者是否所有 Python >= 3.3 都具有相同的行为。
  • 我的猜测是某处某处正在执行os.path.realpath()调用而不应该调用。 它看起来不像是distlib那样做。 如果(根据 Ned 的评论) sys.executable和 env var 总是相同的,那么它应该无关紧要; 但如果事情做了realpathsys.executable ,并通过结果来执行的Python,那么这将导致意想不到的家当(因为sys.executable然后将被解除引用的路径)。

问题的(另一个)简单演示,Homebrew 在https://github.com/Homebrew/homebrew-core/pull/8129 中重新发现:

$ virtualenv -p python3 test
$ test/bin/python3 -c 'import sys; print(sys.executable)'
/Users/tim/test/bin/python3

$ /usr/local/bin/python3 -c 'import subprocess; subprocess.call(["/Users/tim/test/bin/python3", "-c", "import sys; print(sys.executable)"])'
/usr/local/bin/python3

$ /usr/local/bin/python3 -c 'import subprocess, os; del os.environ["__PYVENV_LAUNCHER__"]; subprocess.call(["/Users/tim/test/bin/python3", "-c", "import sys; print(sys.executable)"])'
/Users/tim/test/bin/python3

我只是被这个确切的错误咬了一口。 在我看来,这是一个相当严重的错误,主要是因为它非常令人困惑,并且很难弄清楚它何时发生。

我被我认为是这个问题更严重的表现所打击。

我已经开始使用 xonsh 作为我的日常外壳。

但是当我尝试在 xonsh 下使用 virtualenv 时,它无法使用:

~ $ cd ~/draft
draft $ virtualenv --version
16.0.0
draft $ virtualenv .env
Using base prefix '/Library/Frameworks/Python.framework/Versions/3.7'
New python executable in /Users/jaraco/draft/.env/bin/python3
Also creating executable in /Users/jaraco/draft/.env/bin/python
Installing setuptools, pip, wheel...done.
draft $ .env/bin/pip install paver
Collecting paver
  Using cached https://files.pythonhosted.org/packages/98/1e/37ba8a75bd77ea56a75ef5ae66fe657b79205bbc36556c9456cd641782a4/Paver-1.3.4-py2.py3-none-any.whl
Collecting six (from paver)
  Using cached https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl
Installing collected packages: six, paver
Successfully installed paver-1.3.4 six-1.11.0
  The script paver is installed in '/Users/jaraco/draft/.env/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
draft $ .env/bin/paver --help
Traceback (most recent call last):
  File ".env/bin/paver", line 7, in <module>
    from paver.tasks import main
ModuleNotFoundError: No module named 'paver'
draft $ head -n 1 .env/bin/paver
#!/Library/Frameworks/Python.framework/Versions/3.7/bin/python3

所有已安装的脚本似乎都在获取系统前缀而不是 virtualenv 前缀。

这可能源于 xonsh 在 Python 3.7(系统前缀)下运行的事实,因此在 env 中有这个:

draft $ env | grep LAUNCHER
__PYVENV_LAUNCHER__=/Library/Frameworks/Python.framework/Versions/3.7/bin/python3

如果我改为启动 zsh 并运行相同的命令,或者如果我首先调用del $__PYVENV_LAUNCHER__ ,paver 会按预期运行并且 shebang 行指向 virtualenv。

xonsh 是否应该在启动时清除该环境变量?

https://github.com/python/cpython/pull/9516有一个可能的错误修复

此问题已自动标记为过时,因为它最近没有活动。 如果没有进一步的活动发生,它将被关闭。 如果您想让它保持打开状态,只需添加评论。 感谢你的贡献。

我仍然在使用 Python 3.7.6(通过 Homebrew 安装)的 Mojave 上看到相同的行为。

你可以检查重写分支吗?

@gaborbernat尝试构建rewrite分支(fbdd782257d8eace7f5440a2b665f2ddb72e9db6)给我一个来自python3 setup.py build的构建错误。

...src/virtualenv/__init__.py", line 3, in <module>
    from .version import __version__
ModuleNotFoundError: No module named 'virtualenv.version'

这看起来与这个问题无关。

尝试构建重写分支 (fbdd782) 给我一个来自 python3 setup.py build 的构建错误。

您应该使用 pip 进行构建 - pip wheel . - 因为 rewrite 分支使用pyproject.toml来定义构建过程。

你得到的错误是 setuptools 说我不支持构建 python 库的新标准方法 (PEP-517/518)。 这充其量只是一个 setuptools 错误,但正如@pfmoore指出的那样,您应该使用 pip 构建一个轮子,或者甚至更好地将其指向 virtualenv 文件夹以安装它(它会自动构建一个轮子并一次性安装它)。

测试用例:

#!/bin/bash
rm -rf venv-test
virtualenv --python python3 venv-test
python3 -c 'import subprocess; subprocess.check_call(["./venv-test/bin/pip3", "install", "markdown"])'
shebang=$(head -1 venv-test/bin/markdown_py)
expected_shebang="#!$(pwd)/venv-test/bin/python"
if [ "$shebang" == "$expected_shebang" ]; then
    echo "PASSED"
else
    echo "FAILED: \"$shebang\" != \"$expected_shebang\""
    exit 1
fi

这在 python3+virtualenv 16.7.9 下失败,并在 python3+virtualenv-16.7.10.dev11+gfbdd782(即rewrite分支)下通过。

如上所述在重写中修复。

此页面是否有帮助?
0 / 5 - 0 等级