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

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

52 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 

11from inspect import Parameter, Signature 

12from weakref import WeakKeyDictionary 

13 

14from hypothesis.control import note, should_note 

15from hypothesis.errors import InvalidState 

16from hypothesis.internal.reflection import ( 

17 convert_positional_arguments, 

18 get_signature, 

19 nicerepr, 

20 proxies, 

21 repr_call, 

22) 

23from hypothesis.strategies._internal.lazy import unwrap_strategies 

24from hypothesis.strategies._internal.strategies import ( 

25 RecurT, 

26 SampledFromStrategy, 

27 SearchStrategy, 

28) 

29from hypothesis.utils.conventions import UniqueIdentifier 

30 

31can_vary = UniqueIdentifier("can_vary") 

32 

33 

34class FunctionStrategy(SearchStrategy): 

35 def __init__(self, like, returns, pure): 

36 super().__init__() 

37 self.like = like 

38 self.returns = returns 

39 self.pure = pure 

40 # If `returns` can only generate a single value - just(), none(), or a 

41 # one-element sampled_from() - we know what our functions return before 

42 # they are ever called, and show them as constant lambdas instead. We 

43 # have to unwrap lazy wrappers to see that, and to bail out if any 

44 # .map() or .filter() was applied, since those transformations could 

45 # change or reject the value we'd otherwise report. 

46 unwrapped = unwrap_strategies(returns) 

47 if ( 

48 isinstance(unwrapped, SampledFromStrategy) 

49 and len(unwrapped.elements) == 1 

50 and not unwrapped._transformations 

51 ): 

52 self._constant = unwrapped.elements[0] 

53 else: 

54 self._constant = can_vary 

55 # Using wekrefs-to-generated-functions means that the cache can be 

56 # garbage-collected at the end of each example, reducing memory use. 

57 self._cache = WeakKeyDictionary() 

58 

59 def _pretty_constant_function(self, p, cycle): 

60 # Annotations are valid in a signature, but not in a lambda. 

61 sig = get_signature(self.like, follow_wrapped=False) 

62 params = str( 

63 sig.replace( 

64 parameters=[ 

65 param.replace(annotation=Parameter.empty) 

66 for param in sig.parameters.values() 

67 ], 

68 return_annotation=Signature.empty, 

69 ) 

70 )[1:-1] 

71 p.text(f"lambda {params}: " if params else "lambda: ") 

72 p.pretty(self._constant) 

73 

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

75 return recur(self.returns) 

76 

77 def do_draw(self, data): 

78 # If we know what the function returns, we show it as a constant lambda 

79 # instead of noting every call - the notes would be redundant. 

80 varies = self._constant is can_vary 

81 

82 @proxies(self.like) 

83 def inner(*args, **kwargs): 

84 if data.frozen: 

85 raise InvalidState( 

86 f"This generated {nicerepr(self.like)} function can only " 

87 "be called within the scope of the @given that created it." 

88 ) 

89 if self.pure: 

90 args, kwargs = convert_positional_arguments(self.like, args, kwargs) 

91 key = (args, frozenset(kwargs.items())) 

92 cache = self._cache.setdefault(inner, {}) 

93 if key not in cache: 

94 cache[key] = data.draw(self.returns) 

95 # optimization to avoid needless repr_call 

96 if varies and should_note(): 

97 rep = repr_call(self.like, args, kwargs, reorder=False) 

98 note(f"Called function: {rep} -> {cache[key]!r}") 

99 return cache[key] 

100 else: 

101 val = data.draw(self.returns) 

102 if varies and should_note(): 

103 rep = repr_call(self.like, args, kwargs, reorder=False) 

104 note(f"Called function: {rep} -> {val!r}") 

105 return val 

106 

107 if not varies: 

108 inner._repr_pretty_ = self._pretty_constant_function 

109 return inner