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