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

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

592 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 for i, element in enumerate(self.elements): 

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

815 return (i,) 

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

817 

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

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

820 # the same element twice during a draw. 

821 known_bad_indices: set[int] = set() 

822 

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

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

825 for _ in range(3): 

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

827 if i not in known_bad_indices: 

828 element = self.get_element(i, data) 

829 if element is not filter_not_satisfied: 

830 return element 

831 if not known_bad_indices: 

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

833 known_bad_indices.add(i) 

834 

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

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

837 if not max_good_indices: 

838 return filter_not_satisfied 

839 

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

841 # on very large element lists. 

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

843 

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

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

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

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

848 

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

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

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

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

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

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

855 if i not in known_bad_indices: 

856 element = self.get_element(i, data) 

857 if element is not filter_not_satisfied: 

858 assert not isinstance(element, UniqueIdentifier) 

859 allowed.append((i, element)) 

860 if len(allowed) > speculative_index: 

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

862 # we just return the corresponding element. 

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

864 return element 

865 

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

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

868 if allowed: 

869 i, element = data.choice(allowed) 

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

871 return element 

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

873 return filter_not_satisfied 

874 

875 

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

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

878_inverting_one_ofs = threading.local() 

879 

880 

881class OneOfStrategy(SearchStrategy[Ex]): 

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

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

884 

885 The conditional distribution draws uniformly at random from some 

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

887 conditional distribution of that strategy. 

888 """ 

889 

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

891 super().__init__() 

892 self.original_strategies = tuple(strategies) 

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

894 self.__in_branches = False 

895 self._branches_lock = RLock() 

896 

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

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

899 

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

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

902 

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

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

905 

906 @property 

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

908 if self.__element_strategies is None: 

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

910 # therefore distinguished only by identity. 

911 # 

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

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

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

915 # 

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

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

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

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

920 # 

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

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

923 # See also issues #2291 and #2327. 

924 seen: set[SearchStrategy] = {self} 

925 strategies: list[SearchStrategy] = [] 

926 for arg in self.original_strategies: 

927 check_strategy(arg) 

928 if not arg.is_empty: 

929 for s in arg.branches: 

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

931 seen.add(s) 

932 strategies.append(s) 

933 self.__element_strategies = strategies 

934 return self.__element_strategies 

935 

936 def calc_label(self) -> int: 

937 return combine_labels( 

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

939 ) 

940 

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

942 strategies = self.element_strategies 

943 if len(strategies) == 1: 

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

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

946 return data.draw(strategies[0]) 

947 

948 strategy = data.draw( 

949 SampledFromStrategy(strategies).filter( 

950 lambda s: not s.is_currently_empty(data) 

951 ) 

952 ) 

953 return data.draw(strategy) 

954 

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

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

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

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

959 # shrinker's ordering over choice sequences. 

960 # 

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

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

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

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

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

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

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

968 

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

970 if active is None: 

971 active = _inverting_one_ofs.ids = set() 

972 if id(self) in active: 

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

974 active.add(id(self)) 

975 try: 

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

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

978 try: 

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

980 except CannotInvert: 

981 continue 

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

983 best = candidate 

984 if len(best) <= 2: 

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

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

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

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

989 break 

990 finally: 

991 active.discard(id(self)) 

992 if best is None: 

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

994 return best 

995 

996 def __repr__(self) -> str: 

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

998 

999 def do_validate(self) -> None: 

1000 for e in self.element_strategies: 

1001 e.validate() 

1002 

1003 @property 

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

1005 if self.__element_strategies is not None: 

1006 # common fast path which avoids the lock 

1007 return self.element_strategies 

1008 

1009 with self._branches_lock: 

1010 if not self.__in_branches: 

1011 try: 

1012 self.__in_branches = True 

1013 return self.element_strategies 

1014 finally: 

1015 self.__in_branches = False 

1016 else: 

1017 return [self] 

1018 

1019 @overload 

1020 def filter( 

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

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

1023 @overload 

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

1025 def filter(self, condition): 

1026 return FilteredStrategy( 

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

1028 conditions=(), 

1029 ) 

1030 

1031 

1032@overload 

1033def one_of( 

1034 __args: Sequence[SearchStrategy[Ex]], 

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

1036 

1037 

1038@overload 

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

1040 

1041 

1042@overload 

1043def one_of( 

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

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

1046 

1047 

1048@overload 

1049def one_of( 

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

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

1052 

1053 

1054@overload 

1055def one_of( 

1056 __a1: SearchStrategy[Ex], 

1057 __a2: SearchStrategy[T], 

1058 __a3: SearchStrategy[T3], 

1059 __a4: SearchStrategy[T4], 

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

1061 

1062 

1063@overload 

1064def one_of( 

1065 __a1: SearchStrategy[Ex], 

1066 __a2: SearchStrategy[T], 

1067 __a3: SearchStrategy[T3], 

1068 __a4: SearchStrategy[T4], 

1069 __a5: SearchStrategy[T5], 

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

1071 

1072 

1073@overload 

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

1075 

1076 

1077@defines_strategy(eager=True) 

1078def one_of( 

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

1080) -> SearchStrategy[Any]: 

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

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

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

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

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

1086 strategies. 

1087 

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

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

1090 equivalent. 

1091 

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

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

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

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

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

1097 

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

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

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

1101 very badly indeed. 

1102 """ 

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

