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

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

259 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 inspect 

12import math 

13from random import Random 

14from typing import Any, Dict 

15 

16import attr 

17 

18from hypothesis.control import should_note 

19from hypothesis.internal.reflection import define_function_signature 

20from hypothesis.reporting import report 

21from hypothesis.strategies._internal.core import ( 

22 binary, 

23 lists, 

24 permutations, 

25 sampled_from, 

26) 

27from hypothesis.strategies._internal.numbers import floats, integers 

28from hypothesis.strategies._internal.strategies import SearchStrategy 

29 

30 

31class HypothesisRandom(Random): 

32 """A subclass of Random designed to expose the seed it was initially 

33 provided with.""" 

34 

35 def __init__(self, note_method_calls): 

36 self.__note_method_calls = note_method_calls 

37 

38 def __deepcopy__(self, table): 

39 return self.__copy__() 

40 

41 def __repr__(self): 

42 raise NotImplementedError 

43 

44 def seed(self, seed): 

45 raise NotImplementedError 

46 

47 def getstate(self): 

48 raise NotImplementedError 

49 

50 def setstate(self, state): 

51 raise NotImplementedError 

52 

53 def _hypothesis_log_random(self, method, kwargs, result): 

54 if not (self.__note_method_calls and should_note()): 

55 return 

56 

57 args, kwargs = convert_kwargs(method, kwargs) 

58 argstr = ", ".join( 

59 list(map(repr, args)) + [f"{k}={v!r}" for k, v in kwargs.items()] 

60 ) 

61 report(f"{self!r}.{method}({argstr}) -> {result!r}") 

62 

63 def _hypothesis_do_random(self, method, kwargs): 

64 raise NotImplementedError 

65 

66 

67RANDOM_METHODS = [ 

68 name 

69 for name in [ 

70 "_randbelow", 

71 "betavariate", 

72 "binomialvariate", 

73 "choice", 

74 "choices", 

75 "expovariate", 

76 "gammavariate", 

77 "gauss", 

78 "getrandbits", 

79 "lognormvariate", 

80 "normalvariate", 

81 "paretovariate", 

82 "randint", 

83 "random", 

84 "randrange", 

85 "sample", 

86 "shuffle", 

87 "triangular", 

88 "uniform", 

89 "vonmisesvariate", 

90 "weibullvariate", 

91 "randbytes", 

92 ] 

93 if hasattr(Random, name) 

94] 

95 

96 

97# Fake shims to get a good signature 

98def getrandbits(self, n: int) -> int: # type: ignore 

99 raise NotImplementedError 

100 

101 

102def random(self) -> float: # type: ignore 

103 raise NotImplementedError 

104 

105 

106def _randbelow(self, n: int) -> int: # type: ignore 

107 raise NotImplementedError 

108 

109 

110STUBS = {f.__name__: f for f in [getrandbits, random, _randbelow]} 

111 

112 

113SIGNATURES: Dict[str, inspect.Signature] = {} 

114 

115 

116def sig_of(name): 

117 try: 

118 return SIGNATURES[name] 

119 except KeyError: 

120 pass 

121 

122 target = getattr(Random, name) 

123 result = inspect.signature(STUBS.get(name, target)) 

124 SIGNATURES[name] = result 

125 return result 

126 

127 

128def define_copy_method(name): 

129 target = getattr(Random, name) 

130 

131 def implementation(self, **kwargs): 

132 result = self._hypothesis_do_random(name, kwargs) 

133 self._hypothesis_log_random(name, kwargs, result) 

134 return result 

135 

136 sig = inspect.signature(STUBS.get(name, target)) 

137 

138 result = define_function_signature(target.__name__, target.__doc__, sig)( 

139 implementation 

140 ) 

141 

142 result.__module__ = __name__ 

143 result.__qualname__ = "HypothesisRandom." + result.__name__ 

144 

145 setattr(HypothesisRandom, name, result) 

146 

147 

148for r in RANDOM_METHODS: 

149 define_copy_method(r) 

150 

151 

152@attr.s(slots=True) 

153class RandomState: 

154 next_states: dict = attr.ib(factory=dict) 

155 state_id: Any = attr.ib(default=None) 

156 

157 

158def state_for_seed(data, seed): 

159 try: 

160 seeds_to_states = data.seeds_to_states 

161 except AttributeError: 

162 seeds_to_states = {} 

163 data.seeds_to_states = seeds_to_states 

164 

165 try: 

166 state = seeds_to_states[seed] 

167 except KeyError: 

168 state = RandomState() 

169 seeds_to_states[seed] = state 

170 

171 return state 

172 

173 

174UNIFORM = floats(0, 1) 

175 

176 

