|
21 | 21 | from typing import ( |
22 | 22 | TYPE_CHECKING, |
23 | 23 | Any, |
| 24 | + ClassVar, |
24 | 25 | Literal, |
25 | 26 | Optional, |
26 | 27 | TypedDict, |
|
62 | 63 | next_up, |
63 | 64 | ) |
64 | 65 | from hypothesis.internal.intervalsets import IntervalSet |
65 | | -from hypothesis.internal.observability import InfoObservationType |
| 66 | +from hypothesis.internal.observability import InfoObservationType, TestCaseObservation |
66 | 67 |
|
67 | 68 | if TYPE_CHECKING: |
68 | 69 | from typing import TypeAlias |
@@ -356,6 +357,15 @@ class PrimitiveProvider(abc.ABC): |
356 | 357 | #: Only set this to ``True`` if it is necessary for your backend. |
357 | 358 | avoid_realization = False |
358 | 359 |
|
| 360 | + #: If ``True``, |PrimitiveProvider.on_observation| will be added as a |
| 361 | + #: callback to |TESTCASE_CALLBACKS|, enabling observability during the lifetime |
| 362 | + #: of this provider. If ``False``, |PrimitiveProvider.on_observation| will |
| 363 | + #: never be called by Hypothesis. |
| 364 | + #: |
| 365 | + #: The opt-in behavior of observability is because enabling observability |
| 366 | + #: might increase runtime or memory usage. |
| 367 | + add_observability_callback: ClassVar[bool] = False |
| 368 | + |
359 | 369 | def __init__(self, conjecturedata: Optional["ConjectureData"], /) -> None: |
360 | 370 | self._cd = conjecturedata |
361 | 371 |
|
@@ -544,6 +554,41 @@ def observe_information_messages( |
544 | 554 | assert lifetime in ("test_case", "test_function") |
545 | 555 | yield from [] |
546 | 556 |
|
| 557 | + def on_observation(self, observation: TestCaseObservation) -> None: # noqa: B027 |
| 558 | + """ |
| 559 | + Called at the end of each test case which uses this provider, with the same |
| 560 | + ``observation["type"] == "test_case"`` observation that is passed to |
| 561 | + other callbacks in |TESTCASE_CALLBACKS|. This method is not called with |
| 562 | + ``observation["type"] in {"info", "alert", "error"}`` observations. |
| 563 | +
|
| 564 | + .. important:: |
| 565 | +
|
| 566 | + For |PrimitiveProvider.on_observation| to be called by Hypothesis, |
| 567 | + |PrimitiveProvider.add_observability_callback| must be set to ``True``, |
| 568 | +
|
| 569 | + |PrimitiveProvider.on_observation| is explicitly opt-in, as enabling |
| 570 | + observability might increase runtime or memory usage. |
| 571 | +
|
| 572 | + Calls to this method are guaranteed to alternate with calls to |
| 573 | + |PrimitiveProvider.per_test_case_context_manager|. For example: |
| 574 | +
|
| 575 | + .. code-block:: python |
| 576 | +
|
| 577 | + # test function starts |
| 578 | + per_test_case_context_manager() |
| 579 | + on_observation() |
| 580 | + per_test_case_context_manager() |
| 581 | + on_observation() |
| 582 | + ... |
| 583 | + # test function ends |
| 584 | +
|
| 585 | + Note that |PrimitiveProvider.on_observation| will not be called for test |
| 586 | + cases which did not use this provider during generation, for example |
| 587 | + during |Phase.reuse| or |Phase.shrink|, or because Hypothesis switched |
| 588 | + to the standard Hypothesis backend after this backend raised too many |
| 589 | + |BackendCannotProceed| exceptions. |
| 590 | + """ |
| 591 | + |
547 | 592 | def span_start(self, label: int, /) -> None: # noqa: B027 # non-abstract noop |
548 | 593 | """Marks the beginning of a semantically meaningful span of choices. |
549 | 594 |
|
|
0 commit comments