Skip to content

Commit 184b0dd

Browse files
author
Sylvain MARIE
committed
Better explanation and implementation for empty ids in pytest 5.4+
1 parent fc5f88a commit 184b0dd

File tree

8 files changed

+48
-36
lines changed

8 files changed

+48
-36
lines changed

pytest_cases/fixture_parametrize_plus.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
from .common_mini_six import string_types
2525
from .common_others import AUTO, robust_isinstance
26-
from .common_pytest_marks import has_pytest_param, get_param_argnames_as_list, PYTEST421_OR_GREATER
26+
from .common_pytest_marks import has_pytest_param, get_param_argnames_as_list, PYTEST421_OR_GREATER, PYTEST54_OR_GREATER
2727
from .common_pytest_lazy_values import is_lazy_value, get_lazy_args
2828
from .common_pytest import get_fixture_name, remove_duplicates, mini_idvalset, is_marked_parameter_value, \
2929
extract_parameterset_info, ParameterSet, cart_product_pytest, mini_idval, inject_host, \
@@ -516,9 +516,26 @@ def get_alternative_id(self):
516516
return mini_idvalset(self.argnames, argval, idx=self.alternative_index)
517517

518518

519-
if PYTEST421_OR_GREATER:
519+
if PYTEST54_OR_GREATER:
520+
# an empty string will be taken into account but NOT filtered out in CallSpec2.id.
521+
# so instead we create a dedicated unique string and return it.
522+
# Ugly but the only viable alternative seems worse: it would be to return an empty string
523+
# and in `remove_empty_ids` to always remove all empty strings (not necessary the ones set by us).
524+
# That is too much of a change.
525+
526+
EMPTY_ID = "13h#987__36a721f8Z44526!8*72"
527+
528+
def remove_empty_ids(callspec):
529+
# used by plugin.py to remove the EMPTY_ID from the callspecs
530+
callspec._idlist = [c for c in callspec._idlist if c != EMPTY_ID]
531+
532+
elif PYTEST421_OR_GREATER:
533+
# an empty string will be taken into account and filtered out in CallSpec2.id.
520534
EMPTY_ID = ""
535+
521536
else:
537+
# an empty string will only be taken into account if its truth value is True
538+
# it will be filtered out in CallSpec2.id
522539
class EmptyId(str):
523540
def __new__(cls):
524541
return str.__new__(cls, "")

pytest_cases/plugin.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
# License: 3-clause BSD, <https://github.com/smarie/python-pytest-cases/blob/master/LICENSE>
55
from collections import OrderedDict, namedtuple
66
from copy import copy
7-
from distutils.version import LooseVersion
87
from functools import partial
98
from warnings import warn
109

@@ -34,6 +33,9 @@
3433

3534
from .fixture_core1_unions import NOT_USED, is_fixture_union_params, UnionFixtureAlternative
3635

36+
if PYTEST54_OR_GREATER:
37+
# we will need to clean the empty ids explicitly in the plugin :'(
38+
from .fixture_parametrize_plus import remove_empty_ids
3739

3840
_DEBUG = False
3941

@@ -729,19 +731,22 @@ def __str__(self):
729731
def parametrize(metafunc, argnames, argvalues, indirect=False, ids=None, scope=None, **kwargs):
730732
"""
731733
This alternate implementation of metafunc.parametrize creates a list of calls that is not just the cartesian
732-
product of all parameters (like the pytest behaviour).
733-
734-
Instead, it offers an alternate list of calls takinginto account all union fixtures.
734+
product of all parameters (like the pytest behaviour). Instead, it offers an alternate list of calls taking into
735+
account all "union" fixtures.
735736
736737
For this, it replaces the `metafunc._calls` attribute with a `CallsReactor` instance, and feeds it with all
737-
parameters and parametrized fixtures independently (not doing any cross-product).
738-
739-
The resulting `CallsReactor` instance is then able to dynamically behave like the correct list of calls,
740-
lazy-creating that list when it is used.
738+
parameters and parametrized fixtures independently (not doing any cross-product during this call). The resulting
739+
`CallsReactor` instance is then able to dynamically behave like the correct list of calls, lazy-creating that list
740+
when it is used.
741741
"""
742742
if not isinstance(metafunc.fixturenames, SuperClosure):
743743
# legacy method
744744
metafunc.__class__.parametrize(metafunc, argnames, argvalues, indirect=indirect, ids=ids, scope=scope, **kwargs)
745+
746+
# clean EMPTY_ID : since they are never set by us in a normal parametrize, no need to do this here.
747+
# if PYTEST54_OR_GREATER:
748+
# for callspec in metafunc._calls:
749+
# remove_empty_ids(callspec)
745750
else:
746751
# get or create our special container object
747752
if not isinstance(metafunc._calls, CallsReactor): # noqa
@@ -864,6 +869,11 @@ def create_call_list_from_pending_parametrizations(self):
864869
c.id, c.funcargs, c.params)
865870
for c in calls]) + "\n")
866871

