Skip to content

Commit

Permalink
[WIP/RFC] parametrized: ids: support generator/iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
blueyed committed Nov 14, 2019
1 parent eae61d1 commit 91dfd2c
Showing 1 changed file with 37 additions and 16 deletions.
53 changes: 37 additions & 16 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -943,13 +943,18 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None, scope=None)
function so that it can perform more expensive setups during the
setup phase of a test rather than at collection time.
:arg ids: list of string ids, or a callable.
:arg ids: sequence of string ids, a generator or a callable.
If strings, each is corresponding to the argvalues so that they are
part of the test id. If None is given as id of specific test, the
automatically generated id for that argument will be used.
If callable, it should take one argument (a single argvalue) and return
a string or return None. If None, the automatically generated id for that
argument will be used.
If callable, it should take either one argument (a single argvalue)
and return a string or return None. If it returns None, the
automatically generated id for that argument will be used.
You can also pass a generator, or iterator like
``itertools.count()``.
If no ids are provided they will be generated automatically from
the argvalues.
Expand Down Expand Up @@ -1013,27 +1018,43 @@ def _resolve_arg_ids(self, argnames, ids, parameters, item):
:rtype: List[str]
:return: the list of ids for each argname given
"""
from _pytest._io.saferepr import saferepr

idfn = None
if callable(ids):
idfn = ids
ids = None
if ids:
func_name = self.function.__name__
if len(ids) != len(parameters):
msg = "In {}: {} parameter sets specified, with different number of ids: {}"
fail(msg.format(func_name, len(parameters), len(ids)), pytrace=False)
for id_value in ids:
if id_value is not None and not isinstance(id_value, str):
msg = "In {}: ids must be list of strings, found: {} (type: {!r})"
fail(
msg.format(func_name, saferepr(id_value), type(id_value)),
pytrace=False,
)
ids = self._validate_ids(ids, parameters, func_name)
ids = idmaker(argnames, parameters, idfn, ids, self.config, item=item)
return ids

def _validate_ids(self, ids, parameters, func_name):
try:
len_ids = len(ids)
except TypeError:
try:
it = iter(ids)
except TypeError:
raise TypeError("ids must be a callable, sequence or generator")
else:
import itertools

ids = list(itertools.islice(it, len(parameters)))

if len_ids != len(parameters):
msg = "In {}: {} parameter sets specified, with different number of ids: {}"
fail(msg.format(func_name, len(parameters), len(ids)), pytrace=False)
for id_value in ids:
if id_value is not None and not isinstance(id_value, str):
from _pytest._io.saferepr import saferepr

msg = "In {}: ids must be list of strings, found: {} (type: {!r})"
fail(
msg.format(func_name, saferepr(id_value), type(id_value)),
pytrace=False,
)
return ids

def _resolve_arg_value_types(self, argnames, indirect):
"""Resolves if each parametrized argument must be considered a parameter to a fixture or a "funcarg"
to the function, based on the ``indirect`` parameter of the parametrized() call.
Expand Down

0 comments on commit 91dfd2c

Please sign in to comment.