Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/hypothesis/internal/compat.py: 35%

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

134 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 codecs 

12import copy 

13import dataclasses 

14import inspect 

15import itertools 

16import platform 

17import sys 

18import sysconfig 

19import typing 

20from functools import partial 

21from typing import Any, ForwardRef, Optional, TypedDict as TypedDict, get_args 

22 

23try: 

24 BaseExceptionGroup = BaseExceptionGroup 

25 ExceptionGroup = ExceptionGroup # pragma: no cover 

26except NameError: 

27 from exceptiongroup import ( 

28 BaseExceptionGroup as BaseExceptionGroup, 

29 ExceptionGroup as ExceptionGroup, 

30 ) 

31if typing.TYPE_CHECKING: # pragma: no cover 

32 from typing_extensions import ( 

33 Concatenate as Concatenate, 

34 NotRequired as NotRequired, 

35 ParamSpec as ParamSpec, 

36 TypeAlias as TypeAlias, 

37 TypedDict as TypedDict, 

38 override as override, 

39 ) 

40 

41 from hypothesis.internal.conjecture.engine import ConjectureRunner 

42else: 

43 # In order to use NotRequired, we need the version of TypedDict included in Python 3.11+. 

44 if sys.version_info[:2] >= (3, 11): 

45 from typing import NotRequired as NotRequired, TypedDict as TypedDict 

46 else: 

47 try: 

48 from typing_extensions import ( 

49 NotRequired as NotRequired, 

50 TypedDict as TypedDict, 

51 ) 

52 except ImportError: 

53 # We can use the old TypedDict from Python 3.8+ at runtime. 

54 class NotRequired: 

55 """A runtime placeholder for the NotRequired type, which is not available in Python <3.11.""" 

56 

57 def __class_getitem__(cls, item): 

58 return cls 

59 

60 try: 

61 from typing import ( 

62 Concatenate as Concatenate, 

63 ParamSpec as ParamSpec, 

64 TypeAlias as TypeAlias, 

65 override as override, 

66 ) 

67 except ImportError: 

68 try: 

69 from typing_extensions import ( 

70 Concatenate as Concatenate, 

71 ParamSpec as ParamSpec, 

72 TypeAlias as TypeAlias, 

73 override as override, 

74 ) 

75 except ImportError: 

76 Concatenate, ParamSpec = None, None 

77 TypeAlias = None 

78 override = lambda f: f 

79 

80 

81PYPY = platform.python_implementation() == "PyPy" 

82GRAALPY = platform.python_implementation() == "GraalVM" 

83WINDOWS = platform.system() == "Windows" 

84# First defined in CPython 3.13, defaults to False 

85FREE_THREADED_CPYTHON = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) 

86 

87 

88def add_note(exc, note): 

89 try: 

90 exc.add_note(note) 

91 except AttributeError: 

92 if not hasattr(exc, "__notes__"): 

93 try: 

94 exc.__notes__ = [] 

95 except AttributeError: 

96 return # give up, might be e.g. a frozen dataclass 

97 exc.__notes__.append(note) 

98 

99 

100def escape_unicode_characters(s: str) -> str: 

101 return codecs.encode(s, "unicode_escape").decode("ascii") 

102 

103 

104def int_from_bytes(data: typing.Union[bytes, bytearray]) -> int: 

105 return int.from_bytes(data, "big") 

106 

107 

108def int_to_bytes(i: int, size: int) -> bytes: 

109 return i.to_bytes(size, "big") 

110 

111 

112def int_to_byte(i: int) -> bytes: 

113 return bytes([i]) 

114 

115 

116def is_typed_named_tuple(cls: type) -> bool: 

117 """Return True if cls is probably a subtype of `typing.NamedTuple`. 

118 

119 Unfortunately types created with `class T(NamedTuple):` actually 

120 subclass `tuple` directly rather than NamedTuple. This is annoying, 

121 and means we just have to hope that nobody defines a different tuple 

122 subclass with similar attributes. 

123 """ 

124 return ( 

125 issubclass(cls, tuple) 

126 and hasattr(cls, "_fields") 

127 and (hasattr(cls, "_field_types") or hasattr(cls, "__annotations__")) 

128 ) 

129 

130 

131def _hint_and_args(x): 

132 return (x, *get_args(x)) 

133 

134 

135def get_type_hints(thing: object) -> dict[str, Any]: 

136 """Like the typing version, but tries harder and never errors. 

137 

138 Tries harder: if the thing to inspect is a class but typing.get_type_hints 

139 raises an error or returns no hints, then this function will try calling it 

140 on the __init__ method. This second step often helps with user-defined 

141 classes on older versions of Python. The third step we take is trying 

142 to fetch types from the __signature__ property. 

143 They override any other ones we found earlier. 

144 

145 Never errors: instead of raising TypeError for uninspectable objects, or 

146 NameError for unresolvable forward references, just return an empty dict. 

147 """ 

148 if isinstance(thing, partial): 

149 from hypothesis.internal.reflection import get_signature 

150 

151 bound = set(get_signature(thing.func).parameters).difference( 

152 get_signature(thing).parameters 

153 ) 

154 return {k: v for k, v in get_type_hints(thing.func).items() if k not in bound} 

155 

156 try: 

157 hints = typing.get_type_hints(thing, include_extras=True) 

158 except (AttributeError, TypeError, NameError): # pragma: no cover 

159 hints = {} 

160 

161 if inspect.isclass(thing): 

162 try: 

163 hints.update(typing.get_type_hints(thing.__init__, include_extras=True)) 

