Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix installers to be insensitive to extras iteration order. #532

Merged
merged 2 commits into from
Jul 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions pex/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def function_wrapper(self, *args, **kw):

class InstallerBase(object):
SETUP_BOOTSTRAP_HEADER = "import sys"
SETUP_BOOTSTRAP_MODULE = "sys.path.insert(0, %(path)r); import %(module)s"
SETUP_BOOTSTRAP_PYPATH = "sys.path.insert(0, %(path)r)"
SETUP_BOOTSTRAP_MODULE = "import %(module)s"
SETUP_BOOTSTRAP_FOOTER = """
__file__ = 'setup.py'
sys.argv[0] = 'setup.py'
Expand Down Expand Up @@ -85,15 +86,21 @@ def capability(self):

@property
def bootstrap_script(self):
bootstrap_sys_paths = []
bootstrap_modules = []
for module, requirement in self.mixins().items():
path = self._interpreter.get_location(requirement)
if not path:
assert not self._strict # This should be caught by validation
continue
bootstrap_modules.append(self.SETUP_BOOTSTRAP_MODULE % {'path': path, 'module': module})
bootstrap_sys_paths.append(self.SETUP_BOOTSTRAP_PYPATH % {'path': path})
bootstrap_modules.append(self.SETUP_BOOTSTRAP_MODULE % {'module': module})
return '\n'.join(
[self.SETUP_BOOTSTRAP_HEADER] + bootstrap_modules + [self.SETUP_BOOTSTRAP_FOOTER])
[self.SETUP_BOOTSTRAP_HEADER] +
bootstrap_sys_paths +
bootstrap_modules +
[self.SETUP_BOOTSTRAP_FOOTER]
)

def run(self):
if self._installed is not None:
Expand Down
4 changes: 2 additions & 2 deletions pex/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,13 @@ def write_zipfile(directory, dest, reverse=False):

@contextlib.contextmanager
def make_installer(name='my_project', version='0.0.0', installer_impl=EggInstaller, zip_safe=True,
install_reqs=None):
install_reqs=None, **kwargs):
interp = {'project_name': name,
'version': version,
'zip_safe': zip_safe,
'install_requires': install_reqs or []}
with temporary_content(PROJECT_CONTENT, interp=interp) as td:
yield installer_impl(td)
yield installer_impl(td, **kwargs)


@contextlib.contextmanager
Expand Down
60 changes: 60 additions & 0 deletions tests/test_installer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
import contextlib
from collections import OrderedDict

import pytest

from pex.bin.pex import get_interpreter
from pex.installer import WheelInstaller
from pex.testing import ensure_python_interpreter, make_installer, temporary_dir
from pex.version import SETUPTOOLS_REQUIREMENT, WHEEL_REQUIREMENT


class OrderableInstaller(WheelInstaller):
def __init__(self, source_dir, strict=True, interpreter=None, install_dir=None, mixins=None):
self._mixins = mixins
super(OrderableInstaller, self).__init__(source_dir, strict, interpreter, install_dir)

def mixins(self):
return self._mixins


@contextlib.contextmanager
def bare_interpreter():
with temporary_dir() as interpreter_cache:
yield get_interpreter(
python_interpreter=ensure_python_interpreter('3.6.3'),
interpreter_cache_dir=interpreter_cache,
repos=None,
use_wheel=True
)


@contextlib.contextmanager
def wheel_installer(*mixins):
with bare_interpreter() as interpreter:
with make_installer(installer_impl=OrderableInstaller,
interpreter=interpreter,
mixins=OrderedDict(mixins)) as installer:
yield installer


WHEEL_EXTRA = ('wheel', WHEEL_REQUIREMENT)
SETUPTOOLS_EXTRA = ('setuptools', SETUPTOOLS_REQUIREMENT)


def test_wheel_before_setuptools():
with wheel_installer(WHEEL_EXTRA, SETUPTOOLS_EXTRA) as installer:
installer.bdist()


def test_setuptools_before_wheel():
with wheel_installer(SETUPTOOLS_EXTRA, WHEEL_EXTRA) as installer:
installer.bdist()


def test_no_wheel():
with wheel_installer(SETUPTOOLS_EXTRA) as installer:
with pytest.raises(installer.InstallFailure):
installer.bdist()