Skip to content

Commit c0cc004

Browse files
committed
Raise an error if the user tries to use PIP_NO_USE_PEP517.
1 parent 47d7f2b commit c0cc004

File tree

2 files changed

+158
-1
lines changed

2 files changed

+158
-1
lines changed

src/pip/_internal/cli/cmdoptions.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"""
1010
from __future__ import absolute_import
1111

12+
import textwrap
1213
import warnings
1314
from distutils.util import strtobool
1415
from functools import partial
@@ -28,6 +29,20 @@
2829
from pip._internal.cli.parser import ConfigOptionParser # noqa: F401
2930

3031

32+
def raise_option_error(parser, option, msg):
33+
"""
34+
Raise an option parsing error using parser.error().
35+
36+
Args:
37+
parser: an OptionParser instance.
38+
option: an Option instance.
39+
msg: the error text.
40+
"""
41+
msg = '{} error: {}'.format(option, msg)
42+
msg = textwrap.fill(' '.join(msg.split()))
43+
parser.error(msg)
44+
45+
3146
def make_option_group(group, parser):
3247
# type: (Dict[str, Any], ConfigOptionParser) -> OptionGroup
3348
"""
@@ -601,6 +616,30 @@ def no_cache_dir_callback(option, opt, value, parser):
601616
'if this option is used.'
602617
) # type: Callable[..., Option]
603618

619+
620+
def no_use_pep517_callback(option, opt, value, parser):
621+
"""
622+
Process a value provided for the --no-use-pep517 option.
623+
624+
This is an optparse.Option callback for the no_use_pep517 option.
625+
"""
626+
# Since --no-use-pep517 doesn't accept arguments, the value argument
627+
# will be None if --no-use-pep517 is passed via the command-line.
628+
# However, the value can be non-None if the option is triggered e.g.
629+
# by an environment variable, for example "PIP_NO_USE_PEP517=true".
630+
if value is not None:
631+
msg = """A value was passed for --no-use-pep517,
632+
probably using either the PIP_NO_USE_PEP517 environment variable
633+
or the "no-use-pep517" config file option. Use an appropriate value
634+
of the PIP_USE_PEP517 environment variable or the "use-pep517"
635+
config file option instead.
636+
"""
637+
raise_option_error(parser, option=option, msg=msg)
638+
639+
# Otherwise, --no-use-pep517 was passed via the command-line.
640+
parser.values.use_pep517 = False
641+
642+
604643
use_pep517 = partial(
605644
Option,
606645
'--use-pep517',
@@ -615,7 +654,8 @@ def no_cache_dir_callback(option, opt, value, parser):
615654
Option,
616655
'--no-use-pep517',
617656
dest='use_pep517',
618-
action='store_false',
657+
action='callback',
658+
callback=no_use_pep517_callback,
619659
default=None,
620660
help=SUPPRESS_HELP
621661
) # type: Any

tests/unit/test_options.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,27 @@
55

66
import pip._internal.configuration
77
from pip._internal import main
8+
from pip._internal.commands import DownloadCommand
89
from tests.lib.options_helpers import AddFakeCommandMixin
910

1011

