Exemplo n.º 1
0
def test_get_real_func():
    """Check that get_real_func correctly unwraps decorators until reaching the real function"""

    def decorator(f):
        @wraps(f)
        def inner():
            pass

        if six.PY2:
            inner.__wrapped__ = f
        return inner

    def func():
        pass

    wrapped_func = decorator(decorator(func))
    assert get_real_func(wrapped_func) is func

    wrapped_func2 = decorator(decorator(wrapped_func))
    assert get_real_func(wrapped_func2) is func

    # special case for __pytest_wrapped__ attribute: used to obtain the function up until the point
    # a function was wrapped by pytest itself
    wrapped_func2.__pytest_wrapped__ = _PytestWrapper(wrapped_func)
    assert get_real_func(wrapped_func2) is wrapped_func
Exemplo n.º 2
0
def test_get_real_func_partial() -> None:
    """Test get_real_func handles partial instances correctly"""
    def foo(x):
        return x

    assert get_real_func(foo) is foo
    assert get_real_func(partial(foo)) is foo
Exemplo n.º 3
0
def test_get_real_func():
    """Check that get_real_func correctly unwraps decorators until reaching the real function"""

    def decorator(f):
        @wraps(f)
        def inner():
            pass  # pragma: no cover

        if six.PY2:
            inner.__wrapped__ = f
        return inner

    def func():
        pass  # pragma: no cover

    wrapped_func = decorator(decorator(func))
    assert get_real_func(wrapped_func) is func

    wrapped_func2 = decorator(decorator(wrapped_func))
    assert get_real_func(wrapped_func2) is func

    # special case for __pytest_wrapped__ attribute: used to obtain the function up until the point
    # a function was wrapped by pytest itself
    wrapped_func2.__pytest_wrapped__ = _PytestWrapper(wrapped_func)
    assert get_real_func(wrapped_func2) is wrapped_func
Exemplo n.º 4
0
def pytest_pycollect_makeitem(collector, name, obj):
    outcome = yield
    res = outcome.get_result()
    if res is not None:
        return
    # nothing was collected elsewhere, let's do it here
    if safe_isclass(obj):
        if collector.istestclass(obj, name):
            outcome.force_result(Class(name, parent=collector))
    elif collector.istestfunction(obj, name):
        # mock seems to store unbound methods (issue473), normalize it
        obj = getattr(obj, "__func__", obj)
        # We need to try and unwrap the function if it's a functools.partial
        # or a funtools.wrapped.
        # We musn't if it's been wrapped with mock.patch (python 2 only)
        if not (isfunction(obj) or isfunction(get_real_func(obj))):
            filename, lineno = getfslineno(obj)
            warnings.warn_explicit(
                message=PytestWarning(
                    "cannot collect %r because it is not a function." % name
                ),
                category=None,
                filename=str(filename),
                lineno=lineno + 1,
            )
        elif getattr(obj, "__test__", True):
            if is_generator(obj):
                res = Function(name, parent=collector)
                reason = deprecated.YIELD_TESTS.format(name=name)
                res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
                res.warn(PytestWarning(reason))
            else:
                res = list(collector._genfunctions(name, obj))
            outcome.force_result(res)
Exemplo n.º 5
0
def pytest_pycollect_makeitem(collector, name, obj):
    outcome = yield
    res = outcome.get_result()
    if res is not None:
        return
    # nothing was collected elsewhere, let's do it here
    if safe_isclass(obj):
        if collector.istestclass(obj, name):
            outcome.force_result(Class(name, parent=collector))
    elif collector.istestfunction(obj, name):
        # mock seems to store unbound methods (issue473), normalize it
        obj = getattr(obj, "__func__", obj)
        # We need to try and unwrap the function if it's a functools.partial
        # or a funtools.wrapped.
        # We musn't if it's been wrapped with mock.patch (python 2 only)
        if not (isfunction(obj) or isfunction(get_real_func(obj))):
            filename, lineno = getfslineno(obj)
            warnings.warn_explicit(
                message=PytestWarning(
                    "cannot collect %r because it is not a function." % name
                ),
                category=None,
                filename=str(filename),
                lineno=lineno + 1,
            )
        elif getattr(obj, "__test__", True):
            if is_generator(obj):
                res = Function(name, parent=collector)
                reason = deprecated.YIELD_TESTS.format(name=name)
                res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
                res.warn(PytestWarning(reason))
            else:
                res = list(collector._genfunctions(name, obj))
            outcome.force_result(res)
