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 pip install --target #4111

Closed
wants to merge 5 commits into from
Closed
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
1 change: 1 addition & 0 deletions news/4111.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix ``pip install --target`` by making ``--target``, ``--user``, and ``--prefix`` mutually exclusive.
11 changes: 11 additions & 0 deletions pip/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ def run(self, options, args):
"Can not combine '--user' and '--prefix' as they imply "
"different installation locations"
)
if options.target_dir:
raise CommandError(
"Can not combine '--user' and '--target' as they imply "
"different installation locations"
)
if virtualenv_no_global():
raise InstallationError(
"Can not perform a '--user' install. User site-packages "
Expand All @@ -191,6 +196,11 @@ def run(self, options, args):

temp_target_dir = None
if options.target_dir:
if options.prefix_path:
raise CommandError(
"Can not combine '--target' and '--prefix' as they imply "
"different installation locations"
)
options.ignore_installed = True
temp_target_dir = tempfile.mkdtemp()
options.target_dir = os.path.abspath(options.target_dir)
Expand All @@ -201,6 +211,7 @@ def run(self, options, args):
"continue."
)
install_options.append('--home=' + temp_target_dir)
install_options.append('--prefix=')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add --prefix here?

Copy link
Author

@halms halms Nov 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the same thing as already exists for the --user command (line 260).
An empty prefix is set in order to overwrite one set as a default somewhere else (e.g. as it is the case with homebrew python).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch should be dead because of exception above. Filled #4132

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Tests are failing. Looks like this piece of code is still needed.

Copy link
Author

@halms halms Nov 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my understanding the exception only avoids both options being set at the pip command line.
This line ensures that a prefix set somwhere else is effectively overriden as it does basically the same as the workaround there:
https://github.com/Homebrew/brew/blob/master/docs/Homebrew-and-Python.md#note-on-pip-install---user

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not fully understand, how install_options and distutils_scheme() in locations.py do work together, but it seems to work this way.


global_options = options.global_options or []

Expand Down
6 changes: 4 additions & 2 deletions pip/locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,11 @@ def distutils_scheme(dist_name, user=False, home=None, root=None,
# or user base for installations during finalize_options()
# ideally, we'd prefer a scheme class that has no side-effects.
assert not (user and prefix), "user={0} prefix={1}".format(user, prefix)
i.user = user or i.user
if user:
assert not (home and prefix), "home={0} prefix={1}".format(home, prefix)
assert not (home and user), "home={0} user={1}".format(home, user)
if user or home:
i.prefix = ""
i.user = user or i.user
i.prefix = prefix or i.prefix
i.home = home or i.home
i.root = root or i.root
Expand Down
31 changes: 31 additions & 0 deletions tests/functional/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,37 @@ def test_install_package_conflict_prefix_and_user(script, data):
)


def test_install_package_conflict_target_and_user(script, data):
"""
Test installing a package using pip install --target --user errors out
"""
target_path = script.scratch_path / 'target'
result = script.pip(
'install', '-f', data.find_links, '--no-index', '--user',
'--target', target_path, 'simple==1.0',
expect_error=True, quiet=True,
)
assert (
"Can not combine '--user' and '--target'" in result.stderr
)


def test_install_package_conflict_prefix_and_target(script, data):
"""
Test installing a package using pip install --prefix --targetx errors out
"""
prefix_path = script.scratch_path / 'prefix'
target_path = script.scratch_path / 'target'
result = script.pip(
'install', '-f', data.find_links, '--no-index', '--target',
target_path, '--prefix', prefix_path, 'simple==1.0',
expect_error=True, quiet=True,
)
assert (
"Can not combine '--target' and '--prefix'" in result.stderr
)


# skip on win/py3 for now, see issue #782
@pytest.mark.skipif("sys.platform == 'win32' and sys.version_info >= (3,)")
def test_install_package_that_emits_unicode(script, data):
Expand Down
54 changes: 54 additions & 0 deletions tests/functional/test_install_reqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,60 @@ def test_wheel_user_with_prefix_in_pydistutils_cfg(script, data, virtualenv):
assert 'installed requiresupper' in result.stdout


def test_nowheel_user_with_prefix_in_pydistutils_cfg(script, data, virtualenv):
virtualenv.system_site_packages = True
homedir = script.environ["HOME"]
script.scratch_path.join("bin").mkdir()
with open(os.path.join(homedir, ".pydistutils.cfg"), "w") as cfg:
cfg.write(textwrap.dedent("""
[install]
prefix=%s""" % script.scratch_path))

result = script.pip('install', '--no-binary=:all:', '--user', '--no-index',
'-f', data.find_links, 'requiresupper',
expect_stderr=True)
assert 'installed requiresupper' in result.stdout


@pytest.mark.network
def test_wheel_target_with_prefix_in_pydistutils_cfg(script, data,
virtualenv):
# Make sure wheel is available in the virtualenv
script.pip('install', 'wheel')
virtualenv.system_site_packages = True
homedir = script.environ["HOME"]
script.scratch_path.join("bin").mkdir()
with open(os.path.join(homedir, ".pydistutils.cfg"), "w") as cfg:
cfg.write(textwrap.dedent("""
[install]
prefix=%s""" % script.scratch_path))

target_path = script.scratch_path / 'target'
result = script.pip('install', '--target', target_path, '--no-index', '-f',
data.find_links, 'requiresupper')
# Check that we are really installing a wheel
assert 'Running setup.py install for requiresupper' not in result.stdout
assert 'installed requiresupper' in result.stdout


def test_nowheel_target_with_prefix_in_pydistutils_cfg(script, data,
virtualenv):
virtualenv.system_site_packages = True
homedir = script.environ["HOME"]
script.scratch_path.join("bin").mkdir()
with open(os.path.join(homedir, ".pydistutils.cfg"), "w") as cfg:
cfg.write(textwrap.dedent("""
[install]
prefix=%s""" % script.scratch_path))

target_path = script.scratch_path / 'target'
result = script.pip('install', '--no-binary=:all:',
'--target', target_path,
'--no-index', '-f', data.find_links, 'requiresupper',
expect_stderr=True)
assert 'installed requiresupper' in result.stdout


def test_install_option_in_requirements_file(script, data, virtualenv):
"""
Test --install-option in requirements file overrides same option in cli
Expand Down