Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/hypothesis/strategies/_internal/strategies.py: 47%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

599 statements  

1# This file is part of Hypothesis, which may be found at 

2# https://github.com/HypothesisWorks/hypothesis/ 

3# 

4# Copyright the Hypothesis Authors. 

5# Individual contributors are listed in AUTHORS.rst and the git log. 

6# 

7# This Source Code Form is subject to the terms of the Mozilla Public License, 

8# v. 2.0. If a copy of the MPL was not distributed with this file, You can 

9# obtain one at https://mozilla.org/MPL/2.0/. 

10 

11import os 

12import sys 

13import threading 

14import warnings 

15from collections import abc, defaultdict 

16from collections.abc import Callable, Sequence 

17from functools import lru_cache 

18from random import shuffle 

19from threading import RLock 

20from types import FrameType 

21from typing import ( 

22 TYPE_CHECKING, 

23 Any, 

24 ClassVar, 

25 Generic, 

26 Literal, 

27 TypeAlias, 

28 TypeGuard, 

29 TypeVar, 

30 cast, 

31 overload, 

32) 

33 

34from hypothesis._settings import HealthCheck, Phase, Verbosity, settings 

35from hypothesis.control import _current_build_context, current_build_context 

36from hypothesis.errors import ( 

37 CannotInvert, 

38 HypothesisException, 

39 HypothesisWarning, 

40 InvalidArgument, 

41 NonInteractiveExampleWarning, 

42 UnsatisfiedAssumption, 

43) 

44from hypothesis.internal.conjecture import utils as cu 

45from hypothesis.internal.conjecture.choice import ChoiceT 

46from hypothesis.internal.conjecture.data import ConjectureData 

47from hypothesis.internal.conjecture.junkdrawer import equal_values 

48from hypothesis.internal.conjecture.utils import ( 

49 calc_label_from_cls, 

50 calc_label_from_hash, 

51 calc_label_from_name, 

52 combine_labels, 

53) 

54from hypothesis.internal.coverage import check_function 

55from hypothesis.internal.escalation import is_hypothesis_file 

56from hypothesis.internal.reflection import ( 

57 get_pretty_function_description, 

58 is_identity_function, 

59) 

60from hypothesis.strategies._internal.utils import defines_strategy 

61from hypothesis.utils.conventions import UniqueIdentifier, not_set 

62from hypothesis.utils.dynamicvariables import DynamicVariable 

63 

64if TYPE_CHECKING: 

65 Ex = TypeVar("Ex", covariant=True, default=Any) 

66else: 

67 Ex = TypeVar("Ex", covariant=True) 

68 

69T = TypeVar("T") 

70T3 = TypeVar("T3") 

71T4 = TypeVar("T4") 

72T5 = TypeVar("T5") 

73MappedFrom = TypeVar("MappedFrom") 

74MappedTo = TypeVar("MappedTo") 

75RecurT: TypeAlias = Callable[["SearchStrategy"], bool] 

76calculating = UniqueIdentifier("calculating") 

77 

78MAPPED_SEARCH_STRATEGY_DO_DRAW_LABEL = calc_label_from_name( 

79 "another attempted draw in MappedStrategy" 

80) 

81 

82FILTERED_SEARCH_STRATEGY_DO_DRAW_LABEL = calc_label_from_name( 

83 "single loop iteration in FilteredStrategy" 

84) 

85 

86label_lock = RLock() 

87 

88# When hypothesis re-applies a filter condition internally, eg in LazyStrategy, we override 

89# stack walking with the correct original location here. 

90_filter_location_override: DynamicVariable[Any] = DynamicVariable(not_set) 

91 

92 

93def current_filter_call_site() -> str | None: 

94 """The filename:lineno of the nearest enclosing non-hypothesis .filter() call. 

95 

96 Callers are expected to only call this from within a .filter() implementation. This 

97 function simply respects _filter_location_override or walks to the nearest non-hypothesis 

98 frame. 

99 """ 

100 if (location := _filter_location_override.value) is not not_set: 

101 return location 

102 frame: FrameType | None = sys._getframe(1) 

103 while frame is not None and is_hypothesis_file(frame.f_code.co_filename): 

104 frame = frame.f_back 

105 if frame is None: # pragma: no cover # ran out of frames 

106 return None 

107 return f"{frame.f_code.co_filename}:{frame.f_lineno}" 

108 

109 

110def recursive_property(strategy: "SearchStrategy", name: str, default: object) -> Any: 

111 """Handle properties which may be mutually recursive among a set of 

112 strategies. 

113 

114 These are essentially lazily cached properties, with the ability to set 

115 an override: If the property has not been explicitly set, we calculate 

116 it on first access and memoize the result for later. 

117 

118 The problem is that for properties that depend on each other, a naive 

119 calculation strategy may hit infinite recursion. Consider for example 

120 the property is_empty. A strategy defined as x = st.deferred(lambda: x) 

121 is certainly empty (in order to draw a value from x we would have to 

122 draw a value from x, for which we would have to draw a value from x, 

123 ...), but in order to calculate it the naive approach would end up 

124 calling x.is_empty in order to calculate x.is_empty in order to etc. 

125 

126 The solution is one of fixed point calculation. We start with a default 

127 value that is the value of the property in the absence of evidence to 

128 the contrary, and then update the values of the property for all 

129 dependent strategies until we reach a fixed point. 

130 

131 The approach taken roughly follows that in section 4.2 of Adams, 

132 Michael D., Celeste Hollenbeck, and Matthew Might. "On the complexity 

133 and performance of parsing with derivatives." ACM SIGPLAN Notices 51.6 

134 (2016): 224-236. 

135 """ 

136 assert name in {"is_empty", "has_reusable_values", "is_cacheable"} 

137 cache_key = "cached_" + name 

138 calculation = "calc_" + name 

139 force_key = "force_" + name 

140 

141 def forced_or_cached_value(target: SearchStrategy) -> Any: 

142 try: 

143 return getattr(target, force_key) 

144 except AttributeError: 

145 return getattr(target, cache_key) 

146 

147 try: 

148 return forced_or_cached_value(strategy) 

149 except AttributeError: 

150 pass 

151 

152 mapping: dict[SearchStrategy, Any] = {} 

153 sentinel = object() 

154 hit_recursion = False 

155 

156 # For a first pass we do a direct recursive calculation of the 

157 # property, but we block recursively visiting a value in the 

158 # computation of its property: When that happens, we simply 

159 # note that it happened and return the default value. 

160 def recur(strat: SearchStrategy) -> Any: 

161 nonlocal hit_recursion 

162 try: 

163 return forced_or_cached_value(strat) 

164 except AttributeError: 

165 pass 

166 result = mapping.get(strat, sentinel) 

167 if result is calculating: 

168 hit_recursion = True 

169 return default 

170 elif result is sentinel: 

171 mapping[strat] = calculating 

172 mapping[strat] = getattr(strat, calculation)(recur) 

173 return mapping[strat] 

174 return result 

175 

176 recur(strategy) 

177 

178 # If we hit self-recursion in the computation of any strategy 

179 # value, our mapping at the end is imprecise - it may or may 

180 # not have the right values in it. We now need to proceed with 

181 # a more careful fixed point calculation to get the exact 

182 # values. Hopefully our mapping is still pretty good and it 

183 # won't take a large number of updates to reach a fixed point. 

184 if hit_recursion: 

185 needs_update = set(mapping) 

186 

187 # We track which strategies use which in the course of 

188 # calculating their property value. If A ever uses B in 

189 # the course of calculating its value, then whenever the 

190 # value of B changes we might need to update the value of 

191 # A. 

