From 8a6aa5e17edcbafc9c37c11acdb17e32af024d62 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 8 Jul 2015 21:22:08 -0300 Subject: [PATCH] Print inifile and rootdir when there's usage errors Related to #821 --- CHANGELOG | 5 +++++ _pytest/config.py | 24 +++++++++++++++++++----- testing/test_config.py | 13 +++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 349e072cd..89817c6dd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,11 @@ 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 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). diff --git a/_pytest/config.py b/_pytest/config.py index a717c5f0a..f06dcbbba 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -382,7 +382,11 @@ class PytestPluginManager(PluginManager): 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): self._anonymous = OptionGroup("custom options", parser=self) @@ -391,6 +395,7 @@ class Parser: self._usage = usage self._inidict = {} self._ininames = [] + self.extra_info = {} def processoption(self, option): if self._processopt: @@ -444,7 +449,7 @@ class Parser: def _getparser(self): from _pytest._argcomplete import filescompleter - optparser = MyOptionParser(self) + optparser = MyOptionParser(self, self.extra_info) groups = self._groups + [self._anonymous] for group in groups: if group.options: @@ -669,10 +674,15 @@ class OptionGroup: class MyOptionParser(argparse.ArgumentParser): - def __init__(self, parser): + def __init__(self, parser, extra_info=None): + if not extra_info: + extra_info = {} self._parser = parser argparse.ArgumentParser.__init__(self, usage=parser._usage, 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): """allow splitting of positional arguments""" @@ -680,8 +690,10 @@ class MyOptionParser(argparse.ArgumentParser): if argv: for arg in argv: if arg and arg[0] == '-': - msg = argparse._('unrecognized arguments: %s') - self.error(msg % ' '.join(argv)) + lines = ['unrecognized arguments: %s' % (' '.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) return args @@ -863,6 +875,8 @@ class Config(object): parsed_args = self._parser.parse_known_args(args) r = determine_setup(parsed_args.inifilename, parsed_args.file_or_dir) 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._parser.addini('addopts', 'extra command line options', 'args') self._parser.addini('minversion', 'minimally required pytest version') diff --git a/testing/test_config.py b/testing/test_config.py index 73dd6412d..490fa96d0 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -326,6 +326,19 @@ def test_cmdline_processargs_simple(testdir): "*-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'") def test_toolongargs_issue224(testdir):