164 except (TypeError, NameError, AttributeError): 

165 pass 

166 

167 try: 

168 if hasattr(thing, "__signature__"): 

169 # It is possible for the signature and annotations attributes to 

170 # differ on an object due to renamed arguments. 

171 from hypothesis.internal.reflection import get_signature 

172 from hypothesis.strategies._internal.types import is_a_type 

173 

174 vkinds = (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD) 

175 for p in get_signature(thing).parameters.values(): 

176 if ( 

177 p.kind not in vkinds 

178 and is_a_type(p.annotation) 

179 and p.annotation is not p.empty 

180 ): 

181 p_hint = p.annotation 

182 

183 # Defer to `get_type_hints` if signature annotation is, or 

184 # contains, a forward reference that is otherwise resolved. 

185 if any( 

186 isinstance(sig_hint, ForwardRef) 

187 and not isinstance(hint, ForwardRef) 

188 for sig_hint, hint in zip( 

189 _hint_and_args(p.annotation), 

190 _hint_and_args(hints.get(p.name, Any)), 

191 ) 

192 ): 

193 p_hint = hints[p.name] 

194 if p.default is None: 

195 hints[p.name] = typing.Optional[p_hint] 

196 else: 

197 hints[p.name] = p_hint 

198 except (AttributeError, TypeError, NameError): # pragma: no cover 

199 pass 

200 

201 return hints 

202 

203 

204# Under Python 2, math.floor and math.ceil returned floats, which cannot 

205# represent large integers - eg `float(2**53) == float(2**53 + 1)`. 

206# We therefore implement them entirely in (long) integer operations. 

207# We still use the same trick on Python 3, because Numpy values and other 

208# custom __floor__ or __ceil__ methods may convert via floats. 

209# See issue #1667, Numpy issue 9068. 

210def floor(x): 

211 y = int(x) 

212 if y != x and x < 0: 

213 return y - 1 

214 return y 

215 

216 

217def ceil(x): 

218 y = int(x) 

219 if y != x and x > 0: 

220 return y + 1 

221 return y 

222 

223 

224def extract_bits(x: int, /, width: Optional[int] = None) -> list[int]: 

225 assert x >= 0 

226 result = [] 

227 while x: 

228 result.append(x & 1) 

229 x >>= 1 

230 if width is not None: 

231 result = (result + [0] * width)[:width] 

232 result.reverse() 

233 return result 

234 

235 

236# int.bit_count was added in python 3.10 

237try: 

238 bit_count = int.bit_count 

239except AttributeError: # pragma: no cover 

240 bit_count = lambda self: sum(extract_bits(abs(self))) 

241 

242 

243def bad_django_TestCase(runner: Optional["ConjectureRunner"]) -> bool: 

244 if runner is None or "django.test" not in sys.modules: 

245 return False 

246 else: # pragma: no cover 

247 if not isinstance(runner, sys.modules["django.test"].TransactionTestCase): 

248 return False 

249 

250 from hypothesis.extra.django._impl import HypothesisTestCase 

251 

252 return not isinstance(runner, HypothesisTestCase) 

253 

254 

255# see issue #3812 

256if sys.version_info[:2] < (3, 12): 

257 

258 def dataclass_asdict(obj, *, dict_factory=dict): 

259 """ 

260 A vendored variant of dataclasses.asdict. Includes the bugfix for 

261 defaultdicts (cpython/32056) for all versions. See also issues/3812. 

262 

263 This should be removed whenever we drop support for 3.11. We can use the 

264 standard dataclasses.asdict after that point. 

265 """ 

266 if not dataclasses._is_dataclass_instance(obj): # pragma: no cover 

267 raise TypeError("asdict() should be called on dataclass instances") 

268 return _asdict_inner(obj, dict_factory) 

269 

270else: # pragma: no cover 

271 dataclass_asdict = dataclasses.asdict 

272 

273 

274def _asdict_inner(obj, dict_factory): 

275 if dataclasses._is_dataclass_instance(obj): 

276 return dict_factory( 

277 (f.name, _asdict_inner(getattr(obj, f.name), dict_factory)) 

278 for f in dataclasses.fields(obj) 

279 ) 

280 elif isinstance(obj, tuple) and hasattr(obj, "_fields"): 

281 return type(obj)(*[_asdict_inner(v, dict_factory) for v in obj]) 

282 elif isinstance(obj, (list, tuple)): 

283 return type(obj)(_asdict_inner(v, dict_factory) for v in obj) 

284 elif isinstance(obj, dict): 

285 if hasattr(type(obj), "default_factory"): 

286 result = type(obj)(obj.default_factory) 

287 for k, v in obj.items(): 

288 result[_asdict_inner(k, dict_factory)] = _asdict_inner(v, dict_factory) 

289 return result 

290 return type(obj)( 

291 (_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory)) 

292 for k, v in obj.items() 

293 ) 

294 else: 

295 return copy.deepcopy(obj) 

296 

297 

298if sys.version_info[:2] < (3, 13): 

299 # batched was added in 3.12, strict flag in 3.13 

300 # copied from 3.13 docs reference implementation 

301 

302 def batched(iterable, n, *, strict=False): 

303 if n < 1: 

304 raise ValueError("n must be at least one") 

305 iterator = iter(iterable) 

306 while batch := tuple(itertools.islice(iterator, n)): 

307 if strict and len(batch) != n: # pragma: no cover 

308 raise ValueError("batched(): incomplete batch") 

309 yield batch 

310 

311else: # pragma: no cover 

312 batched = itertools.batched