192 listeners: dict[SearchStrategy, set[SearchStrategy]] = defaultdict(set) 

193 else: 

194 needs_update = None 

195 

196 def recur2(strat: SearchStrategy) -> Any: 

197 def recur_inner(other: SearchStrategy) -> Any: 

198 try: 

199 return forced_or_cached_value(other) 

200 except AttributeError: 

201 pass 

202 listeners[other].add(strat) 

203 result = mapping.get(other, sentinel) 

204 if result is sentinel: 

205 assert needs_update is not None 

206 needs_update.add(other) 

207 mapping[other] = default 

208 return default 

209 return result 

210 

211 return recur_inner 

212 

213 count = 0 

214 seen = set() 

215 while needs_update: 

216 count += 1 

217 # If we seem to be taking a really long time to stabilize we 

218 # start tracking seen values to attempt to detect an infinite 

219 # loop. This should be impossible, and most code will never 

220 # hit the count, but having an assertion for it means that 

221 # testing is easier to debug and we don't just have a hung 

222 # test. 

223 if count > 50: 

224 key = frozenset(mapping.items()) 

225 assert key not in seen, (key, name) 

226 seen.add(key) 

227 to_update = needs_update 

228 needs_update = set() 

229 for strat in to_update: 

230 new_value = getattr(strat, calculation)(recur2(strat)) 

231 if new_value != mapping[strat]: 

232 needs_update.update(listeners[strat]) 

233 mapping[strat] = new_value 

234 

235 # We now have a complete and accurate calculation of the 

236 # property values for everything we have seen in the course of 

237 # running this calculation. We simultaneously update all of 

238 # them (not just the strategy we started out with). 

239 for k, v in mapping.items(): 

240 setattr(k, cache_key, v) 

241 # This used to simply be `getattr(strategy, cache_key)`. That relied on the invariant 

242 # that our loop above has set `strategy.cached_* = v` on `strategy` if we've reached 

243 # here. However, under threading, this is not necessarily true. If a concurrent thread 

244 # sets `strategy.force_* = v` in between the two places we check for `force_*`, we 

245 # will not set `strategy.cached_*`. 

246 # 

247 # There are several places where we might do this. unwrap_strategies sets 

248 # force_has_reusable_values = True. our numpy.py's `arrays` strategy also does. 

249 # 

250 # We guard against this in general by checking the forced and cached values here, 

251 # rather than just the cached value. 

252 return forced_or_cached_value(strategy) 

253 

254 

255class SearchStrategy(Generic[Ex]): 

256 """A ``SearchStrategy`` tells Hypothesis how to generate that kind of input. 

257 

258 This class is only part of the public API for use in type annotations, so that 

259 you can write e.g. ``-> SearchStrategy[Foo]`` for your function which returns 

260 ``builds(Foo, ...)``. Do not inherit from or directly instantiate this class. 

261 """ 

262 

263 __module__: str = "hypothesis.strategies" 

264 LABELS: ClassVar[dict[type, int]] = {} 

265 # triggers `assert isinstance(label, int)` under threading when setting this 

266 # in init instead of a classvar. I'm not sure why, init should be safe. But 

267 # this works so I'm not looking into it further atm. 

268 __label: int | UniqueIdentifier | None = None 

269 

270 def __init__(self) -> None: 

271 self.validate_called: dict[int, bool] = {} 

272 

273 def is_currently_empty(self, data: ConjectureData) -> bool: 

274 """ 

275 Returns whether this strategy is currently empty. Unlike ``empty``, 

276 which is computed based on static information and cannot change, 

277 ``is_currently_empty`` may change over time based on choices made 

278 during the test case. 

279 

280 This is currently only used for stateful testing, where |Bundle| grows a 

281 list of values to choose from over the course of a test case. 

282 

283 ``data`` will only be used for introspection. No values will be drawn 

284 from it in a way that modifies the choice sequence. 

285 """ 

286 return self.is_empty 

287 

288 @property 

289 def is_empty(self) -> Any: 

290 # Returns True if this strategy can never draw a value and will always 

291 # result in the data being marked invalid. 

292 # The fact that this returns False does not guarantee that a valid value 

293 # can be drawn - this is not intended to be perfect, and is primarily 

294 # intended to be an optimisation for some cases. 

295 return recursive_property(self, "is_empty", True) 

296 

297 # Returns True if values from this strategy can safely be reused without 

298 # this causing unexpected behaviour. 

299 

300 # True if values from this strategy can be implicitly reused (e.g. as 

301 # background values in a numpy array) without causing surprising 

302 # user-visible behaviour. Should be false for built-in strategies that 

303 # produce mutable values, and for strategies that have been mapped/filtered 

304 # by arbitrary user-provided functions. 

305 @property 

306 def has_reusable_values(self) -> Any: 

307 return recursive_property(self, "has_reusable_values", True) 

308 

309 @property 

310 def is_cacheable(self) -> Any: 

311 """ 

312 Whether it is safe to hold on to instances of this strategy in a cache. 

313 See _STRATEGY_CACHE. 

314 """ 

315 return recursive_property(self, "is_cacheable", True) 

316 

317 def calc_is_cacheable(self, recur: RecurT) -> bool: 

318 return True 

319 

320 def calc_is_empty(self, recur: RecurT) -> bool: 

321 # Note: It is correct and significant that the default return value 

322 # from calc_is_empty is False despite the default value for is_empty 

323 # being true. The reason for this is that strategies should be treated 

324 # as empty absent evidence to the contrary, but most basic strategies 

325 # are trivially non-empty and it would be annoying to have to override 

326 # this method to show that. 

327 return False 

328 

329 def calc_has_reusable_values(self, recur: RecurT) -> bool: 

330 return False 

331 

332 def example(self) -> Ex: # FIXME 

333 """Provide an example of the sort of value that this strategy generates. 

334 

335 This method is designed for use in a REPL, and will raise an error if 

336 called from inside |@given| or a strategy definition. For serious use, 

337 see |@composite| or |st.data|. 

338 """ 

339 if getattr(sys, "ps1", None) is None and ( 

340 # The main module's __spec__ is None when running interactively 

341 # or running a source file directly. 

342 # See https://docs.python.org/3/reference/import.html#main-spec. 

343 sys.modules["__main__"].__spec__ is not None 

344 # __spec__ is also None under pytest-xdist. To avoid an unfortunate 

345 # missed alarm here, always warn under pytest. 

346 or os.environ.get("PYTEST_CURRENT_TEST") is not None 

347 ): # pragma: no branch 

348 # The other branch *is* covered in cover/test_interactive_example.py; 

349 # but as that uses `pexpect` for an interactive session `coverage` 

350 # doesn't see it. 

351 warnings.warn( 

352 "The `.example()` method is good for exploring strategies, but should " 

353 "only be used interactively. We recommend using `@given` for tests - " 

354 "it performs better, saves and replays failures to avoid flakiness, " 

355 f"and reports minimal failing test cases. (strategy: {self!r})", 

356 NonInteractiveExampleWarning, 

357 stacklevel=2, 

358 ) 

359 

360 context = _current_build_context.value 

361 if context is not None: 

362 if context.data is not None and context.data.depth > 0: 

363 raise HypothesisException( 

364 "Using example() inside a strategy definition is a bad " 

365 "idea. Instead consider using hypothesis.strategies.builds() " 

366 "or @hypothesis.strategies.composite to define your strategy." 

367 " See https://hypothesis.readthedocs.io/en/latest/reference/" 

368 "strategies.html#hypothesis.strategies.builds or " 

369 "https://hypothesis.readthedocs.io/en/latest/reference/" 

370 "strategies.html#hypothesis.strategies.composite for more " 

371 "details." 

372 ) 

