#!/usr/bin/env python3
# --------------------( LICENSE                           )--------------------
# Copyright (c) 2014-2021 Beartype authors.
# See "LICENSE" for further details.

'''
**:mod:`pytest` test-marking decorators.**

This submodule provides decorators *not* conditionally marking their decorated
tests as either failed, parametrized, or skipped.
'''

# ....................{ IMPORTS                           }....................
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# WARNING: To raise human-readable test errors, avoid importing from
# package-specific submodules at module scope.
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
import pytest
from beartype_test.util.pytroar import BeartypeTestMarkException

# ....................{ MARKS                             }....................
noop = pytest.mark.noop
'''
Preserve the decorated test or test parameter as is with *no* modification.

Caveats
----------
This decorator has the unintended side effect of also marking all tests and
test parameters decorated by this decorated with the ``noop`` tag. This
harmless albeit unfortunate side effect is the result of the :mod:`pytest.mark`
API, which strictly requires that *all* decorators passed to the ``marks``
parameter of the :mod:`pytest.param` function be strictly generated by the
:mod:`pytest.mark` API, which then imposes this seemingly arbitrary constraint.

In other words, there's absolutely nothing to see here, folks.
'''


def ignore_warnings(warning_cls: type) -> 'Callable':
    '''
    Decorate the passed test to ignore all warnings subclassing the passed
    :class:`Warning` class.

    Caveats
    ----------
    **This high-level decorator should always be called in lieu of the
    low-level** :func:`pytest.mark.filterwarnings` **decorator,** whose syntax
    is fragile, poorly documented, and likely to silently fail.

    Parameters
    ----------
    warning_cls : type
        :class:`Warning` class to be ignored when running the decorated test.

    Returns
    ----------
    Callable
        This test decorated to ignore all warnings of this class.

    Raises
    ----------
    BeartypeTestMarkException
        If this object is either:

        * *Not* a class.
        * A class that is either *not* the builtin :class:`Warning` class or a
          subclass of that class.
    '''

    # Defer heavyweight imports.
    from beartype._util.utilobject import get_object_type_name

    # If this object is *NOT* a class, raise an exception.
    if not isinstance(warning_cls, type):
        raise BeartypeTestMarkException(f'{repr(warning_cls)} not type.')
    # Else, this object is a class.
    #
    # If this class is *NOT* a "Warning" subclass, raise an exception.
    if not issubclass(warning_cls, Warning):
        raise BeartypeTestMarkException(
            f'{repr(warning_cls)} not {repr(Warning)} subclass.')
    # Else, this class is a "Warning" subclass.

    # Fully-qualified name of this class.
    warning_classname = get_object_type_name(warning_cls)

    # Return the low-level pytest mark decorator ignoring all warnings of this
    # "Warning" subclass with a filter adhering to Python's peculiar warning
    # filter syntax. See also:
    #     https://docs.python.org/3/library/warnings.html#describing-warning-filters
    return pytest.mark.filterwarnings(f'ignore::{warning_classname}')
