diff --git a/_pytest/config.py b/_pytest/config.py index 525f0fbf2..5ffed81dd 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -455,11 +455,11 @@ class Parser: """ self._anonymous.addoption(*opts, **attrs) - def parse(self, args): + def parse(self, args, namespace=None): from _pytest._argcomplete import try_argcomplete self.optparser = self._getparser() try_argcomplete(self.optparser) - return self.optparser.parse_args([str(x) for x in args]) + return self.optparser.parse_args([str(x) for x in args], namespace=namespace) def _getparser(self): from _pytest._argcomplete import filescompleter @@ -477,25 +477,25 @@ class Parser: optparser.add_argument(FILE_OR_DIR, nargs='*').completer=filescompleter return optparser - def parse_setoption(self, args, option): - parsedoption = self.parse(args) + def parse_setoption(self, args, option, namespace=None): + parsedoption = self.parse(args, namespace=namespace) for name, value in parsedoption.__dict__.items(): setattr(option, name, value) return getattr(parsedoption, FILE_OR_DIR) - def parse_known_args(self, args): + def parse_known_args(self, args, namespace=None): """parses and returns a namespace object with known arguments at this point. """ - return self.parse_known_and_unknown_args(args)[0] + return self.parse_known_and_unknown_args(args, namespace=namespace)[0] - def parse_known_and_unknown_args(self, args): + def parse_known_and_unknown_args(self, args, namespace=None): """parses and returns a namespace object with known arguments, and the remaining arguments unknown at this point. """ optparser = self._getparser() args = [str(x) for x in args] - return optparser.parse_known_args(args) + return optparser.parse_known_args(args, namespace=namespace) def addini(self, name, help, type=None, default=None): """ register an ini-file option. @@ -779,10 +779,12 @@ def _ensure_removed_sysmodule(modname): class CmdOptions(object): """ holds cmdline options as attributes.""" - def __init__(self, **kwargs): - self.__dict__.update(kwargs) + def __init__(self, values=()): + self.__dict__.update(values) def __repr__(self): return "" %(self.__dict__,) + def copy(self): + return CmdOptions(self.__dict__) class Notset: def __repr__(self): @@ -879,8 +881,8 @@ class Config(object): def fromdictargs(cls, option_dict, args): """ constructor useable for subprocesses. """ config = get_config() - config.parse(args, addopts=False) config.option.__dict__.update(option_dict) + config.parse(args, addopts=False) for x in config.option.plugins: config.pluginmanager.consider_pluginarg(x) return config @@ -898,7 +900,7 @@ class Config(object): self.pluginmanager._set_initial_conftests(early_config.known_args_namespace) def _initini(self, args): - ns, unknown_args = self._parser.parse_known_and_unknown_args(args) + ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy()) r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args) self.rootdir, self.inifile, self.inicfg = r self._parser.extra_info['rootdir'] = self.rootdir @@ -919,7 +921,7 @@ class Config(object): except ImportError as e: self.warn("I2", "could not load setuptools entry import: %s" % (e,)) self.pluginmanager.consider_env() - self.known_args_namespace = ns = self._parser.parse_known_args(args) + self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy()) if self.known_args_namespace.confcutdir is None and self.inifile: confcutdir = py.path.local(self.inifile).dirname self.known_args_namespace.confcutdir = confcutdir @@ -957,7 +959,7 @@ class Config(object): self._preparse(args, addopts=addopts) # XXX deprecated hook: self.hook.pytest_cmdline_preparse(config=self, args=args) - args = self._parser.parse_setoption(args, self.option) + args = self._parser.parse_setoption(args, self.option, namespace=self.option) if not args: cwd = os.getcwd() if cwd == self.rootdir: diff --git a/testing/test_config.py b/testing/test_config.py index 981138e75..f8492097c 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -269,42 +269,56 @@ class TestConfigFromdictargs: def test_basic_behavior(self): from _pytest.config import Config option_dict = { - 'verbose': 1e100, + 'verbose': 444, 'foo': 'bar', + 'capture': 'no', } args = ['a', 'b'] config = Config.fromdictargs(option_dict, args) with pytest.raises(AssertionError): - config.parse(['should to parse again']) - assert config.option.verbose == 1e100 + config.parse(['should refuse to parse again']) + assert config.option.verbose == 444 assert config.option.foo == 'bar' + assert config.option.capture == 'no' assert config.args == args def test_origargs(self): """Show that fromdictargs can handle args in their "orig" format""" from _pytest.config import Config option_dict = {} - args = ['-vvvv', 'a', 'b'] + args = ['-vvvv', '-s', 'a', 'b'] config = Config.fromdictargs(option_dict, args) assert config.args == ['a', 'b'] - assert config._origargs == ['-vvvv', 'a', 'b'] + assert config._origargs == args assert config.option.verbose == 4 + assert config.option.capture == 'no' + + def test_inifilename(self, tmpdir): + tmpdir.join("foo/bar.ini").ensure().write(py.code.Source(""" + [pytest] + name = value + """)) - @pytest.mark.xfail(reason="fromdictargs currently broken #1060") - def test_inifilename(self): from _pytest.config import Config - inifile = '../../foo/bar.ini', + inifile = '../../foo/bar.ini' option_dict = { 'inifilename': inifile, + 'capture': 'no', } - config = Config.fromdictargs(option_dict, ()) + cwd = tmpdir.join('a/b') + with cwd.ensure(dir=True).as_cwd(): + config = Config.fromdictargs(option_dict, ()) + + assert config.args == [str(cwd)] assert config.option.inifilename == inifile + assert config.option.capture == 'no' # this indicates this is the file used for getting configuration values assert config.inifile == inifile + assert config.inicfg.get('name') == 'value' def test_options_on_small_file_do_not_blow_up(testdir):