373 else: 

374 raise HypothesisException( 

375 "Using example() inside a test function is a bad " 

376 "idea. Instead consider using hypothesis.strategies.data() " 

377 "to draw more values during testing. See " 

378 "https://hypothesis.readthedocs.io/en/latest/reference/" 

379 "strategies.html#hypothesis.strategies.data for more details." 

380 ) 

381 

382 try: 

383 return self.__examples.pop() 

384 except (AttributeError, IndexError): 

385 self.__examples: list[Ex] = [] 

386 

387 from hypothesis.core import given 

388 

389 # Note: this function has a weird name because it might appear in 

390 # tracebacks, and we want users to know that they can ignore it. 

391 @given(self) 

392 @settings( 

393 database=None, 

394 # generate only a few examples at a time to avoid slow interactivity 

395 # for large strategies. The overhead of @given is very small relative 

396 # to generation, so a small batch size is fine. 

397 max_examples=10, 

398 deadline=None, 

399 verbosity=Verbosity.quiet, 

400 phases=(Phase.generate,), 

401 suppress_health_check=list(HealthCheck), 

402 ) 

403 def example_generating_inner_function( 

404 ex: Ex, # type: ignore # mypy is overzealous in preventing covariant params 

405 ) -> None: 

406 self.__examples.append(ex) 

407 

408 example_generating_inner_function() 

409 shuffle(self.__examples) 

410 return self.__examples.pop() 

411 

412 def map(self, pack: Callable[[Ex], T]) -> "SearchStrategy[T]": 

413 """Returns a new strategy which generates a value from this one, and 

414 then returns ``pack(value)``. For example, ``integers().map(str)`` 

415 could generate ``str(5)`` == ``"5"``. 

416 """ 

417 if is_identity_function(pack): 

418 return self # type: ignore # Mypy has no way to know that `Ex == T` 

419 return MappedStrategy(self, pack=pack) 

420 

421 def flatmap( 

422 self, expand: Callable[[Ex], "SearchStrategy[T]"] 

423 ) -> "SearchStrategy[T]": # FIXME 

424 """Old syntax for a special case of |@composite|: 

425 

426 .. code-block:: python 

427 

428 @st.composite 

429 def flatmap_like(draw, base_strategy, expand): 

430 value = draw(base_strategy) 

431 new_strategy = expand(value) 

432 return draw(new_strategy) 

433 

434 We find that the greater readability of |@composite| usually outweighs 

435 the verbosity, with a few exceptions for simple cases or recipes like 

436 ``from_type(type).flatmap(from_type)`` ("pick a type, get a strategy for 

437 any instance of that type, and then generate one of those"). 

438 """ 

439 from hypothesis.strategies._internal.flatmapped import FlatMapStrategy 

440 

441 return FlatMapStrategy(self, expand=expand) 

442 

443 # Note that we previously had condition extracted to a type alias as 

444 # PredicateT. However, that was only useful when not specifying a relationship 

445 # between the generic Ts and some other function param / return value. 

446 # If we do want to - like here, where we want to say that the Ex arg to condition 

447 # is of the same type as the strategy's Ex - then you need to write out the 

448 # entire Callable[[Ex], Any] expression rather than use a type alias. 

449 # TypeAlias is *not* simply a macro that inserts the text. TypeAlias will not 

450 # reference the local TypeVar context. 

451 @overload 

452 def filter( 

453 self, condition: Callable[[Ex], TypeGuard[T]] 

454 ) -> "SearchStrategy[T]": ... 

455 @overload 

456 def filter(self, condition: Callable[[Ex], Any]) -> "SearchStrategy[Ex]": ... 

457 def filter(self, condition): 

458 """Returns a new strategy that generates values from this strategy 

459 which satisfy the provided condition. 

460 

461 Note that if the condition is too hard to satisfy this might result 

462 in your tests failing with an Unsatisfiable exception. 

463 A basic version of the filtering logic would look something like: 

464 

465 .. code-block:: python 

466 

467 @st.composite 

468 def filter_like(draw, strategy, condition): 

469 for _ in range(3): 

470 value = draw(strategy) 

471 if condition(value): 

472 return value 

473 assume(False) 

474 """ 

475 return FilteredStrategy( 

476 self, 

477 conditions=(condition,), 

478 condition_locations=(current_filter_call_site(),), 

479 ) 

480 

481 @property 

482 def branches(self) -> Sequence["SearchStrategy[Ex]"]: 

483 return [self] 

484 

485 def __or__(self, other: "SearchStrategy[T]") -> "SearchStrategy[Ex | T]": 

486 """Return a strategy which produces values by randomly drawing from one 

487 of this strategy or the other strategy. 

488 

489 This method is part of the public API. 

490 """ 

491 if not isinstance(other, SearchStrategy): 

492 raise ValueError(f"Cannot | a SearchStrategy with {other!r}") 

493 

494 # Unwrap explicitly or'd strategies. This turns the 

495 # common case of e.g. st.integers() | st.integers() | st.integers() from 

496 # 

497 # one_of(one_of(integers(), integers()), integers()) 

498 # 

499 # into 

500 # 

501 # one_of(integers(), integers(), integers()) 

502 # 

503 # This is purely an aesthetic unwrapping, for e.g. reprs. In practice 

504 # we use .branches / .element_strategies to get the list of possible 

505 # strategies, so this unwrapping is *not* necessary for correctness. 

506 strategies: list[SearchStrategy] = [] 

507 strategies.extend( 

508 self.original_strategies if isinstance(self, OneOfStrategy) else [self] 

509 ) 

510 strategies.extend( 

511 other.original_strategies if isinstance(other, OneOfStrategy) else [other] 

512 ) 

513 return OneOfStrategy(strategies) 

514 

515 def __bool__(self) -> bool: 

516 warnings.warn( 

517 f"bool({self!r}) is always True, did you mean to draw a value?", 

518 HypothesisWarning, 

519 stacklevel=2, 

520 ) 

521 return True 

522 

523 def validate(self) -> None: 

524 """Throw an exception if the strategy is not valid. 

525 

526 Strategies should implement ``do_validate``, which is called by this 

527 method. They should not override ``validate``. 

528 

529 This can happen due to invalid arguments, or lazy construction. 

530 """ 

531 thread_id = threading.get_ident() 

532 if self.validate_called.get(thread_id, False): 

533 return 

534 # we need to set validate_called before calling do_validate, for 

535 # recursive / deferred strategies. But if a thread switches after 

536 # validate_called but before do_validate, we might have a strategy 

537 # which does weird things like drawing when do_validate would error but 

538 # its params are technically valid (e.g. a param was passed as 1.0 

539 # instead of 1) and get into weird internal states. 

540 # 

541 # There are two ways to fix this. 

542 # (1) The first is a per-strategy lock around do_validate. Even though we 

543 # expect near-zero lock contention, this still adds the lock overhead. 

544 # (2) The second is allowing concurrent .validate calls. Since validation 

545 # is (assumed to be) deterministic, both threads will produce the same 

546 # end state, so the validation order or race conditions does not matter. 

547 # 

548 # In order to avoid the lock overhead of (1), we use (2) here. See also 

549 # discussion in https://github.com/HypothesisWorks/hypothesis/pull/4473. 

550 try: 

551 self.validate_called[thread_id] = True 

552 self.do_validate() 

553 self.is_empty 

554 self.has_reusable_values 

555 except Exception: 

556 self.validate_called[thread_id] = False 

557 raise 

558 

559 @property 

560 def class_label(self) -> int: 

561 cls = self.__class__ 

562 try: 

563 return cls.LABELS[cls] 

