Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2python version compatibility code 

3""" 

4import functools 

5import inspect 

6import io 

7import os 

8import re 

9import sys 

10from contextlib import contextmanager 

11from inspect import Parameter 

12from inspect import signature 

13from typing import Any 

14from typing import Callable 

15from typing import Generic 

16from typing import Optional 

17from typing import overload 

18from typing import Tuple 

19from typing import TypeVar 

20from typing import Union 

21 

22import attr 

23import py 

24 

25import _pytest 

26from _pytest._io.saferepr import saferepr 

27from _pytest.outcomes import fail 

28from _pytest.outcomes import TEST_OUTCOME 

29 

30if sys.version_info < (3, 5, 2): 

31 TYPE_CHECKING = False # type: bool 

32else: 

33 from typing import TYPE_CHECKING 

34 

35 

36if TYPE_CHECKING: 

37 from typing import Type # noqa: F401 (used in type string) 

38 

39 

40_T = TypeVar("_T") 

41_S = TypeVar("_S") 

42 

43 

44NOTSET = object() 

45 

46MODULE_NOT_FOUND_ERROR = ( 

47 "ModuleNotFoundError" if sys.version_info[:2] >= (3, 6) else "ImportError" 

48) 

49 

50 

51if sys.version_info >= (3, 8): 

52 from importlib import metadata as importlib_metadata 

53else: 

54 import importlib_metadata # noqa: F401 

55 

56 

57def _format_args(func: Callable[..., Any]) -> str: 

58 return str(signature(func)) 

59 

60 

61# The type of re.compile objects is not exposed in Python. 

62REGEX_TYPE = type(re.compile("")) 

63 

64 

65if sys.version_info < (3, 6): 

66 

67 def fspath(p): 

68 """os.fspath replacement, useful to point out when we should replace it by the 

69 real function once we drop py35. 

70 """ 

71 return str(p) 

72 

73 

74else: 

75 fspath = os.fspath 

76 

77 

78def is_generator(func: object) -> bool: 

79 genfunc = inspect.isgeneratorfunction(func) 

80 return genfunc and not iscoroutinefunction(func) 

81 

82 

83def iscoroutinefunction(func: object) -> bool: 

84 """ 

