From a67c553beb0253f30262b4e44a1a929f316aebc7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 16 Jun 2020 05:39:36 -0400 Subject: [PATCH] Disable caching when evaluating expressions in marks (#7373) --- changelog/7360.bugfix.rst | 2 ++ src/_pytest/mark/evaluate.py | 21 +++++---------------- testing/test_mark.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 changelog/7360.bugfix.rst diff --git a/changelog/7360.bugfix.rst b/changelog/7360.bugfix.rst new file mode 100644 index 000000000..b84ce4614 --- /dev/null +++ b/changelog/7360.bugfix.rst @@ -0,0 +1,2 @@ +Fix possibly incorrect evaluation of string expressions passed to ``pytest.mark.skipif`` and ``pytest.mark.xfail``, +in rare circumstances where the exact same string is used but refers to different global values. diff --git a/src/_pytest/mark/evaluate.py b/src/_pytest/mark/evaluate.py index 759191668..eb9903a59 100644 --- a/src/_pytest/mark/evaluate.py +++ b/src/_pytest/mark/evaluate.py @@ -10,25 +10,14 @@ from typing import Optional from ..outcomes import fail from ..outcomes import TEST_OUTCOME from .structures import Mark -from _pytest.config import Config from _pytest.nodes import Item -from _pytest.store import StoreKey -evalcache_key = StoreKey[Dict[str, Any]]() +def compiled_eval(expr: str, d: Dict[str, object]) -> Any: + import _pytest._code - -def cached_eval(config: Config, expr: str, d: Dict[str, object]) -> Any: - default = {} # type: Dict[str, object] - evalcache = config._store.setdefault(evalcache_key, default) - try: - return evalcache[expr] - except KeyError: - import _pytest._code - - exprcode = _pytest._code.compile(expr, mode="eval") - evalcache[expr] = x = eval(exprcode, d) - return x + exprcode = _pytest._code.compile(expr, mode="eval") + return eval(exprcode, d) class MarkEvaluator: @@ -98,7 +87,7 @@ class MarkEvaluator: self.expr = expr if isinstance(expr, str): d = self._getglobals() - result = cached_eval(self.item.config, expr, d) + result = compiled_eval(expr, d) else: if "reason" not in mark.kwargs: # XXX better be checked at collection time diff --git a/testing/test_mark.py b/testing/test_mark.py index cdd4df9dd..f261c8922 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -706,6 +706,36 @@ class TestFunctional: reprec = testdir.inline_run() reprec.assertoutcome(skipped=1) + def test_reevaluate_dynamic_expr(self, testdir): + """#7360""" + py_file1 = testdir.makepyfile( + test_reevaluate_dynamic_expr1=""" + import pytest + + skip = True + + @pytest.mark.skipif("skip") + def test_should_skip(): + assert True + """ + ) + py_file2 = testdir.makepyfile( + test_reevaluate_dynamic_expr2=""" + import pytest + + skip = False + + @pytest.mark.skipif("skip") + def test_should_not_skip(): + assert True + """ + ) + + file_name1 = os.path.basename(py_file1.strpath) + file_name2 = os.path.basename(py_file2.strpath) + reprec = testdir.inline_run(file_name1, file_name2) + reprec.assertoutcome(passed=1, skipped=1) + class TestKeywordSelection: def test_select_simple(self, testdir):