564 except KeyError: 

565 pass 

566 result = calc_label_from_cls(cls) 

567 cls.LABELS[cls] = result 

568 return result 

569 

570 @property 

571 def label(self) -> int: 

572 if isinstance((label := self.__label), int): 

573 # avoid locking if we've already completely computed the label. 

574 return label 

575 

576 with label_lock: 

577 if self.__label is calculating: 

578 return 0 

579 self.__label = calculating 

580 self.__label = self.calc_label() 

581 return self.__label 

582 

583 def calc_label(self) -> int: 

584 return self.class_label 

585 

586 def do_validate(self) -> None: 

587 pass 

588 

589 def do_draw(self, data: ConjectureData) -> Ex: 

590 raise NotImplementedError(f"{type(self).__name__}.do_draw") 

591 

592 def _invert(self, value: Any) -> tuple[ChoiceT, ...]: 

593 """ 

594 Return a choice sequence ``choices`` such that we expect 

595 

596 data = ConjectureData.for_choices(choices) 

597 drawn = data.draw(self) 

598 

599 would produce ``drawn == value``. Inversion is best-effort: it should 

600 satisfy this property with high probability, but callers must replay 

601 the returned choices and check the outcome rather than rely on it. 

602 

603 Raises CannotInvert if we cannot construct such a choice sequence, 

604 whether because ``value`` is not produced by this strategy or because 

605 inversion isn't implemented for it. 

606 """ 

607 raise CannotInvert(f"{type(self).__name__} does not support inversion") 

608 

609 

610def _is_hashable(value: object) -> tuple[bool, int | None]: 

611 # hashing can be expensive; return the hash value if we compute it, so that 

612 # callers don't have to recompute. 

613 try: 

614 return (True, hash(value)) 

615 except TypeError: 

616 return (False, None) 

617 

618 

619def is_hashable(value: object) -> bool: 

620 return _is_hashable(value)[0] 

621 

622 

623class SampledFromStrategy(SearchStrategy[Ex]): 

624 """A strategy which samples from a set of elements. This is essentially 

625 equivalent to using a OneOfStrategy over Just strategies but may be more 

626 efficient and convenient. 

627 """ 

628 

629 _MAX_FILTER_CALLS: ClassVar[int] = 10_000 

630 

631 def __init__( 

632 self, 

633 elements: Sequence[Ex], 

634 *, 

635 force_repr: str | None = None, 

636 force_repr_braces: tuple[str, str] | None = None, 

637 # (name, function, location of the .filter()/.map() call, if known) 

638 transformations: tuple[ 

639 tuple[Literal["filter", "map"], Callable[[Ex], Any], str | None], 

640 ..., 

641 ] = (), 

642 ): 

643 super().__init__() 

644 self.elements = cu.check_sample(elements, "sampled_from") 

645 assert self.elements 

646 self.force_repr = force_repr 

647 self.force_repr_braces = force_repr_braces 

648 self._transformations = transformations 

649 

650 self._cached_repr: str | None = None 

651 

652 def map(self, pack: Callable[[Ex], T]) -> SearchStrategy[T]: 

653 s = type(self)( 

654 self.elements, 

655 force_repr=self.force_repr, 

656 force_repr_braces=self.force_repr_braces, 

657 transformations=(*self._transformations, ("map", pack, None)), 

658 ) 

659 # guaranteed by the ("map", pack) transformation 

660 return cast(SearchStrategy[T], s) 

661 

662 @overload 

663 def filter( 

664 self, condition: Callable[[Ex], TypeGuard[T]] 

665 ) -> "SearchStrategy[T]": ... 

666 @overload 

667 def filter(self, condition: Callable[[Ex], Any]) -> "SearchStrategy[Ex]": ... 

668 def filter(self, condition): 

669 return type(self)( 

670 self.elements, 

671 force_repr=self.force_repr, 

672 force_repr_braces=self.force_repr_braces, 

673 transformations=( 

674 *self._transformations, 

675 ("filter", condition, current_filter_call_site()), 

676 ), 

677 ) 

678 

679 def __repr__(self): 

680 if self._cached_repr is None: 

681 rep = get_pretty_function_description 

682 elements_s = ( 

683 ", ".join(rep(v) for v in self.elements[:512]) + ", ..." 

684 if len(self.elements) > 512 

685 else ", ".join(rep(v) for v in self.elements) 

686 ) 

687 braces = self.force_repr_braces or ("(", ")") 

688 instance_s = ( 

689 self.force_repr or f"sampled_from({braces[0]}{elements_s}{braces[1]})" 

690 ) 

691 transforms_s = "".join( 

692 f".{name}({get_pretty_function_description(f)})" 

693 for name, f, _ in self._transformations 

694 ) 

695 repr_s = instance_s + transforms_s 

696 self._cached_repr = repr_s 

697 return self._cached_repr 

698 

699 def calc_label(self) -> int: 

700 # strategy.label is effectively an under-approximation of structural 

701 # equality (i.e., some strategies may have the same label when they are not 

702 # structurally identical). More importantly for calculating the 

703 # SampledFromStrategy label, we might have hash(s1) != hash(s2) even 

704 # when s1 and s2 are structurally identical. For instance: 

705 # 

706 # s1 = st.sampled_from([st.none()]) 

707 # s2 = st.sampled_from([st.none()]) 

708 # assert hash(s1) != hash(s2) 

709 # 

710 # (see also test cases in test_labels.py). 

711 # 

712 # We therefore use the labels of any component strategies when calculating 

713 # our label, and only use the hash if it is not a strategy. 

714 # 

715 # That's the ideal, anyway. In reality the logic is more complicated than 

716 # necessary in order to be efficient in the presence of (very) large sequences: 

717 # * add an unabashed special case for range, to avoid iteration over an 

718 # enormous range when we know it is entirely integers. 

719 # * if there is at least one strategy in self.elements, use strategy label, 

720 # and the element hash otherwise. 

721 # * if there are no strategies in self.elements, take the hash of the 

722 # entire sequence. This prevents worst-case performance of hashing each 

723 # element when a hash of the entire sequence would have sufficed. 

724 # 

725 # The worst case performance of this scheme is 

726 # itertools.chain(range(2**100), [st.none()]), where it degrades to 

727 # hashing every int in the range. 

728 elements_is_hashable, hash_value = _is_hashable(self.elements) 

729 if isinstance(self.elements, range) or ( 

730 elements_is_hashable 

731 and not any(isinstance(e, SearchStrategy) for e in self.elements) 

732 ): 

733 return combine_labels( 

734 self.class_label, calc_label_from_name(str(hash_value)) 

735 ) 

736 

737 labels = [self.class_label] 

738 for element in self.elements: 

739 if not is_hashable(element): 

740 continue 

741 

742 labels.append( 

743 element.label 

744 if isinstance(element, SearchStrategy) 

745 else calc_label_from_hash(element) 

746 ) 

747 

748 return combine_labels(*labels) 

749 

750 def calc_has_reusable_values(self, recur: RecurT) -> bool: 

751 # Because our custom .map/.filter implementations skip the normal 

752 # wrapper strategies (which would automatically return False for us), 

753 # we need to manually return False here if any transformations have 

754 # been applied. 

755 return not self._transformations 

756 

757 def calc_is_cacheable(self, recur: RecurT) -> bool: 

758 return is_hashable(self.elements) 

759 

760 def _transform( 

761 self, 

762 # https://github.com/python/mypy/issues/7049, we're not writing `element` 

763 # anywhere in the class so this is still type-safe. mypy is being more 

764 # conservative than necessary 

765 element: Ex, # type: ignore 

766 *, 

767 # None for _invert, which has no ConjectureData 

768 data: ConjectureData | None, 

769 ) -> Ex | UniqueIdentifier: 

