pytest-threadpool 0.3.5__tar.gz → 0.3.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/CHANGELOG.md +9 -0
  2. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/PKG-INFO +1 -1
  3. pytest_threadpool-0.3.6/examples/test_event_bus/event_bus.py +76 -0
  4. pytest_threadpool-0.3.6/examples/test_event_bus/test_event_bus.py +110 -0
  5. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/src/pytest_threadpool/_runner.py +35 -8
  6. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/src/pytest_threadpool/_version.py +2 -2
  7. pytest_threadpool-0.3.6/tests/integration_tests/cases/fixture_class_across_groups.py +48 -0
  8. pytest_threadpool-0.3.6/tests/integration_tests/cases/fixture_module_across_groups.py +50 -0
  9. pytest_threadpool-0.3.6/tests/integration_tests/cases/fixture_package_across_groups.py +70 -0
  10. pytest_threadpool-0.3.6/tests/integration_tests/cases/fixture_session_across_groups.py +50 -0
  11. pytest_threadpool-0.3.6/tests/integration_tests/cases/xunit_setup_class_across_groups.py +61 -0
  12. pytest_threadpool-0.3.6/tests/integration_tests/cases/xunit_setup_method_across_groups.py +53 -0
  13. pytest_threadpool-0.3.6/tests/integration_tests/cases/xunit_setup_module_across_groups.py +47 -0
  14. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_fixtures.py +39 -0
  15. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_xunit.py +18 -0
  16. pytest_threadpool-0.3.5/examples/test_event_bus/event_bus.py +0 -85
  17. pytest_threadpool-0.3.5/examples/test_event_bus/test_event_bus.py +0 -112
  18. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/.github/workflows/ci.yml +0 -0
  19. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/.github/workflows/publish.yml +0 -0
  20. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/.gitignore +0 -0
  21. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/.pre-commit-config.yaml +0 -0
  22. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/CONTRIBUTING.md +0 -0
  23. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/LICENSE +0 -0
  24. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/README.md +0 -0
  25. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/ROADMAP.md +0 -0
  26. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/codecov.yml +0 -0
  27. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/__init__.py +0 -0
  28. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_di/__init__.py +0 -0
  29. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_di/conftest.py +0 -0
  30. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_di/container.py +0 -0
  31. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_di/providers.py +0 -0
  32. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_di/services.py +0 -0
  33. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_di/test_factory.py +0 -0
  34. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_di/test_local.py +0 -0
  35. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_di/test_singleton.py +0 -0
  36. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_di/test_thread_local.py +0 -0
  37. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_event_bus/__init__.py +0 -0
  38. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_logging/__init__.py +0 -0
  39. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_logging/test_log_capture.py +0 -0
  40. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_queue/__init__.py +0 -0
  41. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_queue/conftest.py +0 -0
  42. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_queue/test_queue.py +0 -0
  43. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_queue/user_pool.py +0 -0
  44. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_shared_state/__init__.py +0 -0
  45. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_shared_state/test_barrier.py +0 -0
  46. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_shared_state/test_counter.py +0 -0
  47. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/examples/test_shared_state/test_counter_async.py +0 -0
  48. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/pyproject.toml +0 -0
  49. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/scripts/setup-dev +0 -0
  50. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/src/pytest_threadpool/__init__.py +0 -0
  51. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/src/pytest_threadpool/_api.py +0 -0
  52. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/src/pytest_threadpool/_constants.py +0 -0
  53. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/src/pytest_threadpool/_fixtures.py +0 -0
  54. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/src/pytest_threadpool/_grouping.py +0 -0
  55. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/src/pytest_threadpool/_markers.py +0 -0
  56. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/src/pytest_threadpool/plugin.py +0 -0
  57. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/src/pytest_threadpool/py.typed +0 -0
  58. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/__init__.py +0 -0
  59. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/__init__.py +0 -0
  60. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/__init__.py +0 -0
  61. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/_templates.py +0 -0
  62. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/capture_print_parallel.py +0 -0
  63. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/capture_two_groups.py +0 -0
  64. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/class_barrier_concurrency.py +0 -0
  65. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/class_single_method.py +0 -0
  66. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/class_thread_verification.py +0 -0
  67. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/collected_count.py +0 -0
  68. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_cross_module_group.py +0 -0
  69. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_first_item_setup_fail.py +0 -0
  70. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_keyboard_interrupt.py +0 -0
  71. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_maxfail_parallel.py +0 -0
  72. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_nested_threads.py +0 -0
  73. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_sigint.py +0 -0
  74. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_sigint_many.py +0 -0
  75. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_system_exit.py +0 -0
  76. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_teardown_exception.py +0 -0
  77. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_worker_exception.py +0 -0
  78. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/edge_worker_exception_conftest.py +0 -0
  79. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_autouse_function.py +0 -0
  80. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_class_scoped_once.py +0 -0
  81. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_class_yield.py +0 -0
  82. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_addfinalizer.py +0 -0
  83. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_chain.py +0 -0
  84. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_from_conftest.py +0 -0
  85. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_from_conftest_conftest.py +0 -0
  86. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_implicit_scope.py +0 -0
  87. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_mixed_parallel.py +0 -0
  88. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_multiple_per_test.py +0 -0
  89. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_setup_parallel.py +0 -0
  90. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_with_shared_deps.py +0 -0
  91. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_with_tmp_path.py +0 -0
  92. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_with_xunit.py +0 -0
  93. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_func_yield_teardown.py +0 -0
  94. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_function_scoped.py +0 -0
  95. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_interdependent_finalizers.py +0 -0
  96. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_multiple_scopes.py +0 -0
  97. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_parameterized.py +0 -0
  98. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_teardown_exception.py +0 -0
  99. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/fixture_yield_cleanup.py +0 -0
  100. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/marks_custom.py +0 -0
  101. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/marks_standard.py +0 -0
  102. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/parallel_only_skip.py +0 -0
  103. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/reporting_cross_module.py +0 -0
  104. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/reporting_incremental.py +0 -0
  105. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/reporting_incremental_conftest.py +0 -0
  106. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/reporting_package_children.py +0 -0
  107. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/reporting_single_file_params.py +0 -0
  108. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/reporting_stdout_during_parallel.py +0 -0
  109. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/runner_collect_only.py +0 -0
  110. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/runner_setup_only.py +0 -0
  111. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/runner_setup_show.py +0 -0
  112. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/runner_single_parallel_item.py +0 -0
  113. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/runner_single_worker.py +0 -0
  114. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_all_merged.py +0 -0
  115. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_all_not_parallelizable.py +0 -0
  116. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_children_on_function.py +0 -0
  117. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_children_separate_params.py +0 -0
  118. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_dynamic_parametrize.py +0 -0
  119. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_method_overrides_class.py +0 -0
  120. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_mixed_fail_skip.py +0 -0
  121. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_module_children.py +0 -0
  122. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_not_parallelizable_function.py +0 -0
  123. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_not_parallelizable_method.py +0 -0
  124. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_parameters.py +0 -0
  125. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/scope_two_classes_mixed.py +0 -0
  126. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/sequential_bare_functions.py +0 -0
  127. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/sequential_unmarked_class.py +0 -0
  128. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/setup_all_fail.py +0 -0
  129. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/setup_mixed_pass_fail.py +0 -0
  130. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/shared_counter.py +0 -0
  131. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/shared_cross_group_non_pickleable.py +0 -0
  132. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/shared_dict_mutation.py +0 -0
  133. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/shared_non_pickleable.py +0 -0
  134. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/shared_two_phase_barrier.py +0 -0
  135. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/teardown_after_test_failure.py +0 -0
  136. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/teardown_parallel_timing.py +0 -0
  137. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/teardown_same_thread.py +0 -0
  138. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/teardown_xunit_function.py +0 -0
  139. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/teardown_xunit_method_thread.py +0 -0
  140. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/validate_threadpool.py +0 -0
  141. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/validate_threadpool_conftest.py +0 -0
  142. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/xunit_class_setup.py +0 -0
  143. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/xunit_combined_setup.py +0 -0
  144. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/xunit_function_mixed_parallel.py +0 -0
  145. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/xunit_function_setup.py +0 -0
  146. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/xunit_method_mixed_parallel.py +0 -0
  147. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/xunit_method_setup.py +0 -0
  148. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/xunit_method_teardown_runs.py +0 -0
  149. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/cases/xunit_module_setup.py +0 -0
  150. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/conftest.py +0 -0
  151. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_capture.py +0 -0
  152. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_class.py +0 -0
  153. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_edge_cases.py +0 -0
  154. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_func_fixtures.py +0 -0
  155. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_marks.py +0 -0
  156. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_package.py +0 -0
  157. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_parallel_teardown.py +0 -0
  158. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_reporting.py +0 -0
  159. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_scopes.py +0 -0
  160. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_sequential.py +0 -0
  161. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/integration_tests/test_pytester_shared.py +0 -0
  162. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/unit_tests/__init__.py +0 -0
  163. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/unit_tests/test_unit_api.py +0 -0
  164. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/unit_tests/test_unit_fixtures.py +0 -0
  165. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/unit_tests/test_unit_grouping.py +0 -0
  166. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/unit_tests/test_unit_markers.py +0 -0
  167. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/unit_tests/test_unit_plugin.py +0 -0
  168. {pytest_threadpool-0.3.5 → pytest_threadpool-0.3.6}/tests/unit_tests/test_unit_stream_proxy.py +0 -0
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.6
4
+
5
+ ### Fixes
6
+
7
+ - Fixed session, module, and class-scoped fixtures being torn down and
8
+ re-created between parallel groups instead of being resolved once and
9
+ shared for their entire scope. The same fix applies to xunit-style
10
+ `setup_module`/`setup_class` hooks.
11
+
3
12
  ## 0.3.5
