1"""
2python version compatibility code
3"""
4from __future__ import absolute_import, division, print_function
5import sys
6import inspect
7import types
8import re
9import functools
10
11import py
12
13import  _pytest
14
15
16
17try:
18    import enum
19except ImportError:  # pragma: no cover
20    # Only available in Python 3.4+ or as a backport
21    enum = None
22
23
24_PY3 = sys.version_info > (3, 0)
25_PY2 = not _PY3
26
27
28NoneType = type(None)
29NOTSET = object()
30
31PY35 = sys.version_info[:2] >= (3, 5)
32PY36 = sys.version_info[:2] >= (3, 6)
33MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError'
34
35if hasattr(inspect, 'signature'):
36    def _format_args(func):
37        return str(inspect.signature(func))
38else:
39    def _format_args(func):
40        return inspect.formatargspec(*inspect.getargspec(func))
41
42isfunction = inspect.isfunction
43isclass = inspect.isclass
44# used to work around a python2 exception info leak
45exc_clear = getattr(sys, 'exc_clear', lambda: None)
46# The type of re.compile objects is not exposed in Python.
47REGEX_TYPE = type(re.compile(''))
48
49
50def is_generator(func):
51    genfunc = inspect.isgeneratorfunction(func)
52    return genfunc and not iscoroutinefunction(func)
53
54
55def iscoroutinefunction(func):
56    """Return True if func is a decorated coroutine function.
57
58    Note: copied and modified from Python 3.5's builtin couroutines.py to avoid import asyncio directly,
59    which in turns also initializes the "logging" module as side-effect (see issue #8).
60    """
61    return (getattr(func, '_is_coroutine', False) or
62           (hasattr(inspect, 'iscoroutinefunction') and inspect.iscoroutinefunction(func)))
63
64
65def getlocation(function, curdir):
66    import inspect
67    fn = py.path.local(inspect.getfile(function))
68    lineno = py.builtin._getcode(function).co_firstlineno
69    if fn.relto(curdir):
70        fn = fn.relto(curdir)
71    return "%s:%d" %(fn, lineno+1)
72
73
74def num_mock_patch_args(function):
75    """ return number of arguments used up by mock arguments (if any) """
76    patchings = getattr(function, "patchings", None)
77    if not patchings:
78        return 0
79    mock = sys.modules.get("mock", sys.modules.get("unittest.mock", None))
80    if mock is not None:
81        return len([p for p in patchings
82                        if not p.attribute_name and p.new is mock.DEFAULT])
83    return len(patchings)
84
85
86def getfuncargnames(function, startindex=None):
87    # XXX merge with main.py's varnames
88    #assert not isclass(function)
89    realfunction = function
90    while hasattr(realfunction, "__wrapped__"):
91        realfunction = realfunction.__wrapped__
92    if startindex is None:
93        startindex = inspect.ismethod(function) and 1 or 0
94    if realfunction != function:
95        startindex += num_mock_patch_args(function)
96        function = realfunction
97    if isinstance(function, functools.partial):
98        argnames = inspect.getargs(_pytest._code.getrawcode(function.func))[0]
99        partial = function
100        argnames = argnames[len(partial.args):]
101        if partial.keywords:
102            for kw in partial.keywords:
103                argnames.remove(kw)
104    else:
105        argnames = inspect.getargs(_pytest._code.getrawcode(function))[0]
106    defaults = getattr(function, 'func_defaults',
107                       getattr(function, '__defaults__', None)) or ()
108    numdefaults = len(defaults)
109    if numdefaults:
110        return tuple(argnames[startindex:-numdefaults])
111    return tuple(argnames[startindex:])
112
113
114
115if  sys.version_info[:2] == (2, 6):
116    def isclass(object):
117        """ Return true if the object is a class. Overrides inspect.isclass for
118        python 2.6 because it will return True for objects which always return
119        something on __getattr__ calls (see #1035).
120        Backport of https://hg.python.org/cpython/rev/35bf8f7a8edc
121        """
122        return isinstance(object, (type, types.ClassType))
123
124
125if _PY3:
126    import codecs
127    imap = map
128    STRING_TYPES = bytes, str
129    UNICODE_TYPES = str,
130
131    def _escape_strings(val):
132        """If val is pure ascii, returns it as a str().  Otherwise, escapes
133        bytes objects into a sequence of escaped bytes:
134
135        b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
136
137        and escapes unicode objects into a sequence of escaped unicode
138        ids, e.g.:
139
140        '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
141
142        note:
143           the obvious "v.decode('unicode-escape')" will return
144           valid utf-8 unicode if it finds them in bytes, but we
145           want to return escaped bytes for any byte, even if they match
146           a utf-8 string.
147
148        """
149        if isinstance(val, bytes):
150            if val:
151                # source: http://goo.gl/bGsnwC
152                encoded_bytes, _ = codecs.escape_encode(val)
153                return encoded_bytes.decode('ascii')
154            else:
155                # empty bytes crashes codecs.escape_encode (#1087)
156                return ''
157        else:
158            return val.encode('unicode_escape').decode('ascii')
159else:
160    STRING_TYPES = bytes, str, unicode
161    UNICODE_TYPES = unicode,
162
163    from itertools import imap  # NOQA
164
165    def _escape_strings(val):
166        """In py2 bytes and str are the same type, so return if it's a bytes
167        object, return it unchanged if it is a full ascii string,
168        otherwise escape it into its binary form.
169
170        If it's a unicode string, change the unicode characters into
171        unicode escapes.
172
173        """
174        if isinstance(val, bytes):
175            try:
176                return val.encode('ascii')
177            except UnicodeDecodeError:
178                return val.encode('string-escape')
179        else:
180            return val.encode('unicode-escape')
181
182
183def get_real_func(obj):
184    """ gets the real function object of the (possibly) wrapped object by
185    functools.wraps or functools.partial.
186    """
187    start_obj = obj
188    for i in range(100):
189        new_obj = getattr(obj, '__wrapped__', None)
190        if new_obj is None:
191            break
192        obj = new_obj
193    else:
194        raise ValueError(
195            ("could not find real function of {start}"
196             "\nstopped at {current}").format(
197                start=py.io.saferepr(start_obj),
198                current=py.io.saferepr(obj)))
199    if isinstance(obj, functools.partial):
200        obj = obj.func
201    return obj
202
203
204def getfslineno(obj):
205    # xxx let decorators etc specify a sane ordering
206    obj = get_real_func(obj)
207    if hasattr(obj, 'place_as'):
208        obj = obj.place_as
209    fslineno = _pytest._code.getfslineno(obj)
210    assert isinstance(fslineno[1], int), obj
211    return fslineno
212
213
214def getimfunc(func):
215    try:
216        return func.__func__
217    except AttributeError:
218        try:
219            return func.im_func
220        except AttributeError:
221            return func
222
223
224def safe_getattr(object, name, default):
225    """ Like getattr but return default upon any Exception.
226
227    Attribute access can potentially fail for 'evil' Python objects.
228    See issue #214.
229    """
230    try:
231        return getattr(object, name, default)
232    except Exception:
233        return default
234
235
236def _is_unittest_unexpected_success_a_failure():
237    """Return if the test suite should fail if a @expectedFailure unittest test PASSES.
238
239    From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
240        Changed in version 3.4: Returns False if there were any
241        unexpectedSuccesses from tests marked with the expectedFailure() decorator.
242    """
243    return sys.version_info >= (3, 4)
244
245
246if _PY3:
247    def safe_str(v):
248        """returns v as string"""
249        return str(v)
250else:
251    def safe_str(v):
252        """returns v as string, converting to ascii if necessary"""
253        try:
254            return str(v)
255        except UnicodeError:
256            if not isinstance(v, unicode):
257                v = unicode(v)
258            errors = 'replace'
259            return v.encode('utf-8', errors)
260
261
262COLLECT_FAKEMODULE_ATTRIBUTES = (
263    'Collector',
264    'Module',
265    'Generator',
266    'Function',
267    'Instance',
268    'Session',
269    'Item',
270    'Class',
271    'File',
272    '_fillfuncargs',
273)
274
275
276def _setup_collect_fakemodule():
277    from types import ModuleType
278    import pytest
279    pytest.collect = ModuleType('pytest.collect')
280    pytest.collect.__all__ = []  # used for setns
281    for attr in COLLECT_FAKEMODULE_ATTRIBUTES:
282        setattr(pytest.collect, attr, getattr(pytest, attr))
283
284
285if _PY2:
286    from py.io import TextIO as CaptureIO
287else:
288    import io
289
290    class CaptureIO(io.TextIOWrapper):
291        def __init__(self):
292            super(CaptureIO, self).__init__(
293                io.BytesIO(),
294                encoding='UTF-8', newline='', write_through=True,
295            )
296
297        def getvalue(self):
298            return self.buffer.getvalue().decode('UTF-8')
299
300class FuncargnamesCompatAttr(object):
301    """ helper class so that Metafunc, Function and FixtureRequest
302    don't need to each define the "funcargnames" compatibility attribute.
303    """
304    @property
305    def funcargnames(self):
306        """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
307        return self.fixturenames
308