1104 try: 

1105 args = tuple(args[0]) 

1106 except TypeError: 

1107 pass 

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

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

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

1111 # strategy, and keeps our reprs simple. 

1112 return args[0] 

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

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

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

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

1117 raise InvalidArgument( 

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

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

1120 ) 

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

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

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

1124 return OneOfStrategy(args) 

1125 

1126 

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

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

1129 strategy. 

1130 

1131 Its parameter and distribution come from that other strategy. 

1132 """ 

1133 

1134 def __init__( 

1135 self, 

1136 strategy: SearchStrategy[MappedFrom], 

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

1138 ) -> None: 

1139 super().__init__() 

1140 self.mapped_strategy = strategy 

1141 self.pack = pack 

1142 

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

1144 return recur(self.mapped_strategy) 

1145 

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

1147 return recur(self.mapped_strategy) 

1148 

1149 def __repr__(self) -> str: 

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

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

1152 return self._cached_repr 

1153 

1154 def do_validate(self) -> None: 

1155 self.mapped_strategy.validate() 

1156 

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

1158 with warnings.catch_warnings(): 

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

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

1161 ): 

1162 warnings.simplefilter("ignore", BytesWarning) 

1163 for _ in range(3): 

1164 try: 

1165 data.start_span(MAPPED_SEARCH_STRATEGY_DO_DRAW_LABEL) 

1166 x = data.draw(self.mapped_strategy) 

1167 result = self.pack(x) 

1168 data.stop_span() 

1169 current_build_context().record_call( 

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

1171 ) 

1172 return result 

1173 except UnsatisfiedAssumption as err: 

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

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

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

1177 # drop the real err.location for observability. 

1178 # 

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

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

1181 failed_assumption = err 

1182 data.stop_span(discard=True) 

1183 raise failed_assumption 

1184 

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

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

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

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

1189 if ( 

1190 isinstance(self.pack, type) 

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

1192 and isinstance(value, self.pack) 

1193 ): 

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

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

1196 

1197 @property 

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

1199 return [ 

1200 MappedStrategy(strategy, pack=self.pack) 

1201 for strategy in self.mapped_strategy.branches 

1202 ] 

1203 

1204 @overload 

1205 def filter( 

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

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

1208 @overload 

1209 def filter( 

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

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

1212 def filter(self, condition): 

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

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

1215 ListStrategy = _list_strategy_type() 

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

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

1218 or self.pack in _collection_ish_functions() 

1219 ): 

1220 return super().filter(condition) 

1221 

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

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

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

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

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

1227 

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

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

1230 return FilteredStrategy( 

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

1232 conditions=(condition,), 

1233 condition_locations=(current_filter_call_site(),), 

1234 ) 

1235 

1236 

1237@lru_cache 

1238def _list_strategy_type() -> Any: 

1239 from hypothesis.strategies._internal.collections import ListStrategy 

1240 

1241 return ListStrategy 

1242 

1243 

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

1245 funcs = [sorted] 

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

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

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

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

1250 funcs += [ 

1251 np.empty_like, 

1252 np.eye, 

1253 np.identity, 

1254 np.ones_like, 

1255 np.zeros_like, 

1256 np.array, 

1257 np.asarray, 

1258 np.asanyarray, 

1259 np.ascontiguousarray, 

1260 np.asmatrix, 

1261 np.copy, 

1262 np.rec.array, 

1263 np.rec.fromarrays, 

1264 np.rec.fromrecords, 

1265 np.diag, 

1266 # bonus undocumented functions from tab-completion: 

1267 np.asarray_chkfinite, 

1268 np.asfortranarray, 

1269 ] 

1270 

1271 return funcs 

1272 

1273 

1274filter_not_satisfied = UniqueIdentifier("filter not satisfied") 

1275 

1276 

1277class FilteredStrategy(SearchStrategy[Ex]): 

1278 def __init__( 

1279 self, 

1280 strategy: SearchStrategy[Ex], 

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

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

1283 ): 

1284 super().__init__() 

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

1286 if condition_locations is None: 

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

1288 assert len(condition_locations) == len(conditions) 

1289 if isinstance(strategy, FilteredStrategy): 

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

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

1292 strategy.flat_conditions + conditions 

1293 ) 

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

1295 strategy.condition_locations + condition_locations 

1296 ) 

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

1298 else: 

1299 self.flat_conditions = conditions 

1300 self.condition_locations = condition_locations 

1301 self.filtered_strategy = strategy 

1302 

1303 assert isinstance(self.flat_conditions, tuple) 

1304 assert not isinstance(self.filtered_strategy, FilteredStrategy) 

1305 

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

1307 

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

1309 return recur(self.filtered_strategy) 

1310 

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

1312 return recur(self.filtered_strategy) 

1313 

1314 def __repr__(self) -> str: 

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

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

1317 self.filtered_strategy, 

1318 "".join( 

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

1320 for cond in self.flat_conditions 

1321 ), 

1322 ) 

1323 return self._cached_repr 

1324 

1325 def do_validate(self) -> None: 

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

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

1328 # be passed through. 

1329 self.filtered_strategy.validate() 

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

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

1332 # replaces the `fresh` strategy too! 

1333 fresh = self.filtered_strategy 

1334 for cond, location in zip( 

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

1336 ): 

1337 with _filter_location_override.with_value(location): 

1338 fresh = fresh.filter(cond) 

1339 if isinstance(fresh, FilteredStrategy): 

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

1341 # so we just re-initialize the strategy. 

1342 FilteredStrategy.__init__( 

1343 self, 

1344 fresh.filtered_strategy, 

1345 fresh.flat_conditions, 

1346 fresh.condition_locations, 

1347 ) 

1348 else: 

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

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

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

1352 

1353 @overload 

1354 def filter( 

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

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

1357 @overload 

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

1359 def filter(self, condition): 

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

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

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

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

1364 out = self.filtered_strategy.filter(condition) 

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

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

1367 if isinstance(out, FilteredStrategy): 

1368 return FilteredStrategy( 

1369 out.filtered_strategy, 

1370 self.flat_conditions + out.flat_conditions, 

1371 self.condition_locations + out.condition_locations, 

1372 ) 

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

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

1375 

1376 @property 

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

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

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

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

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

1382 return condition 

1383 

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

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

1386 condition = self.flat_conditions[0] 

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

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

1389 condition = lambda _: True 

1390 else: 

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

1392 self.__condition = condition 

1393 return condition 

1394 

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

1396 result = self.do_filtered_draw(data) 

1397 if result is not filter_not_satisfied: 

1398 return cast(Ex, result) 

1399 

1400 # do_filtered_draw records data._last_rejected_filter. 

1401 data.mark_invalid( 

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

1403 location=data.last_rejected_filter_location(), 

1404 ) 

1405 

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

1407 for i in range(3): 

1408 data.start_span(FILTERED_SEARCH_STRATEGY_DO_DRAW_LABEL) 

1409 value = data.draw(self.filtered_strategy) 

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

1411 # so that we can set data._last_rejected_filter. 

1412 failing = next( 

1413 ( 

1414 (cond, location) 

1415 for cond, location in zip( 

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

1417 ) 

1418 if not cond(value) 

1419 ), 

1420 None, 

1421 ) 

1422 if failing is None: 

1423 data.stop_span() 

1424 return value 

1425 else: 

1426 data._last_rejected_filter = failing 

1427 data.stop_span(discard=True) 

1428 if i == 0: 

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

1430 

1431 return filter_not_satisfied 

1432 

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

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

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

1436 if not self.condition(value): 

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

1438 return self.filtered_strategy._invert(value) 

1439 

1440 @property 

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

1442 return [ 

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

1444 for strategy in self.filtered_strategy.branches 

1445 ] 

1446 

1447 

1448@check_function 

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

1450 assert isinstance(name, str) 

1451 if not isinstance(arg, SearchStrategy): 

1452 hint = "" 

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

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

1455 if name: 

1456 name += "=" 

1457 raise InvalidArgument( 

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

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

1460 )