Exemplo n.º 6
0
def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]:
    """ Return source location (path, lineno) for the given object.
    If the source cannot be determined return ("", -1).

    The line number is 0-based.
    """
    from .code import Code

    # xxx let decorators etc specify a sane ordering
    # NOTE: this used to be done in _pytest.compat.getfslineno, initially added
    #       in 6ec13a2b9.  It ("place_as") appears to be something very custom.
    obj = get_real_func(obj)
    if hasattr(obj, "place_as"):
        obj = obj.place_as

    try:
        code = Code(obj)
    except TypeError:
        try:
            fn = inspect.getsourcefile(obj) or inspect.getfile(obj)
        except TypeError:
            return "", -1

        fspath = fn and py.path.local(fn) or ""
        lineno = -1
        if fspath:
            try:
                _, lineno = findsource(obj)
            except IOError:
                pass
        return fspath, lineno
    else:
        return code.path, code.firstlineno
Exemplo n.º 7
0
def test_source_with_decorator() -> None:
    """Test behavior with Source / Code().source with regard to decorators."""
    from _pytest.compat import get_real_func

    @pytest.mark.foo
    def deco_mark():
        assert False

    src = inspect.getsource(deco_mark)
    assert textwrap.indent(str(Source(deco_mark)), "    ") + "\n" == src
    assert src.startswith("    @pytest.mark.foo")

    @pytest.fixture
    def deco_fixture():
        assert False

    src = inspect.getsource(deco_fixture)
    assert src == "    @pytest.fixture\n    def deco_fixture():\n        assert False\n"
    # currently Source does not unwrap decorators, testing the
    # existing behavior here for explicitness, but perhaps we should revisit/change this
    # in the future
    assert str(Source(deco_fixture)).startswith("@functools.wraps(function)")
    assert (
        textwrap.indent(str(Source(get_real_func(deco_fixture))), "    ") + "\n" == src
    )
Exemplo n.º 8
0
def pytest_pycollect_makeitem(collector, name, obj):
    outcome = yield
    res = outcome.get_result()
    if res is not None:
        return
    # nothing was collected elsewhere, let's do it here
    if isclass(obj):
        if collector.istestclass(obj, name):
            Class = collector._getcustomclass("Class")
            outcome.force_result(Class(name, parent=collector))
    elif collector.istestfunction(obj, name):
        # mock seems to store unbound methods (issue473), normalize it
        obj = getattr(obj, "__func__", obj)
        # We need to try and unwrap the function if it's a functools.partial
        # or a funtools.wrapped.
        # We musn't if it's been wrapped with mock.patch (python 2 only)
        if not (isfunction(obj) or isfunction(get_real_func(obj))):
            collector.warn(
                code="C2",
                message="cannot collect %r because it is not a function." % name,
            )
        elif getattr(obj, "__test__", True):
            if is_generator(obj):
                res = Generator(name, parent=collector)
            else:
                res = list(collector._genfunctions(name, obj))
            outcome.force_result(res)
Exemplo n.º 9
0
def pytest_pycollect_makeitem(collector, name, obj):
    outcome = yield
    res = outcome.get_result()
    if res is not None:
        return
    # nothing was collected elsewhere, let's do it here
    if isclass(obj):
        if collector.istestclass(obj, name):
            Class = collector._getcustomclass("Class")
            outcome.force_result(Class(name, parent=collector))
    elif collector.istestfunction(obj, name):
        # mock seems to store unbound methods (issue473), normalize it
        obj = getattr(obj, "__func__", obj)
        # We need to try and unwrap the function if it's a functools.partial
        # or a funtools.wrapped.
        # We musn't if it's been wrapped with mock.patch (python 2 only)
        if not (isfunction(obj) or isfunction(get_real_func(obj))):
            collector.warn(code="C2", message="cannot collect %r because it is not a function."
                           % name, )
        elif getattr(obj, "__test__", True):
            if is_generator(obj):
                res = Generator(name, parent=collector)
            else:
                res = list(collector._genfunctions(name, obj))
            outcome.force_result(res)