177def normalize_zero(f: float) -> float: 

178 if f == 0.0: 

179 return 0.0 

180 else: 

181 return f 

182 

183 

184class ArtificialRandom(HypothesisRandom): 

185 VERSION = 10**6 

186 

187 def __init__(self, note_method_calls, data): 

188 super().__init__(note_method_calls=note_method_calls) 

189 self.__data = data 

190 self.__state = RandomState() 

191 

192 def __repr__(self): 

193 return "HypothesisRandom(generated data)" 

194 

195 def __copy__(self): 

196 result = ArtificialRandom( 

197 note_method_calls=self._HypothesisRandom__note_method_calls, 

198 data=self.__data, 

199 ) 

200 result.setstate(self.getstate()) 

201 return result 

202 

203 def __convert_result(self, method, kwargs, result): 

204 if method == "choice": 

205 return kwargs.get("seq")[result] 

206 if method in ("choices", "sample"): 

207 seq = kwargs["population"] 

208 return [seq[i] for i in result] 

209 if method == "shuffle": 

210 seq = kwargs["x"] 

211 original = list(seq) 

212 for i, i2 in enumerate(result): 

213 seq[i] = original[i2] 

214 return None 

215 return result 

216 

217 def _hypothesis_do_random(self, method, kwargs): 

218 if method == "choices": 

219 key = (method, len(kwargs["population"]), kwargs.get("k")) 

220 elif method == "choice": 

221 key = (method, len(kwargs["seq"])) 

222 elif method == "shuffle": 

223 key = (method, len(kwargs["x"])) 

224 else: 

225 key = (method, *sorted(kwargs)) 

226 

227 try: 

228 result, self.__state = self.__state.next_states[key] 

229 except KeyError: 

230 pass 

231 else: 

232 return self.__convert_result(method, kwargs, result) 

233 

234 if method == "_randbelow": 

235 result = self.__data.draw_integer(0, kwargs["n"] - 1) 

236 elif method in ("betavariate", "random"): 

237 result = self.__data.draw(UNIFORM) 

238 elif method == "uniform": 

239 a = normalize_zero(kwargs["a"]) 

240 b = normalize_zero(kwargs["b"]) 

241 result = self.__data.draw(floats(a, b)) 

242 elif method in ("weibullvariate", "gammavariate"): 

243 result = self.__data.draw(floats(min_value=0.0, allow_infinity=False)) 

244 elif method in ("gauss", "normalvariate"): 

245 mu = kwargs["mu"] 

246 result = mu + self.__data.draw( 

247 floats(allow_nan=False, allow_infinity=False) 

248 ) 

249 elif method == "vonmisesvariate": 

250 result = self.__data.draw(floats(0, 2 * math.pi)) 

251 elif method == "randrange": 

252 if kwargs["stop"] is None: 

253 stop = kwargs["start"] 

254 start = 0 

255 else: 

256 start = kwargs["start"] 

257 stop = kwargs["stop"] 

258 

259 step = kwargs["step"] 

260 if start == stop: 

261 raise ValueError(f"empty range for randrange({start}, {stop}, {step})") 

262 

263 if step != 1: 

264 endpoint = (stop - start) // step 

265 if (start - stop) % step == 0: 

266 endpoint -= 1 

267 

268 i = self.__data.draw_integer(0, endpoint) 

269 result = start + i * step 

270 else: 

271 result = self.__data.draw_integer(start, stop - 1) 

272 elif method == "randint": 

273 result = self.__data.draw_integer(kwargs["a"], kwargs["b"]) 

274 # New in Python 3.12, so not taken by our coverage job 

275 elif method == "binomialvariate": # pragma: no cover 

276 result = self.__data.draw_integer(0, kwargs["n"]) 

277 elif method == "choice": 

278 seq = kwargs["seq"] 

279 result = self.__data.draw_integer(0, len(seq) - 1) 

280 elif method == "choices": 

281 k = kwargs["k"] 

282 result = self.__data.draw( 

283 lists( 

284 integers(0, len(kwargs["population"]) - 1), 

285 min_size=k, 

286 max_size=k, 

287 ) 

288 ) 

289 elif method == "sample": 

290 k = kwargs["k"] 

291 seq = kwargs["population"] 

292 

293 if k > len(seq) or k < 0: 

294 raise ValueError( 

295 f"Sample size {k} not in expected range 0 <= k <= {len(seq)}" 

296 ) 

297 

298 if k == 0: 

299 result = [] 

300 else: 

301 result = self.__data.draw( 

302 lists( 

303 sampled_from(range(len(seq))), 

304 min_size=k, 

305 max_size=k, 

306 unique=True, 

307 ) 

308 ) 

