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

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

142 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 ( 

22 TYPE_CHECKING, 

23 Any, 

24 ForwardRef, 

25 Optional, 

26 TypedDict as TypedDict, 

27 Union, 

28 get_args, 

29) 

30 

31try: 

32 BaseExceptionGroup = BaseExceptionGroup 

33 ExceptionGroup = ExceptionGroup # pragma: no cover 

34except NameError: 

35 from exceptiongroup import ( 

36 BaseExceptionGroup as BaseExceptionGroup, 

37 ExceptionGroup as ExceptionGroup, 

38 ) 

39if TYPE_CHECKING: 

40 from typing_extensions import ( 

41 Concatenate as Concatenate, 

42 NotRequired as NotRequired, 

43 ParamSpec as ParamSpec, 

44 TypeAlias as TypeAlias, 

45 TypedDict as TypedDict, 

46 override as override, 

47 ) 

48 

49 from hypothesis.internal.conjecture.engine import ConjectureRunner 

50else: 

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

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

53 from typing import NotRequired as NotRequired, TypedDict as TypedDict 

54 else: 

55 try: 

56 from typing_extensions import ( 

57 NotRequired as NotRequired, 

58 TypedDict as TypedDict, 

59 ) 

60 except ImportError: 

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

62 class NotRequired: 

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

64 

65 def __class_getitem__(cls, item): 

66 return cls 

67 

68 try: 

69 from typing import ( 

70 Concatenate as Concatenate, 

71 ParamSpec as ParamSpec, 

72 TypeAlias as TypeAlias, 

73 override as override, 

74 ) 

75 except ImportError: 

76 try: 

77 from typing_extensions import ( 

78 Concatenate as Concatenate, 

79 ParamSpec as ParamSpec, 

80 TypeAlias as TypeAlias, 

81 override as override, 

82 ) 

83 except ImportError: 

84 Concatenate, ParamSpec = None, None 

85 TypeAlias = None 

86 override = lambda f: f 

87 

88if sys.version_info >= (3, 10): 

89 from types import EllipsisType as EllipsisType 

90elif TYPE_CHECKING: 

91 from builtins import ellipsis as EllipsisType 

92else: # pragma: no cover 

93 EllipsisType = type(Ellipsis) 

94 

95 

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

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

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

99# First defined in CPython 3.13, defaults to False 

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

101 

102 

103def add_note(exc, note): 

104 try: 

105 exc.add_note(note) 

106 except AttributeError: 

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

108 try: 

109 exc.__notes__ = [] 

110 except AttributeError: 

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

112 exc.__notes__.append(note) 

113 

114 

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

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

117 

118 

119def int_from_bytes(data: Union[bytes, bytearray]) -> int: 

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

121 

122 

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

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

125 

126 

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

128 return bytes([i]) 

129 

130 

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

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

133 

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

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

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

137 subclass with similar attributes. 

138 """ 

139 return ( 

140 issubclass(cls, tuple) 

141 and hasattr(cls, "_fields") 

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

143 ) 

144 

145 

146def _hint_and_args(x): 

147 return (x, *get_args(x)) 

148 

149 

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

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

152 

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

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

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

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

157 to fetch types from the __signature__ property. 

158 They override any other ones we found earlier. 

159 

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

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

162 """ 

163 if isinstance(thing, partial): 

164 from hypothesis.internal.reflection import get_signature 

165 

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

167 get_signature(thing).parameters 

168 ) 

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

170 

171 try: 

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

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

174 hints = {} 

175 

176 if inspect.isclass(thing): 

177 try: 

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

179 except (TypeError, NameError, AttributeError): 

180 pass 

181 

182 try: 

183 if hasattr(thing, "__signature__"): 

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

185 # differ on an object due to renamed arguments. 

186 from hypothesis.internal.reflection import get_signature 

187 from hypothesis.strategies._internal.types import is_a_type 

188 

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

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

191 if ( 

192 p.kind not in vkinds 

193 and is_a_type(p.annotation) 

194 and p.annotation is not p.empty 

195 ): 

196 p_hint = p.annotation 

197 

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

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

200 if any( 

201 isinstance(sig_hint, ForwardRef) 

202 and not isinstance(hint, ForwardRef) 

203 for sig_hint, hint in zip( 

204 _hint_and_args(p.annotation), 

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

206 ) 

207 ): 

208 p_hint = hints[p.name] 

209 if p.default is None: 

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

211 else: 

212 hints[p.name] = p_hint 

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

214 pass 

215 

216 return hints 

217 

218 

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

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

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

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

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

224# See issue #1667, Numpy issue 9068. 

225def floor(x): 

226 y = int(x) 

227 if y != x and x < 0: 

228 return y - 1 

229 return y 

230 

231 

232def ceil(x): 

233 y = int(x) 

234 if y != x and x > 0: 

235 return y + 1 

236 return y 

237 

238 

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

240 assert x >= 0 

241 result = [] 

242 while x: 

243 result.append(x & 1) 

244 x >>= 1 

245 if width is not None: 

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

247 result.reverse() 

248 return result 

249 

250 

251# int.bit_count was added in python 3.10 

252try: 

253 bit_count = int.bit_count 

254except AttributeError: # pragma: no cover 

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

256 

257 

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

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

260 return False 

261 else: # pragma: no cover 

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

263 return False 

264 

265 from hypothesis.extra.django._impl import HypothesisTestCase 

266 

267 return not isinstance(runner, HypothesisTestCase) 

268 

269 

270# see issue #3812 

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

272 

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

274 """ 

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

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

277 

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

279 standard dataclasses.asdict after that point. 

280 """ 

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

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

283 return _asdict_inner(obj, dict_factory) 

284 

285else: # pragma: no cover 

286 dataclass_asdict = dataclasses.asdict 

287 

288 

289def _asdict_inner(obj, dict_factory): 

290 if dataclasses._is_dataclass_instance(obj): 

291 return dict_factory( 

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

293 for f in dataclasses.fields(obj) 

294 ) 

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

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

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

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

299 elif isinstance(obj, dict): 

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

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

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

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

304 return result 

305 return type(obj)( 

306 (_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory)) 

307 for k, v in obj.items() 

308 ) 

309 else: 

310 return copy.deepcopy(obj) 

311 

312 

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

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

315 # copied from 3.13 docs reference implementation 

316 

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

318 if n < 1: 

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

320 iterator = iter(iterable) 

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

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

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

324 yield batch 

325 

326else: # pragma: no cover 

327 batched = itertools.batched