Virtualenv: Scripts instalados com conteúdo incorreto no subprocesso

Criado em 20 jan. 2016  ·  27Comentários  ·  Fonte: pypa/virtualenv

No Python 3.5.1, mas não no 2.7.10, quando eu invoco o pip por meio de um subprocesso em um virtualenv, os scripts são criados com o shebang errado e, portanto, falham ao executar:

$ 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

Seria de se esperar que o script py.test tivesse um shebang com 'env' nele.

Usar pyvenv em vez disso não está sujeito ao problema. Remover a variável de ambiente __PYVENV_LAUNCHER__ antes de iniciar o subprocesso resolve o problema, conforme relatado aqui . O problema foi observado com 13.1.2 e 14.0.0.

Este comportamento é esperado? Em caso afirmativo, remover a variável de ambiente é a coisa apropriada para um processo pai fazer para contornar o problema?

Comentários muito úteis

Eu fui mordido por este inseto exato. É um bug bastante grave na minha opinião, principalmente porque é extremamente confuso e difícil de descobrir quando ele atinge.

Todos 27 comentários

faz

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

Apresenta resultados iguais ou diferentes?

E a mesma pergunta com

$ env/bin/python -m pip install pytest

?

Possivelmente uma regressão de alguma forma em https://github.com/pypa/virtualenv/issues/322 / https://github.com/pypa/virtualenv/pull/541

Nesse caso, você poderia testar se

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

ajuda ou não?

Hmm, havia código em pip / distlib para talvez lidar com isso, mas foi comentado há um ano-

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

Apresenta resultados iguais ou diferentes?

Em ambos os casos, invocar pip resulta diretamente em um shebang adequado:

$ 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

Além disso, invocar com um env vazio funciona:

$ 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

De acordo com o commit , essa solução alternativa foi comentada com a inclusão do distlib 0.2.0 lançado no pip 6.0.

Esse commit faz referência ao pip 2031 .

Além disso, instalei o Python a partir do instalador python.org Mac, não por meio de homebrew ou outros métodos.

O código foi removido da distlib neste commit , o que fornece pouca justificativa para a mudança.

Eu acho que é "esperado", dado que seu $ python está configurando __VENV_LAUNCHER__ e, a partir daí, enganando outros scripts / executáveis. Infelizmente não tenho o OS X para depurar exatamente como esse processo de "enganar" continua.

Também pode ser semelhante / mesmo problema que foi relatado em # 620

@jaraco pode valer a pena modificar um pip em um virtualenv para descomentar as linhas distlib mencionadas e ver se isso parece consertar o comportamento do pip de pegar o python errado para escrever

Rastreei esse código de detecção de venv de volta a este commit , o que infelizmente não elucida as origens da ideia.

Eu encontrei essa explicação .

Minha inclinação era semelhante à que Ronald sugeriu aqui .

@vsajip você poderia dar algum conselho?

Alguns pensamentos:

  • __PYVENV_LAUNCHER__ foi adicionado porque para versões anteriores do Python (<3.3, de acordo com Ronald), mas não para versões posteriores, site.py do Python no OS X precisava distinguir a localização dos stub launchers dos executáveis ​​do framework. De acordo com este comentário de Ned Deily, sys.executable agora aponta para o iniciador de stub, e é por isso que distlib parou de usar __PYVENV_LAUNCHER__ . O único uso dele agora é em site.py - se disponível, no OS X, é usado para localizar o arquivo pyvenv.cfg .
  • Pode valer a pena verificar o que acontece, por exemplo, no Python 3.4, para ver se é algum tipo de regressão ou se todos os Python> = 3.3 têm o mesmo comportamento.
  • Meu palpite é que algo, em algum lugar, está fazendo uma chamada os.path.realpath() quando não deveria. Não parece que distlib fazendo isso. Se (de acordo com o comentário de Ned) sys.executable e o env var forem sempre os mesmos, então não deve importar; mas se algo faz um realpath em um sys.executable e executa Python por meio do resultado, isso resultaria em um golpe inesperado (porque sys.executable seria um caminho não referenciado).

Uma (outra) demonstração simples do problema, que o Homebrew redescobriu em 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

Eu fui mordido por este inseto exato. É um bug bastante grave na minha opinião, principalmente porque é extremamente confuso e difícil de descobrir quando ele atinge.

Fui atingido pelo que considero uma manifestação mais grave desse problema.

Comecei a usar o xonsh como meu shell diário.

Mas quando tento usar o virtualenv no xonsh, ele é inutilizável:

~ $ 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

Todos os scripts instalados parecem estar recebendo o prefixo do sistema e não o prefixo virtualenv.

Isso provavelmente se origina do fato de que o xonsh está sendo executado no Python 3.7 (prefixo do sistema) e, portanto, está no env:

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

Se eu, em vez disso, iniciar o zsh e executar os mesmos comandos ou se primeiro invocar del $__PYVENV_LAUNCHER__ , a pavimentadora será executada conforme o esperado e a linha shebang apontará para o virtualenv.

O xonsh deve limpar essa variável de ambiente quando é inicializado?

Existe uma possível correção de bug em https://github.com/python/cpython/pull/9516

Este problema foi marcado automaticamente como obsoleto porque não teve atividades recentes. Ele será fechado se nenhuma outra atividade ocorrer. Basta adicionar um comentário se quiser mantê-lo aberto. Obrigado por suas contribuições.

Ainda vejo esse mesmo comportamento no Mojave com Python 3.7.6 (instalado via Homebrew).

Você pode verificar com o branch de reescrita?

@gaborbernat Tentar construir o branch rewrite (fbdd782257d8eace7f5440a2b665f2ddb72e9db6) me dá um erro de construção de python3 setup.py build .

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

que parece não ter relação com este problema.

Tentar construir o branch de reescrita (fbdd782) me dá um erro de compilação de python3 setup.py build.

Você deve usar pip para fazer a construção - pip wheel . - já que o branch de reescrita usa pyproject.toml para definir o processo de construção.

O erro que você está recebendo é a maneira de setuptools de dizer que não suporte a nova forma padrão de construção de bibliotecas Python (PEP-517/518). É um bug de setuptools na melhor das hipóteses, mas como @pfmoore apontou você deve usar pip para construir um wheel, ou melhor ainda, apontar para a pasta virtualenv para instalá-lo (ele irá automaticamente construir um wheel e instalá-lo de uma só vez).

Caso de teste:

#!/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

Isso falha em python3 + virtualenv 16.7.9 e passa em python3 + virtualenv-16.7.10.dev11 + gfbdd782 (ou seja, o ramo rewrite ).

Corrigido na reescrita conforme acima.

Esta página foi útil?
0 / 5 - 0 avaliações