309 

310 elif method == "getrandbits": 

311 result = self.__data.draw_integer(0, 2 ** kwargs["n"] - 1) 

312 elif method == "triangular": 

313 low = normalize_zero(kwargs["low"]) 

314 high = normalize_zero(kwargs["high"]) 

315 mode = normalize_zero(kwargs["mode"]) 

316 if mode is None: 

317 result = self.__data.draw(floats(low, high)) 

318 elif self.__data.draw_boolean(0.5): 

319 result = self.__data.draw(floats(mode, high)) 

320 else: 

321 result = self.__data.draw(floats(low, mode)) 

322 elif method in ("paretovariate", "expovariate", "lognormvariate"): 

323 result = self.__data.draw(floats(min_value=0.0)) 

324 elif method == "shuffle": 

325 result = self.__data.draw(permutations(range(len(kwargs["x"])))) 

326 elif method == "randbytes": 

327 n = kwargs["n"] 

328 result = self.__data.draw(binary(min_size=n, max_size=n)) 

329 else: 

330 raise NotImplementedError(method) 

331 

332 new_state = RandomState() 

333 self.__state.next_states[key] = (result, new_state) 

334 self.__state = new_state 

335 

336 return self.__convert_result(method, kwargs, result) 

337 

338 def seed(self, seed): 

339 self.__state = state_for_seed(self.__data, seed) 

340 

341 def getstate(self): 

342 if self.__state.state_id is not None: 

343 return self.__state.state_id 

344 

345 try: 

346 states_for_ids = self.__data.states_for_ids 

347 except AttributeError: 

348 states_for_ids = {} 

349 self.__data.states_for_ids = states_for_ids 

350 

351 self.__state.state_id = len(states_for_ids) 

352 states_for_ids[self.__state.state_id] = self.__state 

353 

354 return self.__state.state_id 

355 

356 def setstate(self, state): 

357 self.__state = self.__data.states_for_ids[state] 

358 

359 

360DUMMY_RANDOM = Random(0) 

361 

362 

363def convert_kwargs(name, kwargs): 

364 kwargs = dict(kwargs) 

365 

366 signature = sig_of(name) 

367 

368 bound = signature.bind(DUMMY_RANDOM, **kwargs) 

369 bound.apply_defaults() 

370 

371 for k in list(kwargs): 

372 if ( 

373 kwargs[k] is signature.parameters[k].default 

374 or signature.parameters[k].kind != inspect.Parameter.KEYWORD_ONLY 

375 ): 

376 kwargs.pop(k) 

377 

378 arg_names = list(signature.parameters)[1:] 

379 

380 args = [] 

381 

382 for a in arg_names: 

383 if signature.parameters[a].kind == inspect.Parameter.KEYWORD_ONLY: 

384 break 

385 args.append(bound.arguments[a]) 

386 kwargs.pop(a, None) 

387 

388 while args: 

389 name = arg_names[len(args) - 1] 

390 if args[-1] is signature.parameters[name].default: 

391 args.pop() 

392 else: 

393 break 

394 

395 return (args, kwargs) 

396 

397 

398class TrueRandom(HypothesisRandom): 

399 def __init__(self, seed, note_method_calls): 

400 super().__init__(note_method_calls) 

401 self.__seed = seed 

402 self.__random = Random(seed) 

403 

404 def _hypothesis_do_random(self, method, kwargs): 

405 args, kwargs = convert_kwargs(method, kwargs) 

406 

407 return getattr(self.__random, method)(*args, **kwargs) 

408 

409 def __copy__(self): 

410 result = TrueRandom( 

411 seed=self.__seed, 

412 note_method_calls=self._HypothesisRandom__note_method_calls, 

413 ) 

414 result.setstate(self.getstate()) 

415 return result 

416 

417 def __repr__(self): 

418 return f"Random({self.__seed!r})" 

419 

420 def seed(self, seed): 

421 self.__random.seed(seed) 

422 self.__seed = seed 

423 

424 def getstate(self): 

425 return self.__random.getstate() 

426 

427 def setstate(self, state): 

428 self.__random.setstate(state) 

429 

430 

431class RandomStrategy(SearchStrategy): 

432 def __init__(self, note_method_calls, use_true_random): 

433 self.__note_method_calls = note_method_calls 

434 self.__use_true_random = use_true_random 

435 

436 def do_draw(self, data): 

437 if self.__use_true_random: 

438 seed = data.draw_integer(0, 2**64 - 1) 

439 return TrueRandom(seed=seed, note_method_calls=self.__note_method_calls) 

440 else: 

441 return ArtificialRandom( 

442 note_method_calls=self.__note_method_calls, data=data 

443 )