Disable caching when evaluating expressions in marks (#7373)

This commit is contained in:
Andrew 2020-06-16 05:39:36 -04:00 committed by GitHub
parent b6fd89ef31
commit a67c553beb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 16 deletions

View File

@ -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.

View File

@ -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

View File

@ -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):