Exemplo n.º 10
0
    def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
        if nodeid is not NOTSET:
            holderobj = node_or_obj
        else:
            holderobj = node_or_obj.obj
            nodeid = node_or_obj.nodeid
        if holderobj in self._holderobjseen:
            return

        self._holderobjseen.add(holderobj)
        autousenames = []
        for name in dir(holderobj):
            # The attribute can be an arbitrary descriptor, so the attribute
            # access below can raise. safe_getatt() ignores such exceptions.
            obj = safe_getattr(holderobj, name, None)
            marker = getfixturemarker(obj)
            if not isinstance(marker, FixtureFunctionMarker):
                # magic globals  with __getattr__ might have got us a wrong
                # fixture attribute
                continue

            if marker.name:
                name = marker.name

            # during fixture definition we wrap the original fixture function
            # to issue a warning if called directly, so here we unwrap it in order to not emit the warning
            # when pytest itself calls the fixture function
            if six.PY2 and unittest:
                # hack on Python 2 because of the unbound methods
                obj = get_real_func(obj)
            else:
                obj = get_real_method(obj, holderobj)

            fixture_def = FixtureDef(
                self,
                nodeid,
                name,
                obj,
                marker.scope,
                marker.params,
                unittest=unittest,
                ids=marker.ids,
            )

            faclist = self._arg2fixturedefs.setdefault(name, [])
            if fixture_def.has_location:
                faclist.append(fixture_def)
            else:
                # fixturedefs with no location are at the front
                # so this inserts the current fixturedef after the
                # existing fixturedefs from external plugins but
                # before the fixturedefs provided in conftests.
                i = len([f for f in faclist if not f.has_location])
                faclist.insert(i, fixture_def)
            if marker.autouse:
                autousenames.append(name)

        if autousenames:
            self._nodeid_and_autousenames.append((nodeid or "", autousenames))
Exemplo n.º 11
0
    def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
        if nodeid is not NOTSET:
            holderobj = node_or_obj
        else:
            holderobj = node_or_obj.obj
            nodeid = node_or_obj.nodeid
        if holderobj in self._holderobjseen:
            return

        self._holderobjseen.add(holderobj)
        autousenames = []
        for name in dir(holderobj):
            # The attribute can be an arbitrary descriptor, so the attribute
            # access below can raise. safe_getatt() ignores such exceptions.
            obj = safe_getattr(holderobj, name, None)
            marker = getfixturemarker(obj)
            if not isinstance(marker, FixtureFunctionMarker):
                # magic globals  with __getattr__ might have got us a wrong
                # fixture attribute
                continue

            if marker.name:
                name = marker.name

            # during fixture definition we wrap the original fixture function
            # to issue a warning if called directly, so here we unwrap it in order to not emit the warning
            # when pytest itself calls the fixture function
            if six.PY2 and unittest:
                # hack on Python 2 because of the unbound methods
                obj = get_real_func(obj)
            else:
                obj = get_real_method(obj, holderobj)

            fixture_def = FixtureDef(
                self,
                nodeid,
                name,
                obj,
                marker.scope,
                marker.params,
                unittest=unittest,
                ids=marker.ids,
            )

            faclist = self._arg2fixturedefs.setdefault(name, [])
            if fixture_def.has_location:
                faclist.append(fixture_def)
            else:
                # fixturedefs with no location are at the front
                # so this inserts the current fixturedef after the
                # existing fixturedefs from external plugins but
                # before the fixturedefs provided in conftests.
                i = len([f for f in faclist if not f.has_location])
                faclist.insert(i, fixture_def)
            if marker.autouse:
                autousenames.append(name)

        if autousenames:
            self._nodeid_and_autousenames.append((nodeid or "", autousenames))
Exemplo n.º 12
0
def test_real_func_loop_limit():
    class Evil:
        def __init__(self):
            self.left = 1000

        def __repr__(self):
            return "<Evil left={left}>".format(left=self.left)

        def __getattr__(self, attr):
            if not self.left:
                raise RuntimeError("it's over")  # pragma: no cover
            self.left -= 1
            return self

    evil = Evil()

    with pytest.raises(
            ValueError,
            match=("could not find real function of <Evil left=800>\n"
                   "stopped at <Evil left=800>"),
    ):
        get_real_func(evil)
Exemplo n.º 13
0
def pytest_pycollect_makeitem(collector, name, obj):
    outcome = yield
    item = outcome.get_result()
    concurrent_mark = getattr(obj, 'concurrent', None)
    if not concurrent_mark:
        concurrent_mark = getattr(obj, 'async', None)
    if isinstance(concurrent_mark, MarkInfo):
        if isinstance(item, Generator):
            obj = get_real_func(obj)
            items = list(collector._genfunctions(name, obj))
            outcome.force_result(items)
        else:
            raise Exception(
                'Attempt to set `concurrent` mark for non generator %s' % name)