12+
@contextmanager
13+
def temp_environment_variable(name, value):
14+
not_set = object()
15+
original = os.environ[name] if name in os.environ else not_set
16+
os.environ[name] = value
17+
18+
try:
19+
yield
20+
finally:
21+
# Return the environment variable to its original state.
22+
if original is not_set:
23+
if name in os.environ:
24+
del os.environ[name]
25+
else:
26+
os.environ[name] = original
27+
28+
1129
@contextmanager
1230
def assert_raises_message(exc_class, expected):
1331
"""
@@ -19,6 +37,22 @@ def assert_raises_message(exc_class, expected):
1937
assert str(excinfo.value) == expected
2038

2139

40+
@contextmanager
41+
def assert_option_error(capsys, expected):
42+
"""
43+
Assert that a SystemExit occurred because of a parsing error.
44+
45+
Args:
46+
expected: an expected substring of stderr.
47+
"""
48+
with pytest.raises(SystemExit) as excinfo:
49+
yield
50+
51+
assert excinfo.value.code == 2
52+
stderr = capsys.readouterr().err
53+
assert expected in stderr
54+
55+
2256
def assert_is_default_cache_dir(value):
2357
# This path looks different on different platforms, but the path always
2458
# has the substring "pip".
@@ -157,6 +191,89 @@ def test_cache_dir__PIP_NO_CACHE_DIR_invalid__with_no_cache_dir(self):
157191
main(['--no-cache-dir', 'fake'])
158192

159193

194+
class TestUsePEP517Options(object):
195+
196+
"""
197+
Test options related to using --use-pep517.
198+
"""
199+
200+
def parse_args(self, args):
201+
# We use DownloadCommand since that is one of the few Command
202+
# classes with the use_pep517 options.
203+
command = DownloadCommand()
204+
options, args = command.parse_args(args)
205+
206+
return options
207+
208+
def test_no_option(self):
209+
"""
210+
Test passing no option.
211+
"""
212+
options = self.parse_args([])
213+
assert options.use_pep517 is None
214+
215+
def test_use_pep517(self):
216+
"""
217+
Test passing --use-pep517.
218+
"""
219+
options = self.parse_args(['--use-pep517'])
220+
assert options.use_pep517 is True
221+
222+
def test_no_use_pep517(self):
223+
"""
224+
Test passing --no-use-pep517.
225+
"""
226+
options = self.parse_args(['--no-use-pep517'])
227+
assert options.use_pep517 is False
228+
229+
def test_PIP_USE_PEP517_true(self):
230+
"""
231+
Test setting PIP_USE_PEP517 to "true".
232+
"""
233+
with temp_environment_variable('PIP_USE_PEP517', 'true'):
234+
options = self.parse_args([])
235+
# This is an int rather than a boolean because strtobool() in pip's
236+
# configuration code returns an int.
237+
assert options.use_pep517 == 1
238+
239+
def test_PIP_USE_PEP517_false(self):
240+
"""
241+
Test setting PIP_USE_PEP517 to "false".
242+
"""
243+
with temp_environment_variable('PIP_USE_PEP517', 'false'):
244+
options = self.parse_args([])
245+
# This is an int rather than a boolean because strtobool() in pip's
246+
# configuration code returns an int.
247+
assert options.use_pep517 == 0
248+
249+
def test_use_pep517_and_PIP_USE_PEP517_false(self):
250+
"""
251+
Test passing --use-pep517 and setting PIP_USE_PEP517 to "false".
252+
"""
253+
with temp_environment_variable('PIP_USE_PEP517', 'false'):
254+
options = self.parse_args(['--use-pep517'])
255+
assert options.use_pep517 is True
256+
257+
def test_no_use_pep517_and_PIP_USE_PEP517_true(self):
258+
"""
259+
Test passing --no-use-pep517 and setting PIP_USE_PEP517 to "true".
260+
"""
261+
with temp_environment_variable('PIP_USE_PEP517', 'true'):
262+
options = self.parse_args(['--no-use-pep517'])
263+
assert options.use_pep517 is False
264+
265+
def test_PIP_NO_USE_PEP517(self, capsys):
266+
"""
267+
Test setting PIP_NO_USE_PEP517, which isn't allowed.
268+
"""
269+
expected_err = (
270+
'--no-use-pep517 error: A value was passed for --no-use-pep517,\n'
271+
)
272+
with temp_environment_variable('PIP_NO_USE_PEP517', 'true'):
273+
with assert_option_error(capsys, expected=expected_err):
274+
self.parse_args([])
275+
276+
160277
class TestOptionsInterspersed(AddFakeCommandMixin):
161278

162279
def test_general_option_after_subcommand(self):

0 commit comments

Comments
 (0)