Skip to content

Commit

Permalink
added indicative error when parametrizing an argument with a default …
Browse files Browse the repository at this point in the history
…value
  • Loading branch information
brianmaissy committed Apr 12, 2018
1 parent 2962c73 commit d3fb77f
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 7 deletions.
8 changes: 8 additions & 0 deletions _pytest/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ def getfuncargnames(function, is_method=False, cls=None):
return arg_names


def get_default_arg_names(function):
# Note: this code intentionally mirrors the code at the beginning of getfuncargnames,
# to get the arguments which were excluded from its result because they had default values
return tuple(p.name for p in signature(function).parameters.values()
if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) and
p.default is not Parameter.empty)


if _PY3:
STRING_TYPES = bytes, str
UNICODE_TYPES = str,
Expand Down
18 changes: 11 additions & 7 deletions _pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
isclass, isfunction, is_generator, ascii_escaped,
REGEX_TYPE, STRING_TYPES, NoneType, NOTSET,
get_real_func, getfslineno, safe_getattr,
safe_str, getlocation, enum,
safe_str, getlocation, enum, get_default_arg_names
)
from _pytest.outcomes import fail
from _pytest.mark.structures import transfer_markers
Expand Down Expand Up @@ -789,6 +789,7 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
argnames, parameters = ParameterSet._for_parametrize(
argnames, argvalues, self.function, self.config)
del argvalues
default_arg_names = set(get_default_arg_names(self.function))

if scope is None:
scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
Expand All @@ -797,13 +798,16 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
valtypes = {}
for arg in argnames:
if arg not in self.fixturenames:
if isinstance(indirect, (tuple, list)):
name = 'fixture' if arg in indirect else 'argument'
if arg in default_arg_names:
raise ValueError("%r already takes an argument %r with a default value" % (self.function, arg))
else:
name = 'fixture' if indirect else 'argument'
raise ValueError(
"%r uses no %s %r" % (
self.function, name, arg))
if isinstance(indirect, (tuple, list)):
name = 'fixture' if arg in indirect else 'argument'
else:
name = 'fixture' if indirect else 'argument'
raise ValueError(
"%r uses no %s %r" % (
self.function, name, arg))

if indirect is True:
valtypes = dict.fromkeys(argnames, "params")
Expand Down
1 change: 1 addition & 0 deletions changelog/3221.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added a more indicative error message when parametrizing a function whose argument takes a default value.
13 changes: 13 additions & 0 deletions testing/python/metafunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,19 @@ def test_simple(x):
"*uses no argument 'y'*",
])

def test_parametrize_gives_indicative_error_on_function_with_default_argument(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.mark.parametrize('x, y', [('a', 'b')])
def test_simple(x, y=1):
assert len(x) == 1
""")
result = testdir.runpytest("--collect-only")
result.stdout.fnmatch_lines([
"*already takes an argument 'y' with a default value",
])

def test_addcalls_and_parametrize_indirect(self):
def func(x, y):
pass
Expand Down

0 comments on commit d3fb77f

Please sign in to comment.