4
13
 
5
14
  ### Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytest-threadpool
3
- Version: 0.3.5
3
+ Version: 0.3.6
4
4
  Summary: Parallel test execution for free-threaded Python builds
5
5
  Project-URL: Homepage, https://github.com/pytest-threadpool/pytest-threadpool
6
6
  Project-URL: Source, https://github.com/pytest-threadpool/pytest-threadpool
@@ -0,0 +1,76 @@
1
+ """In-memory event bus — thread-safe test double.
2
+
3
+ A minimal pub/sub bus that records all published events. Tests publish
4
+ events concurrently, then verify delivery, ordering, and deduplication
5
+ against the shared log.
6
+
7
+ With pytest-xdist this would require an external broker (Redis, RabbitMQ)
8
+ or a socket-based mock server since each worker is a separate process.
9
+ With pytest-threadpool it's a plain Python object protected by a Lock.
10
+ """
11
+
12
+ import threading
13
+
14
+
15
+ class EventBus:
16
+ """Thread-safe in-memory event bus for testing.
17
+
18
+ Instance-based: each test scope gets its own bus via a fixture,
19
+ with proper setup/teardown handled by pytest's fixture lifecycle.
20
+ """
21
+
22
+ def __init__(self):
23
+ self._lock = threading.Lock()
24
+ self._events: list[dict] = []
25
+ self._subscribers: dict[str, list] = {}
26
+ self._waiters: list[tuple[str | None, int, threading.Event]] = []
27
+
28
+ def publish(self, topic: str, payload: dict) -> None:
29
+ with self._lock:
30
+ event = {"topic": topic, "payload": payload, "thread": threading.current_thread().name}
31
+ self._events.append(event)
32
+ for callback in self._subscribers.get(topic, []):
33
+ callback(event)
34
+ self._check_waiters()
35
+
36
+ def subscribe(self, topic: str, callback) -> None:
37
+ with self._lock:
38
+ self._subscribers.setdefault(topic, []).append(callback)
39
+
40
+ def events(self, topic: str | None = None) -> list[dict]:
41
+ with self._lock:
42
+ if topic is None:
43
+ return list(self._events)
44
+ return [e for e in self._events if e["topic"] == topic]
45
+
46
+ def wait_for(self, count: int, topic: str | None = None, timeout: float = 10) -> list[dict]:
47
+ """Block until at least ``count`` events match, then return them."""
48
+ ready = threading.Event()
49
+ with self._lock:
50
+ matched = self._filter(topic)
51
+ if len(matched) >= count:
52
+ return matched
53
+ self._waiters.append((topic, count, ready))
54
+ if not ready.wait(timeout=timeout):
55
+ actual = len(self.events(topic))
56
+ label = f"topic={topic!r}" if topic else "all topics"
57
+ raise TimeoutError(
58
+ f"EventBus.wait_for: expected {count} events on {label}, "
59
+ f"got {actual} after {timeout}s"
60
+ )
61
+ return self.events(topic)
62
+
63
+ def _check_waiters(self) -> None:
64
+ """Signal any waiters whose condition is met. Must hold _lock."""
65
+ remaining = []
66
+ for topic, count, event in self._waiters:
67
+ if len(self._filter(topic)) >= count:
68
+ event.set()
69
+ else:
70
+ remaining.append((topic, count, event))
71
+ self._waiters[:] = remaining
72
+
73
+ def _filter(self, topic: str | None) -> list[dict]:
74
+ if topic is None:
75
+ return list(self._events)
76
+ return [e for e in self._events if e["topic"] == topic]
@@ -0,0 +1,110 @@
1
+ """Shared in-memory test double — concurrent access to a shared event bus.
2
+
3
+ This pattern is impossible with pytest-xdist: each subprocess has its own
4
+ memory space, so a plain Python object can't collect events from multiple
5
+ workers. You'd need Redis, a socket server, or a temp file to coordinate.
6
+
7
+ With pytest-threadpool, all tests share the same process. A class-scoped
8
+ fixture creates one EventBus instance, shared by all parallel methods —
9
+ proper lifecycle management with no global mutable state.
10
+
11
+ Patterns demonstrated:
12
+ - Class-scoped fixture providing a shared test double to parallel workers
13
+ - Concurrent access to a shared in-memory object from parallel threads
14
+ - Subscriber callbacks firing synchronously during publish
15
+ - Aggregate cross-test visibility via wait_for
16
+ """
17
+
18
+ import threading
19
+
20
+ import pytest
21
+
22
+ from examples.test_event_bus.event_bus import EventBus
23
+
24
+ _N_PUBLISHERS = 4
25
+
26
+
27
+ @pytest.fixture(scope="class")
28
+ def bus():
29
+ return EventBus()
30
+
31
+
32
+ @pytest.mark.parallelizable("children")
33
+ class TestEventBus:
34
+ """All tests run in parallel, sharing one bus via a class-scoped fixture."""
35
+
36
+ def _publish_own_events(self, bus: EventBus, name: str) -> None:
37
+ """Each test publishes its own uniquely-tagged events."""
38
+ bus.publish("work", {"source": name, "step": "start"})
39
+ bus.publish("work", {"source": name, "step": "done"})
40
+
41
+ def test_publish_and_verify_signup(self, bus):
42
+ """Publish signup events and verify only own events are correct."""
43
+ bus.publish("user.created", {"user_id": "u1"})
44
+ bus.publish("email.queued", {"to": "a@test.com", "template": "welcome"})
45
+
46
+ own_emails = [e for e in bus.events("email.queued") if e["payload"]["to"] == "a@test.com"]
47
+ assert len(own_emails) == 1
48
+ assert own_emails[0]["payload"]["template"] == "welcome"
49
+
50
+ def test_publish_and_verify_purchase(self, bus):
51
+ """Publish purchase events and verify own events landed."""
52
+ bus.publish("order.created", {"order_id": "o1", "user_id": "u2"})
53
+ bus.publish("payment.charged", {"order_id": "o1", "amount": 99})
54
+
55
+ own = [e for e in bus.events("payment.charged") if e["payload"]["order_id"] == "o1"]
56
+ assert len(own) == 1
57
+ assert own[0]["payload"]["amount"] == 99
58
+
59
+ def test_subscriber_callback_fires_on_publish(self):
60
+ """Subscribe, publish, verify callback fired — all within one test.
61
+
62
+ Uses a dedicated EventBus instance so the subscriber doesn't leak
63
+ into the shared class-scoped bus.
64
+ """
65
+ local_bus = EventBus()
66
+ received: list[dict] = []
67
+
68
+ def on_event(event):
69
+ received.append(event)
70
+
71
+ local_bus.subscribe("audit.log", on_event)
72
+ local_bus.publish("audit.log", {"action": "login", "user": "alice"})
73
+ local_bus.publish("audit.log", {"action": "logout", "user": "alice"})
74
+
75
+ actions = [e["payload"]["action"] for e in received]
76
+ assert actions == ["login", "logout"]
77
+
78
+ def test_wait_for_own_events(self, bus):
79
+ """Publish from a background thread, wait_for in the test thread."""
80
+ topic = "async.work"
81
+
82
+ def background_publisher():
83
+ bus.publish(topic, {"step": 1})
84
+ bus.publish(topic, {"step": 2})
85
+
86
+ t = threading.Thread(target=background_publisher)
87
+ t.start()
88
+
89
+ events = bus.wait_for(2, topic=topic)
90
+ t.join(timeout=10)
91
+
92
+ steps = [e["payload"]["step"] for e in events]
93
+ assert 1 in steps
94
+ assert 2 in steps
95
+
96
+ # -- aggregate visibility: each parametrized worker publishes tagged events,
97
+ # then waits for all N_PUBLISHERS to have contributed --
98
+
99
+ @pytest.mark.parametrize("worker", range(_N_PUBLISHERS))
100
+ def test_publish_and_observe_aggregate(self, bus, worker):
101
+ """Each worker publishes its own events, then waits to see all workers' events.
102
+
103
+ This proves cross-test visibility: events from parallel threads
104
+ are visible to every other thread via the shared bus.
105
+ """
106
+ self._publish_own_events(bus, f"worker_{worker}")
107
+
108
+ all_events = bus.wait_for(_N_PUBLISHERS * 2, topic="work")
109
+ sources = {e["payload"]["source"] for e in all_events}
110
+ assert len(sources) == _N_PUBLISHERS
@@ -356,22 +356,34 @@ class ParallelRunner:
356
356
  )
