Merge pull request #822 from nicoddemus/extra-usage-info
Print inifile and rootdir when there's usage errors
This commit is contained in:
		
						commit
						1baa1a4d01
					
				| 
						 | 
					@ -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). 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue