@@ -379,6 +379,29 @@ The ``smtp_connection`` connection will be closed after the test finished
379
379
execution because the ``smtp_connection `` object automatically closes when
380
380
the ``with `` statement ends.
381
381
382
+ Using the contextlib.ExitStack context manager finalizers will always be called
383
+ regardless if the fixture *setup * code raises an exception. This is handy to properly
384
+ close all resources created by a fixture even if one of them fails to be created/acquired:
385
+
386
+ .. code-block :: python
387
+
388
+ # content of test_yield3.py
389
+
390
+ import contextlib
391
+
392
+ import pytest
393
+
394
+ from .utils import connect
395
+
396
+
397
+ @pytest.fixture
398
+ def equipments ():
399
+ with contextlib.ExitStack() as stack:
400
+ yield [stack.enter_context(connect(port)) for port in (" C1" , " C3" , " C28" )]
401
+
402
+ In the example above, if ``"C28" `` fails with an exception, ``"C1" `` and ``"C3" `` will still
403
+ be properly closed.
404
+
382
405
Note that if an exception happens during the *setup * code (before the ``yield `` keyword), the
383
406
*teardown * code (after the ``yield ``) will not be called.
384
407
@@ -407,27 +430,34 @@ Here's the ``smtp_connection`` fixture changed to use ``addfinalizer`` for clean
407
430
return smtp_connection # provide the fixture value
408
431
409
432
410
- Both ``yield `` and ``addfinalizer `` methods work similarly by calling their code after the test
411
- ends, but ``addfinalizer `` has two key differences over ``yield ``:
433
+ Here's the ``equipments `` fixture changed to use ``addfinalizer `` for cleanup:
412
434
413
- 1. It is possible to register multiple finalizer functions.
435
+ .. code-block :: python
414
436
415
- 2. Finalizers will always be called regardless if the fixture *setup * code raises an exception.
416
- This is handy to properly close all resources created by a fixture even if one of them
417
- fails to be created/acquired::
437
+ # content of test_yield3.py
418
438
419
- @pytest.fixture
420
- def equipments(request):
421
- r = []
422
- for port in ('C1', 'C3', 'C28'):
423
- equip = connect(port)
424
- request.addfinalizer(equip.disconnect)
425
- r.append(equip)
426
- return r
427
-
428
- In the example above, if ``"C28" `` fails with an exception, ``"C1" `` and ``"C3" `` will still
429
- be properly closed. Of course, if an exception happens before the finalize function is
430
- registered then it will not be executed.
439
+ import contextlib
440
+ import functools
441
+
442
+ import pytest
443
+
444
+ from .utils import connect
445
+
446
+
447
+ @pytest.fixture
448
+ def equipments (request ):
449
+ r = []
450
+ for port in (" C1" , " C3" , " C28" ):
451
+ cm = connect(port)
452
+ equip = cm.__enter__ ()
453
+ request.addfinalizer(functools.partial(cm.__exit__ , None , None , None ))
454
+ r.append(equip)
455
+ return r
456
+
457
+
458
+ Both ``yield `` and ``addfinalizer `` methods work similarly by calling their code after the test
459
+ ends. Of course, if an exception happens before the finalize function is registered then it
460
+ will not be executed.
431
461
432
462
433
463
.. _`request-context` :
0 commit comments