Print inifile and rootdir when there's usage errors

Related to #821
This commit is contained in:
Bruno Oliveira 2015-07-08 21:22:08 -03:00
parent 65c56d4c00
commit 8a6aa5e17e
3 changed files with 37 additions and 5 deletions

View File

@ -1,6 +1,11 @@
2.8.0.dev (compared to 2.7.X) 2.8.0.dev (compared to 2.7.X)
----------------------------- -----------------------------
- rootdir and inifile are now displayed during usage errors to help
users diagnose problems such as unexpected ini files which add
unknown options being picked up by pytest. Thanks to Pavel Savchenko for
bringing the problem to attention in #821 and Bruno Oliveira for the PR.
- Summary bar now is colored yellow for warning - Summary bar now is colored yellow for warning
situations such as: all tests either were skipped or xpass/xfailed, situations such as: all tests either were skipped or xpass/xfailed,
or no tests were run at all (this is a partial fix for issue500). or no tests were run at all (this is a partial fix for issue500).

View File

@ -382,7 +382,11 @@ class PytestPluginManager(PluginManager):
class Parser: class Parser:
""" Parser for command line arguments and ini-file values. """ """ Parser for command line arguments and ini-file values.
:ivar extra_info: dict of generic param -> value to display in case
there's an error processing the command line arguments.
"""
def __init__(self, usage=None, processopt=None): def __init__(self, usage=None, processopt=None):
self._anonymous = OptionGroup("custom options", parser=self) self._anonymous = OptionGroup("custom options", parser=self)
@ -391,6 +395,7 @@ class Parser:
self._usage = usage self._usage = usage
self._inidict = {} self._inidict = {}
self._ininames = [] self._ininames = []
self.extra_info = {}
def processoption(self, option): def processoption(self, option):
if self._processopt: if self._processopt:
@ -444,7 +449,7 @@ class Parser:
def _getparser(self): def _getparser(self):
from _pytest._argcomplete import filescompleter from _pytest._argcomplete import filescompleter
optparser = MyOptionParser(self) optparser = MyOptionParser(self, self.extra_info)
groups = self._groups + [self._anonymous] groups = self._groups + [self._anonymous]
for group in groups: for group in groups:
if group.options: if group.options:
@ -669,10 +674,15 @@ class OptionGroup:
class MyOptionParser(argparse.ArgumentParser): class MyOptionParser(argparse.ArgumentParser):
def __init__(self, parser): def __init__(self, parser, extra_info=None):
if not extra_info:
extra_info = {}
self._parser = parser self._parser = parser
argparse.ArgumentParser.__init__(self, usage=parser._usage, argparse.ArgumentParser.__init__(self, usage=parser._usage,
add_help=False, formatter_class=DropShorterLongHelpFormatter) add_help=False, formatter_class=DropShorterLongHelpFormatter)
# extra_info is a dict of (param -> value) to display if there's
# an usage error to provide more contextual information to the user
self.extra_info = extra_info
def parse_args(self, args=None, namespace=None): def parse_args(self, args=None, namespace=None):
"""allow splitting of positional arguments""" """allow splitting of positional arguments"""
@ -680,8 +690,10 @@ class MyOptionParser(argparse.ArgumentParser):
if argv: if argv:
for arg in argv: for arg in argv:
if arg and arg[0] == '-': if arg and arg[0] == '-':
msg = argparse._('unrecognized arguments: %s') lines = ['unrecognized arguments: %s' % (' '.join(argv))]
self.error(msg % ' '.join(argv)) for k, v in sorted(self.extra_info.items()):
lines.append(' %s: %s' % (k, v))
self.error('\n'.join(lines))
getattr(args, FILE_OR_DIR).extend(argv) getattr(args, FILE_OR_DIR).extend(argv)
return args return args
@ -863,6 +875,8 @@ class Config(object):
parsed_args = self._parser.parse_known_args(args) parsed_args = self._parser.parse_known_args(args)
r = determine_setup(parsed_args.inifilename, parsed_args.file_or_dir) r = determine_setup(parsed_args.inifilename, parsed_args.file_or_dir)
self.rootdir, self.inifile, self.inicfg = r self.rootdir, self.inifile, self.inicfg = r
self._parser.extra_info['rootdir'] = self.rootdir
self._parser.extra_info['inifile'] = self.inifile
self.invocation_dir = py.path.local() self.invocation_dir = py.path.local()
self._parser.addini('addopts', 'extra command line options', 'args') self._parser.addini('addopts', 'extra command line options', 'args')
self._parser.addini('minversion', 'minimally required pytest version') self._parser.addini('minversion', 'minimally required pytest version')

View File

@ -326,6 +326,19 @@ def test_cmdline_processargs_simple(testdir):
"*-h*", "*-h*",
]) ])
def test_invalid_options_show_extra_information(testdir):
"""display extra information when pytest exits due to unrecognized
options in the command-line"""
testdir.makeini("""
[pytest]
addopts = --invalid-option
""")
result = testdir.runpytest()
result.stderr.fnmatch_lines([
"*error: unrecognized arguments: --invalid-option*",
"* inifile: %s*" % testdir.tmpdir.join('tox.ini'),
"* rootdir: %s*" % testdir.tmpdir,
])
@pytest.mark.skipif("sys.platform == 'win32'") @pytest.mark.skipif("sys.platform == 'win32'")
def test_toolongargs_issue224(testdir): def test_toolongargs_issue224(testdir):