diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 6f1a0880d..207bf27e4 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -789,23 +789,26 @@ def call_fixture_func(fixturefunc, request, kwargs): if yieldctx: it = fixturefunc(**kwargs) res = next(it) - - def teardown(): - try: - next(it) - except StopIteration: - pass - else: - fail_fixturefunc( - fixturefunc, "yield_fixture function has more than one 'yield'" - ) - - request.addfinalizer(teardown) + finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, it) + request.addfinalizer(finalizer) else: res = fixturefunc(**kwargs) return res +def _teardown_yield_fixture(fixturefunc, it): + """Executes the teardown of a fixture function by advancing the iterator after the + yield and ensure the iteration ends (if not it means there is more than one yield in the function""" + try: + next(it) + except StopIteration: + pass + else: + fail_fixturefunc( + fixturefunc, "yield_fixture function has more than one 'yield'" + ) + + class FixtureDef(object): """ A container for a factory definition. """ @@ -896,15 +899,10 @@ class FixtureDef(object): ) -def pytest_fixture_setup(fixturedef, request): - """ Execution of fixture setup. """ - kwargs = {} - for argname in fixturedef.argnames: - fixdef = request._get_active_fixturedef(argname) - result, arg_cache_key, exc = fixdef.cached_result - request._check_scope(argname, request.scope, fixdef.scope) - kwargs[argname] = result - +def resolve_fixture_function(fixturedef, request): + """Gets the actual callable that can be called to obtain the fixture value, dealing with unittest-specific + instances and bound methods. + """ fixturefunc = fixturedef.func if fixturedef.unittest: if request.instance is not None: @@ -918,6 +916,19 @@ def pytest_fixture_setup(fixturedef, request): fixturefunc = getimfunc(fixturedef.func) if fixturefunc != fixturedef.func: fixturefunc = fixturefunc.__get__(request.instance) + return fixturefunc + + +def pytest_fixture_setup(fixturedef, request): + """ Execution of fixture setup. """ + kwargs = {} + for argname in fixturedef.argnames: + fixdef = request._get_active_fixturedef(argname) + result, arg_cache_key, exc = fixdef.cached_result + request._check_scope(argname, request.scope, fixdef.scope) + kwargs[argname] = result + + fixturefunc = resolve_fixture_function(fixturedef, request) my_cache_key = request.param_index try: result = call_fixture_func(fixturefunc, request, kwargs) @@ -1016,13 +1027,7 @@ def yield_fixture(scope="function", params=None, autouse=False, ids=None, name=N .. deprecated:: 3.0 Use :py:func:`pytest.fixture` directly instead. """ - if callable(scope) and params is None and not autouse: - # direct decoration - return FixtureFunctionMarker("function", params, autouse, ids=ids, name=name)( - scope - ) - else: - return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name) + return fixture(scope=scope, params=params, autouse=autouse, ids=ids, name=name) defaultfuncargprefixmarker = fixture()