Fix ansible-test virtualenv real python search.

ansible-test now properly searches for `pythonX.Y` instead of `python` when looking for the real python that created a `virtualenv`.
This commit is contained in:
Matt Clay 2019-10-03 12:48:29 -07:00
parent f2214951c5
commit b91f452f4f
2 changed files with 60 additions and 12 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- ansible-test now properly searches for ``pythonX.Y`` instead of ``python`` when looking for the real python that created a ``virtualenv``

View file

@ -4,6 +4,7 @@ __metaclass__ = type
import json
import os
import sys
from . import types as t
@ -46,7 +47,11 @@ def create_virtual_environment(args, # type: EnvironmentConfig
if python_version >= (3, 0):
# use the built-in 'venv' module on Python 3.x
if run_venv(args, python, system_site_packages, pip, path):
# creating a virtual environment using 'venv' when running in a virtual environment created by 'virtualenv' results
# in a copy of the original virtual environment instead of creation of a new one
# avoid this issue by only using "real" python interpreters to invoke 'venv'
for real_python in iterate_real_pythons(args, version):
if run_venv(args, real_python, system_site_packages, pip, path):
display.info('Created Python %s virtual environment using "venv": %s' % (version, path), verbosity=1)
return True
@ -80,6 +85,56 @@ def create_virtual_environment(args, # type: EnvironmentConfig
return False
def iterate_real_pythons(args, version): # type: (EnvironmentConfig, str) -> t.Iterable[str]
"""
Iterate through available real python interpreters of the requested version.
The current interpreter will be checked and then the path will be searched.
"""
version_info = tuple(int(n) for n in version.split('.'))
current_python = None
if version_info == sys.version_info[:len(version_info)]:
current_python = sys.executable
real_prefix = get_python_real_prefix(args, current_python)
if real_prefix:
current_python = find_python(version, os.path.join(real_prefix, 'bin'))
if current_python:
yield current_python
path = os.environ.get('PATH', os.path.defpath)
if not path:
return
found_python = find_python(version, path)
if not found_python:
return
if found_python == current_python:
return
real_prefix = get_python_real_prefix(args, found_python)
if real_prefix:
found_python = find_python(version, os.path.join(real_prefix, 'bin'))
if found_python:
yield found_python
def get_python_real_prefix(args, path): # type: (EnvironmentConfig, str) -> t.Optional[str]
"""
Return the real prefix of the specified interpreter or None if the interpreter is not a virtual environment created by 'virtualenv'.
"""
cmd = [path, os.path.join(os.path.join(ANSIBLE_TEST_DATA_ROOT, 'virtualenvcheck.py'))]
check_result = json.loads(run_command(args, cmd, capture=True, always=True)[0])
real_prefix = check_result['real_prefix']
return real_prefix
def run_venv(args, # type: EnvironmentConfig
run_python, # type: str
system_site_packages, # type: bool
@ -87,15 +142,6 @@ def run_venv(args, # type: EnvironmentConfig
path, # type: str
): # type: (...) -> bool
"""Create a virtual environment using the 'venv' module. Not available on Python 2.x."""
cmd = [run_python, os.path.join(os.path.join(ANSIBLE_TEST_DATA_ROOT, 'virtualenvcheck.py'))]
check_result = json.loads(run_command(args, cmd, capture=True, always=True)[0])
real_prefix = check_result['real_prefix']
if real_prefix:
# we must use the real python to create a virtual environment with venv
# attempting to use python from a virtualenv created virtual environment results in a copy of that environment instead
run_python = os.path.join(real_prefix, 'bin', 'python')
cmd = [run_python, '-m', 'venv']
if system_site_packages: