Skip to content

Commit f826b23

Browse files
Merge pull request #2458 from segevfiner/fix-required-options-help
Fix --help with required options
2 parents bcbad5b + 9abff7f commit f826b23

File tree

5 files changed

+66
-8
lines changed

5 files changed

+66
-8
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ Ross Lawley
144144
Russel Winder
145145
Ryan Wooden
146146
Samuele Pedroni
147+
Segev Finer
147148
Simon Gomizelj
148149
Skylar Downes
149150
Stefan Farmbauer

_pytest/config.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ class UsageError(Exception):
7171
""" error in pytest usage or invocation"""
7272

7373

74+
class PrintHelp(Exception):
75+
"""Raised when pytest should print it's help to skip the rest of the
76+
argument parsing and validation."""
77+
pass
78+
79+
7480
def filename_arg(path, optname):
7581
""" Argparse type validator for filename arguments.
7682
@@ -1100,14 +1106,18 @@ def parse(self, args, addopts=True):
11001106
self._preparse(args, addopts=addopts)
11011107
# XXX deprecated hook:
11021108
self.hook.pytest_cmdline_preparse(config=self, args=args)
1103-
args = self._parser.parse_setoption(args, self.option, namespace=self.option)
1104-
if not args:
1105-
cwd = os.getcwd()
1106-
if cwd == self.rootdir:
1107-
args = self.getini('testpaths')
1109+
self._parser.after_preparse = True
1110+
try:
1111+
args = self._parser.parse_setoption(args, self.option, namespace=self.option)
11081112
if not args:
1109-
args = [cwd]
1110-
self.args = args
1113+
cwd = os.getcwd()
1114+
if cwd == self.rootdir:
1115+
args = self.getini('testpaths')
1116+
if not args:
1117+
args = [cwd]
1118+
self.args = args
1119+
except PrintHelp:
1120+
pass
11111121

11121122
def addinivalue_line(self, name, line):
11131123
""" add a line to an ini-file option. The option must have been

_pytest/helpconfig.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,46 @@
33

44
import py
55
import pytest
6+
from _pytest.config import PrintHelp
67
import os, sys
8+
from argparse import Action
9+
10+
11+
class HelpAction(Action):
12+
"""This is an argparse Action that will raise an exception in
13+
order to skip the rest of the argument parsing when --help is passed.
14+
This prevents argparse from quitting due to missing required arguments
15+
when any are defined, for example by ``pytest_addoption``.
16+
This is similar to the way that the builtin argparse --help option is
17+
implemented by raising SystemExit.
18+
"""
19+
20+
def __init__(self,
21+
option_strings,
22+
dest=None,
23+
default=False,
24+
help=None):
25+
super(HelpAction, self).__init__(
26+
option_strings=option_strings,
27+
dest=dest,
28+
const=True,
29+
default=default,
30+
nargs=0,
31+
help=help)
32+
33+
def __call__(self, parser, namespace, values, option_string=None):
34+
setattr(namespace, self.dest, self.const)
35+
36+
# We should only skip the rest of the parsing after preparse is done
37+
if getattr(parser._parser, 'after_preparse', False):
38+
raise PrintHelp
39+
740

841
def pytest_addoption(parser):
942
group = parser.getgroup('debugconfig')
1043
group.addoption('--version', action="store_true",
1144
help="display pytest lib version and import information.")
12-
group._addoption("-h", "--help", action="store_true", dest="help",
45+
group._addoption("-h", "--help", action=HelpAction, dest="help",
1346
help="show help message and configuration info")
1447
group._addoption('-p', action="append", dest="plugins", default = [],
1548
metavar="name",

changelog/1999.bugfix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Required options added via ``pytest_addoption`` will no longer prevent
2+
using --help without passing them.

testing/test_conftest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,3 +449,15 @@ def pytest_ignore_collect(path, config):
449449
'*test_foo4.py*',
450450
'*3 passed*',
451451
])
452+
453+
454+
def test_required_option_help(testdir):
455+
testdir.makeconftest("assert 0")
456+
x = testdir.mkdir("x")
457+
x.join("conftest.py").write(_pytest._code.Source("""
458+
def pytest_addoption(parser):
459+
parser.addoption("--xyz", action="store_true", required=True)
460+
"""))
461+
result = testdir.runpytest("-h", x)
462+
assert 'argument --xyz is required' not in result.stdout.str()
463+
assert 'general:' in result.stdout.str()

0 commit comments

Comments
 (0)