Skip to content

Commit 0cfd873

Browse files
committed
implement index-based mechanizm for collection of parametrized tests
--HG-- branch : parametrize-hashable
1 parent d30ad3f commit 0cfd873

File tree

3 files changed

+47
-10
lines changed

3 files changed

+47
-10
lines changed

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Unreleased
22
-----------------------------------
33

4+
- fix issue244 by implementing special index for parameters to only use
5+
indices for paramentrized test ids
6+
47
- fix issue287 by running all finalizers but saving the exception
58
from the first failing finalizer and re-raising it so teardown will
69
still have failed. We reraise the first failing exception because

_pytest/python.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -594,12 +594,14 @@ def __init__(self, metafunc):
594594
self._globalparam = _notexists
595595
self._arg2scopenum = {} # used for sorting parametrized resources
596596
self.keywords = {}
597+
self.indices = {}
597598

598599
def copy(self, metafunc):
599600
cs = CallSpec2(self.metafunc)
600601
cs.funcargs.update(self.funcargs)
601602
cs.params.update(self.params)
602603
cs.keywords.update(self.keywords)
604+
cs.indices.update(self.indices)
603605
cs._arg2scopenum.update(self._arg2scopenum)
604606
cs._idlist = list(self._idlist)
605607
cs._globalid = self._globalid
@@ -623,14 +625,16 @@ def getparam(self, name):
623625
def id(self):
624626
return "-".join(map(str, filter(None, self._idlist)))
625627

626-
def setmulti(self, valtype, argnames, valset, id, keywords, scopenum=0):
628+
def setmulti(self, valtype, argnames, valset, id, keywords, scopenum,
629+
param_index):
627630
for arg,val in zip(argnames, valset):
628631
self._checkargnotcontained(arg)
629632
getattr(self, valtype)[arg] = val
630633
# we want self.params to be always set because of
631634
# parametrize_sorted() which groups tests by params/scope
632635
if valtype == "funcargs":
633636
self.params[arg] = id
637+
self.indices[arg] = param_index
634638
self._arg2scopenum[arg] = scopenum
635639
if val is _notexists:
636640
self._emptyparamspecified = True
@@ -740,11 +744,12 @@ def parametrize(self, argnames, argvalues, indirect=False, ids=None,
740744
ids = idmaker(argnames, argvalues)
741745
newcalls = []
742746
for callspec in self._calls or [CallSpec2(self)]:
743-
for i, valset in enumerate(argvalues):
747+
for param_index, valset in enumerate(argvalues):
744748
assert len(valset) == len(argnames)
745749
newcallspec = callspec.copy(self)
746-
newcallspec.setmulti(valtype, argnames, valset, ids[i],
747-
newkeywords.get(i, {}), scopenum)
750+
newcallspec.setmulti(valtype, argnames, valset, ids[param_index],
751+
newkeywords.get(param_index, {}), scopenum,
752+
param_index)
748753
newcalls.append(newcallspec)
749754
self._calls = newcalls
750755

@@ -1862,33 +1867,33 @@ def getfuncargparams(item, ignore, scopenum, cache):
18621867
except AttributeError:
18631868
return []
18641869
if scopenum == 0:
1865-
argparams = [x for x in cs.params.items() if x not in ignore
1870+
argparams = [x for x in cs.indices.items() if x not in ignore
18661871
and cs._arg2scopenum[x[0]] == scopenum]
18671872
elif scopenum == 1: # module
18681873
argparams = []
1869-
for argname, param in cs.params.items():
1874+
for argname, valindex in cs.indices.items():
18701875
if cs._arg2scopenum[argname] == scopenum:
1871-
key = (argname, param, item.fspath)
1876+
key = (argname, valindex, item.fspath)
18721877
if key in ignore:
18731878
continue
18741879
argparams.append(key)
18751880
elif scopenum == 2: # class
18761881
argparams = []
1877-
for argname, param in cs.params.items():
1882+
for argname, valindex in cs.indices.items():
18781883
if cs._arg2scopenum[argname] == scopenum:
18791884
l = cache.setdefault(item.fspath, [])
18801885
try:
18811886
i = l.index(item.cls)
18821887
except ValueError:
18831888
i = len(l)
18841889
l.append(item.cls)
1885-
key = (argname, param, item.fspath, i)
1890+
key = (argname, valindex, item.fspath, i)
18861891
if key in ignore:
18871892
continue
18881893
argparams.append(key)
18891894
#elif scopenum == 3:
18901895
# argparams = []
1891-
# for argname, param in cs.params.items():
1896+
# for argname, param in cs.indices.items():
18921897
# if cs._arg2scopenum[argname] == scopenum:
18931898
# key = (argname, param, getfslineno(item.obj))
18941899
# if key in ignore:

testing/python/collect.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,35 @@ def test_archival_to_version(key, value):
355355
rec = testdir.inline_run()
356356
rec.assertoutcome(passed=2)
357357

358+
359+
def test_parametrize_with_non_hashable_values_indirect(self, testdir):
360+
"""Test parametrization with non-hashable values with indirect parametrization."""
361+
testdir.makepyfile("""
362+
archival_mapping = {
363+
'1.0': {'tag': '1.0'},
364+
'1.2.2a1': {'tag': 'release-1.2.2a1'},
365+
}
366+
367+
import pytest
368+
369+
@pytest.fixture
370+
def key(request):
371+
return request.param
372+
373+
@pytest.fixture
374+
def value(request):
375+
return request.param
376+
377+
@pytest.mark.parametrize('key value'.split(),
378+
archival_mapping.items(), indirect=True)
379+
def test_archival_to_version(key, value):
380+
assert key in archival_mapping
381+
assert value == archival_mapping[key]
382+
""")
383+
rec = testdir.inline_run()
384+
rec.assertoutcome(passed=2)
385+
386+
358387
def test_parametrize_with_mark(selfself, testdir):
359388
items = testdir.getitems("""
360389
import pytest

0 commit comments

Comments
 (0)