357
357
 
358
358
  needs_sep = False
359
- for group_key, items in groups:
359
+ for group_idx, (group_key, items) in enumerate(groups):
360
360
  if session.shouldfail:
361
361
  raise session.Failed(session.shouldfail)
362
362
  if session.shouldstop:
363
363
  raise session.Interrupted(session.shouldstop)
364
364
 
365
+ # First item of the next group — tells teardown_exact which
366
+ # session/module/class nodes to keep alive across groups.
367
+ next_group_first = None
368
+ for _, future_items in groups[group_idx + 1 :]:
369
+ if future_items:
370
+ next_group_first = future_items[0]
371
+ break
372
+
365
373
  if group_key is None or len(items) <= 1 or self._nthreads <= 1:
366
374
  for i, item in enumerate(items):
367
- nextitem = items[i + 1] if i + 1 < len(items) else None
375
+ nextitem = items[i + 1] if i + 1 < len(items) else next_group_first
368
376
  if has_parallel:
369
377
  self._run_sequential_nodeid(item, nextitem)
370
378
  else:
371
379
  self._run_sequential(item, nextitem)
372
380
  needs_sep = bool(items)
373
381
  else:
374
- self._run_parallel(items, after_sequential=needs_sep)
382
+ self._run_parallel(
383
+ items,
384
+ after_sequential=needs_sep,
385
+ next_group_first=next_group_first,
386
+ )
375
387
  needs_sep = True
