strike keyword argument in favour of new pytest.yield_fixture decorator

This commit is contained in:
holger krekel 2013-09-30 13:42:39 +02:00
parent 431ce79d94
commit 086d4e4ced
5 changed files with 44 additions and 29 deletions

View File

@ -16,9 +16,12 @@ known incompatibilities:
new features:
- experimentally allow fixture functions to "yield" instead of "return"
a fixture value, allowing direct integration with with-context managers
in fixture functions and avoiding registration of finalization callbacks.
- experimentally introduce a new pytest.yield_fixture decorator which
has exactly the same parameters as pytest.fixture but expects
a ``yield`` statement instead of a ``return statement`` from fixture functions.
This allows direct integration with with-context managers
in fixture functions and generally avoids registering of finalization callbacks
in favour of treating the "after-yield" as teardown code.
Thanks Andreas Pelme, Vladimir Keleshev, Floris Bruynooghe, Ronny Pfannschmidt
and many others for discussions.

View File

@ -40,7 +40,7 @@ class FixtureFunctionMarker:
return function
def fixture(scope="function", params=None, autouse=False, yieldctx=False):
def fixture(scope="function", params=None, autouse=False):
""" (return a) decorator to mark a fixture factory function.
This decorator can be used (with or or without parameters) to define
@ -62,16 +62,29 @@ def fixture(scope="function", params=None, autouse=False, yieldctx=False):
can see it. If False (the default) then an explicit
reference is needed to activate the fixture.
:arg yieldctx: if True, the fixture function yields a fixture value.
Code after such a ``yield`` statement is treated as
teardown code.
"""
if callable(scope) and params is None and autouse == False:
# direct decoration
return FixtureFunctionMarker(
"function", params, autouse, yieldctx)(scope)
"function", params, autouse)(scope)
else:
return FixtureFunctionMarker(scope, params, autouse, yieldctx)
return FixtureFunctionMarker(scope, params, autouse)
def yield_fixture(scope="function", params=None, autouse=False):
""" (return a) decorator to mark a yield-fixture factory function
(EXPERIMENTAL).
This takes the same arguments as :py:func:`pytest.fixture` but
expects a fixture function to use a ``yield`` instead of a ``return``
statement to provide a fixture. See
http://pytest.org/en/latest/yieldfixture.html for more info.
"""
if callable(scope) and params is None and autouse == False:
# direct decoration
return FixtureFunctionMarker(
"function", params, autouse, yieldctx=True)(scope)
else:
return FixtureFunctionMarker(scope, params, autouse, yieldctx=True)
defaultfuncargprefixmarker = fixture()
@ -136,6 +149,7 @@ def pytest_namespace():
raises.Exception = pytest.fail.Exception
return {
'fixture': fixture,
'yield_fixture': yield_fixture,
'raises' : raises,
'collect': {
'Module': Module, 'Class': Class, 'Instance': Instance,
@ -1675,7 +1689,7 @@ def call_fixture_func(fixturefunc, request, kwargs, yieldctx):
if yieldctx:
if not is_generator(fixturefunc):
fail_fixturefunc(fixturefunc,
msg="yieldctx=True requires yield statement")
msg="yield_fixture requires yield statement in function")
iter = fixturefunc(**kwargs)
next = getattr(iter, "__next__", None)
if next is None:
@ -1688,7 +1702,7 @@ def call_fixture_func(fixturefunc, request, kwargs, yieldctx):
pass
else:
fail_fixturefunc(fixturefunc,
"fixture function has more than one 'yield'")
"yield_fixture function has more than one 'yield'")
request.addfinalizer(teardown)
else:
res = fixturefunc(**kwargs)

View File

@ -276,8 +276,8 @@ module itself does not need to change or know about these details
of fixture setup.
Note that pytest-2.4 introduced an experimental alternative
:ref:`yield fixture mechanism <yieldctx>` for easier context manager integration
and more linear writing of teardown code.
:ref:`yield fixture mechanism <yieldfixture>` for easier context manager
integration and more linear writing of teardown code.
.. _`request-context`:

View File

@ -1,5 +1,5 @@
.. _yieldctx:
.. _yieldfixture:
Fixture functions using "yield" / context manager integration
---------------------------------------------------------------
@ -16,10 +16,9 @@ fully supporting all other fixture features.
"yielding" fixture values is an experimental feature and its exact
declaration may change later but earliest in a 2.5 release. You can thus
safely use this feature in the 2.4 series but may need to adapt your
fixtures later. Test functions themselves will not need to change
(they can be completely ignorant of the return/yield modes of
fixture functions).
safely use this feature in the 2.4 series but may need to adapt later.
Test functions themselves will not need to change (as a general
feature, they are ignorant of how fixtures are setup).
Let's look at a simple standalone-example using the new ``yield`` syntax::
@ -27,7 +26,7 @@ Let's look at a simple standalone-example using the new ``yield`` syntax::
import pytest
@pytest.fixture(yieldctx=True)
@pytest.yield_fixture
def passwd():
print ("\nsetup before yield")
f = open("/etc/passwd")
@ -62,7 +61,7 @@ Let's simplify the above ``passwd`` fixture::
import pytest
@pytest.fixture(yieldctx=True)
@pytest.yield_fixture
def passwd():
with open("/etc/passwd") as f:
yield f.readlines()

View File

@ -1980,7 +1980,7 @@ class TestContextManagerFixtureFuncs:
def test_simple(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture(yieldctx=True)
@pytest.yield_fixture
def arg1():
print ("setup")
yield 1
@ -2004,7 +2004,7 @@ class TestContextManagerFixtureFuncs:
def test_scoped(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture(scope="module", yieldctx=True)
@pytest.yield_fixture(scope="module")
def arg1():
print ("setup")
yield 1
@ -2025,7 +2025,7 @@ class TestContextManagerFixtureFuncs:
def test_setup_exception(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture(scope="module", yieldctx=True)
@pytest.yield_fixture(scope="module")
def arg1():
pytest.fail("setup")
yield 1
@ -2041,7 +2041,7 @@ class TestContextManagerFixtureFuncs:
def test_teardown_exception(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture(scope="module", yieldctx=True)
@pytest.yield_fixture(scope="module")
def arg1():
yield 1
pytest.fail("teardown")
@ -2057,7 +2057,7 @@ class TestContextManagerFixtureFuncs:
def test_yields_more_than_one(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture(scope="module", yieldctx=True)
@pytest.yield_fixture(scope="module")
def arg1():
yield 1
yield 2
@ -2074,7 +2074,7 @@ class TestContextManagerFixtureFuncs:
def test_no_yield(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture(scope="module", yieldctx=True)
@pytest.yield_fixture(scope="module")
def arg1():
return 1
def test_1(arg1):
@ -2082,9 +2082,8 @@ class TestContextManagerFixtureFuncs:
""")
result = testdir.runpytest("-s")
result.stdout.fnmatch_lines("""
*yieldctx*requires*yield*
*yieldctx=True*
*yield_fixture*requires*yield*
*yield_fixture*
*def arg1*
""")