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