Skip to content

Commit 63bb80c

Browse files
authored
Merge pull request #4254 from tybug/tcs-cleanup
Reorganize code for the typed choice sequence
2 parents 9d7e1d3 + 869e3de commit 63bb80c

22 files changed

+730
-710
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+
Registration of experimental :ref:`alternative-backends` is now done via ``hypothesis.internal.conjecture.providers.AVAILABLE_PROVIDERS`` instead of ``hypothesis.internal.conjecture.data.AVAILABLE_PROVIDERS``.

hypothesis-python/src/hypothesis/_settings.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
InvalidArgument,
4040
InvalidState,
4141
)
42+
from hypothesis.internal.conjecture.providers import AVAILABLE_PROVIDERS
4243
from hypothesis.internal.reflection import get_pretty_function_description
4344
from hypothesis.internal.validation import check_type, try_convert
4445
from hypothesis.utils.conventions import not_set
@@ -296,8 +297,6 @@ def __setattr__(self, name: str, value: object) -> NoReturn:
296297
raise AttributeError("settings objects are immutable")
297298

298299
def __repr__(self) -> str:
299-
from hypothesis.internal.conjecture.data import AVAILABLE_PROVIDERS
300-
301300
bits = sorted(
302301
f"{name}={getattr(self, name)!r}"
303302
for name in all_settings
@@ -731,8 +730,6 @@ def is_in_ci() -> bool:
731730

732731

733732
def _backend_validator(value: str) -> str:
734-
from hypothesis.internal.conjecture.data import AVAILABLE_PROVIDERS
735-
736733
if value not in AVAILABLE_PROVIDERS:
737734
if value == "crosshair": # pragma: no cover
738735
install = '`pip install "hypothesis[crosshair]"` and try again.'

hypothesis-python/src/hypothesis/core.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
settings as Settings,
5151
)
5252
from hypothesis.control import BuildContext
53-
from hypothesis.database import ir_from_bytes, ir_to_bytes
53+
from hypothesis.database import choices_from_bytes, choices_to_bytes
5454
from hypothesis.errors import (
5555
BackendCannotProceed,
5656
DeadlineExceeded,
@@ -77,17 +77,16 @@
7777
int_from_bytes,
7878
)
7979
from hypothesis.internal.conjecture.choice import ChoiceT
80-
from hypothesis.internal.conjecture.data import (
81-
ConjectureData,
82-
PrimitiveProvider,
83-
Status,
84-
)
80+
from hypothesis.internal.conjecture.data import ConjectureData, Status
8581
from hypothesis.internal.conjecture.engine import BUFFER_SIZE, ConjectureRunner
8682
from hypothesis.internal.conjecture.junkdrawer import (
8783
ensure_free_stackframes,
8884
gc_cumulative_time,
8985
)
90-
from hypothesis.internal.conjecture.providers import BytestringProvider
86+
from hypothesis.internal.conjecture.providers import (
87+
BytestringProvider,
88+
PrimitiveProvider,
89+
)
9190
from hypothesis.internal.conjecture.shrinker import sort_key
9291
from hypothesis.internal.entropy import deterministic_PRNG
9392
from hypothesis.internal.escalation import (
@@ -323,7 +322,7 @@ def accept(test):
323322

324323

325324
def encode_failure(choices):
326-
blob = ir_to_bytes(choices)
325+
blob = choices_to_bytes(choices)
327326
compressed = zlib.compress(blob)
328327
if len(compressed) < len(blob):
329328
blob = b"\1" + compressed
@@ -353,7 +352,7 @@ def decode_failure(blob: bytes) -> Sequence[ChoiceT]:
353352
f"Could not decode blob {blob!r}: Invalid start byte {prefix!r}"
354353
)
355354

356-
choices = ir_from_bytes(decoded)
355+
choices = choices_from_bytes(decoded)
357356
if choices is None:
358357
raise InvalidArgument(f"Invalid serialized choice sequence for blob {blob!r}")
359358

