Skip to content

Commit

Permalink
Fix Pex resolver API PYTHONPATH hermeticity.
Browse files Browse the repository at this point in the history
As described in pex-tool#892, when the Pex resolver API is used in-process, a
sufficiently antagonistic PYTHONPATH could lead to a failure to launch
the pip PEX we delegate resolving to. Fix this by scrubbing PYTHONPATH
from the pip PEX environment pre-launch.

Fixes pex-tool#892
  • Loading branch information
jsirois committed Feb 20, 2020
1 parent 97849d4 commit e66592a
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 0 deletions.
7 changes: 7 additions & 0 deletions pex/pip.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from pex.compatibility import urlparse
from pex.distribution_target import DistributionTarget
from pex.jobs import Job
from pex.tracer import TRACER
from pex.variables import ENV


Expand Down Expand Up @@ -70,6 +71,12 @@ def _spawn_pip_isolated(self, args, cache=None, interpreter=None):

command = pip_args + args
with ENV.strip().patch(PEX_ROOT=ENV.PEX_ROOT, PEX_VERBOSE=str(pex_verbosity)) as env:
# Guard against API calls from environment with ambient PYTHONPATH preventing pip PEX
# bootstrapping. See: https://github.com/pantsbuild/pex/issues/892
pythonpath = env.pop('PYTHONPATH', None)
if pythonpath:
TRACER.log('Scrubbed PYTHONPATH={} from the pip PEX environment.'.format(pythonpath), V=3)

from pex.pex import PEX
pip = PEX(pex=self._pip_pex_path, interpreter=interpreter)
return Job(
Expand Down
41 changes: 41 additions & 0 deletions tests/test_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import functools
import os
import subprocess
from textwrap import dedent

import pytest

Expand Down Expand Up @@ -308,3 +310,42 @@ def resolve_pytest(python_version, pytest_version):
assert 'configparser' in resolved_project_to_version
assert 'pathlib2' in resolved_project_to_version
assert 'contextlib2' in resolved_project_to_version


def test_issues_892():
python27 = ensure_python_interpreter(PY27)
program = dedent("""\
from __future__ import print_function
import os
import sys
# This puts python3.6 stdlib on PYTHONPATH.
os.environ['PYTHONPATH'] = os.pathsep.join(sys.path)
from pex import resolver
from pex.interpreter import PythonInterpreter
python27 = PythonInterpreter.from_binary({python27!r})
result = resolver.resolve(requirements=['packaging==19.2'], interpreter=python27)
print('Resolved: {{}}'.format(result))
""".format(python27=python27))

python36 = ensure_python_interpreter(PY36)
cmd, process = PythonInterpreter.from_binary(python36).open_process(
args=['-c', program],
stderr=subprocess.PIPE
)
_, stderr = process.communicate()
assert process.returncode == 0, dedent(
"""
Command {cmd} failed with {returncode}.
STDERR
======
{stderr}
""".format(cmd=cmd, returncode=process.returncode, stderr=stderr.decode('utf8'))
)

0 comments on commit e66592a

Please sign in to comment.