Skip to content
Merged
74 changes: 72 additions & 2 deletions doc/en/how-to/fixtures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -735,8 +735,78 @@ does offer some nuances for when you're in a pinch.
.. code-block:: pytest

$ pytest -q test_emaillib.py
. [100%]
1 passed in 0.12s
. [100%]
1 passed in 0.12s

Note on finalizer order
""""""""""""""""""""""""

Finalizers are executed in a first-in-last-out order, while operations after yield are executed sequentially.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while operations after yield are executed sequentially.

This sentence needs tweaking if we look at the output. 👍

Finalizers are executed in FILO order, that is correct, because we can see that adding fin2 and fin1 we get back fn1, fn2 in the output.

But the same can be said for the yield fixtures: we request them in fix_w_yield1, fix_w_yield2 order, and we get fix_w_yield2 and fix_w_yield1 in the output.

That's because yield fixtures use addfinalizer behind the scenes: when the fixture executes, we register a function with addfinalizer that resumes the generator (which in turn calls the teardown code).

Not sure how to "fix" this sentence, or perhaps we can explain something along the lines of what I wrote above, if that makes things clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that makes sense.
I tried to explain how both examples show the similar FILO behavior, plus added the hidden usage of addfinalizer after the examples.
What do you think?

Consider the differences in the following examples:

.. regendoc:wipe

.. code-block:: python

import pytest


@pytest.fixture
def test_bar(fix_w_yield1, fix_w_yield2):
print("test_bar")


@pytest.fixture
def fix_w_yield1():
yield
print("after_yield_1")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a good example I think, because of course both prints will be executed sequentially (pytest cannot mess with how Python executes code within a function).

I suggest we use two fixtures:

    @pytest.fixture
    def fix_w_yield1():
        yield
        print("after_yield_1")

    @pytest.fixture
    def fix_w_yield2():
        yield
        print("after_yield_2")

And then make test_bar request both:

    def test_bar(fix_w_yield1, fix_w_yield2):
        print("test_bar")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see.
The execution order with the addfinalizer method is evident, so the execution order only needs clarification in this case.
Lovely! How about now?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can keep the examples using addfinalizer as they are useful for comparision, so could you please bring them back? Thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing, re-added.



@pytest.fixture
def fix_w_yield2():
yield
print("after_yield_2")


.. code-block:: pytest

$ pytest test_module.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
collected 1 item

test_module.py test_bar
.after_yield_2
after_yield_1


.. code-block:: python

import pytest


@pytest.fixture
def fix_w_finalizers(request):
request.addfinalizer(partial(print, "finalizer_2"))
request.addfinalizer(partial(print, "finalizer_1"))


@pytest.fixture
def test_bar(fix_w_finalizers):
print("test_bar")


.. code-block:: pytest

$ pytest test_module.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
collected 1 item

test_module.py test_bar
.finalizer_1
finalizer_2


.. _`safe teardowns`:

Expand Down