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

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

99 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 signature 

12from typing import MutableMapping 

13from weakref import WeakKeyDictionary 

14 

15from hypothesis.configuration import check_sideeffect_during_initialization 

16from hypothesis.internal.reflection import ( 

17 convert_keyword_arguments, 

18 convert_positional_arguments, 

19 get_pretty_function_description, 

20 repr_call, 

21) 

22from hypothesis.strategies._internal.strategies import SearchStrategy 

23 

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

25unwrap_depth = 0 

26 

27 

28def unwrap_strategies(s): 

29 global unwrap_depth 

30 

31 if not isinstance(s, SearchStrategy): 

32 return s 

33 try: 

34 return unwrap_cache[s] 

35 except KeyError: 

36 pass 

37 

38 unwrap_cache[s] = s 

39 

40 try: 

41 unwrap_depth += 1 

42 try: 

43 result = unwrap_strategies(s.wrapped_strategy) 

44 unwrap_cache[s] = result 

45 try: 

46 assert result.force_has_reusable_values == s.force_has_reusable_values 

47 except AttributeError: 

48 pass 

49 

50 try: 

51 result.force_has_reusable_values = s.force_has_reusable_values 

52 except AttributeError: 

53 pass 

54 return result 

55 except AttributeError: 

56 return s 

57 finally: 

58 unwrap_depth -= 1 

59 if unwrap_depth <= 0: 

60 unwrap_cache.clear() 

61 assert unwrap_depth >= 0 

62 

63 

64class LazyStrategy(SearchStrategy): 

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

66 strategy. 

67 

68 Its parameter and distribution come from that other strategy. 

69 """ 

70 

71 def __init__(self, function, args, kwargs, *, transforms=(), force_repr=None): 

72 super().__init__() 

73 self.__wrapped_strategy = None 

74 self.__representation = force_repr 

75 self.function = function 

76 self.__args = args 

77 self.__kwargs = kwargs 

78 self._transformations = transforms 

79 

80 @property 

81 def supports_find(self): 

82 return self.wrapped_strategy.supports_find 

83 

84 def calc_is_empty(self, recur): 

85 return recur(self.wrapped_strategy) 

86 

87 def calc_has_reusable_values(self, recur): 

88 return recur(self.wrapped_strategy) 

89 

90 def calc_is_cacheable(self, recur): 

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

92 for v in source: 

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

94 return False 

95 return True 

96 

97 @property 

98 def wrapped_strategy(self): 

99 if self.__wrapped_strategy is None: 

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

101 

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

103 unwrapped_kwargs = { 

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

105 } 

106 

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

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

109 self.__wrapped_strategy = base 

110 else: 

111 self.__wrapped_strategy = self.function( 

112 *unwrapped_args, **unwrapped_kwargs 

113 ) 

114 for method, fn in self._transformations: 

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

116 return self.__wrapped_strategy 

117 

118 def __with_transform(self, method, fn): 

119 repr_ = self.__representation 

120 if repr_: 

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

122 return type(self)( 

123 self.function, 

124 self.__args, 

125 self.__kwargs, 

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

127 force_repr=repr_, 

128 ) 

129 

130 def map(self, pack): 

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

132 

133 def filter(self, condition): 

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

135 

136 def do_validate(self): 

137 w = self.wrapped_strategy 

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

139 w.validate() 

140 

141 def __repr__(self): 

142 if self.__representation is None: 

143 sig = signature(self.function) 

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

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

146 _args, _kwargs = convert_positional_arguments( 

147 self.function, self.__args, self.__kwargs 

148 ) 

149 else: 

150 _args, _kwargs = convert_keyword_arguments( 

151 self.function, self.__args, self.__kwargs 

152 ) 

153 kwargs_for_repr = { 

154 k: v 

155 for k, v in _kwargs.items() 

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

157 } 

158 self.__representation = repr_call( 

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

160 ) + "".join( 

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

162 for method, fn in self._transformations 

163 ) 

164 return self.__representation 

165 

166 def do_draw(self, data): 

167 return data.draw(self.wrapped_strategy) 

168 

169 @property 

170 def label(self): 

171 return self.wrapped_strategy.label