Skip to content

Commit

Permalink
Fixup PEX.demote_bootstrap: fully unimport.
Browse files Browse the repository at this point in the history
Previously only root 3rdparty packages copied into the bootstrap were
unimported leaving subpackages in-place. This could lead to a mismatch
in 3rdparty package API expectations when a re-imported 3rdparty root
package from the user PEX sys.path was from a different version of the
3rdparty package.

Fixes pex-tool#550
  • Loading branch information
jsirois committed Sep 25, 2018
1 parent 64d2cd3 commit 18ff5bc
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 6 deletions.
11 changes: 7 additions & 4 deletions pex/pex.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,11 +414,14 @@ def demote_bootstrap(cls):
# Move the third party resources pex uses to the end of sys.path for the duration of the run to
# allow conflicting versions supplied by user dependencies to win during the course of the
# execution of user code.
for _, mod, _ in pkgutil.iter_modules([bootstrap_path]):
if mod != root_package: # We let _pex stay imported
for _, submod, _ in pkgutil.iter_modules([bootstrap_path]):
if submod != root_package: # We let _pex stay imported
TRACER.log('Un-importing third party bootstrap dependency %s from %s'
% (mod, bootstrap_path))
sys.modules.pop(mod)
% (submod, bootstrap_path))
sys.modules.pop(submod)
submod_prefix = submod + '.'
for submod in [m for m in sys.modules.keys() if m.startswith(submod_prefix)]:
sys.modules.pop(submod)
sys.path.pop(bootstrap_path_index)
sys.path.append(bootstrap_path)

Expand Down
7 changes: 5 additions & 2 deletions pex/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def make_bdist(name='my_project', version='0.0.0', installer_impl=EggInstaller,
"""


def write_simple_pex(td, exe_contents, dists=None, sources=None, coverage=False):
def write_simple_pex(td, exe_contents, dists=None, sources=None, coverage=False, interpreter=None):
"""Write a pex file that contains an executable entry point
:param td: temporary directory path
Expand All @@ -189,6 +189,7 @@ def write_simple_pex(td, exe_contents, dists=None, sources=None, coverage=False)
:param dists: distributions to include, typically sdists or bdists
:param sources: sources to include, as a list of pairs (env_filename, contents)
:param coverage: include coverage header
:param interpreter: a custom interpreter to use to build the pex
"""
dists = dists or []
sources = sources or []
Expand All @@ -198,7 +199,9 @@ def write_simple_pex(td, exe_contents, dists=None, sources=None, coverage=False)
with open(os.path.join(td, 'exe.py'), 'w') as fp:
fp.write(exe_contents)

pb = PEXBuilder(path=td, preamble=COVERAGE_PREAMBLE if coverage else None)
pb = PEXBuilder(path=td,
preamble=COVERAGE_PREAMBLE if coverage else None,
interpreter=interpreter)

for dist in dists:
pb.add_dist_location(dist.location)
Expand Down
27 changes: 27 additions & 0 deletions tests/test_pex.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import sys
import textwrap
from contextlib import contextmanager
from pex.interpreter import PythonInterpreter
from types import ModuleType

import pytest
Expand Down Expand Up @@ -373,3 +374,29 @@ def test_pex_run_custom_setuptools_useable():
)
rc = PEX(pex.path()).run()
assert rc == 0


def test_pex_run_conflicting_custom_setuptools_useable():
# Here we use an older setuptools to build the pex which has a newer setuptools requirement.
# These setuptools dists have different pkg_resources APIs:
# $ diff \
# <(zipinfo -1 setuptools-20.3.1-py2.py3-none-any.whl | grep pkg_resources/ | sort) \
# <(zipinfo -1 setuptools-40.4.3-py2.py3-none-any.whl | grep pkg_resources/ | sort)
# 2a3,4
# > pkg_resources/py31compat.py
# > pkg_resources/_vendor/appdirs.py
with temporary_dir() as resolve_cache:
dists = resolve(['setuptools==20.3.1'], cache=resolve_cache)
interpreter = PythonInterpreter.from_binary(sys.executable,
path_extras=[dist.location for dist in dists],
include_site_extras=False)
dists = resolve(['setuptools==40.4.3'], cache=resolve_cache)
with temporary_dir() as temp_dir:
pex = write_simple_pex(
temp_dir,
'from pkg_resources import appdirs, py31compat',
dists=dists,
interpreter=interpreter
)
rc = PEX(pex.path()).run()
assert rc == 0

0 comments on commit 18ff5bc

Please sign in to comment.