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

136 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 get_args, 

28) 

29 

30try: 

31 BaseExceptionGroup = BaseExceptionGroup 

32 ExceptionGroup = ExceptionGroup # pragma: no cover 

33except NameError: 

34 from exceptiongroup import ( 

35 BaseExceptionGroup as BaseExceptionGroup, 

36 ExceptionGroup as ExceptionGroup, 

37 ) 

38if TYPE_CHECKING: 

39 from typing_extensions import ( 

40 NotRequired as NotRequired, 

41 TypedDict as TypedDict, 

42 override as override, 

43 ) 

44 

45 from hypothesis.internal.conjecture.engine import ConjectureRunner 

46else: 

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

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

49 from typing import NotRequired as NotRequired, TypedDict as TypedDict 

50 else: 

51 try: 

52 from typing_extensions import ( 

53 NotRequired as NotRequired, 

54 TypedDict as TypedDict, 

55 ) 

56 except ImportError: 

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

58 class NotRequired: 

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

60 

61 def __class_getitem__(cls, item): 

62 return cls 

63 

64 try: 

65 from typing import ( 

66 override as override, 

67 ) 

68 except ImportError: 

69 try: 

70 from typing_extensions import ( 

71 override as override, 

72 ) 

73 except ImportError: 

74 override = lambda f: f 

75 

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

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

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

79# First defined in CPython 3.13, defaults to False 

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

81 

82 

83def add_note(exc, note): 

84 try: 

85 exc.add_note(note) 

86 except AttributeError: 

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

88 try: 

89 exc.__notes__ = [] 

90 except AttributeError: 

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

92 exc.__notes__.append(note) 

93 

94 

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

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

97 

98 

99def int_from_bytes(data: bytes | bytearray) -> int: 

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

101 

102 

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

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

105 

106 

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

108 return bytes([i]) 

109 

110 

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

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

113 

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

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

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

117 subclass with similar attributes. 

118 """ 

119 return ( 

120 issubclass(cls, tuple) 

121 and hasattr(cls, "_fields") 

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

123 ) 

124 

125 

126def _hint_and_args(x): 

127 return (x, *get_args(x)) 

128 

129 

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

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

132 

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

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

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

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

137 to fetch types from the __signature__ property. 

138 They override any other ones we found earlier. 

139 

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

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

142 """ 

143 if isinstance(thing, partial): 

144 from hypothesis.internal.reflection import get_signature 

145 

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

147 get_signature(thing).parameters 

148 ) 

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

150 

151 try: 

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

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

154 hints = {} 

155 

156 if inspect.isclass(thing): 

157 try: 

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

159 except (TypeError, NameError, AttributeError): 

160 pass 

161 

162 try: 

163 if hasattr(thing, "__signature__"): 

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

165 # differ on an object due to renamed arguments. 

166 from hypothesis.internal.reflection import get_signature 

167 from hypothesis.strategies._internal.types import is_a_type 

168 

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

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

171 if ( 

172 p.kind not in vkinds 

173 and is_a_type(p.annotation) 

174 and p.annotation is not p.empty 

175 ): 

176 p_hint = p.annotation 

177 

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

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

180 if any( 

181 isinstance(sig_hint, ForwardRef) 

182 and not isinstance(hint, ForwardRef) 

183 for sig_hint, hint in zip( 

184 _hint_and_args(p.annotation), 

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

186 strict=False, 

187 ) 

188 ): 

189 p_hint = hints[p.name] 

190 if p.default is None: 

191 hints[p.name] = p_hint | None 

192 else: 

193 hints[p.name] = p_hint 

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

195 pass 

196 

197 return hints 

198 

199 

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

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

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

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

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

205# See issue #1667, Numpy issue 9068. 

206def floor(x): 

207 y = int(x) 

208 if y != x and x < 0: 

209 return y - 1 

210 return y 

211 

212 

213def ceil(x): 

214 y = int(x) 

215 if y != x and x > 0: 

216 return y + 1 

217 return y 

218 

219 

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

221 assert x >= 0 

222 result = [] 

223 while x: 

224 result.append(x & 1) 

225 x >>= 1 

226 if width is not None: 

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

228 result.reverse() 

229 return result 

230 

231 

232# int.bit_count was added in python 3.10 

233try: 

234 bit_count = int.bit_count 

235except AttributeError: # pragma: no cover 

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

237 

238 

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

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

241 return False 

242 else: # pragma: no cover 

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

244 return False 

245 

246 from hypothesis.extra.django._impl import HypothesisTestCase 

247 

248 return not isinstance(runner, HypothesisTestCase) 

249 

250 

251# see issue #3812 

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

253 

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

255 """ 

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

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

258 

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

260 standard dataclasses.asdict after that point. 

261 """ 

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

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

264 return _asdict_inner(obj, dict_factory) 

265 

266else: # pragma: no cover 

267 dataclass_asdict = dataclasses.asdict 

268 

269 

270def _asdict_inner(obj, dict_factory): 

271 if dataclasses._is_dataclass_instance(obj): 

272 return dict_factory( 

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

274 for f in dataclasses.fields(obj) 

275 ) 

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

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

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

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

280 elif isinstance(obj, dict): 

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

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

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

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

285 return result 

286 return type(obj)( 

287 (_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory)) 

288 for k, v in obj.items() 

289 ) 

290 else: 

291 return copy.deepcopy(obj) 

292 

293 

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

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

296 # copied from 3.13 docs reference implementation 

297 

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

299 if n < 1: 

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

301 iterator = iter(iterable) 

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

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

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

305 yield batch 

306 

307else: # pragma: no cover 

308 batched = itertools.batched