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

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

100 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 collections.abc import MutableMapping, Sequence 

12from inspect import signature 

13from typing import Any, Callable, Optional 

14from weakref import WeakKeyDictionary 

15 

16from hypothesis.configuration import check_sideeffect_during_initialization 

17from hypothesis.internal.conjecture.data import ConjectureData 

18from hypothesis.internal.reflection import ( 

19 convert_keyword_arguments, 

20 convert_positional_arguments, 

21 get_pretty_function_description, 

22 repr_call, 

23) 

24from hypothesis.strategies._internal.deferred import DeferredStrategy 

25from hypothesis.strategies._internal.strategies import Ex, RecurT, SearchStrategy 

26 

27unwrap_cache: MutableMapping[SearchStrategy, SearchStrategy] = WeakKeyDictionary() 

28unwrap_depth = 0 

29 

30 

31def unwrap_strategies(s): 

32 global unwrap_depth 

33 

34 # optimization 

35 if not isinstance(s, (LazyStrategy, DeferredStrategy)): 

36 return s 

37 

38 try: 

39 return unwrap_cache[s] 

40 except KeyError: 

41 pass 

42 

43 unwrap_cache[s] = s 

44 unwrap_depth += 1 

45 

46 try: 

47 result = unwrap_strategies(s.wrapped_strategy) 

48 unwrap_cache[s] = result 

49 

50 try: 

51 assert result.force_has_reusable_values == s.force_has_reusable_values 

52 except AttributeError: 

53 pass 

54 

55 try: 

56 result.force_has_reusable_values = s.force_has_reusable_values 

57 except AttributeError: 

58 pass 

59 

60 return result 

61 finally: 

62 unwrap_depth -= 1 

63 if unwrap_depth <= 0: 

64 unwrap_cache.clear() 

65 assert unwrap_depth >= 0 

66 

67 

68class LazyStrategy(SearchStrategy[Ex]): 

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

70 strategy. 

71 

72 Its parameter and distribution come from that other strategy. 

73 """ 

74 

75 def __init__( 

76 self, 

77 function: Callable[..., SearchStrategy[Ex]], 

78 args: Sequence[object], 

79 kwargs: dict[str, object], 

80 *, 

81 transforms: tuple[tuple[str, Callable[..., Any]], ...] = (), 

82 force_repr: Optional[str] = None, 

83 ): 

84 super().__init__() 

85 self.__wrapped_strategy: Optional[SearchStrategy[Ex]] = None 

86 self.__representation: Optional[str] = force_repr 

87 self.function = function 

88 self.__args = args 

89 self.__kwargs = kwargs 

90 self._transformations = transforms 

91 

92 @property 

93 def supports_find(self) -> bool: 

94 return self.wrapped_strategy.supports_find 

95 

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

97 return recur(self.wrapped_strategy) 

98 

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

100 return recur(self.wrapped_strategy) 

101 

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

103 for source in (self.__args, self.__kwargs.values()): 

104 for v in source: 

105 if isinstance(v, SearchStrategy) and not v.is_cacheable: 

106 return False 

107 return True 

108 

109 def calc_label(self) -> int: 

110 return self.wrapped_strategy.label 

111 

112 @property 

113 def wrapped_strategy(self) -> SearchStrategy[Ex]: 

114 if self.__wrapped_strategy is None: 

115 check_sideeffect_during_initialization("lazy evaluation of {!r}", self) 

116 

117 unwrapped_args = tuple(unwrap_strategies(s) for s in self.__args) 

118 unwrapped_kwargs = { 

119 k: unwrap_strategies(v) for k, v in self.__kwargs.items() 

120 } 

121 

122 base = self.function(*self.__args, **self.__kwargs) 

123 if unwrapped_args == self.__args and unwrapped_kwargs == self.__kwargs: 

124 self.__wrapped_strategy = base 

125 else: 

126 self.__wrapped_strategy = self.function( 

127 *unwrapped_args, **unwrapped_kwargs 

128 ) 

129 for method, fn in self._transformations: 

130 self.__wrapped_strategy = getattr(self.__wrapped_strategy, method)(fn) 

131 assert self.__wrapped_strategy is not None 

132 return self.__wrapped_strategy 

133 

134 def __with_transform(self, method, fn): 

135 repr_ = self.__representation 

136 if repr_: 

137 repr_ = f"{repr_}.{method}({get_pretty_function_description(fn)})" 

138 return LazyStrategy( 

139 self.function, 

140 self.__args, 

141 self.__kwargs, 

142 transforms=(*self._transformations, (method, fn)), 

143 force_repr=repr_, 

144 ) 

145 

146 def map(self, pack): 

147 return self.__with_transform("map", pack) 

148 

149 def filter(self, condition): 

150 return self.__with_transform("filter", condition) 

151 

152 def do_validate(self) -> None: 

153 w = self.wrapped_strategy 

154 assert isinstance(w, SearchStrategy), f"{self!r} returned non-strategy {w!r}" 

155 w.validate() 

156 

157 def __repr__(self) -> str: 

158 if self.__representation is None: 

159 sig = signature(self.function) 

160 pos = [p for p in sig.parameters.values() if "POSITIONAL" in p.kind.name] 

161 if len(pos) > 1 or any(p.default is not sig.empty for p in pos): 

162 _args, _kwargs = convert_positional_arguments( 

163 self.function, self.__args, self.__kwargs 

164 ) 

165 else: 

166 _args, _kwargs = convert_keyword_arguments( 

167 self.function, self.__args, self.__kwargs 

168 ) 

169 kwargs_for_repr = { 

170 k: v 

171 for k, v in _kwargs.items() 

172 if k not in sig.parameters or v is not sig.parameters[k].default 

173 } 

174 self.__representation = repr_call( 

175 self.function, _args, kwargs_for_repr, reorder=False 

176 ) + "".join( 

177 f".{method}({get_pretty_function_description(fn)})" 

178 for method, fn in self._transformations 

179 ) 

180 return self.__representation 

181 

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

183 return data.draw(self.wrapped_strategy)