diff --git a/AUTHORS b/AUTHORS index aa2237c6837..a3200f774cf 100644 --- a/AUTHORS +++ b/AUTHORS @@ -112,6 +112,7 @@ Guido Wesdorp Guoqiang Zhang Harald Armin Massa Henk-Jaap Wagenaar +Holger Kohr Hugo van Kemenade Hui Wang (coldnight) Ian Bicking diff --git a/changelog/6497.bugfix.rst b/changelog/6497.bugfix.rst new file mode 100644 index 00000000000..66d436abd99 --- /dev/null +++ b/changelog/6497.bugfix.rst @@ -0,0 +1,4 @@ +Fix bug in the comparison of request key with cached key in fixture. + +A construct ``if key == cached_key:`` can fail either because ``==`` is explicitly disallowed, or for, e.g., NumPy arrays, where the result of ``a == b`` cannot generally be converted to `bool`. +The implemented fix replaces `==` with ``is``. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 5d54078de6f..e950fc9e859 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -898,7 +898,9 @@ def execute(self, request): cached_result = getattr(self, "cached_result", None) if cached_result is not None: result, cache_key, err = cached_result - if my_cache_key == cache_key: + # note: comparison with `==` can fail (or be expensive) for e.g. + # numpy arrays (#6497) + if my_cache_key is cache_key: if err is not None: _, val, tb = err raise val.with_traceback(tb) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index d9ea5cf58f9..bfbe359515c 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -1102,6 +1102,38 @@ def test_nothing(badscope): "*Fixture 'badscope' from test_invalid_scope.py got an unexpected scope value 'functions'" ) + @pytest.mark.parametrize("scope", ["function", "session"]) + def test_parameters_without_eq_semantics(self, scope, testdir): + testdir.makepyfile( + """ + class NoEq1: # fails on `a == b` statement + def __eq__(self, _): + raise RuntimeError + + class NoEq2: # fails on `if a == b:` statement + def __eq__(self, _): + class NoBool: + def __bool__(self): + raise RuntimeError + return NoBool() + + import pytest + @pytest.fixture(params=[NoEq1(), NoEq2()], scope={scope!r}) + def no_eq(request): + return request.param + + def test1(no_eq): + pass + + def test2(no_eq): + pass + """.format( + scope=scope + ) + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines(["*4 passed*"]) + def test_funcarg_parametrized_and_used_twice(self, testdir): testdir.makepyfile( """