Exemplo n.º 14
0
    def formatrepr(self):
        tblines = []
        addline = tblines.append
        stack = [self.request._pyfuncitem.obj]
        stack.extend(map(lambda x: x.func, self.fixturestack))
        msg = self.msg
        if msg is not None:
            # the last fixture raise an error, let's present
            # it at the requesting side
            stack = stack[:-1]
        for function in stack:
            fspath, lineno = getfslineno(function)
            try:
                lines, _ = inspect.getsourcelines(get_real_func(function))
            except (IOError, IndexError, TypeError):
                error_msg = "file %s, line %s: source code not available"
                addline(error_msg % (fspath, lineno + 1))
            else:
                addline("file %s, line %s" % (fspath, lineno + 1))
                for i, line in enumerate(lines):
                    line = line.rstrip()
                    addline("  " + line)
                    if line.lstrip().startswith("def"):
                        break

        if msg is None:
            fm = self.request._fixturemanager
            available = set()
            parentid = self.request._pyfuncitem.parent.nodeid
            for name, fixturedefs in fm._arg2fixturedefs.items():
                faclist = list(fm._matchfactories(fixturedefs, parentid))
                if faclist:
                    available.add(name)
            if self.argname in available:
                msg = " recursive dependency involving fixture '{}' detected".format(
                    self.argname)
            else:
                msg = "fixture '{}' not found".format(self.argname)
            msg += "\n available fixtures: {}".format(", ".join(
                sorted(available)))
            msg += "\n use 'pytest --fixtures [testpath]' for help on them."

        return FixtureLookupErrorRepr(fspath, lineno, tblines, msg,
                                      self.argname)
Exemplo n.º 15
0
def test_real_func_loop_limit():
    class Evil(object):
        def __init__(self):
            self.left = 1000

        def __repr__(self):
            return "<Evil left={left}>".format(left=self.left)

        def __getattr__(self, attr):
            if not self.left:
                raise RuntimeError("its over")
            self.left -= 1
            return self

    evil = Evil()

    with pytest.raises(ValueError):
        res = get_real_func(evil)
        print(res)
Exemplo n.º 16
0
    def formatrepr(self):
        tblines = []
        addline = tblines.append
        stack = [self.request._pyfuncitem.obj]
        stack.extend(map(lambda x: x.func, self.fixturestack))
        msg = self.msg
        if msg is not None:
            # the last fixture raise an error, let's present
            # it at the requesting side
            stack = stack[:-1]
        for function in stack:
            fspath, lineno = getfslineno(function)
            try:
                lines, _ = inspect.getsourcelines(get_real_func(function))
            except (IOError, IndexError, TypeError):
                error_msg = "file %s, line %s: source code not available"
                addline(error_msg % (fspath, lineno + 1))
            else:
                addline("file %s, line %s" % (fspath, lineno + 1))
                for i, line in enumerate(lines):
                    line = line.rstrip()
                    addline("  " + line)
                    if line.lstrip().startswith("def"):
                        break

        if msg is None:
            fm = self.request._fixturemanager
            available = set()
            parentid = self.request._pyfuncitem.parent.nodeid
            for name, fixturedefs in fm._arg2fixturedefs.items():
                faclist = list(fm._matchfactories(fixturedefs, parentid))
                if faclist:
                    available.add(name)
            if self.argname in available:
                msg = " recursive dependency involving fixture '{}' detected".format(
                    self.argname
                )
            else:
                msg = "fixture '{}' not found".format(self.argname)
            msg += "\n available fixtures: {}".format(", ".join(sorted(available)))
            msg += "\n use 'pytest --fixtures [testpath]' for help on them."

        return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname)
Exemplo n.º 17
0
    def _prunetraceback(self, excinfo):
        if hasattr(self, "_obj") and not self.config.option.fulltrace:
            code = _pytest._code.Code(get_real_func(self.obj))
            path, firstlineno = code.path, code.firstlineno
            traceback = excinfo.traceback
            ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
            if ntraceback == traceback:
                ntraceback = ntraceback.cut(path=path)
                if ntraceback == traceback:
                    ntraceback = ntraceback.filter(filter_traceback)
                    if not ntraceback:
                        ntraceback = traceback

            excinfo.traceback = ntraceback.filter()
            # issue364: mark all but first and last frames to
            # only show a single-line message for each frame
            if self.config.option.tbstyle == "auto":
                if len(excinfo.traceback) > 2:
                    for entry in excinfo.traceback[1:-1]:
                        entry.set_repr_style("short")
