(
self,
test_case: Any,
test: TestCase,
exc_info: ExceptionInfo[BaseException]
| tuple[type[BaseException], BaseException, TracebackType]
| None,
)
| 423 | return ntraceback |
| 424 | |
| 425 | def addSubTest( |
| 426 | self, |
| 427 | test_case: Any, |
| 428 | test: TestCase, |
| 429 | exc_info: ExceptionInfo[BaseException] |
| 430 | | tuple[type[BaseException], BaseException, TracebackType] |
| 431 | | None, |
| 432 | ) -> None: |
| 433 | # Importing this private symbol locally in case this symbol is renamed/removed in the future; importing |
| 434 | # it globally would break pytest entirely, importing it locally only will break unittests using `addSubTest`. |
| 435 | from unittest.case import _subtest_msg_sentinel # type: ignore[attr-defined] |
| 436 | |
| 437 | exception_info: ExceptionInfo[BaseException] | None |
| 438 | match exc_info: |
| 439 | case tuple(): |
| 440 | exception_info = ExceptionInfo(exc_info, _ispytest=True) |
| 441 | case ExceptionInfo() | None: |
| 442 | exception_info = exc_info |
| 443 | case unreachable: |
| 444 | assert_never(unreachable) |
| 445 | |
| 446 | call_info = CallInfo[None]( |
| 447 | None, |
| 448 | exception_info, |
| 449 | start=0, |
| 450 | stop=0, |
| 451 | duration=0, |
| 452 | when="call", |
| 453 | _ispytest=True, |
| 454 | ) |
| 455 | msg = None if test._message is _subtest_msg_sentinel else str(test._message) # type: ignore[attr-defined] |
| 456 | report = self.ihook.pytest_runtest_makereport(item=self, call=call_info) |
| 457 | sub_report = SubtestReport._new( |
| 458 | report, |
| 459 | SubtestContext(msg=msg, kwargs=dict(test.params)), # type: ignore[attr-defined] |
| 460 | captured_output=None, |
| 461 | captured_logs=None, |
| 462 | ) |
| 463 | self.ihook.pytest_runtest_logreport(report=sub_report) |
| 464 | if check_interactive_exception(call_info, sub_report): |
| 465 | self.ihook.pytest_exception_interact( |
| 466 | node=self, call=call_info, report=sub_report |
| 467 | ) |
| 468 | |
| 469 | # For python < 3.11: add non-subtest skips once all subtest failures are processed by # `_addSubTest`. |
| 470 | if sys.version_info < (3, 11): |
| 471 | subtest_errors, non_subtest_skip = self._obtain_errors_and_skips() |
| 472 | |
| 473 | # Check if we have non-subtest skips: if there are also sub failures, non-subtest skips are not treated in |
| 474 | # `_addSubTest` and have to be added using `add_skip` after all subtest failures are processed. |
| 475 | if len(non_subtest_skip) > 0 and len(subtest_errors) > 0: |
| 476 | # Make sure we have processed the last subtest failure |
| 477 | last_subset_error = subtest_errors[-1] |
| 478 | if exc_info is last_subset_error[-1]: |
| 479 | # Add non-subtest skips (as they could not be treated in `_addSkip`) |
| 480 | for testcase, reason in non_subtest_skip: |
| 481 | self.addSkip(testcase, reason, handle_subtests=False) |
| 482 |
no test coverage detected