Coverage for /usr/local/lib/python3.7/site-packages/_pytest/compat.py : 24%

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
22import attr
23import py
25import _pytest
26from _pytest._io.saferepr import saferepr
27from _pytest.outcomes import fail
28from _pytest.outcomes import TEST_OUTCOME
30if sys.version_info < (3, 5, 2):
31 TYPE_CHECKING = False # type: bool
32else:
33 from typing import TYPE_CHECKING
36if TYPE_CHECKING:
37 from typing import Type # noqa: F401 (used in type string)
40_T = TypeVar("_T")
41_S = TypeVar("_S")
44NOTSET = object()
46MODULE_NOT_FOUND_ERROR = (
47 "ModuleNotFoundError" if sys.version_info[:2] >= (3, 6) else "ImportError"
48)
51if sys.version_info >= (3, 8):
52 from importlib import metadata as importlib_metadata
53else:
54 import importlib_metadata # noqa: F401
57def _format_args(func: Callable[..., Any]) -> str:
58 return str(signature(func))
61# The type of re.compile objects is not exposed in Python.
62REGEX_TYPE = type(re.compile(""))
65if sys.version_info < (3, 6):
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)
74else:
75 fspath = os.fspath
78def is_generator(func: object) -> bool:
79 genfunc = inspect.isgeneratorfunction(func)
80 return genfunc and not iscoroutinefunction(func)
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.
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)
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)
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
113 mock_sentinel = getattr(sys.modules.get("mock"), "DEFAULT", object())
114 ut_mock_sentinel = getattr(sys.modules.get("unittest.mock"), "DEFAULT", object())
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 )
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.
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.
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.
145 The name parameter should be the original name in which the function was collected.
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 )
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__
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
189if sys.version_info < (3, 7):
191 @contextmanager
192 def nullcontext():
193 yield
196else:
197 from contextlib import nullcontext # noqa
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 )
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)
219def _translate_non_printable(s: str) -> str:
220 return s.translate(_non_printable_ascii_translate_table)
223STRING_TYPES = bytes, str
226def _bytes_to_ascii(val: bytes) -> str:
227 return val.decode("ascii", "backslashreplace")
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:
234 b'\xc3\xb4\xc5\xd6' -> '\\xc3\\xb4\\xc5\\xd6'
236 and escapes unicode objects into a sequence of escaped unicode
237 ids, e.g.:
239 '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
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.
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)
255@attr.s
256class _PytestWrapper:
257 """Dummy wrapper around a function object for internal use only.
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 """
264 obj = attr.ib()
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
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
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
320def getimfunc(func):
321 try:
322 return func.__func__
323 except AttributeError:
324 return func
327def safe_getattr(object: Any, name: str, default: Any) -> Any:
328 """ Like getattr but return default upon any Exception or any OutcomeException.
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
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
349COLLECT_FAKEMODULE_ATTRIBUTES = (
350 "Collector",
351 "Module",
352 "Function",
353 "Instance",
354 "Session",
355 "Item",
356 "Class",
357 "File",
358 "_fillfuncargs",
359)
362def _setup_collect_fakemodule() -> None:
363 from types import ModuleType
364 import pytest
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
373class CaptureIO(io.TextIOWrapper):
374 def __init__(self) -> None:
375 super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True)
377 def getvalue(self) -> str:
378 assert isinstance(self.buffer, io.BytesIO)
379 return self.buffer.getvalue().decode("UTF-8")
382if sys.version_info < (3, 5, 2):
384 def overload(f): # noqa: F811
385 return f
388if getattr(attr, "__version_info__", ()) >= (19, 2):
389 ATTRS_EQ_FIELD = "eq"
390else:
391 ATTRS_EQ_FIELD = "cmp"
394if sys.version_info >= (3, 8):
395 from functools import cached_property
396else:
398 class cached_property(Generic[_S, _T]):
399 __slots__ = ("func", "__doc__")
401 def __init__(self, func: Callable[[_S], _T]) -> None:
402 self.func = func
403 self.__doc__ = func.__doc__
405 @overload
406 def __get__(
407 self, instance: None, owner: Optional["Type[_S]"] = ...
408 ) -> "cached_property[_S, _T]":
409 raise NotImplementedError()
411 @overload # noqa: F811
412 def __get__( # noqa: F811
413 self, instance: _S, owner: Optional["Type[_S]"] = ...
414 ) -> _T:
415 raise NotImplementedError()
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