Skip to content

Commit 53df53b

Browse files
authored
Merge pull request #4549 from Liam-DeVoe/pytest-stats-observability
Add `observation.metadata.phase`
2 parents c16cf7a + 21b6911 commit 53df53b

File tree

9 files changed

+32
-12
lines changed

9 files changed

+32
-12
lines changed

hypothesis-python/RELEASE.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
RELEASE_TYPE: patch
2+
3+
Add ``phase`` to the :ref:`hypothesis-specific metadata <observability-hypothesis-metadata>` in :ref:`observability <observability>`.

hypothesis-python/docs/reference/schema_metadata.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@
4747
"type": "number",
4848
"description": "The unix timestamp when Hypothesis was imported."
4949
},
50+
"phase": {
51+
"type": "string",
52+
"description": "The Hypothesis |Phase| this test case was generated in."
53+
},
5054
"data_status": {
5155
"type": "number",
5256
"enum": [0, 1, 2, 3],

hypothesis-python/src/hypothesis/internal/conjecture/engine.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -306,13 +306,9 @@ def __init__(
306306
self.first_bug_found_at: Optional[int] = None
307307
self.last_bug_found_at: Optional[int] = None
308308

309-
# At runtime, the keys are only ever type `InterestingOrigin`, but can be `None` during tests.
310-
self.shrunk_examples: set[Optional[InterestingOrigin]] = set()
311-
309+
self.shrunk_examples: set[InterestingOrigin] = set()
312310
self.health_check_state: Optional[HealthCheckState] = None
313-
314311
self.tree: DataTree = DataTree()
315-
316312
self.provider: Union[type, PrimitiveProvider] = _get_provider(
317313
self.settings.backend
318314
)
@@ -383,8 +379,6 @@ def _log_phase_statistics(
383379
self._current_phase = phase
384380
yield
385381
finally:
386-
# We ignore the mypy type error here. Because `phase` is a string literal and "-phase" is a string literal
387-
# as well, the concatenation will always be valid key in the dictionary.
388382
self.statistics[phase + "-phase"] = { # type: ignore
389383
"duration-seconds": time.perf_counter() - start_time,
390384
"test-cases": list(self.stats_per_test_case),

hypothesis-python/src/hypothesis/internal/observability.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ class ObservationMetadata:
171171
os_getpid: int
172172
imported_at: float
173173
data_status: "Status"
174+
phase: str
174175
interesting_origin: Optional[InterestingOrigin]
175176
choice_nodes: Optional[tuple[ChoiceNode, ...]]
176177
choice_spans: Optional["Spans"]
@@ -185,6 +186,7 @@ def to_json(self) -> dict[str, Any]:
185186
"os.getpid()": self.os_getpid,
186187
"imported_at": self.imported_at,
187188
"data_status": self.data_status,
189+
"phase": self.phase,
188190
"interesting_origin": self.interesting_origin,
189191
"choice_nodes": (
190192
None if self.choice_nodes is None else nodes_to_json(self.choice_nodes)
@@ -456,6 +458,7 @@ def make_testcase(
456458
"predicates": dict(data._observability_predicates),
457459
"backend": backend_metadata or {},
458460
"data_status": data.status,
461+
"phase": phase,
459462
"interesting_origin": data.interesting_origin,
460463
"choice_nodes": data.nodes if OBSERVABILITY_CHOICES else None,
461464
"choice_spans": data.spans if OBSERVABILITY_CHOICES else None,

hypothesis-python/tests/conjecture/test_provider.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,8 +654,10 @@ def test_available_providers_deprecation():
654654
"strategy", [st.integers(), st.text(), st.floats(), st.booleans(), st.binary()]
655655
)
656656
def test_can_generate_from_all_available_providers(backend, strategy):
657+
# note: database=InMemoryExampleDatabase() is for compatibility with HypoFuzz
658+
# here.
657659
@given(strategy)
658-
@settings(backend=backend, database=None)
660+
@settings(backend=backend, database=InMemoryExampleDatabase())
659661
def f(x):
660662
raise ValueError
661663

hypothesis-python/tests/conjecture/test_shrinker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -599,8 +599,8 @@ def test_redistribute_numeric_pairs(node1, node2, stop):
599599
assume(node1.value + node2.value > stop)
600600
# don't test extreme shrink_towards values, which lead to this test flaking
601601
# from floating point errors
602-
assume(abs(node1.constraints.get("shrink_towards", 0)) <= 1e20)
603-
assume(abs(node2.constraints.get("shrink_towards", 0)) <= 1e20)
602+
assume(abs(node1.constraints.get("shrink_towards", 0)) <= 1e10)
603+
assume(abs(node2.constraints.get("shrink_towards", 0)) <= 1e10)
604604
# avoid exhausting the tree while generating, which causes @shrinking_from's
605605
# runner to raise
606606
assume(

hypothesis-python/tests/cover/test_observability.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ def f(n):
578578
"os.getpid()",
579579
"imported_at",
580580
"data_status",
581+
"phase",
581582
"interesting_origin",
582583
"choice_nodes",
583584
"choice_spans",

hypothesis-python/tests/cover/test_stateful.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
from tests.common.utils import (
4747
Why,
4848
capture_out,
49+
skipif_threading,
4950
validate_deprecation,
5051
xfail_on_crosshair,
5152
)
@@ -407,6 +408,7 @@ def increment(self):
407408
FailsEventually.TestCase.settings = Settings(stateful_step_count=5)
408409

409410

411+
@skipif_threading
410412
def test_can_explicitly_pass_settings():
411413
run_state_machine_as_test(FailsEventually)
412414
try:
@@ -432,6 +434,7 @@ def test_runner_that_checks_factory_produced_a_machine():
432434
run_state_machine_as_test(object)
433435

434436

437+
@skipif_threading
435438
def test_settings_attribute_is_validated():
436439
real_settings = FailsEventually.TestCase.settings
437440
try:

hypothesis-python/tests/crosshair/test_conformance.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from crosshair.core import IgnoreAttempt, NotDeterministic, UnexploredPath
1212
from hypothesis_crosshair_provider.crosshair_provider import CrossHairPrimitiveProvider
1313

14-
from hypothesis import settings, strategies as st
14+
from hypothesis import Verbosity, settings, strategies as st
1515
from hypothesis.internal.conjecture.provider_conformance import run_conformance_test
1616

1717

@@ -25,6 +25,16 @@ def test_provider_conformance_crosshair():
2525
run_conformance_test(
2626
CrossHairPrimitiveProvider,
2727
context_manager_exceptions=(IgnoreAttempt, UnexploredPath, NotDeterministic),
28-
settings=settings(max_examples=5, stateful_step_count=10),
28+
settings=settings(
29+
max_examples=5,
30+
stateful_step_count=10,
31+
# It's nice to set Verbosity.verbose unconditionally here so we get
32+
# more information during rare errors without having to retry.
33+
#
34+
# Careful, though: it's possible interactions with crosshair differ
35+
# under verbosity (eg realizing the symbolic debug messages), so
36+
# some errors may only surface with or without verbosity.
37+
verbosity=Verbosity.verbose,
38+
),
2939
_realize_objects=_realize_objects,
3040
)

0 commit comments

Comments
 (0)