mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Merge pull request #8556 from chrahunt/maint/fail-on-install-location-options
Disallow explicitly passing install-location-related arguments in --install-options
This commit is contained in:
commit
e9508591ce
4 changed files with 115 additions and 71 deletions
1
news/7309.removal
Normal file
1
news/7309.removal
Normal file
|
@ -0,0 +1 @@
|
|||
Disallow passing install-location-related arguments in ``--install-options``.
|
|
@ -21,7 +21,6 @@ from pip._internal.locations import distutils_scheme
|
|||
from pip._internal.operations.check import check_install_conflicts
|
||||
from pip._internal.req import install_given_reqs
|
||||
from pip._internal.req.req_tracker import get_requirement_tracker
|
||||
from pip._internal.utils.deprecation import deprecated
|
||||
from pip._internal.utils.distutils_args import parse_distutils_args
|
||||
from pip._internal.utils.filesystem import test_writable_dir
|
||||
from pip._internal.utils.misc import (
|
||||
|
@ -290,7 +289,7 @@ class InstallCommand(RequirementCommand):
|
|||
try:
|
||||
reqs = self.get_requirements(args, options, finder, session)
|
||||
|
||||
warn_deprecated_install_options(
|
||||
reject_location_related_install_options(
|
||||
reqs, options.install_options
|
||||
)
|
||||
|
||||
|
@ -606,7 +605,7 @@ def decide_user_install(
|
|||
return True
|
||||
|
||||
|
||||
def warn_deprecated_install_options(requirements, options):
|
||||
def reject_location_related_install_options(requirements, options):
|
||||
# type: (List[InstallRequirement], Optional[List[str]]) -> None
|
||||
"""If any location-changing --install-option arguments were passed for
|
||||
requirements or on the command-line, then show a deprecation warning.
|
||||
|
@ -639,20 +638,12 @@ def warn_deprecated_install_options(requirements, options):
|
|||
if not offenders:
|
||||
return
|
||||
|
||||
deprecated(
|
||||
reason=(
|
||||
"Location-changing options found in --install-option: {}. "
|
||||
"This configuration may cause unexpected behavior and is "
|
||||
"unsupported.".format(
|
||||
"; ".join(offenders)
|
||||
)
|
||||
),
|
||||
replacement=(
|
||||
"using pip-level options like --user, --prefix, --root, and "
|
||||
"--target"
|
||||
),
|
||||
gone_in="20.2",
|
||||
issue=7309,
|
||||
raise CommandError(
|
||||
"Location-changing options found in --install-option: {}."
|
||||
" This is unsupported, use pip-level options like --user,"
|
||||
" --prefix, --root, and --target instead.".format(
|
||||
"; ".join(offenders)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
import os
|
||||
import textwrap
|
||||
|
||||
|
@ -5,6 +6,7 @@ import pytest
|
|||
|
||||
from tests.lib import (
|
||||
_create_test_package_with_subdirectory,
|
||||
create_basic_sdist_for_package,
|
||||
create_basic_wheel_for_package,
|
||||
need_svn,
|
||||
path_to_url,
|
||||
|
@ -15,6 +17,51 @@ from tests.lib.local_repos import local_checkout
|
|||
from tests.lib.path import Path
|
||||
|
||||
|
||||
class ArgRecordingSdist(object):
|
||||
def __init__(self, sdist_path, args_path):
|
||||
self.sdist_path = sdist_path
|
||||
self._args_path = args_path
|
||||
|
||||
def args(self):
|
||||
return json.loads(self._args_path.read_text())
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def arg_recording_sdist_maker(script):
|
||||
arg_writing_setup_py = textwrap.dedent(
|
||||
"""
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
args_path = os.path.join(os.environ["OUTPUT_DIR"], "{name}.json")
|
||||
with open(args_path, 'w') as f:
|
||||
json.dump(sys.argv, f)
|
||||
|
||||
setup(name={name!r}, version="0.1.0")
|
||||
"""
|
||||
)
|
||||
output_dir = script.scratch_path.joinpath(
|
||||
"args_recording_sdist_maker_output"
|
||||
)
|
||||
output_dir.mkdir(parents=True)
|
||||
script.environ["OUTPUT_DIR"] = str(output_dir)
|
||||
|
||||
def _arg_recording_sdist_maker(name):
|
||||
# type: (str) -> ArgRecordingSdist
|
||||
extra_files = {"setup.py": arg_writing_setup_py.format(name=name)}
|
||||
sdist_path = create_basic_sdist_for_package(
|
||||
script, name, "0.1.0", extra_files
|
||||
)
|
||||
args_path = output_dir / "{}.json".format(name)
|
||||
return ArgRecordingSdist(sdist_path, args_path)
|
||||
|
||||
return _arg_recording_sdist_maker
|
||||
|
||||
|
||||
@pytest.mark.network
|
||||
def test_requirements_file(script):
|
||||
"""
|
||||
|
@ -259,28 +306,21 @@ def test_wheel_user_with_prefix_in_pydistutils_cfg(
|
|||
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
|
||||
"""
|
||||
def test_install_option_in_requirements_file_overrides_cli(
|
||||
script, arg_recording_sdist_maker
|
||||
):
|
||||
simple_sdist = arg_recording_sdist_maker("simple")
|
||||
|
||||
script.scratch_path.joinpath("home1").mkdir()
|
||||
script.scratch_path.joinpath("home2").mkdir()
|
||||
reqs_file = script.scratch_path.joinpath("reqs.txt")
|
||||
reqs_file.write_text("simple --install-option='-O0'")
|
||||
|
||||
script.scratch_path.joinpath("reqs.txt").write_text(
|
||||
textwrap.dedent(
|
||||
"""simple --install-option='--home={home}'""".format(
|
||||
home=script.scratch_path.joinpath("home1"))))
|
||||
|
||||
result = script.pip(
|
||||
'install', '--no-index', '-f', data.find_links, '-r',
|
||||
script.scratch_path / 'reqs.txt',
|
||||
'--install-option=--home={home}'.format(
|
||||
home=script.scratch_path.joinpath("home2")),
|
||||
expect_stderr=True)
|
||||
|
||||
package_dir = script.scratch / 'home1' / 'lib' / 'python' / 'simple'
|
||||
result.did_create(package_dir)
|
||||
script.pip(
|
||||
'install', '--no-index', '-f', str(simple_sdist.sdist_path.parent),
|
||||
'-r', str(reqs_file), '--install-option=-O1',
|
||||
)
|
||||
simple_args = simple_sdist.args()
|
||||
assert 'install' in simple_args
|
||||
assert simple_args.index('-O1') < simple_args.index('-O0')
|
||||
|
||||
|
||||
def test_constraints_not_installed_by_default(script, data):
|
||||
|
@ -605,7 +645,7 @@ def test_install_unsupported_wheel_file(script, data):
|
|||
assert len(result.files_created) == 0
|
||||
|
||||
|
||||
def test_install_options_local_to_package(script, data):
|
||||
def test_install_options_local_to_package(script, arg_recording_sdist_maker):
|
||||
"""Make sure --install-options does not leak across packages.
|
||||
|
||||
A requirements.txt file can have per-package --install-options; these
|
||||
|
@ -613,27 +653,41 @@ def test_install_options_local_to_package(script, data):
|
|||
packages. This needs to be a functional test because the bug was around
|
||||
cross-contamination at install time.
|
||||
"""
|
||||
home_simple = script.scratch_path.joinpath("for-simple")
|
||||
test_simple = script.scratch.joinpath("for-simple")
|
||||
home_simple.mkdir()
|
||||
|
||||
simple1_sdist = arg_recording_sdist_maker("simple1")
|
||||
simple2_sdist = arg_recording_sdist_maker("simple2")
|
||||
|
||||
reqs_file = script.scratch_path.joinpath("reqs.txt")
|
||||
reqs_file.write_text(
|
||||
textwrap.dedent("""
|
||||
simple --install-option='--home={home_simple}'
|
||||
INITools
|
||||
""".format(**locals())))
|
||||
result = script.pip(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
simple1 --install-option='-O0'
|
||||
simple2
|
||||
"""
|
||||
)
|
||||
)
|
||||
script.pip(
|
||||
'install',
|
||||
'--no-index', '-f', data.find_links,
|
||||
'--no-index', '-f', str(simple1_sdist.sdist_path.parent),
|
||||
'-r', reqs_file,
|
||||
expect_stderr=True,
|
||||
)
|
||||
|
||||
simple = test_simple / 'lib' / 'python' / 'simple'
|
||||
bad = test_simple / 'lib' / 'python' / 'initools'
|
||||
good = script.site_packages / 'initools'
|
||||
result.did_create(simple)
|
||||
assert result.files_created[simple].dir
|
||||
result.did_not_create(bad)
|
||||
result.did_create(good)
|
||||
assert result.files_created[good].dir
|
||||
simple1_args = simple1_sdist.args()
|
||||
assert 'install' in simple1_args
|
||||
assert '-O0' in simple1_args
|
||||
simple2_args = simple2_sdist.args()
|
||||
assert 'install' in simple2_args
|
||||
assert '-O0' not in simple2_args
|
||||
|
||||
|
||||
def test_location_related_install_option_fails(script):
|
||||
simple_sdist = create_basic_sdist_for_package(script, "simple", "0.1.0")
|
||||
reqs_file = script.scratch_path.joinpath("reqs.txt")
|
||||
reqs_file.write_text("simple --install-option='--home=/tmp'")
|
||||
result = script.pip(
|
||||
'install',
|
||||
'--no-index', '-f', str(simple_sdist.parent),
|
||||
'-r', reqs_file,
|
||||
expect_error=True
|
||||
)
|
||||
assert "['--home'] from simple" in result.stderr
|
||||
|
|
|
@ -7,8 +7,9 @@ from pip._vendor.packaging.requirements import Requirement
|
|||
from pip._internal.commands.install import (
|
||||
create_env_error_message,
|
||||
decide_user_install,
|
||||
warn_deprecated_install_options,
|
||||
reject_location_related_install_options,
|
||||
)
|
||||
from pip._internal.exceptions import CommandError
|
||||
from pip._internal.req.req_install import InstallRequirement
|
||||
|
||||
|
||||
|
@ -44,16 +45,15 @@ class TestDecideUserInstall:
|
|||
assert decide_user_install(use_user_site=None) is result
|
||||
|
||||
|
||||
def test_deprecation_notice_for_pip_install_options(recwarn):
|
||||
def test_rejection_for_pip_install_options():
|
||||
install_options = ["--prefix=/hello"]
|
||||
warn_deprecated_install_options([], install_options)
|
||||
with pytest.raises(CommandError) as e:
|
||||
reject_location_related_install_options([], install_options)
|
||||
|
||||
assert len(recwarn) == 1
|
||||
message = recwarn[0].message.args[0]
|
||||
assert "['--prefix'] from command line" in message
|
||||
assert "['--prefix'] from command line" in str(e.value)
|
||||
|
||||
|
||||
def test_deprecation_notice_for_requirement_options(recwarn):
|
||||
def test_rejection_for_location_requirement_options():
|
||||
install_options = []
|
||||
|
||||
bad_named_req_options = ["--home=/wow"]
|
||||
|
@ -67,18 +67,16 @@ def test_deprecation_notice_for_requirement_options(recwarn):
|
|||
None, "requirements2.txt", install_options=bad_unnamed_req_options
|
||||
)
|
||||
|
||||
warn_deprecated_install_options(
|
||||
[bad_named_req, bad_unnamed_req], install_options
|
||||
)
|
||||
|
||||
assert len(recwarn) == 1
|
||||
message = recwarn[0].message.args[0]
|
||||
with pytest.raises(CommandError) as e:
|
||||
reject_location_related_install_options(
|
||||
[bad_named_req, bad_unnamed_req], install_options
|
||||
)
|
||||
|
||||
assert (
|
||||
"['--install-lib'] from <InstallRequirement> (from requirements2.txt)"
|
||||
in message
|
||||
in str(e.value)
|
||||
)
|
||||
assert "['--home'] from hello (from requirements.txt)" in message
|
||||
assert "['--home'] from hello (from requirements.txt)" in str(e.value)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('error, show_traceback, using_user_site, expected', [
|
||||
|
|
Loading…
Reference in a new issue