770 # Used in UniqueSampledListStrategy 

771 for name, f, location in self._transformations: 

772 if name == "map": 

773 result = f(element) 

774 if build_context := _current_build_context.value: 

775 build_context.record_call(result, f, args=[element], kwargs={}) 

776 element = result 

777 else: 

778 assert name == "filter" 

779 if not f(element): 

780 if data is not None: 

781 data._last_rejected_filter = (f, location) 

782 return filter_not_satisfied 

783 return element 

784 

785 def do_draw(self, data: ConjectureData) -> Ex: 

786 result = self.do_filtered_draw(data) 

787 if isinstance(result, SearchStrategy) and all( 

788 isinstance(x, SearchStrategy) for x in self.elements 

789 ): 

790 data._sampled_from_all_strategies_elements_message = ( 

791 ( 

792 "sampled_from was given a collection of strategies: " 

793 "{!r}. Was one_of intended?" 

794 ), 

795 self.elements, 

796 ) 

797 if result is filter_not_satisfied: 

798 # do_filtered_draw records data._last_rejected_filter. 

799 data.mark_invalid( 

800 f"Aborted test because unable to satisfy {self!r}", 

801 location=data.last_rejected_filter_location(), 

802 ) 

803 assert not isinstance(result, UniqueIdentifier) 

804 return result 

805 

806 def get_element(self, i: int, data: ConjectureData) -> Ex | UniqueIdentifier: 

807 return self._transform(self.elements[i], data=data) 

808 

809 def _invert(self, value: Any) -> tuple[ChoiceT, ...]: 

810 # The smallest index whose (possibly transformed) element equals value. 

811 # _transform might depend on external state and give us a wrong answer 

812 # here; that's fine, since _invert is allowed to be fallible. 

813 try: 

814 for i, element in enumerate(self.elements): 

815 if equal_values(self._transform(element, data=None), value): 

816 return (i,) 

817 except Exception: 

818 # `self._transform` may include arbitrary user code (map/filter functions) 

819 raise CannotInvert(f"transforming elements of {self!r} errored") from None 

820 raise CannotInvert(f"{value!r} is not produced by {self!r}") 

821 

822 def do_filtered_draw(self, data: ConjectureData) -> Ex | UniqueIdentifier: 

823 # Set of indices that have been tried so far, so that we never test 

824 # the same element twice during a draw. 

825 known_bad_indices: set[int] = set() 

826 

827 # Start with ordinary rejection sampling. It's fast if it works, and 

828 # if it doesn't work then it was only a small amount of overhead. 

829 for _ in range(3): 

830 i = data.draw_integer(0, len(self.elements) - 1) 

831 if i not in known_bad_indices: 

832 element = self.get_element(i, data) 

833 if element is not filter_not_satisfied: 

834 return element 

835 if not known_bad_indices: 

836 data.events[f"Retried draw from {self!r} to satisfy filter"] = "" 

837 known_bad_indices.add(i) 

838 

839 # If we've tried all the possible elements, give up now. 

840 max_good_indices = len(self.elements) - len(known_bad_indices) 

841 if not max_good_indices: 

842 return filter_not_satisfied 

843 

844 # Impose an arbitrary cutoff to prevent us from wasting too much time 

845 # on very large element lists. 

846 max_good_indices = min(max_good_indices, self._MAX_FILTER_CALLS - 3) 

847 

848 # Before building the list of allowed indices, speculatively choose 

849 # one of them. We don't yet know how many allowed indices there will be, 

850 # so this choice might be out-of-bounds, but that's OK. 

851 speculative_index = data.draw_integer(0, max_good_indices - 1) 

852 

853 # Calculate the indices of allowed values, so that we can choose one 

854 # of them at random. But if we encounter the speculatively-chosen one, 

855 # just use that and return immediately. Note that we also track the 

856 # allowed elements, in case of .map(some_stateful_function) 

857 allowed: list[tuple[int, Ex]] = [] 

858 for i in range(min(len(self.elements), self._MAX_FILTER_CALLS - 3)): 

859 if i not in known_bad_indices: 

860 element = self.get_element(i, data) 

861 if element is not filter_not_satisfied: 

862 assert not isinstance(element, UniqueIdentifier) 

863 allowed.append((i, element)) 

864 if len(allowed) > speculative_index: 

865 # Early-exit case: We reached the speculative index, so 

866 # we just return the corresponding element. 

867 data.draw_integer(0, len(self.elements) - 1, forced=i) 

868 return element 

869 

870 # The speculative index didn't work out, but at this point we've built 

871 # and can choose from the complete list of allowed indices and elements. 

872 if allowed: 

873 i, element = data.choice(allowed) 

874 data.draw_integer(0, len(self.elements) - 1, forced=i) 

875 return element 

876 # If there are no allowed indices, the filter couldn't be satisfied. 

877 return filter_not_satisfied 

878 

879 

880# The ids of OneOfStrategy instances an _invert call is currently walking 

881# through, per thread. See OneOfStrategy._invert. 

882_inverting_one_ofs = threading.local() 

883 

884 

885class OneOfStrategy(SearchStrategy[Ex]): 

886 """Implements a union of strategies. Given a number of strategies this 

887 generates values which could have come from any of them. 

888 

889 The conditional distribution draws uniformly at random from some 

890 non-empty subset of these strategies and then draws from the 

891 conditional distribution of that strategy. 

892 """ 

893 

894 def __init__(self, strategies: Sequence[SearchStrategy[Ex]]): 

895 super().__init__() 

896 self.original_strategies = tuple(strategies) 

897 self.__element_strategies: Sequence[SearchStrategy[Ex]] | None = None 

898 self.__in_branches = False 

899 self._branches_lock = RLock() 

900 

901 def calc_is_empty(self, recur: RecurT) -> bool: 

902 return all(recur(e) for e in self.original_strategies) 

903 

904 def calc_has_reusable_values(self, recur: RecurT) -> bool: 

905 return all(recur(e) for e in self.original_strategies) 

906 

907 def calc_is_cacheable(self, recur: RecurT) -> bool: 

908 return all(recur(e) for e in self.original_strategies) 

909 

910 @property 

911 def element_strategies(self) -> Sequence[SearchStrategy[Ex]]: 

912 if self.__element_strategies is None: 

913 # While strategies are hashable, they use object.__hash__ and are 

914 # therefore distinguished only by identity. 

915 # 

916 # In principle we could "just" define a __hash__ method 

917 # (and __eq__, but that's easy in terms of type() and hash()) 

918 # to make this more powerful, but this is harder than it sounds: 

919 # 

920 # 1. Strategies are often distinguished by non-hashable attributes, 

921 # or by attributes that have the same hash value ("^.+" / b"^.+"). 

922 # 2. LazyStrategy: can't reify the wrapped strategy without breaking 

923 # laziness, so there's a hash each for the lazy and the nonlazy. 

924 # 

925 # Having made several attempts, the minor benefits of making strategies 

926 # hashable are simply not worth the engineering effort it would take. 

927 # See also issues #2291 and #2327. 

928 seen: set[SearchStrategy] = {self} 

929 strategies: list[SearchStrategy] = [] 

930 for arg in self.original_strategies: 

931 check_strategy(arg) 

932 if not arg.is_empty: 

933 for s in arg.branches: 

934 if s not in seen and not s.is_empty: 

935 seen.add(s) 

936 strategies.append(s) 

937 self.__element_strategies = strategies 

938 return self.__element_strategies 

939 

940 def calc_label(self) -> int: 

