Virtualenv: Scripts installés avec le mauvais shebang sous le sous-processus

Créé le 20 janv. 2016  ·  27Commentaires  ·  Source: pypa/virtualenv

Sur Python 3.5.1 mais pas sur 2.7.10, lorsque j'invoque pip via un sous-processus dans un virtualenv, les scripts sont créés avec le mauvais shebang et ne parviennent donc pas à s'exécuter :

$ 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

On s'attendrait à ce que le script py.test ait un shebang avec 'env' dedans.

L'utilisation de pyvenv à la place n'est pas sujette au problème. La suppression de la variable d'environnement __PYVENV_LAUNCHER__ avant de lancer le sous-processus contourne le problème, comme indiqué ici . Le problème a été observé avec 13.1.2 et 14.0.0.

Ce comportement est-il attendu ? Si tel est le cas, la suppression de la variable d'environnement est-elle la chose appropriée à faire pour un processus parent pour contourner le problème ?

Commentaire le plus utile

Je viens d'être piqué par ce bug exact. C'est un bug assez grave à mon avis, principalement parce qu'il est extrêmement déroutant et difficile à comprendre quand il frappe.

Tous les 27 commentaires

Est-ce que

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

Donner des résultats identiques ou différents ?

Et même question avec

$ env/bin/python -m pip install pytest

?

Peut-être une régression en quelque sorte sur https://github.com/pypa/virtualenv/issues/322 / https://github.com/pypa/virtualenv/pull/541

Dans ce cas, pourriez-vous tester si

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

aide ou pas ?

Hmm, il y avait du code dans pip / distlib pour peut-être gérer cela, mais il a été commenté il y a un an-

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

Donner des résultats identiques ou différents ?

Dans les deux cas, l'invocation de pip entraîne directement un bon 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

De plus, l'invocation avec un env vide fonctionne :

$ 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

Selon le commit , cette solution de contournement a été commentée avec l'inclusion de distlib 0.2.0 publiée dans pip 6.0.

Ce commit fait référence au pip 2031 .

De plus, j'ai installé Python à partir du programme d'installation Mac de python.org, et non via homebrew ou d'autres méthodes.

Le code a été supprimé de distlib dans ce commit , ce qui justifie peu le changement.

Je suppose que c'est "attendu" étant donné que votre $ python initial définit __VENV_LAUNCHER__ et par la suite trompe d'autres scripts / exécutables. Malheureusement, je n'ai pas OS X pour déboguer comment se déroule exactement ce processus de "tromperie".

Cela pourrait également être similaire / même problème que celui signalé dans # 620

@jaraco, cela pourrait valoir la peine de modifier un pip dans un virtualenv pour décommenter les lignes de distlib mentionnées et voir si cela semble corriger le comportement de pip consistant à choisir le mauvais python à écrire

J'ai retracé ce code de détection de venv jusqu'à ce commit , ce qui n'explique malheureusement pas les origines de l'idée.

J'ai trouvé cette explication .

Mon inclination était similaire à ce que Ronald a suggéré ici .

@vsajip pouvez-vous me conseiller ?

Quelques idées:

  • __PYVENV_LAUNCHER__ été ajouté car pour les versions antérieures de Python (< 3.3, selon Ronald) mais pas pour les versions ultérieures, le site.py Python sur OS X devait distinguer l'emplacement des lanceurs de stub des exécutables du framework. Selon ce commentaire de Ned Deily, sys.executable pointe maintenant vers le lanceur de stub, et c'est pourquoi distlib cessé d'utiliser __PYVENV_LAUNCHER__ . La seule utilisation de celui-ci est maintenant dans site.py - s'il est disponible, sur OS X, il est utilisé pour localiser le fichier pyvenv.cfg .
  • Cela peut valoir la peine de vérifier ce qui se passe dans Python 3.4, par exemple, pour voir s'il s'agit d'une sorte de régression ou si tous les Python >= 3.3 ont le même comportement.
  • Je suppose que quelque chose, quelque part, fait un appel os.path.realpath() alors qu'il ne devrait pas l'être. On dirait que c'est distlib qui fait ça. Si (selon le commentaire de Ned) sys.executable et la var env sont toujours les mêmes, alors cela ne devrait pas avoir d'importance ; mais si quelque chose fait un realpath sur un sys.executable et exécute Python via le résultat, alors cela entraînerait un shebang inattendu (car sys.executable serait alors un chemin déréférencé).

Une (autre) démonstration simple du problème, que Homebrew a redécouvert dans 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

Je viens d'être piqué par ce bug exact. C'est un bug assez grave à mon avis, principalement parce qu'il est extrêmement déroutant et difficile à comprendre quand il frappe.

J'ai été frappé par ce que je pense être une manifestation plus grave de ce problème.

J'ai commencé à utiliser xonsh comme shell quotidien.

Mais quand j'essaye d'utiliser virtualenv sous xonsh, c'est inutilisable :

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

Tous les scripts installés semblent obtenir le préfixe système et non le préfixe virtualenv.

Cela vient probablement du fait que xonsh s'exécute sous Python 3.7 (préfixe système) et a donc ceci dans l'environnement :

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

Si je lance plutôt zsh et lance les mêmes commandes ou si j'invoque d'abord del $__PYVENV_LAUNCHER__ , le finisseur s'exécute comme prévu et la ligne shebang pointe vers virtualenv.

Est-ce que xonsh doit effacer cette variable d'environnement au démarrage ?

Il existe une correction de bogue possible sur https://github.com/python/cpython/pull/9516

Ce problème a été automatiquement marqué comme obsolète car il n'a pas eu d'activité récente. Il sera fermé si aucune autre activité ne se produit. Ajoutez simplement un commentaire si vous voulez le garder ouvert. Merci pour vos contributions.

Je vois toujours ce même comportement sur Mojave avec Python 3.7.6 (installé via Homebrew).

Pouvez-vous vérifier avec la branche de réécriture ?

@gaborbernat Tenter de construire la branche rewrite (fbdd782257d8eace7f5440a2b665f2ddb72e9db6) me donne une erreur de construction de python3 setup.py build .

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

qui semble sans rapport avec ce problème.

Tenter de construire la branche de réécriture (fbdd782) me donne une erreur de construction de python3 setup.py build.

Vous devez utiliser pip pour faire la construction - pip wheel . - car la branche de réécriture utilise pyproject.toml pour définir le processus de construction.

L'erreur que vous obtenez est la manière de setuptools de dire que je ne prends pas en charge la nouvelle méthode standard de création de bibliothèques python (PEP-517/518). C'est au mieux un bogue de setuptools, mais comme @pfmoore l'a souligné, vous devriez utiliser pip pour construire une roue, ou mieux encore pour le pointer vers le dossier virtualenv pour l'installer (il construira automatiquement une roue et l'installera en une seule fois).

Cas de test:

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

Cela échoue sous python3+virtualenv 16.7.9, et passe sous python3+virtualenv-16.7.10.dev11+gfbdd782 (c'est-à-dire la branche rewrite ).

Corrigé dans la réécriture comme ci-dessus.

Cette page vous a été utile?
0 / 5 - 0 notes