Virtualenv: Scripts instalados con shebang incorrecto en subproceso

Creado en 20 ene. 2016  ·  27Comentarios  ·  Fuente: pypa/virtualenv

En Python 3.5.1 pero no en 2.7.10, cuando invoco pip a través de un subproceso en un virtualenv, los scripts se crean con el shebang incorrecto y, por lo tanto, no se ejecutan:

$ 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

Uno esperaría que el script py.test tuviera un shebang con 'env' en él.

El uso de pyvenv en su lugar no está sujeto al problema. Eliminar la variable de entorno __PYVENV_LAUNCHER__ antes de iniciar el subproceso soluciona el problema, como se informa aquí . El problema se observó con 13.1.2 y 14.0.0.

¿Se espera este comportamiento? Si es así, ¿eliminar la variable de entorno es lo apropiado que debe hacer un proceso principal para solucionar el problema?

Comentario más útil

Me acaba de picar este error exacto. En mi opinión, es un error bastante severo, principalmente porque es extremadamente confuso y difícil de averiguar cuándo ocurre.

Todos 27 comentarios

lo hace

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

¿Dar resultados iguales o diferentes?

Y la misma pregunta con

$ env/bin/python -m pip install pytest

?

Posiblemente una regresión de alguna manera en https://github.com/pypa/virtualenv/issues/322 / https://github.com/pypa/virtualenv/pull/541

En cuyo caso, ¿podría probar si

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

ayuda o no?

Hmm, había un código en pip / distlib para tal vez lidiar con esto, pero se comentó hace un año.

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

¿Dar resultados iguales o diferentes?

En ambos casos, la invocación de pip da como resultado un shebang adecuado:

$ 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

Además, invocar con un env vacío 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

Según el compromiso , esa solución se comentó con la inclusión de distlib 0.2.0 lanzado en pip 6.0.

Ese compromiso hace referencia al pip 2031 .

Además, instalé Python desde el instalador de Python.org para Mac, no a través de homebrew u otros métodos.

El código se eliminó de distlib en esta confirmación , lo que proporciona pocas razones para el cambio.

Supongo que es "esperado" dado que su $ python inicial está configurando __VENV_LAUNCHER__ y luego engañando a otros scripts / ejecutables. Desafortunadamente, no tengo OS X para depurar cómo se desarrolla exactamente ese proceso de "engañar".

También podría ser un problema similar o el mismo que se informó en el n. ° 620

@jaraco , podría valer la pena modificar un pip en un virtualenv para descomentar las líneas distlib mencionadas y ver si eso parece arreglar el comportamiento de pip de elegir el pitón incorrecto para escribir

Rastreé ese código de detección de venv hasta este compromiso , que desafortunadamente no aclara los orígenes de la idea.

Encontré esta explicación .

Mi inclinación fue similar a lo que sugirió Ronald aquí .

@vsajip, ¿ podrías dar algún consejo?

Algunos pensamientos:

  • Se agregó __PYVENV_LAUNCHER__ porque para las versiones anteriores de Python (<3.3, según Ronald) pero no para las versiones posteriores, el site.py Python en OS X necesitaba distinguir la ubicación de los lanzadores de stub de los ejecutables del marco. Según este comentario de Ned Deily, sys.executable ahora apunta al lanzador de stub, y esta es la razón por la que distlib dejó de usar __PYVENV_LAUNCHER__ . El único uso de él ahora es en site.py ; si está disponible, en OS X, se usa para ubicar el archivo pyvenv.cfg .
  • Puede valer la pena comprobar para ver qué sucede, por ejemplo, en Python 3.4, para ver si hay algún tipo de regresión, o si todo Python> = 3.3 tiene el mismo comportamiento.
  • Supongo que algo, en algún lugar, está haciendo una llamada os.path.realpath() cuando no debería ser. No parece que sea distlib haciendo eso. Si (según el comentario de Ned) sys.executable y la var env son siempre iguales, entonces no debería importar; pero si algo hace un realpath en un sys.executable y ejecuta Python a través del resultado, entonces eso daría como resultado un shebang inesperado (porque sys.executable sería entonces una ruta desreferenciada).

Una (otra) demostración simple del problema, que Homebrew redescubrió en 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

Me acaba de picar este error exacto. En mi opinión, es un error bastante severo, principalmente porque es extremadamente confuso y difícil de averiguar cuándo ocurre.

Me ha afectado lo que creo que es una manifestación más grave de este problema.

Empecé a usar xonsh como mi caparazón diario.

Pero cuando trato de usar virtualenv bajo xonsh, es inutilizable:

~ $ 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 los scripts instalados parecen tener el prefijo del sistema y no el prefijo virtualenv.

Esto probablemente se deba al hecho de que xonsh se ejecuta bajo Python 3.7 (prefijo del sistema) y también lo tiene en el entorno:

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

Si, en cambio, ejecuto zsh y ejecuto los mismos comandos o si primero invoco del $__PYVENV_LAUNCHER__ , la pavimentadora se ejecuta como se esperaba y la línea shebang apunta al virtualenv.

¿Debería xonsh borrar esa variable de entorno cuando se inicia?

Existe una posible corrección de errores en https://github.com/python/cpython/pull/9516

Este problema se ha marcado automáticamente como obsoleto porque no ha tenido actividad reciente. Se cerrará si no se produce más actividad. Simplemente agregue un comentario si desea mantenerlo abierto. Gracias por sus aportaciones.

Sigo viendo este mismo comportamiento en Mojave con Python 3.7.6 (instalado a través de Homebrew).

¿Puedes consultar con la rama de reescritura?

@gaborbernat Intentar construir la rama rewrite (fbdd782257d8eace7f5440a2b665f2ddb72e9db6) me da un error de compilación 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 no estar relacionado con este problema.

Intentar construir la rama de reescritura (fbdd782) me da un error de compilación de python3 setup.py build.

Debe usar pip para hacer la compilación - pip wheel . - ya que la rama de reescritura usa pyproject.toml para definir el proceso de compilación.

El error que está recibiendo es la forma de setuptools de decir que no soy compatible con la nueva forma estándar de crear bibliotecas de Python (PEP-517/518). En el mejor de los casos, es un error de setuptools, pero como @pfmoore señaló, debería usar pip para construir una rueda, o mejor aún, apuntar a la carpeta virtualenv para instalarla (automáticamente construirá una rueda y la instalará de una vez).

Caso de prueba:

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

Esto falla bajo python3 + virtualenv 16.7.9, y pasa bajo python3 + virtualenv-16.7.10.dev11 + gfbdd782 (es decir, la rama rewrite ).

Se corrigió en la reescritura como se indicó anteriormente.

¿Fue útil esta página
0 / 5 - 0 calificaciones