332 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			332 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
"""
 | 
						|
python version compatibility code
 | 
						|
"""
 | 
						|
from __future__ import absolute_import, division, print_function
 | 
						|
 | 
						|
import codecs
 | 
						|
import functools
 | 
						|
import inspect
 | 
						|
import re
 | 
						|
import sys
 | 
						|
 | 
						|
import py
 | 
						|
 | 
						|
import _pytest
 | 
						|
from _pytest.outcomes import TEST_OUTCOME
 | 
						|
 | 
						|
try:
 | 
						|
    import enum
 | 
						|
except ImportError:  # pragma: no cover
 | 
						|
    # Only available in Python 3.4+ or as a backport
 | 
						|
    enum = None
 | 
						|
 | 
						|
 | 
						|
_PY3 = sys.version_info > (3, 0)
 | 
						|
_PY2 = not _PY3
 | 
						|
 | 
						|
 | 
						|
if _PY3:
 | 
						|
    from inspect import signature, Parameter as Parameter
 | 
						|
else:
 | 
						|
    from funcsigs import signature, Parameter as Parameter
 | 
						|
 | 
						|
 | 
						|
NoneType = type(None)
 | 
						|
NOTSET = object()
 | 
						|
 | 
						|
PY35 = sys.version_info[:2] >= (3, 5)
 | 
						|
PY36 = sys.version_info[:2] >= (3, 6)
 | 
						|
MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError'
 | 
						|
 | 
						|
if _PY3:
 | 
						|
    from collections.abc import MutableMapping as MappingMixin  # noqa
 | 
						|
    from collections.abc import Sequence  # noqa
 | 
						|
else:
 | 
						|
    # those raise DeprecationWarnings in Python >=3.7
 | 
						|
    from collections import MutableMapping as MappingMixin  # noqa
 | 
						|
    from collections import Sequence  # noqa
 | 
						|
 | 
						|
 | 
						|
def _format_args(func):
 | 
						|
    return str(signature(func))
 | 
						|
 | 
						|
 | 
						|
isfunction = inspect.isfunction
 | 
						|
isclass = inspect.isclass
 | 
						|
# used to work around a python2 exception info leak
 | 
						|
exc_clear = getattr(sys, 'exc_clear', lambda: None)
 | 
						|
# The type of re.compile objects is not exposed in Python.
 | 
						|
REGEX_TYPE = type(re.compile(''))
 | 
						|
 | 
						|
 | 
						|
def is_generator(func):
 | 
						|
    genfunc = inspect.isgeneratorfunction(func)
 | 
						|
    return genfunc and not iscoroutinefunction(func)
 | 
						|
 | 
						|
 | 
						|
def iscoroutinefunction(func):
 | 
						|
    """Return True if func is a decorated coroutine function.
 | 
						|
 | 
						|
    Note: copied and modified from Python 3.5's builtin couroutines.py to avoid import asyncio directly,
 | 
						|
    which in turns also initializes the "logging" module as side-effect (see issue #8).
 | 
						|
    """
 | 
						|
    return (getattr(func, '_is_coroutine', False) or
 | 
						|
            (hasattr(inspect, 'iscoroutinefunction') and inspect.iscoroutinefunction(func)))
 | 
						|
 | 
						|
 | 
						|
def getlocation(function, curdir):
 | 
						|
    fn = py.path.local(inspect.getfile(function))
 | 
						|
    lineno = py.builtin._getcode(function).co_firstlineno
 | 
						|
    if fn.relto(curdir):
 | 
						|
        fn = fn.relto(curdir)
 | 
						|
    return "%s:%d" % (fn, lineno + 1)
 | 
						|
 | 
						|
 | 
						|
def num_mock_patch_args(function):
 | 
						|
    """ return number of arguments used up by mock arguments (if any) """
 | 
						|
    patchings = getattr(function, "patchings", None)
 | 
						|
    if not patchings:
 | 
						|
        return 0
 | 
						|
    mock_modules = [sys.modules.get("mock"), sys.modules.get("unittest.mock")]
 | 
						|
    if any(mock_modules):
 | 
						|
        sentinels = [m.DEFAULT for m in mock_modules if m is not None]
 | 
						|
        return len([p for p in patchings
 | 
						|
                    if not p.attribute_name and p.new in sentinels])
 | 
						|
    return len(patchings)
 | 
						|
 | 
						|
 | 
						|
