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

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

27) 

28 

29if TYPE_CHECKING: 

30 from typing_extensions import override as override 

31 

32 from hypothesis.internal.conjecture.engine import ConjectureRunner 

33else: 

34 try: 

35 from typing import ( 

36 override as override, 

37 ) 

38 except ImportError: 

39 try: 

40 from typing_extensions import ( 

41 override as override, 

42 ) 

43 except ImportError: 

44 override = lambda f: f 

45 

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

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

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

49# First defined in CPython 3.13, defaults to False 

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

51 

52 

53def add_note(exc, note): 

54 try: 

55 exc.add_note(note) 

56 except AttributeError: 

57 pass # give up, might be e.g. a frozen dataclass 

58 

59 

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

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

62 

63 

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

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

66 

67 

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

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

70 

71 

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

73 return bytes([i]) 

74 

75 

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

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

78 

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

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

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

82 subclass with similar attributes. 

83 """ 

84 return ( 

85 issubclass(cls, tuple) 

86 and hasattr(cls, "_fields") 

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

88 ) 

89 

90 

91def _hint_and_args(x): 

92 return (x, *get_args(x)) 

93 

94 

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

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

97 

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

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

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

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

102 to fetch types from the __signature__ property. 

103 They override any other ones we found earlier. 

104 

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

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

107 """ 

108 if isinstance(thing, partial): 

109 from hypothesis.internal.reflection import get_signature 

110 

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

112 get_signature(thing).parameters 

113 ) 

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

115 

116 try: 

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

118 except (AttributeError, TypeError, NameError): 

119 hints = {} 

120 

121 if inspect.isclass(thing): 

122 try: 

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

124 except (TypeError, NameError, AttributeError): 

125 pass 

126 

127 try: 

128 if hasattr(thing, "__signature__"): 

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

130 # differ on an object due to renamed arguments. 

131 from hypothesis.internal.reflection import get_signature 

132 from hypothesis.strategies._internal.types import is_a_type 

133 

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

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

136 if ( 

137 p.kind not in vkinds 

138 and is_a_type(p.annotation) 

139 and p.annotation is not p.empty 

140 ): 

141 p_hint = p.annotation 

142 

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

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

145 if any( 

146 isinstance(sig_hint, ForwardRef) 

147 and not isinstance(hint, ForwardRef) 

148 for sig_hint, hint in zip( 

149 _hint_and_args(p.annotation), 

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

151 strict=False, 

152 ) 

153 ): 

154 p_hint = hints[p.name] 

155 if p.default is None: 

156 hints[p.name] = p_hint | None 

157 else: 

158 hints[p.name] = p_hint 

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

160 pass 

161 

162 return hints 

163 

164 

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

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

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

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

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

170# See issue #1667, Numpy issue 9068. 

171def floor(x): 

172 y = int(x) 

173 if y != x and x < 0: 

174 return y - 1 

175 return y 

176 

177 

178def ceil(x): 

179 y = int(x) 

180 if y != x and x > 0: 

181 return y + 1 

182 return y 

183 

184 

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

186 assert x >= 0 

187 result = [] 

188 while x: 

189 result.append(x & 1) 

190 x >>= 1 

191 if width is not None: 

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

193 result.reverse() 

194 return result 

195 

196 

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

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

199 return False 

200 else: # pragma: no cover 

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

202 return False 

203 

204 from hypothesis.extra.django._impl import HypothesisTestCase 

205 

206 return not isinstance(runner, HypothesisTestCase) 

207 

208 

209# see issue #3812 

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

211 

212 def _asdict_inner(obj, dict_factory): 

213 if dataclasses._is_dataclass_instance(obj): 

214 return dict_factory( 

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

216 for f in dataclasses.fields(obj) 

217 ) 

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

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

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

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

222 elif isinstance(obj, dict): 

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

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

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

226 result[_asdict_inner(k, dict_factory)] = _asdict_inner( 

227 v, dict_factory 

228 ) 

229 return result 

230 return type(obj)( 

231 (_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory)) 

232 for k, v in obj.items() 

233 ) 

234 else: 

235 return copy.deepcopy(obj) 

236 

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

238 """ 

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

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

241 

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

243 standard dataclasses.asdict after that point. 

244 """ 

245 if not dataclasses._is_dataclass_instance(obj): 

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

247 return _asdict_inner(obj, dict_factory) 

248 

249else: 

250 dataclass_asdict = dataclasses.asdict 

251 

252 

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

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

255 # copied from 3.13 docs reference implementation 

256 

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

258 if n < 1: 

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

260 iterator = iter(iterable) 

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

262 if strict and len(batch) != n: 

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

264 yield batch 

265 

266else: 

267 batched = itertools.batched