941 return combine_labels( 

942 self.class_label, *(p.label for p in self.original_strategies) 

943 ) 

944 

945 def do_draw(self, data: ConjectureData) -> Ex: 

946 strategies = self.element_strategies 

947 if len(strategies) == 1: 

948 # optimization: skip constructing SampledFromStrategy if we only have one 

949 # strategy. This can happen for eg `st.integers() | st.nothing()`. 

950 return data.draw(strategies[0]) 

951 

952 strategy = data.draw( 

953 SampledFromStrategy(strategies).filter( 

954 lambda s: not s.is_currently_empty(data) 

955 ) 

956 ) 

957 return data.draw(strategy) 

958 

959 def _invert(self, value: Any) -> tuple[ChoiceT, ...]: 

960 # do_draw first draws a branch index, then draws from that branch. 

961 # Return the simplest candidate across all branches: the shortest 

962 # encoding, breaking ties by the lower branch index - matching the 

963 # shrinker's ordering over choice sequences. 

964 # 

965 # Self-referential strategies (e.g. via st.deferred) can route a 

966 # branch's inversion of the same value back to this one_of, so guard 

967 # against re-entry rather than recursing forever. Unlike a structural 

968 # recursion into a subvalue - which terminates because values are 

969 # finite - a same-value cycle can make no progress. 

970 if len(self.element_strategies) == 1: 

971 return self.element_strategies[0]._invert(value) 

972 

973 active = getattr(_inverting_one_ofs, "ids", None) 

974 if active is None: 

975 active = _inverting_one_ofs.ids = set() 

976 if id(self) in active: 

977 raise CannotInvert(f"recursive inversion of {self!r}") 

978 active.add(id(self)) 

979 try: 

980 best: tuple[ChoiceT, ...] | None = None 

981 for i, branch in enumerate(self.element_strategies): 

982 try: 

983 candidate = (i, *branch._invert(value)) 

984 except CannotInvert: 

985 continue 

986 if best is None or len(candidate) < len(best): 

987 best = candidate 

988 if len(best) <= 2: 

989 # A selector plus at most one choice: no later branch is 

990 # worth trying. (A zero-choice just()-like branch could 

991 # still encode in one choice, but we accept two rather 

992 # than scanning - possibly recursively - for it.) 

993 break 

994 finally: 

995 active.discard(id(self)) 

996 if best is None: 

997 raise CannotInvert(f"{value!r} is not produced by any branch of {self!r}") 

998 return best 

999 

1000 def __repr__(self) -> str: 

1001 return "one_of({})".format(", ".join(map(repr, self.original_strategies))) 

1002 

1003 def do_validate(self) -> None: 

1004 for e in self.element_strategies: 

1005 e.validate() 

1006 

1007 @property 

1008 def branches(self) -> Sequence[SearchStrategy[Ex]]: 

1009 if self.__element_strategies is not None: 

1010 # common fast path which avoids the lock 

1011 return self.element_strategies 

1012 

1013 with self._branches_lock: 

1014 if not self.__in_branches: 

1015 try: 

1016 self.__in_branches = True 

1017 return self.element_strategies 

1018 finally: 

1019 self.__in_branches = False 

1020 else: 

1021 return [self] 

1022 

1023 @overload 

1024 def filter( 

1025 self, condition: Callable[[Ex], TypeGuard[T]] 

1026 ) -> "SearchStrategy[T]": ... 

1027 @overload 

1028 def filter(self, condition: Callable[[Ex], Any]) -> "SearchStrategy[Ex]": ... 

1029 def filter(self, condition): 

1030 return FilteredStrategy( 

1031 OneOfStrategy([s.filter(condition) for s in self.original_strategies]), 

1032 conditions=(), 

1033 ) 

1034 

1035 

1036@overload 

1037def one_of( 

1038 __args: Sequence[SearchStrategy[Ex]], 

1039) -> SearchStrategy[Ex]: ... 

1040 

1041 

1042@overload 

1043def one_of(__a1: SearchStrategy[Ex]) -> SearchStrategy[Ex]: ... 

1044 

1045 

1046@overload 

1047def one_of( 

1048 __a1: SearchStrategy[Ex], __a2: SearchStrategy[T] 

1049) -> SearchStrategy[Ex | T]: ... 

1050 

1051 

1052@overload 

1053def one_of( 

1054 __a1: SearchStrategy[Ex], __a2: SearchStrategy[T], __a3: SearchStrategy[T3] 

1055) -> SearchStrategy[Ex | T | T3]: ... 

1056 

1057 

1058@overload 

1059def one_of( 

1060 __a1: SearchStrategy[Ex], 

1061 __a2: SearchStrategy[T], 

1062 __a3: SearchStrategy[T3], 

1063 __a4: SearchStrategy[T4], 

1064) -> SearchStrategy[Ex | T | T3 | T4]: ... 

1065 

1066 

1067@overload 

1068def one_of( 

1069 __a1: SearchStrategy[Ex], 

1070 __a2: SearchStrategy[T], 

1071 __a3: SearchStrategy[T3], 

1072 __a4: SearchStrategy[T4], 

1073 __a5: SearchStrategy[T5], 

1074) -> SearchStrategy[Ex | T | T3 | T4 | T5]: ... 

1075 

1076 

1077@overload 

1078def one_of(*args: SearchStrategy[Any]) -> SearchStrategy[Any]: ... 

1079 

1080 

1081@defines_strategy(eager=True) 

1082def one_of( 

1083 *args: Sequence[SearchStrategy[Any]] | SearchStrategy[Any], 

1084) -> SearchStrategy[Any]: 

1085 # Mypy workaround alert: Any is too loose above; the return parameter 

1086 # should be the union of the input parameters. Unfortunately, Mypy <=0.600 

1087 # raises errors due to incompatible inputs instead. See #1270 for links. 

1088 # v0.610 doesn't error; it gets inference wrong for 2+ arguments instead. 

1089 """Return a strategy which generates values from any of the argument 

1090 strategies. 

1091 

1092 This may be called with one iterable argument instead of multiple 

1093 strategy arguments, in which case ``one_of(x)`` and ``one_of(*x)`` are 

1094 equivalent. 

1095 

1096 Examples from this strategy will generally shrink to ones that come from 

1097 strategies earlier in the list, then shrink according to behaviour of the 

1098 strategy that produced them. In order to get good shrinking behaviour, 

1099 try to put simpler strategies first. e.g. ``one_of(none(), text())`` is 

1100 better than ``one_of(text(), none())``. 

1101 

1102 This is especially important when using recursive strategies. e.g. 

1103 ``x = st.deferred(lambda: st.none() | st.tuples(x, x))`` will shrink well, 

1104 but ``x = st.deferred(lambda: st.tuples(x, x) | st.none())`` will shrink 

1105 very badly indeed. 

1106 """ 

1107 if len(args) == 1 and not isinstance(args[0], SearchStrategy): 

1108 try: 

1109 args = tuple(args[0]) 

1110 except TypeError: 

1111 pass 

1112 if len(args) == 1 and isinstance(args[0], SearchStrategy): 

1113 # This special-case means that we can one_of over lists of any size 

1114 # without incurring any performance overhead when there is only one 

1115 # strategy, and keeps our reprs simple. 

1116 return args[0] 

1117 if args and not any(isinstance(a, SearchStrategy) for a in args): 

1118 # And this special case is to give a more-specific error message if it 

1119 # seems that the user has confused `one_of()` for `sampled_from()`; 

1120 # the remaining validation is left to OneOfStrategy. See PR #2627. 

1121 raise InvalidArgument( 

1122 f"Did you mean st.sampled_from({list(args)!r})? st.one_of() is used " 

1123 "to combine strategies, but all of the arguments were of other types." 

1124 ) 

