# Source code for _pytest.recwarn

    """ recording warnings during test function execution. """

    import inspect
    import py
    import sys
    import warnings
    import pytest

    @pytest.yield_fixture
    def recwarn(request):
        """Return a WarningsRecorder instance that provides these methods:

        * ``pop(category=None)``: return last warning matching the category.
        * ``clear()``: clear list of warnings

        See http://docs.python.org/library/warnings.html for information
        on warning categories.
        """
        wrec = WarningsRecorder()
        with wrec:
            warnings.simplefilter('default')
            yield wrec

    def pytest_namespace():
        return {'deprecated_call': deprecated_call,
                'warns': warns}

    def deprecated_call(func, *args, **kwargs):
        """ assert that calling ``func(*args, **kwargs)`` triggers a
        ``DeprecationWarning`` or ``PendingDeprecationWarning``.

        Note: we cannot use WarningsRecorder here because it is still subject
        to the mechanism that prevents warnings of the same type from being
        triggered twice for the same module. See #1190.
        """
        categories = []

        def warn_explicit(message, category, *args, **kwargs):
            categories.append(category)
            old_warn_explicit(message, category, *args, **kwargs)

        def warn(message, category=None, *args, **kwargs):
            if isinstance(message, Warning):
                categories.append(message.__class__)
            else:
                categories.append(category)
            old_warn(message, category, *args, **kwargs)

        old_warn = warnings.warn
        old_warn_explicit = warnings.warn_explicit
        warnings.warn_explicit = warn_explicit
        warnings.warn = warn
        try:
            ret = func(*args, **kwargs)
        finally:
            warnings.warn_explicit = old_warn_explicit
            warnings.warn = old_warn
        deprecation_categories = (DeprecationWarning, PendingDeprecationWarning)
        if not any(issubclass(c, deprecation_categories) for c in categories):
            __tracebackhide__ = True
            raise AssertionError("%r did not produce DeprecationWarning" % (func,))
        return ret

    def warns(expected_warning, *args, **kwargs):
        """Assert that code raises a particular class of warning.

        Specifically, the input @expected_warning can be a warning class or
        tuple of warning classes, and the code must return that warning
        (if a single class) or one of those warnings (if a tuple).

        This helper produces a list of ``warnings.WarningMessage`` objects,
        one for each warning raised.

        This function can be used as a context manager, or any of the other ways
        ``pytest.raises`` can be used::

            >>> with warns(RuntimeWarning):
            ...    warnings.warn("my warning", RuntimeWarning)
        """
        wcheck = WarningsChecker(expected_warning)
        if not args:
            return wcheck
        elif isinstance(args[0], str):
            code, = args
            assert isinstance(code, str)
            frame = sys._getframe(1)
            loc = frame.f_locals.copy()
            loc.update(kwargs)

            with wcheck:
                code = py.code.Source(code).compile()
                py.builtin.exec_(code, frame.f_globals, loc)
        else:
            func = args[0]
            with wcheck:
                return func(*args[1:], **kwargs)

    class RecordedWarning(object):
        def __init__(self, message, category, filename, lineno, file, line):
            self.message = message
            self.category = category
            self.filename = filename
            self.lineno = lineno
            self.file = file
            self.line = line

    class WarningsRecorder(object):
        """A context manager to record raised warnings.

        Adapted from `warnings.catch_warnings`.
        """

        def __init__(self, module=None):
            self._module = sys.modules['warnings'] if module is None else module
            self._entered = False
            self._list = []

        @property
        def list(self):
            """The list of recorded warnings."""
            return self._list

        def __getitem__(self, i):
            """Get a recorded warning by index."""
            return self._list[i]

        def __iter__(self):
            """Iterate through the recorded warnings."""
            return iter(self._list)

        def __len__(self):
            """The number of recorded warnings."""
            return len(self._list)

        def pop(self, cls=Warning):
            """Pop the first recorded warning, raise exception if not exists."""
            for i, w in enumerate(self._list):
                if issubclass(w.category, cls):
                    return self._list.pop(i)
            __tracebackhide__ = True
            raise AssertionError("%r not found in warning list" % cls)

        def clear(self):
            """Clear the list of recorded warnings."""
            self._list[:] = []

        def __enter__(self):
            if self._entered:
                __tracebackhide__ = True
                raise RuntimeError("Cannot enter %r twice" % self)
            self._entered = True
            self._filters = self._module.filters
            self._module.filters = self._filters[:]
            self._showwarning = self._module.showwarning

            def showwarning(message, category, filename, lineno,
                            file=None, line=None):
                self._list.append(RecordedWarning(
                    message, category, filename, lineno, file, line))

                # still perform old showwarning functionality
                self._showwarning(
                    message, category, filename, lineno, file=file, line=line)

            self._module.showwarning = showwarning

            # allow the same warning to be raised more than once
            self._module.simplefilter('always', append=True)

            return self

        def __exit__(self, *exc_info):
            if not self._entered:
                __tracebackhide__ = True
                raise RuntimeError("Cannot exit %r without entering first" % self)
            self._module.filters = self._filters
            self._module.showwarning = self._showwarning

    class WarningsChecker(WarningsRecorder):
        def __init__(self, expected_warning=None, module=None):
            super(WarningsChecker, self).__init__(module=module)

            msg = ("exceptions must be old-style classes or "
                   "derived from Warning, not %s")
            if isinstance(expected_warning, tuple):
                for exc in expected_warning:
                    if not inspect.isclass(exc):
                        raise TypeError(msg % type(exc))
            elif inspect.isclass(expected_warning):
                expected_warning = (expected_warning,)
            elif expected_warning is not None:
                raise TypeError(msg % type(expected_warning))

            self.expected_warning = expected_warning

        def __exit__(self, *exc_info):
            super(WarningsChecker, self).__exit__(*exc_info)

            # only check if we're not currently handling an exception
            if all(a is None for a in exc_info):
                if self.expected_warning is not None:
                    if not any(r.category in self.expected_warning for r in self):
                        __tracebackhide__ = True
                        pytest.fail("DID NOT WARN")