def getfuncargnames(function, is_method=False, cls=None):
 | 
						|
    """Returns the names of a function's mandatory arguments.
 | 
						|
 | 
						|
    This should return the names of all function arguments that:
 | 
						|
        * Aren't bound to an instance or type as in instance or class methods.
 | 
						|
        * Don't have default values.
 | 
						|
        * Aren't bound with functools.partial.
 | 
						|
        * Aren't replaced with mocks.
 | 
						|
 | 
						|
    The is_method and cls arguments indicate that the function should
 | 
						|
    be treated as a bound method even though it's not unless, only in
 | 
						|
    the case of cls, the function is a static method.
 | 
						|
 | 
						|
    @RonnyPfannschmidt: This function should be refactored when we
 | 
						|
    revisit fixtures. The fixture mechanism should ask the node for
 | 
						|
    the fixture names, and not try to obtain directly from the
 | 
						|
    function object well after collection has occurred.
 | 
						|
 | 
						|
    """
 | 
						|
    # The parameters attribute of a Signature object contains an
 | 
						|
    # ordered mapping of parameter names to Parameter instances.  This
 | 
						|
    # creates a tuple of the names of the parameters that don't have
 | 
						|
    # defaults.
 | 
						|
    arg_names = tuple(p.name for p in signature(function).parameters.values()
 | 
						|
                      if (p.kind is Parameter.POSITIONAL_OR_KEYWORD or
 | 
						|
                          p.kind is Parameter.KEYWORD_ONLY) and
 | 
						|
                      p.default is Parameter.empty)
 | 
						|
    # If this function should be treated as a bound method even though
 | 
						|
    # it's passed as an unbound method or function, remove the first
 | 
						|
    # parameter name.
 | 
						|
    if (is_method or
 | 
						|
        (cls and not isinstance(cls.__dict__.get(function.__name__, None),
 | 
						|
                                staticmethod))):
 | 
						|
        arg_names = arg_names[1:]
 | 
						|
    # Remove any names that will be replaced with mocks.
 | 
						|
    if hasattr(function, "__wrapped__"):
 | 
						|
        arg_names = arg_names[num_mock_patch_args(function):]
 | 
						|
    return arg_names
 | 
						|
 | 
						|
 | 
						|
if _PY3:
 | 
						|
    STRING_TYPES = bytes, str
 | 
						|
    UNICODE_TYPES = str,
 | 
						|
 | 
						|
    if PY35:
 | 
						|
        def _bytes_to_ascii(val):
 | 
						|
            return val.decode('ascii', 'backslashreplace')
 | 
						|
    else:
 | 
						|
        def _bytes_to_ascii(val):
 | 
						|
            if val:
 | 
						|
                # source: http://goo.gl/bGsnwC
 | 
						|
                encoded_bytes, _ = codecs.escape_encode(val)
 | 
						|
                return encoded_bytes.decode('ascii')
 | 
						|
            else:
 | 
						|
                # empty bytes crashes codecs.escape_encode (#1087)
 | 
						|
                return ''
 | 
						|
 | 
						|
    def ascii_escaped(val):
 | 
						|
        """If val is pure ascii, returns it as a str().  Otherwise, escapes
 | 
						|
        bytes objects into a sequence of escaped bytes:
 | 
						|
 | 
						|
        b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
 | 
						|
 | 
						|
        and escapes unicode objects into a sequence of escaped unicode
 | 
						|
        ids, e.g.:
 | 
						|
 | 
						|
        '4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
 | 
						|
 | 
						|
        note:
 | 
						|
           the obvious "v.decode('unicode-escape')" will return
 | 
						|
           valid utf-8 unicode if it finds them in bytes, but we
 | 
						|
           want to return escaped bytes for any byte, even if they match
 | 
						|
           a utf-8 string.
 | 
						|
 | 
						|
        """
 | 
						|
        if isinstance(val, bytes):
 | 
						|
            return _bytes_to_ascii(val)
 | 
						|
        else:
 | 
						|
            return val.encode('unicode_escape').decode('ascii')
 | 
						|
else:
 | 
						|
    STRING_TYPES = bytes, str, unicode
 | 
						|
    UNICODE_TYPES = unicode,
 | 
						|
 | 
						|
    def ascii_escaped(val):
 | 
						|
        """In py2 bytes and str are the same type, so return if it's a bytes
 | 
						|
        object, return it unchanged if it is a full ascii string,
 | 
						|
        otherwise escape it into its binary form.
 | 
						|
 | 
						|
        If it's a unicode string, change the unicode characters into
 | 
						|
        unicode escapes.
 | 
						|
 | 
						|
        """
 | 
						|
        if isinstance(val, bytes):
 | 
						|
            try:
 | 
						|
                return val.encode('ascii')
 | 
						|
            except UnicodeDecodeError:
 | 
						|
                return val.encode('string-escape')
 | 
						|
        else:
 | 
						|
            return val.encode('unicode-escape')
 | 
						|
 | 
						|
 | 
						|
def get_real_func(obj):
 | 
						|
    """ gets the real function object of the (possibly) wrapped object by
 | 
						|
    functools.wraps or functools.partial.
 | 
						|
    """
 | 
						|
    start_obj = obj
 | 
						|
    for i in range(100):
 | 
						|
        new_obj = getattr(obj, '__wrapped__', None)
 | 
						|
        if new_obj is None:
 | 
						|
            break
 | 
						|
        obj = new_obj
 | 
						|
    else:
 | 
						|
        raise ValueError(
 | 
						|
            ("could not find real function of {start}"
 | 
						|
             "\nstopped at {current}").format(
 | 
						|
                start=py.io.saferepr(start_obj),
 | 
						|
                current=py.io.saferepr(obj)))
 | 
						|
    if isinstance(obj, functools.partial):
 | 
						|
        obj = obj.func
 | 
						|
    return obj
 | 
						|
 | 
						|
 | 
						|
