Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'-o' option no longer swallows all other non-options after it #3147

Merged
merged 7 commits into from
Jan 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Anthon van der Neut
Anthony Sottile
Antony Lee
Armin Rigo
Aron Coyle
Aron Curzon
Aviv Palivoda
Barney Gale
Expand Down
17 changes: 8 additions & 9 deletions _pytest/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1188,16 +1188,15 @@ def _getconftest_pathlist(self, name, path):

def _get_override_ini_value(self, name):
value = None
# override_ini is a list of list, to support both -o foo1=bar1 foo2=bar2 and
# and -o foo1=bar1 -o foo2=bar2 options
# always use the last item if multiple value set for same ini-name,
# override_ini is a list of "ini=value" options
# always use the last item if multiple values are set for same ini-name,
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
for ini_config_list in self._override_ini:
for ini_config in ini_config_list:
try:
(key, user_ini_value) = ini_config.split("=", 1)
except ValueError:
raise UsageError("-o/--override-ini expects option=value style.")
for ini_config in self._override_ini:
try:
key, user_ini_value = ini_config.split("=", 1)
except ValueError:
raise UsageError("-o/--override-ini expects option=value style.")
else:
if key == name:
value = user_ini_value
return value
Expand Down
4 changes: 2 additions & 2 deletions _pytest/helpconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ def pytest_addoption(parser):
action="store_true", dest="debug", default=False,
help="store internal tracing debug information in 'pytestdebug.log'.")
group._addoption(
'-o', '--override-ini', nargs='*', dest="override_ini",
'-o', '--override-ini', dest="override_ini",
action="append",
help="override config option with option=value style, e.g. `-o xfail_strict=True`.")
help='override ini option with "option=value" style, e.g. `-o xfail_strict=True -o cache_dir=cache`.')


@pytest.hookimpl(hookwrapper=True)
Expand Down
1 change: 1 addition & 0 deletions changelog/3103.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**Incompatible change**: ``-o/--override`` option no longer eats all the remaining options, which can lead to surprising behavior: for example, ``pytest -o foo=1 /path/to/test.py`` would fail because ``/path/to/test.py`` would be considered as part of the ``-o`` command-line argument. One consequence of this is that now multiple configuration overrides need multiple ``-o`` flags: ``pytest -o foo=1 -o bar=2``.
17 changes: 16 additions & 1 deletion doc/en/customize.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,25 @@ above will show verbose output because ``-v`` overwrites ``-q``.
Builtin configuration file options
----------------------------------------------

Here is a list of builtin configuration options that may be written in a ``pytest.ini``, ``tox.ini`` or ``setup.cfg``
file, usually located at the root of your repository. All options must be under a ``[pytest]`` section
(``[tool:pytest]`` for ``setup.cfg`` files).

Configuration file options may be overwritten in the command-line by using ``-o/--override``, which can also be
passed multiple times. The expected format is ``name=value``. For example::

pytest -o console_output_style=classic -o cache_dir=/tmp/mycache


.. confval:: minversion

Specifies a minimal pytest version required for running tests.

minversion = 2.1 # will fail if we run with pytest-2.0
.. code-block:: ini

# content of pytest.ini
[pytest]
minversion = 3.0 # will fail if we run with pytest-2.8

.. confval:: addopts

Expand All @@ -165,6 +179,7 @@ Builtin configuration file options

.. code-block:: ini

# content of pytest.ini
[pytest]
addopts = --maxfail=2 -rf # exit after 2 failures, report fail info

Expand Down
44 changes: 39 additions & 5 deletions testing/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -781,16 +781,18 @@ def pytest_addoption(parser):
testdir.makeini("""
[pytest]
custom_option_1=custom_option_1
custom_option_2=custom_option_2""")
custom_option_2=custom_option_2
""")
testdir.makepyfile("""
def test_multiple_options(pytestconfig):
prefix = "custom_option"
for x in range(1, 5):
ini_value=pytestconfig.getini("%s_%d" % (prefix, x))
print('\\nini%d:%s' % (x, ini_value))""")
print('\\nini%d:%s' % (x, ini_value))
""")
result = testdir.runpytest(
"--override-ini", 'custom_option_1=fulldir=/tmp/user1',
'custom_option_2=url=/tmp/user2?a=b&d=e',
'-o', 'custom_option_2=url=/tmp/user2?a=b&d=e',
"-o", 'custom_option_3=True',
"-o", 'custom_option_4=no', "-s")
result.stdout.fnmatch_lines(["ini1:fulldir=/tmp/user1",
Expand Down Expand Up @@ -853,10 +855,42 @@ def test_with_existing_file_in_subdir(self, tmpdir):
assert rootdir == tmpdir
assert inifile is None

def test_addopts_before_initini(self, testdir, tmpdir, monkeypatch):
def test_addopts_before_initini(self, monkeypatch):
cache_dir = '.custom_cache'
monkeypatch.setenv('PYTEST_ADDOPTS', '-o cache_dir=%s' % cache_dir)
from _pytest.config import get_config
config = get_config()
config._preparse([], addopts=True)
assert config._override_ini == [['cache_dir=%s' % cache_dir]]
assert config._override_ini == ['cache_dir=%s' % cache_dir]

def test_override_ini_does_not_contain_paths(self):
"""Check that -o no longer swallows all options after it (#3103)"""
from _pytest.config import get_config
config = get_config()
config._preparse(['-o', 'cache_dir=/cache', '/some/test/path'])
assert config._override_ini == ['cache_dir=/cache']

def test_multiple_override_ini_options(self, testdir, request):
"""Ensure a file path following a '-o' option does not generate an error (#3103)"""
testdir.makepyfile(**{
"conftest.py": """
def pytest_addoption(parser):
parser.addini('foo', default=None, help='some option')
parser.addini('bar', default=None, help='some option')
""",
"test_foo.py": """
def test(pytestconfig):
assert pytestconfig.getini('foo') == '1'
assert pytestconfig.getini('bar') == '0'
""",
"test_bar.py": """
def test():
assert False
""",
})
result = testdir.runpytest('-o', 'foo=1', '-o', 'bar=0', 'test_foo.py')
assert 'ERROR:' not in result.stderr.str()
result.stdout.fnmatch_lines([
'collected 1 item',
'*= 1 passed in *=',
])