1125 # we've handled the case where args is a one-element sequence [(s1, s2, ...)] 

1126 # above, so we can assume it's an actual sequence of strategies. 

1127 args = cast(Sequence[SearchStrategy], args) 

1128 return OneOfStrategy(args) 

1129 

1130 

1131class MappedStrategy(SearchStrategy[MappedTo], Generic[MappedFrom, MappedTo]): 

1132 """A strategy which is defined purely by conversion to and from another 

1133 strategy. 

1134 

1135 Its parameter and distribution come from that other strategy. 

1136 """ 

1137 

1138 def __init__( 

1139 self, 

1140 strategy: SearchStrategy[MappedFrom], 

1141 pack: Callable[[MappedFrom], MappedTo], 

1142 ) -> None: 

1143 super().__init__() 

1144 self.mapped_strategy = strategy 

1145 self.pack = pack 

1146 

1147 def calc_is_empty(self, recur: RecurT) -> bool: 

1148 return recur(self.mapped_strategy) 

1149 

1150 def calc_is_cacheable(self, recur: RecurT) -> bool: 

1151 return recur(self.mapped_strategy) 

1152 

1153 def __repr__(self) -> str: 

1154 if not hasattr(self, "_cached_repr"): 

1155 self._cached_repr = f"{self.mapped_strategy!r}.map({get_pretty_function_description(self.pack)})" 

1156 return self._cached_repr 

1157 

1158 def do_validate(self) -> None: 

1159 self.mapped_strategy.validate() 

1160 

1161 def do_draw(self, data: ConjectureData) -> MappedTo: 

1162 with warnings.catch_warnings(): 

1163 if isinstance(self.pack, type) and issubclass( 

1164 self.pack, (abc.Mapping, abc.Set) 

1165 ): 

1166 warnings.simplefilter("ignore", BytesWarning) 

1167 for _ in range(3): 

1168 try: 

1169 data.start_span(MAPPED_SEARCH_STRATEGY_DO_DRAW_LABEL) 

1170 x = data.draw(self.mapped_strategy) 

1171 result = self.pack(x) 

1172 data.stop_span() 

1173 current_build_context().record_call( 

1174 result, self.pack, args=[x], kwargs={} 

1175 ) 

1176 return result 

1177 except UnsatisfiedAssumption as err: 

1178 # we want to preserve the err.location of the actual exception here if we 

1179 # re-throw it outside of the loop at the end. The alternative is a 

1180 # bare `raise UnsatisfiedAssumption` outside of the loop, which would 

1181 # drop the real err.location for observability. 

1182 # 

1183 # If we catch multiple exceptions here, we'll just report the location 

1184 # of the last one, which is a reasonable tradeoff for a singleton field. 

1185 failed_assumption = err 

1186 data.stop_span(discard=True) 

1187 raise failed_assumption 

1188 

1189 def _invert(self, value: Any) -> tuple[ChoiceT, ...]: 

1190 # map() is not invertible in general, but a dict-like pack - e.g. the 

1191 # dict_class which st.dictionaries maps over a unique list of 

1192 # (key, value) tuples - inverts as its list of items. 

1193 if ( 

1194 isinstance(self.pack, type) 

1195 and issubclass(self.pack, abc.Mapping) 

1196 and isinstance(value, self.pack) 

1197 ): 

1198 return self.mapped_strategy._invert(list(value.items())) 

1199 raise CannotInvert(f"cannot invert {self!r} (value={value!r})") 

1200 

1201 @property 

1202 def branches(self) -> Sequence[SearchStrategy[MappedTo]]: 

1203 return [ 

1204 MappedStrategy(strategy, pack=self.pack) 

1205 for strategy in self.mapped_strategy.branches 

1206 ] 

1207 

1208 @overload 

1209 def filter( 

1210 self, condition: Callable[[MappedTo], TypeGuard[T]] 

1211 ) -> "SearchStrategy[T]": ... 

1212 @overload 

1213 def filter( 

1214 self, condition: Callable[[MappedTo], Any] 

1215 ) -> "SearchStrategy[MappedTo]": ... 

1216 def filter(self, condition): 

1217 # Includes a special case so that we can rewrite filters on collection 

1218 # lengths, when most collections are `st.lists(...).map(the_type)`. 

1219 ListStrategy = _list_strategy_type() 

1220 if not isinstance(self.mapped_strategy, ListStrategy) or not ( 

1221 (isinstance(self.pack, type) and issubclass(self.pack, abc.Collection)) 

1222 or self.pack in _collection_ish_functions() 

1223 ): 

1224 return super().filter(condition) 

1225 

1226 # Check whether our inner list strategy can rewrite this filter condition. 

1227 # If not, discard the result and _only_ apply a new outer filter. 

1228 new = ListStrategy.filter(self.mapped_strategy, condition) 

1229 if getattr(new, "filtered_strategy", None) is self.mapped_strategy: 

1230 return super().filter(condition) # didn't rewrite 

1231 

1232 # Apply a new outer filter even though we rewrote the inner strategy, 

1233 # because some collections can change the list length (dict, set, etc). 

1234 return FilteredStrategy( 

1235 type(self)(new, self.pack), 

1236 conditions=(condition,), 

1237 condition_locations=(current_filter_call_site(),), 

1238 ) 

1239 

1240 

1241@lru_cache 

1242def _list_strategy_type() -> Any: 

1243 from hypothesis.strategies._internal.collections import ListStrategy 

1244 

1245 return ListStrategy 

1246 

1247 

1248def _collection_ish_functions() -> Sequence[Any]: 

1249 funcs = [sorted] 

1250 if np := sys.modules.get("numpy"): 

1251 # c.f. https://numpy.org/doc/stable/reference/routines.array-creation.html 

1252 # Probably only `np.array` and `np.asarray` will be used in practice, 

1253 # but why should that stop us when we've already gone this far? 

1254 funcs += [ 

1255 np.empty_like, 

1256 np.eye, 

1257 np.identity, 

1258 np.ones_like, 

1259 np.zeros_like, 

1260 np.array, 

1261 np.asarray, 

1262 np.asanyarray, 

1263 np.ascontiguousarray, 

1264 np.asmatrix, 

1265 np.copy, 

1266 np.rec.array, 

1267 np.rec.fromarrays, 

1268 np.rec.fromrecords, 

1269 np.diag, 

1270 # bonus undocumented functions from tab-completion: 

1271 np.asarray_chkfinite, 

1272 np.asfortranarray, 

1273 ] 

1274 

1275 return funcs 

1276 

1277 

1278filter_not_satisfied = UniqueIdentifier("filter not satisfied") 

1279 

1280 

1281class FilteredStrategy(SearchStrategy[Ex]): 

1282 def __init__( 

1283 self, 

1284 strategy: SearchStrategy[Ex], 

1285 conditions: tuple[Callable[[Ex], Any], ...], 

1286 condition_locations: tuple[str | None, ...] | None = None, 

1287 ): 

1288 super().__init__() 

1289 # Where each condition's .filter() call was made, for observability 

1290 if condition_locations is None: 

1291 condition_locations = (None,) * len(conditions) 

1292 assert len(condition_locations) == len(conditions) 

1293 if isinstance(strategy, FilteredStrategy): 

1294 # Flatten chained filters into a single filter with multiple conditions. 

1295 self.flat_conditions: tuple[Callable[[Ex], Any], ...] = ( 

1296 strategy.flat_conditions + conditions 

1297 ) 

1298 self.condition_locations: tuple[str | None, ...] = ( 

1299 strategy.condition_locations + condition_locations 

1300 ) 