Exemplo n.º 18
0
    def _prunetraceback(self, excinfo):
        if hasattr(self, "_obj") and not self.config.option.fulltrace:
            code = _pytest._code.Code(get_real_func(self.obj))
            path, firstlineno = code.path, code.firstlineno
            traceback = excinfo.traceback
            ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
            if ntraceback == traceback:
                ntraceback = ntraceback.cut(path=path)
                if ntraceback == traceback:
                    ntraceback = ntraceback.filter(filter_traceback)
                    if not ntraceback:
                        ntraceback = traceback

            excinfo.traceback = ntraceback.filter()
            # issue364: mark all but first and last frames to
            # only show a single-line message for each frame
            if self.config.option.tbstyle == "auto":
                if len(excinfo.traceback) > 2:
                    for entry in excinfo.traceback[1:-1]:
                        entry.set_repr_style("short")
Exemplo n.º 19
0
def pytest_pycollect_makeitem(collector, name, obj):
    """
    @see PyCollector._genfunctions
    @see _pytest.python
    """

    if safe_isclass(obj):
        if collector.istestclass(obj, name) or (issubclass(obj, AbstractTestCase) and obj != AbstractTestCase):
            if hasattr(pytest.Class, "from_parent"):
                return pytest.Class.from_parent(collector, name=name)
            else:
                return pytest.Class(name, parent=collector)
    else:
        obj = getattr(obj, "__func__", obj)
        if (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))) and getattr(obj, "__test__", True) and isinstance(collector, pytest.Instance) and hasattr(obj, "pytestmark"):
            if not is_generator(obj):
                return list(collector._genfunctions(name, obj))
            else:
                return []
        else:
            return []
Exemplo n.º 20
0
def wrap_fixture(
    fixturefunc: Callable,
    wrapped_param: str = 'wrapped',
    ignore: Union[str, Iterable[str]] = (),
) -> Callable[[Callable], Callable]:
    """Wrap a fixture function, extending its argspec w/ the decorated method

    pytest will prune the fixture dependency graph of any unneeded fixtures. It
    does this by reading the expected arg names of fixtures. When wrapping a
    fixture function, merely currying along **kwargs will cripple pytest's
    pruning.

    This method retains the arg names from the original fixture function, and
    returns a wrapper method that includes those original arg names, as well as
    any fixtures requested by the decorated function.

    The decorated method will be passed a wrapper of the passed fixturefunc
    that can be called with no arguments — the fixtures it requested will
    receive automagical defaults, though these may be overridden. The argument
    name of this wrapped fixturefunc may be customized with the `wrapped_param`
    arg, so as to avoid any collision with other fixture names.

    Example (contrived):

        bare_user = lambda_fixture(lambda user_factory: user_factory(
            username='******',
            password='******',
        ))

        @pytest.fixture
        @wrap_fixture(bare_user)
        def admin_user(team, wrapped):
            user = wrapped()
            team.add_member(user, role_id=TeamRole.Roles.ADMIN)
            return user

    :param fixturefunc:
        The fixture function to wrap

    :param wrapped_param:
        Name of parameter to pass the wrapped fixturefunc as

    :param ignore:
        Name of parameter(s) from fixturefunc to not include in wrapping
        fixture's args (and thus not request as fixtures from pytest)

    """

    if isinstance(ignore, str):
        ignore = (ignore,)

    fixturefunc = get_real_func(fixturefunc)

    def decorator(fn: Callable):
        decorated_arg_names = set(getfuncargnames(fn))
        if wrapped_param not in decorated_arg_names:
            raise TypeError(
                f'The decorated method must include an arg named {wrapped_param} '
                f'as the wrapped fixture func.')

        # Don't include the wrapped param in the argspec we expose to pytest
        decorated_arg_names -= {wrapped_param}

        fixture_arg_names = set(getfuncargnames(fixturefunc)) - set(ignore)
        all_arg_names = fixture_arg_names | decorated_arg_names | {'request'}

        def extension_impl(**all_args):
            request = all_args['request']

            ###
            # kwargs requested by the wrapped fixture
            #
            fixture_args = {
                name: value
                for name, value in all_args.items()
                if name in fixture_arg_names
            }

            ###
            # kwargs requested by the decorated method
            #
            decorated_args = {
                name: value
                for name, value in all_args.items()
                if name in decorated_arg_names
            }

            @functools.wraps(fixturefunc)
            def wrapped(**overridden_args):
                kwargs = {
                    **fixture_args,
                    **overridden_args,
                }
                return call_fixture_func(fixturefunc, request, kwargs)

            decorated_args[wrapped_param] = wrapped
            return call_fixture_func(fn, request, decorated_args)

        extension = build_wrapped_method(fn.__name__, all_arg_names, extension_impl)
        return extension

    return decorator