implement funcarg factory scope marker and ScopeMismatch detection
This commit is contained in:
parent
f358fe7154
commit
80db25822c
|
@ -886,6 +886,7 @@ class FuncargRequest:
|
|||
self._currentarg = None
|
||||
self.funcargnames = getfuncargnames(self.function)
|
||||
self.parentid = pyfuncitem.parent.nodeid
|
||||
self.scope = "function"
|
||||
|
||||
def _getfaclist(self, argname):
|
||||
faclist = self._name2factory.get(argname, None)
|
||||
|
@ -982,6 +983,9 @@ class FuncargRequest:
|
|||
try:
|
||||
val = cache[cachekey]
|
||||
except KeyError:
|
||||
__tracebackhide__ = True
|
||||
check_scope(self.scope, scope)
|
||||
__tracebackhide__ = False
|
||||
val = setup()
|
||||
cache[cachekey] = val
|
||||
if teardown is not None:
|
||||
|
@ -1007,7 +1011,6 @@ class FuncargRequest:
|
|||
factorylist = self._getfaclist(argname)
|
||||
funcargfactory = factorylist.pop()
|
||||
node = self._pyfuncitem
|
||||
oldarg = self._currentarg
|
||||
mp = monkeypatch()
|
||||
mp.setattr(self, '_currentarg', argname)
|
||||
try:
|
||||
|
@ -1016,11 +1019,24 @@ class FuncargRequest:
|
|||
pass
|
||||
else:
|
||||
mp.setattr(self, 'param', param, raising=False)
|
||||
try:
|
||||
self._funcargs[argname] = val = funcargfactory(request=self)
|
||||
return val
|
||||
finally:
|
||||
mp.undo()
|
||||
|
||||
# implemenet funcarg marker scope
|
||||
marker = getattr(funcargfactory, "funcarg", None)
|
||||
scope = None
|
||||
if marker is not None:
|
||||
scope = marker.kwargs.get("scope")
|
||||
if scope is not None:
|
||||
__tracebackhide__ = True
|
||||
check_scope(self.scope, scope)
|
||||
__tracebackhide__ = False
|
||||
mp.setattr(self, "scope", scope)
|
||||
val = self.cached_setup(lambda: funcargfactory(request=self),
|
||||
scope=scope)
|
||||
else:
|
||||
val = funcargfactory(request=self)
|
||||
mp.undo()
|
||||
self._funcargs[argname] = val
|
||||
return val
|
||||
|
||||
def _getscopeitem(self, scope):
|
||||
if scope == "function":
|
||||
|
@ -1039,7 +1055,7 @@ class FuncargRequest:
|
|||
def addfinalizer(self, finalizer):
|
||||
"""add finalizer function to be called after test function
|
||||
finished execution. """
|
||||
self._addfinalizer(finalizer, scope="function")
|
||||
self._addfinalizer(finalizer, scope=self.scope)
|
||||
|
||||
def _addfinalizer(self, finalizer, scope):
|
||||
colitem = self._getscopeitem(scope)
|
||||
|
@ -1049,3 +1065,15 @@ class FuncargRequest:
|
|||
def __repr__(self):
|
||||
return "<FuncargRequest for %r>" %(self._pyfuncitem)
|
||||
|
||||
class ScopeMismatchError(Exception):
|
||||
""" A funcarg factory tries to access a funcargvalue/factory
|
||||
which has a lower scope (e.g. a Session one calls a function one)
|
||||
"""
|
||||
scopes = "session module class function".split()
|
||||
def check_scope(currentscope, newscope):
|
||||
__tracebackhide__ = True
|
||||
i_currentscope = scopes.index(currentscope)
|
||||
i_newscope = scopes.index(newscope)
|
||||
if i_newscope > i_currentscope:
|
||||
raise ScopeMismatchError("You tried to access a %r scoped funcarg "
|
||||
"from a %r scoped one." % (newscope, currentscope))
|
||||
|
|
|
@ -58,7 +58,7 @@ class TestClass:
|
|||
])
|
||||
|
||||
def test_setup_teardown_class_as_classmethod(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
testdir.makepyfile(test_mod1="""
|
||||
class TestClassMethod:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
|
@ -1698,3 +1698,110 @@ class TestFuncargMarker:
|
|||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=4)
|
||||
|
||||
def test_scope_session(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
l = []
|
||||
@pytest.mark.funcarg(scope="module")
|
||||
def pytest_funcarg__arg(request):
|
||||
l.append(1)
|
||||
return 1
|
||||
|
||||
def test_1(arg):
|
||||
assert arg == 1
|
||||
def test_2(arg):
|
||||
assert arg == 1
|
||||
assert len(l) == 1
|
||||
class TestClass:
|
||||
def test3(self, arg):
|
||||
assert arg == 1
|
||||
assert len(l) == 1
|
||||
""")
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=3)
|
||||
|
||||
def test_scope_module_uses_session(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
l = []
|
||||
@pytest.mark.funcarg(scope="module")
|
||||
def pytest_funcarg__arg(request):
|
||||
l.append(1)
|
||||
return 1
|
||||
|
||||
def test_1(arg):
|
||||
assert arg == 1
|
||||
def test_2(arg):
|
||||
assert arg == 1
|
||||
assert len(l) == 1
|
||||
class TestClass:
|
||||
def test3(self, arg):
|
||||
assert arg == 1
|
||||
assert len(l) == 1
|
||||
""")
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=3)
|
||||
|
||||
def test_scope_module_and_finalizer(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
import pytest
|
||||
finalized = []
|
||||
created = []
|
||||
@pytest.mark.funcarg(scope="module")
|
||||
def pytest_funcarg__arg(request):
|
||||
created.append(1)
|
||||
assert request.scope == "module"
|
||||
request.addfinalizer(lambda: finalized.append(1))
|
||||
def pytest_funcarg__created(request):
|
||||
return len(created)
|
||||
def pytest_funcarg__finalized(request):
|
||||
return len(finalized)
|
||||
""")
|
||||
testdir.makepyfile(
|
||||
test_mod1="""
|
||||
def test_1(arg, created, finalized):
|
||||
assert created == 1
|
||||
assert finalized == 0
|
||||
def test_2(arg, created, finalized):
|
||||
assert created == 1
|
||||
assert finalized == 0""",
|
||||
test_mod2="""
|
||||
def test_3(arg, created, finalized):
|
||||
assert created == 2
|
||||
assert finalized == 1""",
|
||||
test_mode3="""
|
||||
def test_4(arg, created, finalized):
|
||||
assert created == 3
|
||||
assert finalized == 2
|
||||
""")
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=4)
|
||||
|
||||
@pytest.mark.parametrize("method", [
|
||||
'request.getfuncargvalue("arg")',
|
||||
'request.cached_setup(lambda: None, scope="function")',
|
||||
], ids=["getfuncargvalue", "cached_setup"])
|
||||
def test_scope_mismatch(self, testdir, method):
|
||||
testdir.makeconftest("""
|
||||
import pytest
|
||||
finalized = []
|
||||
created = []
|
||||
@pytest.mark.funcarg(scope="function")
|
||||
def pytest_funcarg__arg(request):
|
||||
pass
|
||||
""")
|
||||
testdir.makepyfile(
|
||||
test_mod1="""
|
||||
import pytest
|
||||
@pytest.mark.funcarg(scope="session")
|
||||
def pytest_funcarg__arg(request):
|
||||
%s
|
||||
def test_1(arg):
|
||||
pass
|
||||
""" % method)
|
||||
result = testdir.runpytest()
|
||||
assert result.ret != 0
|
||||
result.stdout.fnmatch_lines([
|
||||
"*ScopeMismatch*You tried*function*from*session*",
|
||||
])
|
||||
|
||||
|
|
Loading…
Reference in New Issue