376
388
 
377
389
  return True
@@ -469,7 +481,7 @@ class ParallelRunner:
469
481
  f.write(f"\n{item.nodeid} {color}{word}{reset}")
470
482
  f.flush()
471
483
 
472
- def _run_parallel(self, items, after_sequential: bool = False) -> None:
484
+ def _run_parallel(self, items, after_sequential: bool = False, next_group_first=None) -> None:
473
485
  """Run a group's tests with parallel fixture setup and calls.
474
486
 
475
487
  Function-scoped FixtureDefs are cloned per-item so their setup can
@@ -525,7 +537,9 @@ class ParallelRunner:
525
537
  ihook.pytest_runtest_logreport(report=setup_reports[item])
526
538
  if setup_passed[item] and session.config.getoption("setupshow", False):
527
539
  show_test_item(item)
528
- self._teardown_all(items, per_item_fixture_fins, {}, saved_collector_fins)
540
+ self._teardown_all(
541
+ items, per_item_fixture_fins, {}, saved_collector_fins, next_group_first
542
+ )
529
543
  return
530
544
 
531
545
  # Phase 1+2: fire hooks for all items, populate shared fixture caches,
@@ -832,13 +846,20 @@ class ParallelRunner:
832
846
  session._setupstate.stack.pop(item) # pyright: ignore[reportPrivateUsage]
833
847
 
834
848
  # Teardown reporting + collector teardown (always runs, even after interrupt)
835
- self._teardown_all(items, per_item_fixture_fins, teardown_infos, saved_collector_fins)
849
+ self._teardown_all(
850
+ items, per_item_fixture_fins, teardown_infos, saved_collector_fins, next_group_first
851
+ )
836
852
 
837
853
  if interrupted:
838
854
  raise KeyboardInterrupt
839
855
 
840
856
  def _teardown_all(
841
- self, items, per_item_fixture_fins, teardown_infos, saved_collector_fins
857
+ self,
858
+ items,
859
+ per_item_fixture_fins,
860
+ teardown_infos,
861
+ saved_collector_fins,
862
+ next_group_first=None,
842
863
  ) -> None:
843
864
  """Report teardown results, run any remaining finalizers, and tear down
