Skip to content

Commit

Permalink
Consider usefixtures marker for enforcing params
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Jan 4, 2020
1 parent 25d8b5a commit 2ef9822
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 11 deletions.
16 changes: 12 additions & 4 deletions src/_pytest/fixtures.py
Expand Up @@ -1272,10 +1272,8 @@ def getfixtureinfo(self, node, func, cls, funcargs=True):
else:
argnames = ()

usefixtures = itertools.chain.from_iterable(
mark.args for mark in node.iter_markers(name="usefixtures")
)
initialnames = tuple(usefixtures) + argnames
usefixtures = get_use_fixtures_for_node(node)
initialnames = usefixtures + argnames
fm = node.session._fixturemanager
initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(
initialnames, node, ignore_args=self._get_direct_parametrize_args(node)
Expand Down Expand Up @@ -1472,3 +1470,13 @@ def _matchfactories(self, fixturedefs, nodeid):
for fixturedef in fixturedefs:
if nodes.ischildnode(fixturedef.baseid, nodeid):
yield fixturedef


def get_use_fixtures_for_node(node) -> Tuple[str, ...]:
"""Returns the names of all the usefixtures() marks on the given node"""
return tuple(
str(x)
for x in itertools.chain.from_iterable(

This comment has been minimized.

Copy link
@RonnyPfannschmidt

RonnyPfannschmidt Jan 21, 2020

Member

in this case a 2nd part comprehension on mark.args would be more effective and readable

This comment has been minimized.

Copy link
@nicoddemus

nicoddemus Jan 21, 2020

Author Member

Indeed, thanks, done. 👍

mark.args for mark in node.iter_markers(name="usefixtures")
)
)
17 changes: 10 additions & 7 deletions src/_pytest/python.py
Expand Up @@ -1162,7 +1162,6 @@ def _validate_explicit_parameters(self, argnames, indirect):
:param indirect: same ``indirect`` parameter of ``parametrize()``.
:raise ValueError: if validation fails
"""
func_name = self.function.__name__
if isinstance(indirect, bool) and indirect is True:
return
parametrized_argnames = list()
Expand All @@ -1173,13 +1172,17 @@ def _validate_explicit_parameters(self, argnames, indirect):
parametrized_argnames.append(arg)
elif indirect is False:
parametrized_argnames = argnames

usefixtures = fixtures.get_use_fixtures_for_node(self.definition)

for arg in parametrized_argnames:
if arg not in funcargnames:
raise ValueError(
f'In function "{func_name}":\n'
f'Parameter "{arg}" should be declared explicitly via indirect\n'
f"or in function itself"
)
if arg not in funcargnames and arg not in usefixtures:
func_name = self.function.__name__
msg = (
'In function "{func_name}":\n'
'Parameter "{arg}" should be declared explicitly via indirect or in function itself'
).format(func_name=func_name, arg=arg)
fail(msg, pytrace=False)


def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
Expand Down
3 changes: 3 additions & 0 deletions testing/python/metafunc.py
Expand Up @@ -28,6 +28,9 @@ def __init__(self, names):
class DefinitionMock(python.FunctionDefinition):
obj = attr.ib()

def listchain(self):
return []

names = fixtures.getfuncargnames(func)
fixtureinfo = FixtureInfo(names)
definition = DefinitionMock._create(func)
Expand Down

0 comments on commit 2ef9822

Please sign in to comment.