872+
# clean EMPTY_ID set by @parametrize when there is at least a MultiParamsAlternative
873+
if PYTEST54_OR_GREATER:
874+
for callspec in calls:
875+
remove_empty_ids(callspec)
876+
867877
# save the list and put back self as the _calls facade
868878
self._call_list = calls
869879
self.metafunc._calls = self
@@ -1107,12 +1117,6 @@ def _parametrize_calls(metafunc, init_calls, argnames, argvalues, discard_id=Fal
11071117
for callspec in new_calls:
11081118
callspec._idlist.pop(-1) # noqa
11091119

1110-
# Fix in pytest 5.4.0 or greater, empty ids are not filtered out correctly anymore
1111-
if PYTEST54_OR_GREATER and len(new_calls) > 0:
1112-
# old_id = type(new_calls[0]).id # https://github.com/pytest-dev/pytest/blob/5.4.0/src/_pytest/python.py#L798
1113-
if type(new_calls[0]).id is not id:
1114-
type(new_calls[0]).id = id
1115-
11161120
# restore the metafunc and return the new calls
11171121
metafunc._calls = bak
11181122
return new_calls

pytest_cases/tests/cases/doc/test_doc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from pytest_harvest import get_session_synthesis_dct
88
from pytest_cases import parametrize_with_cases, AUTO2, fixture, case
9-
from pytest_cases.common_pytest import has_pytest_param
9+
from pytest_cases.common_pytest_marks import has_pytest_param
1010

1111
from . import cases_doc
1212
from .example import foo

pytest_cases/tests/cases/doc/test_generators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pytest_harvest import get_session_synthesis_dct
88

99
from pytest_cases import parametrize_with_cases, parametrize
10-
from pytest_cases.common_pytest import has_pytest_param
10+
from pytest_cases.common_pytest_marks import has_pytest_param
1111

1212
from ...utils import skip
1313

pytest_cases/tests/cases/issues/test_issue_142.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,10 @@ def test_empty_caseid_both(dummy_amount):
3131

3232

3333
def test_synthesis(module_results_dct):
34-
if PYTEST421_OR_GREATER:
35-
# an empty string id will be considered
36-
if PYTEST54_OR_GREATER:
37-
# it will even be kept in CallSpec2.id
38-
last_id_prefix = "-"
39-
else:
40-
# it will be filtered out in CallSpec2.id
41-
last_id_prefix = ""
42-
else:
43-
# an empty string will not be considered
44-
last_id_prefix = "test_empty_caseid_both_dummy_amount1-"
45-
4634
assert list(module_results_dct) == [
4735
'test_empty_prefix[<empty_case_id>-1]',
4836
'test_empty_prefix[<empty_case_id>-0]',
4937
'test_empty_prefix[<empty_case_id>--1]',
50-
'test_empty_caseid_both[%s]' % ("" if PYTEST421_OR_GREATER else "test_empty_caseid_both_dummy_amount0"),
51-
'test_empty_caseid_both[%s1]' % last_id_prefix,
38+
'test_empty_caseid_both[%s]' % ("0" if PYTEST421_OR_GREATER else "test_empty_caseid_both_dummy_amount0"),
39+
'test_empty_caseid_both[%s-1]' % ("1" if PYTEST421_OR_GREATER else "test_empty_caseid_both_dummy_amount1"),
5240
]

pytest_cases/tests/pytest_extension/parametrize_plus/test_basics_misc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
from pytest_cases import parametrize, lazy_value
1010
from pytest_harvest import get_session_synthesis_dct
1111

12-
from pytest_cases.common_pytest import has_pytest_param, cart_product_pytest, get_marked_parameter_values, \
12+
from pytest_cases.common_pytest import cart_product_pytest, get_marked_parameter_values, \
1313
extract_parameterset_info, extract_pset_info_single
14-
from pytest_cases.common_pytest_marks import PYTEST3_OR_GREATER
14+
from pytest_cases.common_pytest_marks import PYTEST3_OR_GREATER, has_pytest_param
1515
from pytest_cases.common_pytest_lazy_values import is_lazy
1616
from pytest_cases.fixture_parametrize_plus import _get_argnames_argvalues
1717
from ...utils import skip

pytest_cases/tests/pytest_extension/parametrize_plus/test_getcallspecs.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import pytest
66

77
from pytest_cases import parametrize
8-
from pytest_cases.common_pytest import get_callspecs, has_pytest_param
8+
from pytest_cases.common_pytest import get_callspecs
9+
from pytest_cases.common_pytest_marks import has_pytest_param
10+
911

1012
if not has_pytest_param:
1113
@pytest.mark.parametrize('new_style', [False, True])

pytest_cases/tests/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
# License: 3-clause BSD, <https://github.com/smarie/python-pytest-cases/blob/master/LICENSE>
55
import pytest
66

7-
from pytest_cases.common_pytest import has_pytest_param
7+
from pytest_cases.common_pytest_marks import has_pytest_param
8+
89

910
if has_pytest_param:
1011
def skip(*argvals):

0 commit comments

Comments
 (0)