844
865
  collectors.
@@ -846,6 +867,10 @@ class ParallelRunner:
846
867
  For items with pre-computed teardown_infos (from parallel workers),
847
868
  only reporting happens here. For items without (setuponly mode),
848
869
  finalizers from per_item_fixture_fins are executed sequentially.
870
+
871
+ next_group_first: first item of the next group (or None if last group).
872
+ Passed to teardown_exact so session/module/class-scoped fixtures that
873
+ are still needed by the next group are preserved rather than torn down.
849
874
  """
850
875
  session = self._session
851
876
 
@@ -882,7 +907,9 @@ class ParallelRunner:
882
907
  item.ihook.pytest_runtest_logfinish(nodeid=item.nodeid, location=item.location)
883
908
 
884
909
  # noinspection PyProtectedMember
885
- session._setupstate.teardown_exact(nextitem=None) # pyright: ignore[reportPrivateUsage]
910
+ # Pass next_group_first so session/module/class nodes needed by the
911
+ # next group stay in the stack with their cached fixtures intact.
912
+ session._setupstate.teardown_exact(nextitem=next_group_first) # pyright: ignore[reportPrivateUsage]
886
913
 
887
914
  exceptions = []
888
915
  for _node, fins in reversed(saved_collector_fins):
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.3.5'
32
- __version_tuple__ = version_tuple = (0, 3, 5)
31
+ __version__ = version = '0.3.6'
32
+ __version_tuple__ = version_tuple = (0, 3, 6)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,48 @@
1
+ """Class-scoped fixture: same object within a class, different across classes."""
2
+
3
+ import threading
4
+
5
+ import pytest
6
+
7
+ _ids_a = []
8
+ _ids_b = []
9
+ lock = threading.Lock()
10
+
11
+
12
+ class Resource:
13
+ """Mutable object whose identity can be checked via id()."""
14
+
15
+
16
+ @pytest.fixture(scope="class")
17
+ def class_resource():
18
+ return Resource()
19
+
20
+
21
+ @pytest.mark.parallelizable("children")
22
+ class TestGroupA:
23
+ def test_a1(self, class_resource):
24
+ with lock:
25
+ _ids_a.append(id(class_resource))
26
+
27
+ def test_a2(self, class_resource):
28
+ with lock:
29
+ _ids_a.append(id(class_resource))
30
+
31
+
32
+ @pytest.mark.parallelizable("children")
33
+ class TestGroupB:
34
+ def test_b1(self, class_resource):
35
+ with lock:
36
+ _ids_b.append(id(class_resource))
37
+
38
+ def test_b2(self, class_resource):
39
+ with lock:
40
+ _ids_b.append(id(class_resource))
41
+
42
+
43
+ def test_verify():
44
+ # Within each class, all tests see the same object
45
+ assert len(set(_ids_a)) == 1, f"class A fixture not shared: {set(_ids_a)}"
46
+ assert len(set(_ids_b)) == 1, f"class B fixture not shared: {set(_ids_b)}"
47
+ # Across classes, fixtures are different objects
48
+ assert _ids_a[0] != _ids_b[0], "class-scoped fixture leaked across classes"
@@ -0,0 +1,50 @@
1
+ """Module-scoped fixture must be the same object across parallel groups and sequential tests."""
2
+
3
+ import threading
4
+
5
+ import pytest
6
+
7
+ _ids = []
8
+ lock = threading.Lock()
9
+
10
+
11
+ class Resource:
12
+ """Mutable object whose identity can be checked via id()."""
13
+
14
+
15
+ @pytest.fixture(scope="module")
16
+ def module_resource():
17
+ return Resource()
18
+
19
+
20
+ @pytest.mark.parallelizable("children")
21
+ class TestGroupA:
22
+ def test_a1(self, module_resource):
23
+ with lock:
24
+ _ids.append(id(module_resource))
25
+
26
+ def test_a2(self, module_resource):
27
+ with lock:
28
+ _ids.append(id(module_resource))
29
+
30
+
31
+ @pytest.mark.not_parallelizable
32
+ def test_sequential_between(module_resource):
33
+ with lock:
34
+ _ids.append(id(module_resource))
35
+
36
+
37
+ @pytest.mark.parallelizable("children")
38
+ class TestGroupB:
39
+ def test_b1(self, module_resource):
40
+ with lock:
41
+ _ids.append(id(module_resource))
42
+
43
+ def test_b2(self, module_resource):
44
+ with lock:
45
+ _ids.append(id(module_resource))
46
+
47
+
48
+ def test_verify():
49
+ assert len(_ids) == 5, f"expected 5 recorded ids, got {len(_ids)}"
50
+ assert len(set(_ids)) == 1, f"module fixture created multiple objects: {set(_ids)}"
@@ -0,0 +1,70 @@
1
+ """Package-scoped fixture shared across parallel groups in the same package.
2
+
3
+ This case provides source strings used by the integration test to
4
+ build a temporary package directory.
5
+ """
6
+
7
+ CONFTEST_SRC = """\
8
+ import pytest
9
+
10
+
11
+ class Resource:
12
+ pass
13
+
14
+
15
+ @pytest.fixture(scope="package")
16
+ def pkg_resource():
17
+ return Resource()
18
+ """
19
+
20
+ INIT_SRC = """\
21
+ import pytest
22
+
23
+ pytestmark = pytest.mark.parallelizable("children")
24
+ """
25
+
26
+ MOD_A_SRC = """\
27
+ import threading
28
+
29
+ lock = threading.Lock()
30
+ ids = []
31
+
32
+
33
+ def test_a1(pkg_resource):
34
+ with lock:
35
+ ids.append(id(pkg_resource))
36
+
37
+
38
+ def test_a2(pkg_resource):
39
+ with lock:
40
+ ids.append(id(pkg_resource))
41
+ """
42
+
43
+ MOD_B_SRC = """\
44
+ import threading
45
+
46
+ lock = threading.Lock()
47
+ ids = []
48
+
49
+
50
+ def test_b1(pkg_resource):
51
+ with lock:
52
+ ids.append(id(pkg_resource))
53
+
54
+
55
+ def test_b2(pkg_resource):
56
+ with lock:
57
+ ids.append(id(pkg_resource))
58
+ """
59
+
60
+ VERIFY_SRC = """\
61
+ from mypkg import test_mod_a, test_mod_b
62
+
63
+
64
+ def test_verify():
65
+ all_ids = test_mod_a.ids + test_mod_b.ids
66
+ assert len(all_ids) == 4, f"expected 4 recorded ids, got {len(all_ids)}"
67
+ assert len(set(all_ids)) == 1, (
68
+ f"package fixture created multiple objects: {set(all_ids)}"
69
+ )
70
+ """
@@ -0,0 +1,50 @@
1
+ """Session-scoped fixture must be the same object across parallel groups and sequential tests."""
2
+
3
+ import threading
4
+
5
+ import pytest
6
+
7
+ _ids = []
8
+ lock = threading.Lock()
9
+
10
+
11
+ class Resource:
12
+ """Mutable object whose identity can be checked via id()."""
13
+
14
+
15
+ @pytest.fixture(scope="session")
16
+ def session_resource():
17
+ return Resource()
18
+
19
+
20
+ @pytest.mark.parallelizable("children")
21
+ class TestGroupA:
22
+ def test_a1(self, session_resource):
23
+ with lock:
24
+ _ids.append(id(session_resource))
25
+
26
+ def test_a2(self, session_resource):
27
+ with lock:
28
+ _ids.append(id(session_resource))
29
+
30
+
31
+ @pytest.mark.not_parallelizable
32
+ def test_sequential_between(session_resource):
33
+ with lock:
34
+ _ids.append(id(session_resource))
35
+
36
+
37
+ @pytest.mark.parallelizable("children")
38
+ class TestGroupB:
39
+ def test_b1(self, session_resource):
40
+ with lock:
41
+ _ids.append(id(session_resource))
42
+
43
+ def test_b2(self, session_resource):
44
+ with lock:
45
+ _ids.append(id(session_resource))
46
+
47
+
48
+ def test_verify():
49
+ assert len(_ids) == 5, f"expected 5 recorded ids, got {len(_ids)}"
50
+ assert len(set(_ids)) == 1, f"session fixture created multiple objects: {set(_ids)}"
@@ -0,0 +1,61 @@
1
+ """setup_class/teardown_class: once per class, independent across parallel groups."""
2
+
3
+ import threading
4
+ from typing import ClassVar
5
+
6
+ import pytest
7
+
8
+ lock = threading.Lock()
9
+
10
+
11
+ @pytest.mark.parallelizable("children")
12
+ class TestGroupA:
13
+ setup_ids: ClassVar[list] = []
14
+
15
+ @classmethod
16
+ def setup_class(cls):
17
+ with lock:
18
+ cls.setup_ids.append(threading.current_thread().name)
19
+
20
+ @classmethod
21
+ def teardown_class(cls):
22
+ with lock:
23
+ cls.setup_ids.append("teardown_a")
24
+
25
+ def test_a1(self):
26
+ assert len([x for x in self.setup_ids if x != "teardown_a"]) == 1
27
+
28
+ def test_a2(self):
29
+ assert len([x for x in self.setup_ids if x != "teardown_a"]) == 1
30
+
31
+
32
+ @pytest.mark.parallelizable("children")
33
+ class TestGroupB:
34
+ setup_ids: ClassVar[list] = []
35
+
36
+ @classmethod
37
+ def setup_class(cls):
38
+ with lock:
39
+ cls.setup_ids.append(threading.current_thread().name)
40
+
41
+ @classmethod
42
+ def teardown_class(cls):
43
+ with lock:
44
+ cls.setup_ids.append("teardown_b")
45
+
46
+ def test_b1(self):
47
+ assert len([x for x in self.setup_ids if x != "teardown_b"]) == 1
48
+
49
+ def test_b2(self):
50
+ assert len([x for x in self.setup_ids if x != "teardown_b"]) == 1
51
+
52
+
53
+ def test_verify():
54
+ # Each class had exactly one setup_class call
55
+ a_setups = [x for x in TestGroupA.setup_ids if x != "teardown_a"]
56
+ b_setups = [x for x in TestGroupB.setup_ids if x != "teardown_b"]
57
+ assert len(a_setups) == 1, f"setup_class ran {len(a_setups)} times for A"
58
+ assert len(b_setups) == 1, f"setup_class ran {len(b_setups)} times for B"
59
+ # Both teardowns ran
60
+ assert "teardown_a" in TestGroupA.setup_ids
61
+ assert "teardown_b" in TestGroupB.setup_ids
@@ -0,0 +1,53 @@
1
+ """setup_method/teardown_method fire for every method across separate parallel classes."""
2
+
3
+ import threading
4
+
5
+ import pytest
6
+
7
+ lock = threading.Lock()
8
+ all_setups: list = []
9
+ all_teardowns: list = []
10
+
11
+
12
+ @pytest.mark.parallelizable("children")
13
+ class TestGroupA:
14
+ barrier = threading.Barrier(2, timeout=10)
15
+
16
+ def setup_method(self, method):
17
+ with lock:
18
+ all_setups.append(f"A.{method.__name__}")
19
+
20
+ def teardown_method(self, method):
21
+ with lock:
22
+ all_teardowns.append(f"A.{method.__name__}")
23
+
24
+ def test_a1(self):
25
+ self.barrier.wait()
26
+
27
+ def test_a2(self):
28
+ self.barrier.wait()
29
+
30
+
31
+ @pytest.mark.parallelizable("children")
32
+ class TestGroupB:
33
+ barrier = threading.Barrier(2, timeout=10)
34
+
35
+ def setup_method(self, method):
36
+ with lock:
37
+ all_setups.append(f"B.{method.__name__}")
38
+
39
+ def teardown_method(self, method):
40
+ with lock:
41
+ all_teardowns.append(f"B.{method.__name__}")
42
+
43
+ def test_b1(self):
44
+ self.barrier.wait()
45
+
46
+ def test_b2(self):
47
+ self.barrier.wait()
48
+
49
+
50
+ def test_verify():
51
+ expected_setups = {"A.test_a1", "A.test_a2", "B.test_b1", "B.test_b2"}
52
+ assert expected_setups == set(all_setups), f"setup_method: {all_setups}"
53
+ assert expected_setups == set(all_teardowns), f"teardown_method: {all_teardowns}"