def getfslineno(obj):
 | 
						|
    # xxx let decorators etc specify a sane ordering
 | 
						|
    obj = get_real_func(obj)
 | 
						|
    if hasattr(obj, 'place_as'):
 | 
						|
        obj = obj.place_as
 | 
						|
    fslineno = _pytest._code.getfslineno(obj)
 | 
						|
    assert isinstance(fslineno[1], int), obj
 | 
						|
    return fslineno
 | 
						|
 | 
						|
 | 
						|
def getimfunc(func):
 | 
						|
    try:
 | 
						|
        return func.__func__
 | 
						|
    except AttributeError:
 | 
						|
        return func
 | 
						|
 | 
						|
 | 
						|
def safe_getattr(object, name, default):
 | 
						|
    """ Like getattr but return default upon any Exception or any OutcomeException.
 | 
						|
 | 
						|
    Attribute access can potentially fail for 'evil' Python objects.
 | 
						|
    See issue #214.
 | 
						|
    It catches OutcomeException because of #2490 (issue #580), new outcomes are derived from BaseException
 | 
						|
    instead of Exception (for more details check #2707)
 | 
						|
    """
 | 
						|
    try:
 | 
						|
        return getattr(object, name, default)
 | 
						|
    except TEST_OUTCOME:
 | 
						|
        return default
 | 
						|
 | 
						|
 | 
						|
def _is_unittest_unexpected_success_a_failure():
 | 
						|
    """Return if the test suite should fail if a @expectedFailure unittest test PASSES.
 | 
						|
 | 
						|
    From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
 | 
						|
        Changed in version 3.4: Returns False if there were any
 | 
						|
        unexpectedSuccesses from tests marked with the expectedFailure() decorator.
 | 
						|
    """
 | 
						|
    return sys.version_info >= (3, 4)
 | 
						|
 | 
						|
 | 
						|
if _PY3:
 | 
						|
    def safe_str(v):
 | 
						|
        """returns v as string"""
 | 
						|
        return str(v)
 | 
						|
else:
 | 
						|
    def safe_str(v):
 | 
						|
        """returns v as string, converting to ascii if necessary"""
 | 
						|
        try:
 | 
						|
            return str(v)
 | 
						|
        except UnicodeError:
 | 
						|
            if not isinstance(v, unicode):
 | 
						|
                v = unicode(v)
 | 
						|
            errors = 'replace'
 | 
						|
            return v.encode('utf-8', errors)
 | 
						|
 | 
						|
 | 
						|
COLLECT_FAKEMODULE_ATTRIBUTES = (
 | 
						|
    'Collector',
 | 
						|
    'Module',
 | 
						|
    'Generator',
 | 
						|
    'Function',
 | 
						|
    'Instance',
 | 
						|
    'Session',
 | 
						|
    'Item',
 | 
						|
    'Class',
 | 
						|
    'File',
 | 
						|
    '_fillfuncargs',
 | 
						|
)
 | 
						|
 | 
						|
 | 
						|
def _setup_collect_fakemodule():
 | 
						|
    from types import ModuleType
 | 
						|
    import pytest
 | 
						|
    pytest.collect = ModuleType('pytest.collect')
 | 
						|
    pytest.collect.__all__ = []  # used for setns
 | 
						|
    for attr in COLLECT_FAKEMODULE_ATTRIBUTES:
 | 
						|
        setattr(pytest.collect, attr, getattr(pytest, attr))
 | 
						|
 | 
						|
 | 
						|
if _PY2:
 | 
						|
    # Without this the test_dupfile_on_textio will fail, otherwise CaptureIO could directly inherit from StringIO.
 | 
						|
    from py.io import TextIO
 | 
						|
 | 
						|
    class CaptureIO(TextIO):
 | 
						|
 | 
						|
        @property
 | 
						|
        def encoding(self):
 | 
						|
            return getattr(self, '_encoding', 'UTF-8')
 | 
						|
 | 
						|
else:
 | 
						|
    import io
 | 
						|
 | 
						|
    class CaptureIO(io.TextIOWrapper):
 | 
						|
        def __init__(self):
 | 
						|
            super(CaptureIO, self).__init__(
 | 
						|
                io.BytesIO(),
 | 
						|
                encoding='UTF-8', newline='', write_through=True,
 | 
						|
            )
 | 
						|
 | 
						|
        def getvalue(self):
 | 
						|
            return self.buffer.getvalue().decode('UTF-8')
 | 
						|
 | 
						|
 | 
						|
class FuncargnamesCompatAttr(object):
 | 
						|
    """ helper class so that Metafunc, Function and FixtureRequest
 | 
						|
    don't need to each define the "funcargnames" compatibility attribute.
 | 
						|
    """
 | 
						|
    @property
 | 
						|
    def funcargnames(self):
 | 
						|
        """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
 | 
						|
        return self.fixturenames
 |