@@ -1883,7 +1882,9 @@ def fuzz_one_input(
18831882
if settings.database is not None and (
18841883
known is None or sort_key(data.nodes) <= sort_key(known)
18851884
):
1886-
settings.database.save(database_key, ir_to_bytes(data.choices))
1885+
settings.database.save(
1886+
database_key, choices_to_bytes(data.choices)
1887+
)
18871888
minimal_failures[data.interesting_origin] = data.nodes
18881889
raise
18891890
assert isinstance(data.provider, BytestringProvider)

hypothesis-python/src/hypothesis/database.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -775,8 +775,8 @@ def _unpack_uleb128(buffer: bytes) -> tuple[int, int]:
775775
return (i + 1, value)
776776

777777

778-
def ir_to_bytes(ir: Iterable[ChoiceT], /) -> bytes:
779-
"""Serialize a list of IR elements to a bytestring. Inverts ir_from_bytes."""
778+
def choices_to_bytes(ir: Iterable[ChoiceT], /) -> bytes:
779+
"""Serialize a list of IR elements to a bytestring. Inverts choices_from_bytes."""
780780
# We use a custom serialization format for this, which might seem crazy - but our
781781
# data is a flat sequence of elements, and standard tools like protobuf or msgpack
782782
# don't deal well with e.g. nonstandard bit-pattern-NaNs, or invalid-utf8 unicode.
@@ -815,7 +815,7 @@ def ir_to_bytes(ir: Iterable[ChoiceT], /) -> bytes:
815815
return b"".join(parts)
816816

817817

818-
def _ir_from_bytes(buffer: bytes, /) -> tuple[ChoiceT, ...]:
818+
def _choices_from_bytes(buffer: bytes, /) -> tuple[ChoiceT, ...]:
819819
# See above for an explanation of the format.
820820
parts: list[ChoiceT] = []
821821
idx = 0
@@ -846,15 +846,15 @@ def _ir_from_bytes(buffer: bytes, /) -> tuple[ChoiceT, ...]:
846846
return tuple(parts)
847847

848848

849-
def ir_from_bytes(buffer: bytes, /) -> Optional[tuple[ChoiceT, ...]]:
849+
def choices_from_bytes(buffer: bytes, /) -> Optional[tuple[ChoiceT, ...]]:
850850
"""
851-
Deserialize a bytestring to a tuple of choices. Inverts ir_to_bytes.
851+
Deserialize a bytestring to a tuple of choices. Inverts choices_to_bytes.
852852
853853
Returns None if the given bytestring is not a valid serialization of choice
854854
sequences.
855855
"""
856856
try:
857-
return _ir_from_bytes(buffer)
857+
return _choices_from_bytes(buffer)
858858
except Exception:
859859
# deserialization error, eg because our format changed or someone put junk
860860
# data in the db.

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
# obtain one at https://mozilla.org/MPL/2.0/.
1010

1111
import math
12-
from collections.abc import Sequence
12+
from collections.abc import Iterable, Sequence
1313
from typing import (
1414
TYPE_CHECKING,
1515
Callable,
@@ -21,6 +21,8 @@
2121
cast,
2222
)
2323

24+
import attr
25+
2426
from hypothesis.errors import ChoiceTooLarge
2527
from hypothesis.internal.conjecture.floats import float_to_lex, lex_to_float
2628
from hypothesis.internal.conjecture.utils import identity
@@ -72,6 +74,16 @@ class BooleanKWargs(TypedDict):
7274
]
7375

7476

77+
@attr.s(slots=True)
78+
class ChoiceTemplate:
79+
type: Literal["simplest"] = attr.ib()
80+
count: Optional[int] = attr.ib()
81+
82+
def __attrs_post_init__(self) -> None:
83+
if self.count is not None:
84+
assert self.count > 0
85+
86+
7587
def _size_to_index(size: int, *, alphabet_size: int) -> int:
7688
# this is the closed form of this geometric series:
7789
# for i in range(size):
@@ -494,3 +506,9 @@ def choice_kwargs_key(ir_type, kwargs):
494506
kwargs["shrink_towards"],
495507
)
496508
return tuple(kwargs[key] for key in sorted(kwargs))
509+
510+
511+
def choices_size(choices: Iterable[ChoiceT]) -> int:
512+
from hypothesis.database import choices_to_bytes
513+
514+
return len(choices_to_bytes(choices))

0 commit comments

Comments
 (0)