compat.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. # -*- coding: utf-8 -*-
  2. """
  3. python version compatibility code
  4. """
  5. from __future__ import absolute_import
  6. from __future__ import division
  7. from __future__ import print_function
  8. import codecs
  9. import functools
  10. import inspect
  11. import re
  12. import sys
  13. from contextlib import contextmanager
  14. import attr
  15. import py
  16. import six
  17. from six import text_type
  18. import _pytest
  19. from _pytest._io.saferepr import saferepr
  20. from _pytest.outcomes import fail
  21. from _pytest.outcomes import TEST_OUTCOME
  22. try:
  23. import enum
  24. except ImportError: # pragma: no cover
  25. # Only available in Python 3.4+ or as a backport
  26. enum = None
  27. _PY3 = sys.version_info > (3, 0)
  28. _PY2 = not _PY3
  29. if _PY3:
  30. from inspect import signature, Parameter as Parameter
  31. else:
  32. from funcsigs import signature, Parameter as Parameter
  33. NOTSET = object()
  34. PY35 = sys.version_info[:2] >= (3, 5)
  35. PY36 = sys.version_info[:2] >= (3, 6)
  36. MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError"
  37. if _PY3:
  38. from collections.abc import MutableMapping as MappingMixin
  39. from collections.abc import Iterable, Mapping, Sequence, Sized
  40. else:
  41. # those raise DeprecationWarnings in Python >=3.7
  42. from collections import MutableMapping as MappingMixin # noqa
  43. from collections import Iterable, Mapping, Sequence, Sized # noqa
  44. if sys.version_info >= (3, 4):
  45. from importlib.util import spec_from_file_location
  46. else:
  47. def spec_from_file_location(*_, **__):
  48. return None
  49. if sys.version_info >= (3, 8):
  50. from importlib import metadata as importlib_metadata # noqa
  51. else:
  52. import importlib_metadata # noqa
  53. def _format_args(func):
  54. return str(signature(func))
  55. isfunction = inspect.isfunction
  56. isclass = inspect.isclass
  57. # used to work around a python2 exception info leak
  58. exc_clear = getattr(sys, "exc_clear", lambda: None)
  59. # The type of re.compile objects is not exposed in Python.
  60. REGEX_TYPE = type(re.compile(""))
  61. def is_generator(func):
  62. genfunc = inspect.isgeneratorfunction(func)
  63. return genfunc and not iscoroutinefunction(func)
  64. def iscoroutinefunction(func):
  65. """Return True if func is a decorated coroutine function.
  66. Note: copied and modified from Python 3.5's builtin couroutines.py to avoid import asyncio directly,
  67. which in turns also initializes the "logging" module as side-effect (see issue #8).
  68. """
  69. return getattr(func, "_is_coroutine", False) or (
  70. hasattr(inspect, "iscoroutinefunction") and inspect.iscoroutinefunction(func)
  71. )
  72. def getlocation(function, curdir):
  73. function = get_real_func(function)
  74. fn = py.path.local(inspect.getfile(function))
  75. lineno = function.__code__.co_firstlineno
  76. if fn.relto(curdir):
  77. fn = fn.relto(curdir)
  78. return "%s:%d" % (fn, lineno + 1)
  79. def num_mock_patch_args(function):
  80. """ return number of arguments used up by mock arguments (if any) """
  81. patchings = getattr(function, "patchings", None)
  82. if not patchings:
  83. return 0
  84. mock_modules = [sys.modules.get("mock"), sys.modules.get("unittest.mock")]
  85. if any(mock_modules):
  86. sentinels = [m.DEFAULT for m in mock_modules if m is not None]
  87. return len(
  88. [p for p in patchings if not p.attribute_name and p.new in sentinels]
  89. )
  90. return len(patchings)
  91. def getfuncargnames(function, is_method=False, cls=None):
  92. """Returns the names of a function's mandatory arguments.
  93. This should return the names of all function arguments that:
  94. * Aren't bound to an instance or type as in instance or class methods.
  95. * Don't have default values.
  96. * Aren't bound with functools.partial.
  97. * Aren't replaced with mocks.
  98. The is_method and cls arguments indicate that the function should
  99. be treated as a bound method even though it's not unless, only in
  100. the case of cls, the function is a static method.
  101. @RonnyPfannschmidt: This function should be refactored when we
  102. revisit fixtures. The fixture mechanism should ask the node for
  103. the fixture names, and not try to obtain directly from the
  104. function object well after collection has occurred.
  105. """
  106. # The parameters attribute of a Signature object contains an
  107. # ordered mapping of parameter names to Parameter instances. This
  108. # creates a tuple of the names of the parameters that don't have
  109. # defaults.
  110. try:
  111. parameters = signature(function).parameters
  112. except (ValueError, TypeError) as e:
  113. fail(
  114. "Could not determine arguments of {!r}: {}".format(function, e),
  115. pytrace=False,
  116. )
  117. arg_names = tuple(
  118. p.name
  119. for p in parameters.values()
  120. if (
  121. p.kind is Parameter.POSITIONAL_OR_KEYWORD
  122. or p.kind is Parameter.KEYWORD_ONLY
  123. )
  124. and p.default is Parameter.empty
  125. )
  126. # If this function should be treated as a bound method even though
  127. # it's passed as an unbound method or function, remove the first
  128. # parameter name.
  129. if is_method or (
  130. cls and not isinstance(cls.__dict__.get(function.__name__, None), staticmethod)
  131. ):
  132. arg_names = arg_names[1:]
  133. # Remove any names that will be replaced with mocks.
  134. if hasattr(function, "__wrapped__"):
  135. arg_names = arg_names[num_mock_patch_args(function) :]
  136. return arg_names
  137. @contextmanager
  138. def dummy_context_manager():
  139. """Context manager that does nothing, useful in situations where you might need an actual context manager or not
  140. depending on some condition. Using this allow to keep the same code"""
  141. yield
  142. def get_default_arg_names(function):
  143. # Note: this code intentionally mirrors the code at the beginning of getfuncargnames,
  144. # to get the arguments which were excluded from its result because they had default values
  145. return tuple(
  146. p.name
  147. for p in signature(function).parameters.values()
  148. if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY)
  149. and p.default is not Parameter.empty
  150. )
  151. _non_printable_ascii_translate_table = {
  152. i: u"\\x{:02x}".format(i) for i in range(128) if i not in range(32, 127)
  153. }
  154. _non_printable_ascii_translate_table.update(
  155. {ord("\t"): u"\\t", ord("\r"): u"\\r", ord("\n"): u"\\n"}
  156. )
  157. def _translate_non_printable(s):
  158. return s.translate(_non_printable_ascii_translate_table)
  159. if _PY3:
  160. STRING_TYPES = bytes, str
  161. UNICODE_TYPES = six.text_type
  162. if PY35:
  163. def _bytes_to_ascii(val):
  164. return val.decode("ascii", "backslashreplace")
  165. else:
  166. def _bytes_to_ascii(val):
  167. if val:
  168. # source: http://goo.gl/bGsnwC
  169. encoded_bytes, _ = codecs.escape_encode(val)
  170. return encoded_bytes.decode("ascii")
  171. else:
  172. # empty bytes crashes codecs.escape_encode (#1087)
  173. return ""
  174. def ascii_escaped(val):
  175. """If val is pure ascii, returns it as a str(). Otherwise, escapes
  176. bytes objects into a sequence of escaped bytes:
  177. b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
  178. and escapes unicode objects into a sequence of escaped unicode
  179. ids, e.g.:
  180. '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
  181. note:
  182. the obvious "v.decode('unicode-escape')" will return
  183. valid utf-8 unicode if it finds them in bytes, but we
  184. want to return escaped bytes for any byte, even if they match
  185. a utf-8 string.
  186. """
  187. if isinstance(val, bytes):
  188. ret = _bytes_to_ascii(val)
  189. else:
  190. ret = val
  191. return ret
  192. else:
  193. STRING_TYPES = six.string_types
  194. UNICODE_TYPES = six.text_type
  195. def ascii_escaped(val):
  196. """In py2 bytes and str are the same type, so return if it's a bytes
  197. object, return it unchanged if it is a full ascii string,
  198. otherwise escape it into its binary form.
  199. If it's a unicode string, change the unicode characters into
  200. unicode escapes.
  201. """
  202. if isinstance(val, bytes):
  203. try:
  204. ret = val.decode("utf-8")
  205. except UnicodeDecodeError:
  206. ret = val.decode("utf-8", "ignore")
  207. else:
  208. ret = val.encode("utf-8", "replace").decode("utf-8")
  209. return ret
  210. class _PytestWrapper(object):
  211. """Dummy wrapper around a function object for internal use only.
  212. Used to correctly unwrap the underlying function object
  213. when we are creating fixtures, because we wrap the function object ourselves with a decorator
  214. to issue warnings when the fixture function is called directly.
  215. """
  216. def __init__(self, obj):
  217. self.obj = obj
  218. def get_real_func(obj):
  219. """ gets the real function object of the (possibly) wrapped object by
  220. functools.wraps or functools.partial.
  221. """
  222. start_obj = obj
  223. for i in range(100):
  224. # __pytest_wrapped__ is set by @pytest.fixture when wrapping the fixture function
  225. # to trigger a warning if it gets called directly instead of by pytest: we don't
  226. # want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774)
  227. new_obj = getattr(obj, "__pytest_wrapped__", None)
  228. if isinstance(new_obj, _PytestWrapper):
  229. obj = new_obj.obj
  230. break
  231. new_obj = getattr(obj, "__wrapped__", None)
  232. if new_obj is None:
  233. break
  234. obj = new_obj
  235. else:
  236. raise ValueError(
  237. ("could not find real function of {start}\nstopped at {current}").format(
  238. start=saferepr(start_obj), current=saferepr(obj)
  239. )
  240. )
  241. if isinstance(obj, functools.partial):
  242. obj = obj.func
  243. return obj
  244. def get_real_method(obj, holder):
  245. """
  246. Attempts to obtain the real function object that might be wrapping ``obj``, while at the same time
  247. returning a bound method to ``holder`` if the original object was a bound method.
  248. """
  249. try:
  250. is_method = hasattr(obj, "__func__")
  251. obj = get_real_func(obj)
  252. except Exception:
  253. return obj
  254. if is_method and hasattr(obj, "__get__") and callable(obj.__get__):
  255. obj = obj.__get__(holder)
  256. return obj
  257. def getfslineno(obj):
  258. # xxx let decorators etc specify a sane ordering
  259. obj = get_real_func(obj)
  260. if hasattr(obj, "place_as"):
  261. obj = obj.place_as
  262. fslineno = _pytest._code.getfslineno(obj)
  263. assert isinstance(fslineno[1], int), obj
  264. return fslineno
  265. def getimfunc(func):
  266. try:
  267. return func.__func__
  268. except AttributeError:
  269. return func
  270. def safe_getattr(object, name, default):
  271. """ Like getattr but return default upon any Exception or any OutcomeException.
  272. Attribute access can potentially fail for 'evil' Python objects.
  273. See issue #214.
  274. It catches OutcomeException because of #2490 (issue #580), new outcomes are derived from BaseException
  275. instead of Exception (for more details check #2707)
  276. """
  277. try:
  278. return getattr(object, name, default)
  279. except TEST_OUTCOME:
  280. return default
  281. def safe_isclass(obj):
  282. """Ignore any exception via isinstance on Python 3."""
  283. try:
  284. return isclass(obj)
  285. except Exception:
  286. return False
  287. def _is_unittest_unexpected_success_a_failure():
  288. """Return if the test suite should fail if an @expectedFailure unittest test PASSES.
  289. From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
  290. Changed in version 3.4: Returns False if there were any
  291. unexpectedSuccesses from tests marked with the expectedFailure() decorator.
  292. """
  293. return sys.version_info >= (3, 4)
  294. if _PY3:
  295. def safe_str(v):
  296. """returns v as string"""
  297. try:
  298. return str(v)
  299. except UnicodeEncodeError:
  300. return str(v, encoding="utf-8")
  301. else:
  302. def safe_str(v):
  303. """returns v as string, converting to utf-8 if necessary"""
  304. try:
  305. return str(v)
  306. except UnicodeError:
  307. if not isinstance(v, text_type):
  308. v = text_type(v)
  309. errors = "replace"
  310. return v.encode("utf-8", errors)
  311. COLLECT_FAKEMODULE_ATTRIBUTES = (
  312. "Collector",
  313. "Module",
  314. "Function",
  315. "Instance",
  316. "Session",
  317. "Item",
  318. "Class",
  319. "File",
  320. "_fillfuncargs",
  321. )
  322. def _setup_collect_fakemodule():
  323. from types import ModuleType
  324. import pytest
  325. pytest.collect = ModuleType("pytest.collect")
  326. pytest.collect.__all__ = [] # used for setns
  327. for attribute in COLLECT_FAKEMODULE_ATTRIBUTES:
  328. setattr(pytest.collect, attribute, getattr(pytest, attribute))
  329. if _PY2:
  330. # Without this the test_dupfile_on_textio will fail, otherwise CaptureIO could directly inherit from StringIO.
  331. from py.io import TextIO
  332. class CaptureIO(TextIO):
  333. @property
  334. def encoding(self):
  335. return getattr(self, "_encoding", "UTF-8")
  336. else:
  337. import io
  338. class CaptureIO(io.TextIOWrapper):
  339. def __init__(self):
  340. super(CaptureIO, self).__init__(
  341. io.BytesIO(), encoding="UTF-8", newline="", write_through=True
  342. )
  343. def getvalue(self):
  344. return self.buffer.getvalue().decode("UTF-8")
  345. class FuncargnamesCompatAttr(object):
  346. """ helper class so that Metafunc, Function and FixtureRequest
  347. don't need to each define the "funcargnames" compatibility attribute.
  348. """
  349. @property
  350. def funcargnames(self):
  351. """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
  352. return self.fixturenames
  353. if six.PY2:
  354. def lru_cache(*_, **__):
  355. def dec(fn):
  356. return fn
  357. return dec
  358. else:
  359. from functools import lru_cache # noqa: F401
  360. if getattr(attr, "__version_info__", ()) >= (19, 2):
  361. ATTRS_EQ_FIELD = "eq"
  362. else:
  363. ATTRS_EQ_FIELD = "cmp"