1301 self.filtered_strategy: SearchStrategy[Ex] = strategy.filtered_strategy 

1302 else: 

1303 self.flat_conditions = conditions 

1304 self.condition_locations = condition_locations 

1305 self.filtered_strategy = strategy 

1306 

1307 assert isinstance(self.flat_conditions, tuple) 

1308 assert not isinstance(self.filtered_strategy, FilteredStrategy) 

1309 

1310 self.__condition: Callable[[Ex], Any] | None = None 

1311 

1312 def calc_is_empty(self, recur: RecurT) -> bool: 

1313 return recur(self.filtered_strategy) 

1314 

1315 def calc_is_cacheable(self, recur: RecurT) -> bool: 

1316 return recur(self.filtered_strategy) 

1317 

1318 def __repr__(self) -> str: 

1319 if not hasattr(self, "_cached_repr"): 

1320 self._cached_repr = "{!r}{}".format( 

1321 self.filtered_strategy, 

1322 "".join( 

1323 f".filter({get_pretty_function_description(cond)})" 

1324 for cond in self.flat_conditions 

1325 ), 

1326 ) 

1327 return self._cached_repr 

1328 

1329 def do_validate(self) -> None: 

1330 # Start by validating our inner filtered_strategy. If this was a LazyStrategy, 

1331 # validation also reifies it so that subsequent calls to e.g. `.filter()` will 

1332 # be passed through. 

1333 self.filtered_strategy.validate() 

1334 # So now we have a reified inner strategy, we'll replay all our saved 

1335 # predicates in case some or all of them can be rewritten. Note that this 

1336 # replaces the `fresh` strategy too! 

1337 fresh = self.filtered_strategy 

1338 for cond, location in zip( 

1339 self.flat_conditions, self.condition_locations, strict=True 

1340 ): 

1341 with _filter_location_override.with_value(location): 

1342 fresh = fresh.filter(cond) 

1343 if isinstance(fresh, FilteredStrategy): 

1344 # In this case we have at least some non-rewritten filter predicates, 

1345 # so we just re-initialize the strategy. 

1346 FilteredStrategy.__init__( 

1347 self, 

1348 fresh.filtered_strategy, 

1349 fresh.flat_conditions, 

1350 fresh.condition_locations, 

1351 ) 

1352 else: 

1353 # But if *all* the predicates were rewritten... well, do_validate() is 

1354 # an in-place method so we still just re-initialize the strategy! 

1355 FilteredStrategy.__init__(self, fresh, ()) 

1356 

1357 @overload 

1358 def filter( 

1359 self, condition: Callable[[Ex], TypeGuard[T]] 

1360 ) -> "FilteredStrategy[T]": ... 

1361 @overload 

1362 def filter(self, condition: Callable[[Ex], Any]) -> "FilteredStrategy[Ex]": ... 

1363 def filter(self, condition): 

1364 # If we can, it's more efficient to rewrite our strategy to satisfy the 

1365 # condition. We therefore exploit the fact that the order of predicates 

1366 # doesn't matter (`f(x) and g(x) == g(x) and f(x)`) by attempting to apply 

1367 # condition directly to our filtered strategy as the inner-most filter. 

1368 out = self.filtered_strategy.filter(condition) 

1369 # If it couldn't be rewritten, we'll get a new FilteredStrategy - and then 

1370 # combine the conditions of each in our expected newest=last order. 

1371 if isinstance(out, FilteredStrategy): 

1372 return FilteredStrategy( 

1373 out.filtered_strategy, 

1374 self.flat_conditions + out.flat_conditions, 

1375 self.condition_locations + out.condition_locations, 

1376 ) 

1377 # But if it *could* be rewritten, we can return the more efficient form! 

1378 return FilteredStrategy(out, self.flat_conditions, self.condition_locations) 

1379 

1380 @property 

1381 def condition(self) -> Callable[[Ex], Any]: 

1382 # We write this defensively to avoid any threading race conditions 

1383 # with our manual FilteredStrategy.__init__ for filter-rewriting. 

1384 # See https://github.com/HypothesisWorks/hypothesis/pull/4522. 

1385 if (condition := self.__condition) is not None: 

1386 return condition 

1387 

1388 if len(self.flat_conditions) == 1: 

1389 # Avoid an extra indirection in the common case of only one condition. 

1390 condition = self.flat_conditions[0] 

1391 elif len(self.flat_conditions) == 0: 

1392 # Possible, if unlikely, due to filter predicate rewriting 

1393 condition = lambda _: True 

1394 else: 

1395 condition = lambda x: all(cond(x) for cond in self.flat_conditions) 

1396 self.__condition = condition 

1397 return condition 

1398 

1399 def do_draw(self, data: ConjectureData) -> Ex: 

1400 result = self.do_filtered_draw(data) 

1401 if result is not filter_not_satisfied: 

1402 return cast(Ex, result) 

1403 

1404 # do_filtered_draw records data._last_rejected_filter. 

1405 data.mark_invalid( 

1406 f"Aborted test because unable to satisfy {self!r}", 

1407 location=data.last_rejected_filter_location(), 

1408 ) 

1409 

1410 def do_filtered_draw(self, data: ConjectureData) -> Ex | UniqueIdentifier: 

1411 for i in range(3): 

1412 data.start_span(FILTERED_SEARCH_STRATEGY_DO_DRAW_LABEL) 

1413 value = data.draw(self.filtered_strategy) 

1414 # Check the conditions individually rather than via self.condition, 

1415 # so that we can set data._last_rejected_filter. 

1416 failing = next( 

1417 ( 

1418 (cond, location) 

1419 for cond, location in zip( 

1420 self.flat_conditions, self.condition_locations, strict=True 

1421 ) 

1422 if not cond(value) 

1423 ), 

1424 None, 

1425 ) 

1426 if failing is None: 

1427 data.stop_span() 

1428 return value 

1429 else: 

1430 data._last_rejected_filter = failing 

1431 data.stop_span(discard=True) 

1432 if i == 0: 

1433 data.events[f"Retried draw from {self!r} to satisfy filter"] = "" 

1434 

1435 return filter_not_satisfied 

1436 

1437 def _invert(self, value: Any) -> tuple[ChoiceT, ...]: 

1438 # If the condition accepts value, do_draw would have succeeded on its 

1439 # first try, drawing exactly the inner strategy's encoding. 

1440 try: 

1441 satisfied = self.condition(value) 

1442 except Exception: 

1443 # `self.condition` is user code and may raise arbitrarily. 

1444 raise CannotInvert(f"{value!r} errored in filter {self!r}") from None 

1445 if not satisfied: 

1446 raise CannotInvert(f"{value!r} does not satisfy filter {self!r}") 

1447 return self.filtered_strategy._invert(value) 

1448 

1449 @property 

1450 def branches(self) -> Sequence[SearchStrategy[Ex]]: 

1451 return [ 

1452 FilteredStrategy(strategy, self.flat_conditions, self.condition_locations) 

1453 for strategy in self.filtered_strategy.branches 

1454 ] 

1455 

1456 

1457@check_function 

1458def check_strategy(arg: object, name: str = "") -> None: 

1459 assert isinstance(name, str) 

1460 if not isinstance(arg, SearchStrategy): 

1461 hint = "" 

1462 if isinstance(arg, (list, tuple)): 

1463 hint = ", such as st.sampled_from({}),".format(name or "...") 

1464 if name: 

1465 name += "=" 

1466 raise InvalidArgument( 

1467 f"Expected a SearchStrategy{hint} but got {name}{arg!r} " 

1468 f"(type={type(arg).__name__})" 

1469 )