85 Return True if func is a coroutine function (a function defined with async 

86 def syntax, and doesn't contain yield), or a function decorated with 

87 @asyncio.coroutine. 

88 

89 Note: copied and modified from Python 3.5's builtin couroutines.py to avoid 

90 importing asyncio directly, which in turns also initializes the "logging" 

91 module as a side-effect (see issue #8). 

92 """ 

93 return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False) 

94 

95 

96def getlocation(function, curdir=None) -> str: 

97 function = get_real_func(function) 

98 fn = py.path.local(inspect.getfile(function)) 

99 lineno = function.__code__.co_firstlineno 

100 if curdir is not None: 

101 relfn = fn.relto(curdir) 

102 if relfn: 

103 return "%s:%d" % (relfn, lineno + 1) 

104 return "%s:%d" % (fn, lineno + 1) 

105 

106 

107def num_mock_patch_args(function) -> int: 

108 """ return number of arguments used up by mock arguments (if any) """ 

109 patchings = getattr(function, "patchings", None) 

110 if not patchings: 

111 return 0 

112 

113 mock_sentinel = getattr(sys.modules.get("mock"), "DEFAULT", object()) 

114 ut_mock_sentinel = getattr(sys.modules.get("unittest.mock"), "DEFAULT", object()) 

115 

116 return len( 

117 [ 

118 p 

119 for p in patchings 

120 if not p.attribute_name 

121 and (p.new is mock_sentinel or p.new is ut_mock_sentinel) 

122 ] 

123 ) 

124 

125 

126def getfuncargnames( 

127 function: Callable[..., Any], 

128 *, 

129 name: str = "", 

130 is_method: bool = False, 

131 cls: Optional[type] = None 

132) -> Tuple[str, ...]: 

133 """Returns the names of a function's mandatory arguments. 

134 

135 This should return the names of all function arguments that: 

136 * Aren't bound to an instance or type as in instance or class methods. 

137 * Don't have default values. 

138 * Aren't bound with functools.partial. 

139 * Aren't replaced with mocks. 

140 

141 The is_method and cls arguments indicate that the function should 

142 be treated as a bound method even though it's not unless, only in 

143 the case of cls, the function is a static method. 

144 

145 The name parameter should be the original name in which the function was collected. 

146 

147 @RonnyPfannschmidt: This function should be refactored when we 

148 revisit fixtures. The fixture mechanism should ask the node for 

149 the fixture names, and not try to obtain directly from the 

150 function object well after collection has occurred. 

151 """ 

152 # The parameters attribute of a Signature object contains an 

153 # ordered mapping of parameter names to Parameter instances. This 

154 # creates a tuple of the names of the parameters that don't have 

155 # defaults. 

156 try: 

157 parameters = signature(function).parameters 

158 except (ValueError, TypeError) as e: 

159 fail( 

160 "Could not determine arguments of {!r}: {}".format(function, e), 

161 pytrace=False, 

162 ) 

163 

164 arg_names = tuple( 

165 p.name 

166 for p in parameters.values() 

167 if ( 

168 p.kind is Parameter.POSITIONAL_OR_KEYWORD 

169 or p.kind is Parameter.KEYWORD_ONLY 

170 ) 

171 and p.default is Parameter.empty 

172 ) 

173 if not name: 

174 name = function.__name__ 

175 

176 # If this function should be treated as a bound method even though 

177 # it's passed as an unbound method or function, remove the first 

178 # parameter name. 

179 if is_method or ( 

180 cls and not isinstance(cls.__dict__.get(name, None), staticmethod) 

181 ): 

182 arg_names = arg_names[1:] 

183 # Remove any names that will be replaced with mocks. 

184 if hasattr(function, "__wrapped__"): 

185 arg_names = arg_names[num_mock_patch_args(function) :] 

186 return arg_names 

187 

188 

189if sys.version_info < (3, 7): 

190 

191 @contextmanager 

192 def nullcontext(): 

193 yield 

194 

195 

196else: 

197 from contextlib import nullcontext # noqa 

198 

199 

200def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]: 

201 # Note: this code intentionally mirrors the code at the beginning of getfuncargnames, 

202 # to get the arguments which were excluded from its result because they had default values 

203 return tuple( 

204 p.name 

205 for p in signature(function).parameters.values() 

206 if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) 

207 and p.default is not Parameter.empty 

208 ) 

209 

210 

211_non_printable_ascii_translate_table = { 

212 i: "\\x{:02x}".format(i) for i in range(128) if i not in range(32, 127) 

213} 

214_non_printable_ascii_translate_table.update( 

215 {ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"} 

216) 

217 

218 

219def _translate_non_printable(s: str) -> str: 

220 return s.translate(_non_printable_ascii_translate_table) 

221 

222 

223STRING_TYPES = bytes, str 

224 

225 

226def _bytes_to_ascii(val: bytes) -> str: 

227 return val.decode("ascii", "backslashreplace") 

228 

229 

230def ascii_escaped(val: Union[bytes, str]): 

231 """If val is pure ascii, returns it as a str(). Otherwise, escapes 

232 bytes objects into a sequence of escaped bytes: 

233 

234 b'\xc3\xb4\xc5\xd6' -> '\\xc3\\xb4\\xc5\\xd6' 

235 

236 and escapes unicode objects into a sequence of escaped unicode 

237 ids, e.g.: 

238 

239 '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944' 

240 

241 note: 

242 the obvious "v.decode('unicode-escape')" will return 

243 valid utf-8 unicode if it finds them in bytes, but we 

244 want to return escaped bytes for any byte, even if they match 

245 a utf-8 string. 

246 

247 """ 

248 if isinstance(val, bytes): 

249 ret = _bytes_to_ascii(val) 

250 else: 

251 ret = val.encode("unicode_escape").decode("ascii") 

252 return _translate_non_printable(ret) 

253 

254 

255@attr.s 

256class _PytestWrapper: 

257 """Dummy wrapper around a function object for internal use only. 

258 

259 Used to correctly unwrap the underlying function object 

260 when we are creating fixtures, because we wrap the function object ourselves with a decorator 

261 to issue warnings when the fixture function is called directly. 

262 """ 

263 

264 obj = attr.ib() 

265 

266 

267def get_real_func(obj): 

268 """ gets the real function object of the (possibly) wrapped object by 

269 functools.wraps or functools.partial. 

270 """ 

271 start_obj = obj 

272 for i in range(100): 

273 # __pytest_wrapped__ is set by @pytest.fixture when wrapping the fixture function 

274 # to trigger a warning if it gets called directly instead of by pytest: we don't 

275 # want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774) 

276 new_obj = getattr(obj, "__pytest_wrapped__", None) 

277 if isinstance(new_obj, _PytestWrapper): 

278 obj = new_obj.obj 

279 break 

280 new_obj = getattr(obj, "__wrapped__", None) 

281 if new_obj is None: 

282 break 

283 obj = new_obj 

284 else: 

285 raise ValueError( 

286 ("could not find real function of {start}\nstopped at {current}").format( 

287 start=saferepr(start_obj), current=saferepr(obj) 

288 ) 

289 ) 

290 if isinstance(obj, functools.partial): 

291 obj = obj.func 

292 return obj 

293 

294 

295def get_real_method(obj, holder): 

296 """ 

297 Attempts to obtain the real function object that might be wrapping ``obj``, while at the same time 

298 returning a bound method to ``holder`` if the original object was a bound method. 

299 """ 

300 try: 

301 is_method = hasattr(obj, "__func__") 

302 obj = get_real_func(obj) 

303 except Exception: # pragma: no cover 

304 return obj 

305 if is_method and hasattr(obj, "__get__") and callable(obj.__get__): 

306 obj = obj.__get__(holder) 

307 return obj 

308 

309 

310def getfslineno(obj): 

311 # xxx let decorators etc specify a sane ordering 

312 obj = get_real_func(obj) 

313 if hasattr(obj, "place_as"): 

314 obj = obj.place_as 

315 fslineno = _pytest._code.getfslineno(obj) 

316 assert isinstance(fslineno[1], int), obj 

317 return fslineno 

318 

319 

320def getimfunc(func): 

321 try: 

322 return func.__func__ 

323 except AttributeError: 

324 return func 

325 

326 

327def safe_getattr(object: Any, name: str, default: Any) -> Any: 

328 """ Like getattr but return default upon any Exception or any OutcomeException. 

329 

330 Attribute access can potentially fail for 'evil' Python objects. 

331 See issue #214. 

332 It catches OutcomeException because of #2490 (issue #580), new outcomes are derived from BaseException 

333 instead of Exception (for more details check #2707) 

334 """ 

335 try: 

336 return getattr(object, name, default) 

337 except TEST_OUTCOME: 

338 return default 

339 

340 

341def safe_isclass(obj: object) -> bool: 

342 """Ignore any exception via isinstance on Python 3.""" 

343 try: 

344 return inspect.isclass(obj) 

345 except Exception: 

346 return False 

347 

348 

349COLLECT_FAKEMODULE_ATTRIBUTES = ( 

350 "Collector", 

351 "Module", 

352 "Function", 

353 "Instance", 

354 "Session", 

355 "Item", 

356 "Class", 

357 "File", 

358 "_fillfuncargs", 

359) 

360 

361 

362def _setup_collect_fakemodule() -> None: 

363 from types import ModuleType 

364 import pytest 

365 

366 # Types ignored because the module is created dynamically. 

367 pytest.collect = ModuleType("pytest.collect") # type: ignore 

368 pytest.collect.__all__ = [] # type: ignore # used for setns 

369 for attr_name in COLLECT_FAKEMODULE_ATTRIBUTES: 

370 setattr(pytest.collect, attr_name, getattr(pytest, attr_name)) # type: ignore 

371 

372 

373class CaptureIO(io.TextIOWrapper): 

374 def __init__(self) -> None: 

375 super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True) 

376 

377 def getvalue(self) -> str: 

378 assert isinstance(self.buffer, io.BytesIO) 

379 return self.buffer.getvalue().decode("UTF-8") 

380 

381 

382if sys.version_info < (3, 5, 2): 

383 

384 def overload(f): # noqa: F811 

385 return f 

386 

387 

388if getattr(attr, "__version_info__", ()) >= (19, 2): 

389 ATTRS_EQ_FIELD = "eq" 

390else: 

391 ATTRS_EQ_FIELD = "cmp" 

392 

393 

394if sys.version_info >= (3, 8): 

395 from functools import cached_property 

396else: 

397 

398 class cached_property(Generic[_S, _T]): 

399 __slots__ = ("func", "__doc__") 

400 

401 def __init__(self, func: Callable[[_S], _T]) -> None: 

402 self.func = func 

403 self.__doc__ = func.__doc__ 

404 

405 @overload 

406 def __get__( 

407 self, instance: None, owner: Optional["Type[_S]"] = ... 

408 ) -> "cached_property[_S, _T]": 

409 raise NotImplementedError() 

410 

411 @overload # noqa: F811 

412 def __get__( # noqa: F811 

413 self, instance: _S, owner: Optional["Type[_S]"] = ... 

414 ) -> _T: 

415 raise NotImplementedError() 

416 

417 def __get__(self, instance, owner=None): # noqa: F811 

418 if instance is None: 

419 return self 

420 value = instance.__dict__[self.func.__name__] = self.func(instance) 

421 return value